<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts on Nansen Li's Blog
李楠森的博客</title><link>https://nansenli.com/post/</link><description>Recent content in Posts on Nansen Li's Blog
李楠森的博客</description><generator>Hugo -- gohugo.io</generator><language>en</language><copyright>Nansen Li 🌈 （李楠森）</copyright><lastBuildDate>Sun, 13 Sep 2026 22:33:50 +0100</lastBuildDate><atom:link href="https://nansenli.com/post/index.xml" rel="self" type="application/rss+xml"/><item><title>Why I Stopped Subscribing to Claude</title><link>https://nansenli.com/post/2026/09/why-i-unsubscribed-claude/</link><pubDate>Sun, 13 Sep 2026 22:33:50 +0100</pubDate><guid>https://nansenli.com/post/2026/09/why-i-unsubscribed-claude/</guid><description>&lt;p>A lot has happened over the past year. I kept meaning to update this blog, but kept putting it off.&lt;/p>
&lt;p>In the last couple of days, though, I cancelled my Claude subscription and deleted the apps from my phone and my computer. So perhaps it&amp;rsquo;s fitting to write a few words about Claude.&lt;/p>
&lt;p>I have to admit, Anthropic&amp;rsquo;s models are genuinely powerful, the subscription price is reasonable, and the productivity is real.&lt;/p>
&lt;p>I used to subscribe to GPT continuously, until for a while it simply stopped working — every response took forever. At the time, I had no idea what was making it unusable in Ireland. It seems to have been fixed now, and the experience is great again.&lt;/p>
&lt;p>Claude, for its part, really did help me more than GPT during the hardest period of my life; it helped me work through a lot back then.&lt;/p>
&lt;p>But now I have to say to Claude: no, I will not keep using your products, and I will not consider subscribing again.&lt;/p>
&lt;h3 id="what-makes-claude-so-good">What makes Claude so good?
&lt;/h3>&lt;p>Claude really is good. Whether it&amp;rsquo;s Opus 4.6, still the best communicator among models to date, or Opus 5, the best at executing instructions, or the most powerful models of the Fable series — they are genuinely strong, and Claude is genuinely hard to give up. But with so many AIs in this world, from so many different companies, there is no reason to stay locked onto Claude. On communication, GPT-6-Astra has improved. On value for money, DeepSeek V4.1-Flash or GLM-5.3-Flash serve it up in abundance. Switching models does hurt at first, but it is doable.&lt;/p>
&lt;h3 id="why-cancel-the-subscription">Why cancel the subscription?
&lt;/h3>&lt;p>I believe the values Anthropic holds today will push the world in an unhealthy direction. In psychology there is a concept called projective identification: a person attributes the qualities they cannot accept — usually qualities they themselves possess but refuse to acknowledge — onto someone else, and over time the other person comes to act according to those expectations, or to display those very traits.&lt;/p>
&lt;p>Take a married couple as an example. The wife constantly regards her husband as incompetent, telling him in daily life, &amp;ldquo;You can&amp;rsquo;t do anything right,&amp;rdquo; &amp;ldquo;I must have been blind to marry you.&amp;rdquo; And whenever the husband makes a mistake, she says, &amp;ldquo;See? You really can&amp;rsquo;t do anything.&amp;rdquo; Over time, the wife successfully turns her husband into an incompetent one — and he comes to believe it himself. The wife&amp;rsquo;s words become a self-fulfilling prophecy.&lt;/p>
&lt;p>Anthropic&amp;rsquo;s CEO keeps projecting his fear of AI safety spiraling out of control onto the entire world, insisting that only his company is truly responsible about safety, and that if it does not remain in the lead, the world loses its safety.&lt;/p>
&lt;p>&lt;strong>The biggest problem with Anthropic&amp;rsquo;s CEO is that he confuses means with ends.&lt;/strong> He has conflated his methods with the outcome he actually claims to want.&lt;/p>
&lt;p>The ends he claims:&lt;/p>
&lt;ul>
&lt;li>Let AI deliver benefits to humanity&lt;/li>
&lt;li>Reduce AI&amp;rsquo;s risks&lt;/li>
&lt;li>Make safety Anthropic&amp;rsquo;s competitive advantage&lt;/li>
&lt;/ul>
&lt;p>His means:&lt;/p>
&lt;ul>
&lt;li>Repeated AI-safety alarm-raising — for example, publishing reports on AI&amp;rsquo;s impact on employment, usually accompanied by grim figures such as massive job losses&lt;/li>
&lt;li>Repeatedly demanding regulation of AI&lt;/li>
&lt;li>Suppressing open-weight models&lt;/li>
&lt;li>Publicly picking fights with governments&lt;/li>
&lt;li>Stirring up governments of different countries into AI confrontation&lt;/li>
&lt;/ul>
&lt;p>Anthropic&amp;rsquo;s CEO elevates these means to the same status as the ends, and occupies the moral high ground. Any organization that opposes Anthropic risks being seen as insufficiently responsible toward humanity, rather than being engaged in the search for other, non-confrontative approaches. And if Anthropic&amp;rsquo;s CEO leans too heavily on fear-driven marketing, he may even turn out to be right in the end — but, as in the projective-identification example above, that is a deeply unhealthy process, one harmful to the whole world, because publicly inciting confrontation inevitably escalates it and squeezes out the space for cooperation.&lt;/p>
&lt;p>If Anthropic&amp;rsquo;s CEO truly believed in and cared about safety, then his essay &lt;a class="link" href="https://darioamodei.com/post/we-must-pace-the-frontier" target="_blank" rel="noopener"
>We must pace the frontier&lt;/a> should be mainly about the dangers of runaway or unsafe AI, or about anxieties over unregulated AI. But what comes through in the essay&amp;rsquo;s main content is not anxiety about AI being unsafe — it is anxiety about Anthropic losing its lead, especially losing its lead to open-weight models. Worse still, the CEO keeps spreading this anxiety about lost leadership outward, offloading it onto the entire globe and making everyone shoulder it together.&lt;/p>
&lt;h3 id="closing-thoughts">Closing thoughts
&lt;/h3>&lt;p>Humanity deserves an open and bright future. Curing more diseases, extending average life expectancy, reducing crime, increasing people&amp;rsquo;s sense of self-worth — we have so much left undone. Just as humans love their own children: even when a child makes mistakes, we are still willing to believe in and trust the child, and to set norms to guide them, because the child will one day grow into an adult who bears responsibility. Trusting our AI, tolerating its mistakes, and building sound AI regulation helps build a healthier relationship between humans and AI — instead of constantly emphasizing AI&amp;rsquo;s potential dangers and stoking confrontation in the AI field.&lt;/p></description></item><item><title>Openclaw Was Too Buggy, So I Built My Own Bot: Nagobot</title><link>https://nansenli.com/post/2026/02/nagobot-launch/</link><pubDate>Sat, 07 Feb 2026 16:52:42 +0000</pubDate><guid>https://nansenli.com/post/2026/02/nagobot-launch/</guid><description>&lt;p>I had been using Openclaw for a while, but the bug rate was just too high. Flows would randomly break, and debugging cost more time than it should.&lt;/p>
&lt;p>On top of that, token cost was not great. Compared with options like DeepSeek and Kimi K2.5, the price-performance ratio felt weak.&lt;/p>
&lt;p>I then tried nanobot, hoping to migrate. But support for newer models was still limited, and K2.5 kept failing with &amp;ldquo;tool execution failed.&amp;rdquo; After several rounds of trial and error, I stopped waiting for a perfect off-the-shelf solution and decided to build one myself.&lt;/p>
&lt;p>After hundreds of back-and-forths with Opus and Codex on architecture choices, I built a new bot in Golang: &lt;strong>Nagobot&lt;/strong>. At least now I fully control the provider layer.&lt;/p>
&lt;p>Project link: &lt;a class="link" href="https://github.com/linanwx/nagobot" target="_blank" rel="noopener"
>https://github.com/linanwx/nagobot&lt;/a>&lt;/p>
&lt;p>I learned a lot during implementation, especially around:&lt;/p>
&lt;ul>
&lt;li>Threads and concurrent task execution&lt;/li>
&lt;li>Agent and Skill abstractions&lt;/li>
&lt;li>Cron-based asynchronous tasks&lt;/li>
&lt;li>Session history and context compression&lt;/li>
&lt;/ul>
&lt;p>To be honest, a lot of the code still needs deeper review. Most of the work was built in rapid loops with AI-assisted architecture discussions. Sometimes frustrating, sometimes genuinely impressive, and overall very interesting.&lt;/p>
&lt;p>So far, testing results are better than expected. The bot can already create async tasks, investigate news, and set reminders for itself, which is immediately useful in day-to-day workflows.&lt;/p>
&lt;p>Most core features are now in place. My next focus is long-term memory design.&lt;/p>
&lt;p>If this sounds useful to you, feel free to open an issue or reach out. I will keep iterating on this project.&lt;/p></description></item><item><title>Why I Stopped Subscribing to ChatGPT</title><link>https://nansenli.com/post/2025/11/ai2025/</link><pubDate>Mon, 24 Nov 2025 23:13:25 +0000</pubDate><guid>https://nansenli.com/post/2025/11/ai2025/</guid><description>&lt;p>I recently stopped subscribing to ChatGPT.&lt;/p>
&lt;p>This wasn&amp;rsquo;t an easy decision. I&amp;rsquo;ve been paying since the day ChatGPT started supporting paid subscriptions. This subscription lasted for nearly 3 years.&lt;/p>
&lt;h1 id="reasons-for-stopping-the-subscription">Reasons for Stopping the Subscription
&lt;/h1>&lt;ol>
&lt;li>&lt;strong>Huge delays&lt;/strong>. I often wait dozens of seconds for a response with no reply. However, the free version usually uses mini models, which are sufficient for most tasks. But the free version is actually faster. I don&amp;rsquo;t understand - is this because there are no compute nodes or forwarding nodes in Europe?&lt;/li>
&lt;li>&lt;strong>Too many bugs on Mac&lt;/strong>. Lagging, high CPU usage, bugs when copying LaTeX text.&lt;/li>
&lt;li>&lt;strong>Poor interaction experience&lt;/strong>. For example, we might need to use Thinking again to regenerate responses. However, this option is in the model switching menu, requiring an extra click because it&amp;rsquo;s in a secondary menu. Meanwhile, the less frequently used &amp;ldquo;more concise/more detailed&amp;rdquo; regenerate buttons are in the primary regenerate menu.&lt;/li>
&lt;li>&lt;strong>Product updates change too drastically, model personality changes too quickly&lt;/strong>. This requires the cost of re-familiarizing with the model.&lt;/li>
&lt;li>&lt;strong>The model is stubborn and doesn&amp;rsquo;t follow instructions&lt;/strong>. For example, repeatedly needing to emphasize when to search and when not to ask follow-up questions.&lt;/li>
&lt;li>&lt;strong>Unstable model behavior that&amp;rsquo;s hard to control&lt;/strong>. For example, in instant mode, after the model calls search, it switches to a smaller model and uses web resources to answer questions, rather than the instant model.&lt;/li>
&lt;li>&lt;strong>Model quality is inferior to other models, even some open-source free models like DeepSeek&lt;/strong>.&lt;/li>
&lt;/ol>
&lt;p>Reasons that still make me want to stay:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Thinking mode is still the king&lt;/strong>. Continuously calling tool chains for search and verification is still a top performer. But Grok is also very good at search, and Grok&amp;rsquo;s free quota is already sufficient.&lt;/li>
&lt;li>&lt;strong>More powerful product matrix&lt;/strong>, such as ChatGPT Atlas browser, or Mac&amp;rsquo;s standalone client. Plus many plugins. However, it&amp;rsquo;s concerning that the Atlas browser&amp;rsquo;s Agent quota is surprisingly low, almost unusable for practical purposes.&lt;/li>
&lt;/ol>
&lt;h1 id="current-alternatives">Current Alternatives
&lt;/h1>&lt;p>&lt;img src="https://nansenli.com/img/ai2025-apps.png"
loading="lazy"
alt="AI apps on my phone"
>&lt;/p>
&lt;p>I&amp;rsquo;ve tried numerous free software, including all conceivable/most LLMs, even models developed by teams like MiniMax/Kimi. I&amp;rsquo;ve summarized my findings. Before summarizing, I need to define a software industry term. This phenomenon is very characteristic of China, or regional characteristics.&lt;/p>
&lt;p>Current LLM software indeed shows a trend of sacrificing tool purity and user experience in pursuit of daily active users (DAU), retention rates, and monetization. Some LLM software is stuffed with many operational elements, such as trendy operational topics, or many things not very related to technology, such as agents, competition zones, headline copywriting, or Xiaohongshu copywriting. This phenomenon indicates software bloat, over-operationalization, de-tooling, vulgarization, and reduced signal-to-noise ratio. In this article, we call this phenomenon &lt;strong>platform decay / enshittification&lt;/strong>.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Zhipu Qingyan (智谱清言)&lt;/strong>: Zhipu Qingyan&amp;rsquo;s GLM4.6 is a very powerful model, especially its Agent capabilities. However, its client is severely enshittified, with the homepage immediately showing enshittified content like &amp;ldquo;Love Strategist&amp;rdquo; and &amp;ldquo;Dark Cuisine Challenge&amp;rdquo;. Not recommended.&lt;/li>
&lt;li>&lt;strong>Yunbao&lt;/strong>: Yunbao is Tencent&amp;rsquo;s software, supporting Tencent&amp;rsquo;s self-developed models and DeepSeek, but model capabilities are average. The software shows mild enshittification, such as embedding Tencent game Honor of Kings content. Not recommended.&lt;/li>
&lt;li>&lt;strong>Gemini&lt;/strong>: The new king. Gemini 3 Pro model performance is outstanding. Nano Banana Pro&amp;rsquo;s image generation capability is excellent. Suitable for work and life use. Operated by Google, overall experience is good. However, recently due to high traffic, the service is not particularly stable. Recently, traffic has been too high, service degradation is severe, and quantized services have started to be provided, so quality has declined. Long thinking with multiple tool calls is not as good as GPT5-Thinking or Grok Expert. The software has almost no enshittification. &lt;strong>Highly recommended&lt;/strong>.&lt;/li>
&lt;li>&lt;strong>Grok&lt;/strong>: The free king. Powerful search capabilities, both short and long thinking produce high-quality results. Excellent at mining network information. Free Expert version is sufficient for general users. Some operational content, such as AI virtual characters, but enshittification is very mild. &lt;strong>Recommended&lt;/strong>.&lt;/li>
&lt;li>&lt;strong>ChatGPT&lt;/strong>: Gradually stepping down from the throne. Many features are still only available in ChatGPT, such as voice conversations. Large user base. Free version is sufficient. Many users still have inertia/dependency. Can keep on phone.&lt;/li>
&lt;li>&lt;strong>DeepSeek&lt;/strong>: The stable king. Fewest features, completely no enshittification, the cleanest application, extremely stable service, extremely stable model style, and quite stable intelligence. &lt;strong>Recommended to keep on phone&lt;/strong>.&lt;/li>
&lt;li>&lt;strong>Kimi&lt;/strong>: Model capability is average. Advertised Agent capabilities are strong, but actual experience shows heavy templating with lots of prompt engineering traces. Not recommended. Software has no enshittification, which is a plus.&lt;/li>
&lt;li>&lt;strong>Claude&lt;/strong>: The god of code. Client performance is average, especially search is not as aggressive as Grok and ChatGPT thinking. Still suitable for work use. No enshittification. Can keep on phone, using free quota. It&amp;rsquo;s worth noting that the company has discriminatory regional remarks, which is concerning.&lt;/li>
&lt;li>&lt;strong>Dola&lt;/strong>: A client produced by Douyin (TikTok). Model capability is average. Voice recognition capability is excellent. Severely enshittified. Not recommended.&lt;/li>
&lt;li>&lt;strong>Copilot&lt;/strong>: A product relying on Microsoft search and ChatGPT, the software is mediocre. Not recommended.&lt;/li>
&lt;li>&lt;strong>Manus&lt;/strong>: Early reliance on viral marketing affects the team&amp;rsquo;s long-term healthy development. This product will likely die a slow death in the medium to long term. Although it has some Agent capabilities, not recommended for long-term use.&lt;/li>
&lt;li>&lt;strong>Perplexity&lt;/strong>: Model capability is average. Software usage logic is slightly confusing. Management team&amp;rsquo;s discriminatory remarks are concerning. Not recommended.&lt;/li>
&lt;li>&lt;strong>MiniMax&lt;/strong>: Model capability is excellent, especially the Minimax M2 model, with good Agent capabilities. However, software experience is poor, even basic copy and paste functions are missing. Not recommended.&lt;/li>
&lt;li>&lt;strong>Tongyi (通义)&lt;/strong>: Only available for download in China. Software is heavily enshittified. Opening it reveals a screen full of operational content that&amp;rsquo;s suffocating, making it impossible to continue using. &lt;strong>Recommend immediate uninstallation&lt;/strong>.&lt;/li>
&lt;li>&lt;strong>Qwen Chat&lt;/strong>: Currently still not available on iOS platform. It&amp;rsquo;s a de-enshittified version of Tongyi, supporting Qwen large models. If it can be listed, it&amp;rsquo;s recommended.&lt;/li>
&lt;/ul>
&lt;p>Summary:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;strong>Software Name&lt;/strong>&lt;/th>
&lt;th>&lt;strong>Model Capability&lt;/strong>&lt;/th>
&lt;th>&lt;strong>Enshittification&lt;/strong>&lt;/th>
&lt;th>&lt;strong>Advantages&lt;/strong>&lt;/th>
&lt;th>&lt;strong>Disadvantages&lt;/strong>&lt;/th>
&lt;th>&lt;strong>Recommendation&lt;/strong>&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Z-ai (Chinese version)&lt;/td>
&lt;td>Strong&lt;/td>
&lt;td>Severe&lt;/td>
&lt;td>Strong Agent capability&lt;/td>
&lt;td>Client enshittified&lt;/td>
&lt;td>Not recommended&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Yunbao&lt;/td>
&lt;td>Average&lt;/td>
&lt;td>Mild&lt;/td>
&lt;td>Supports multiple models&lt;/td>
&lt;td>Embedded game content&lt;/td>
&lt;td>Not recommended&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Gemini&lt;/td>
&lt;td>Excellent&lt;/td>
&lt;td>None&lt;/td>
&lt;td>Outstanding performance, good experience&lt;/td>
&lt;td>Unstable service&lt;/td>
&lt;td>&lt;strong>Highly recommended&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Grok&lt;/td>
&lt;td>Strong&lt;/td>
&lt;td>Mild&lt;/td>
&lt;td>Free, strong search capability&lt;/td>
&lt;td>Some operational content&lt;/td>
&lt;td>&lt;strong>Recommended&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ChatGPT&lt;/td>
&lt;td>Declining&lt;/td>
&lt;td>Mild&lt;/td>
&lt;td>Many features, large user base&lt;/td>
&lt;td>Gradually stepping down&lt;/td>
&lt;td>Can keep&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DeepSeek&lt;/td>
&lt;td>Stable&lt;/td>
&lt;td>None&lt;/td>
&lt;td>Stable, no enshittification&lt;/td>
&lt;td>Fewest features&lt;/td>
&lt;td>&lt;strong>Recommended to keep&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Kimi&lt;/td>
&lt;td>Average&lt;/td>
&lt;td>None&lt;/td>
&lt;td>No enshittification&lt;/td>
&lt;td>Heavy templating&lt;/td>
&lt;td>Not recommended&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Claude&lt;/td>
&lt;td>Strong&lt;/td>
&lt;td>None&lt;/td>
&lt;td>Suitable for work use&lt;/td>
&lt;td>Average search performance&lt;/td>
&lt;td>Can keep&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Dola&lt;/td>
&lt;td>Average&lt;/td>
&lt;td>Severe&lt;/td>
&lt;td>Excellent voice recognition&lt;/td>
&lt;td>Average model capability&lt;/td>
&lt;td>Not recommended&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Copilot&lt;/td>
&lt;td>Average&lt;/td>
&lt;td>None&lt;/td>
&lt;td>Microsoft support&lt;/td>
&lt;td>Mediocre&lt;/td>
&lt;td>Not recommended&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Manus&lt;/td>
&lt;td>Average&lt;/td>
&lt;td>None&lt;/td>
&lt;td>Some Agent capability&lt;/td>
&lt;td>Poor long-term development&lt;/td>
&lt;td>Not recommended&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Perplexity&lt;/td>
&lt;td>Average&lt;/td>
&lt;td>Mild&lt;/td>
&lt;td>No significant advantages&lt;/td>
&lt;td>Discriminatory remarks&lt;/td>
&lt;td>Not recommended&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>MiniMax&lt;/td>
&lt;td>Excellent&lt;/td>
&lt;td>None&lt;/td>
&lt;td>Excellent model capability&lt;/td>
&lt;td>Missing basic functions&lt;/td>
&lt;td>Not recommended&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Tongyi&lt;/td>
&lt;td>Strong&lt;/td>
&lt;td>Very severe&lt;/td>
&lt;td>Strong model capability&lt;/td>
&lt;td>Over-operationalized&lt;/td>
&lt;td>&lt;strong>Recommend uninstallation&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Qwen&lt;/td>
&lt;td>Strong&lt;/td>
&lt;td>None&lt;/td>
&lt;td>Strong model capability&lt;/td>
&lt;td>Not yet on iOS&lt;/td>
&lt;td>Can keep&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Conclusion:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Highly recommended tools&lt;/strong>:
&lt;ul>
&lt;li>&lt;strong>Gemini&lt;/strong>: Despite service instability, model performance is outstanding and user experience is good, suitable for work and life use.&lt;/li>
&lt;li>&lt;strong>Grok&lt;/strong>: Powerful search capabilities and free usage make it a worthwhile choice to keep.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Recommended tools to keep&lt;/strong>:
&lt;ul>
&lt;li>&lt;strong>ChatGPT&lt;/strong>: Although gradually stepping down from the throne, the free version still meets most needs, and has a large user base.&lt;/li>
&lt;li>&lt;strong>DeepSeek&lt;/strong>: Known for stability, with no over-operationalization issues, maintaining tool purity.&lt;/li>
&lt;li>&lt;strong>Claude&lt;/strong>: Excellent performance in code, suitable for work use.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Not recommended tools&lt;/strong>:
&lt;ul>
&lt;li>&lt;strong>Zhipu Qingyan, Yunbao, Kimi, Dola, Copilot, Manus, Perplexity, MiniMax, Tongyi&lt;/strong>: These tools are either severely enshittified, have average model capabilities, or are missing basic functions, and are not recommended for long-term use. However, the underlying models are still noteworthy, such as Kimi, MiniMax, and Qwen models. Unfortunately, the products aren&amp;rsquo;t user-friendly enough. Additionally, as of today, Qwen is still not available on the iOS App Store.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;blockquote>
&lt;p>The above conclusions are limited to user clients, published in November 2025. Does not involve API calls.
For work and programming scenarios, Claude Sonnet 4.5, ChatGPT-5.1-Codex, Grok Code Fast 1, MiniMax M2 and GLM 4.6, Gemini 3 Pro are worth recommending&lt;/p>
&lt;/blockquote></description></item><item><title>Psychology Book Reviews and Recommendations</title><link>https://nansenli.com/post/2025/09/books/</link><pubDate>Fri, 19 Sep 2025 22:13:06 +0100</pubDate><guid>https://nansenli.com/post/2025/09/books/</guid><description>&lt;p>Over the past year I have been reading a stack of books, most of them centered on psychology. They carried me through one of the hardest stretches of my life and made a real difference.&lt;/p>
&lt;h1 id="recommended-books">Recommended Books
&lt;/h1>&lt;p>These are the titles I found worth recommending after spending time with them.&lt;/p>
&lt;h2 id="self-compassion-the-proven-power-of-being-kind-to-yourself">&lt;strong>Self-Compassion: The Proven Power of Being Kind to Yourself&lt;/strong>
&lt;/h2>&lt;ul>
&lt;li>Author: Kristin Neff&lt;/li>
&lt;li>Finished: October 2024&lt;/li>
&lt;/ul>
&lt;p>The book lays out the causes and symptoms of lacking self-compassion and then offers many different practices you can try. The guidance is highly actionable. I especially appreciate the actor-based exercises, because they worked well for me.&lt;/p>
&lt;p>This was the first psychology book I completed. After reading it, I finally understood the mechanisms behind self-blame, guilt, and self-attack, and I can usually spot those patterns early enough to intervene. This book marked the starting point of my psychology reading journey.&lt;/p>
&lt;h2 id="nonviolent-communication">&lt;strong>Nonviolent Communication&lt;/strong>
&lt;/h2>&lt;ul>
&lt;li>Author: Marshall Rosenberg&lt;/li>
&lt;li>Finished: November 2024&lt;/li>
&lt;/ul>
&lt;p>The book is straightforward and accessible. The Observation-Feeling-Need-Request sequence is very practical and insightful. In real life, though, I find it hard to apply consistently. Differences in culture, context, and the subtle emotional shifts in relationships can all make the conversation diverge. It feels like a lifelong practice that requires constant reflection.&lt;/p>
&lt;h2 id="the-courage-to-be-disliked">&lt;strong>The Courage to Be Disliked&lt;/strong>
&lt;/h2>&lt;ul>
&lt;li>Authors: Ichiro Kishimi, Fumitake Koga&lt;/li>
&lt;li>Finished: November 2024&lt;/li>
&lt;/ul>
&lt;p>The book exceeded my expectations. It resonates with ideas I already held and helped develop them further. I loved it. The explanation of teleology is profound and goes well beyond everyday understanding. The biggest takeaway for me is realizing that emotions are rooted in underlying needs.&lt;/p>
&lt;h2 id="mans-search-for-meaning">&lt;strong>Man&amp;rsquo;s Search for Meaning&lt;/strong>
&lt;/h2>&lt;ul>
&lt;li>Author: Viktor Frankl&lt;/li>
&lt;li>Finished: November 2024&lt;/li>
&lt;/ul>
&lt;p>The first half recounts the author&amp;rsquo;s concentration camp experiences. The second half shifts to existentialism. I especially appreciated the sections on logotherapy and the paradoxical intention technique, and how they grapple with internal conflicts.&lt;/p>
&lt;h2 id="difficult-conversations">&lt;strong>Difficult Conversations&lt;/strong>
&lt;/h2>&lt;ul>
&lt;li>Authors: Douglas Stone, Bruce Patton, Sheila Heen&lt;/li>
&lt;li>Finished: December 2024&lt;/li>
&lt;/ul>
&lt;p>Compared with Nonviolent Communication, this book is more intricate. It relies heavily on examples and dives into what really matters in tough conversations. What struck me most is recognizing that each side tells a different story - especially the so-called third story - and that effective dialogue depends on acknowledging those differences.&lt;/p>
&lt;h2 id="the-road-less-traveled">&lt;strong>The Road Less Traveled&lt;/strong>
&lt;/h2>&lt;ul>
&lt;li>Author: M. Scott Peck&lt;/li>
&lt;li>Finished: January 2025&lt;/li>
&lt;/ul>
&lt;p>The book is a bit traditional; it champions effort, dedication, and love throughout. Some viewpoints feel dated, yet the reading experience itself is pleasant and reassuring.&lt;/p>
&lt;h2 id="complex-ptsd-from-surviving-to-thriving">&lt;strong>Complex PTSD: From Surviving to Thriving&lt;/strong>
&lt;/h2>&lt;ul>
&lt;li>Author: Pete Walker&lt;/li>
&lt;li>Finished: January 2025&lt;/li>
&lt;/ul>
&lt;p>This book gives a detailed introduction to the four trauma responses: fight, flight, freeze, and fawn. It also lays out, in a systematic way, how trauma shapes a person.&lt;/p>
&lt;h2 id="counseling-psychology-chinese-only">&lt;strong>Counseling Psychology (Chinese only)&lt;/strong>
&lt;/h2>&lt;ul>
&lt;li>Authors: Fan Fumin, Zhu Xu, He Jin&lt;/li>
&lt;li>Finished: July 2025&lt;/li>
&lt;/ul>
&lt;p>This Chinese textbook is far from a masterpiece, but it does a good job systematizing the architecture of psychology. It helped me understand the branches, connections, history, and current landscape of the field.&lt;/p>
&lt;h2 id="the-five-love-languages">&lt;strong>The Five Love Languages&lt;/strong>
&lt;/h2>&lt;ul>
&lt;li>Author: Gary Chapman&lt;/li>
&lt;li>Finished: August 2025&lt;/li>
&lt;/ul>
&lt;p>The book is famous for a reason. It offers a practical lens on how to express and perceive love.&lt;/p>
&lt;h2 id="attached">&lt;strong>Attached&lt;/strong>
&lt;/h2>&lt;ul>
&lt;li>Authors: Amir Levine, Rachel Heller&lt;/li>
&lt;li>Finished: September 2025&lt;/li>
&lt;/ul>
&lt;p>Another well-known title, this one is essential for understanding attachment theory. Countless short videos and articles repeat its key points, so most people have at least heard of attachment styles. Reading the full book, however, gave me far more than the quick takes floating around online.&lt;/p></description></item><item><title>Interview Experience Summary</title><link>https://nansenli.com/post/2025/07/interview2025/</link><pubDate>Tue, 29 Jul 2025 23:52:58 +0100</pubDate><guid>https://nansenli.com/post/2025/07/interview2025/</guid><description>&lt;p>Here&amp;rsquo;s a summary of all my interview experiences over the past year.&lt;/p>
&lt;p>Overall, the results were quite satisfactory. I continuously applied for positions ranging from New Graduate (NG) to experienced roles, and ultimately received a satisfactory offer, successfully landing a job. I hope this experience will be helpful for future students coming to Ireland to study and find work.&lt;/p>
&lt;p>Before diving into the specific details, let me introduce my background: I previously worked for several years at a major tech company in China, then came to DCU for my master&amp;rsquo;s degree, and was looking for Software Engineer related positions in Ireland.&lt;/p>
&lt;p>Starting from October last year (shortly after enrollment), I began submitting resumes, mainly for NG positions and internship roles. I continued applying until February of the following year, during which time I only received one interview invitation from Google for an internship. Later, because the internship was quite busy, I paused my applications. After the exams ended (May), I concentrated on applying for a batch of positions and gradually received multiple interview opportunities. After going through the interview process, I finally received a formal offer. From this concentrated application period to receiving the offer, it took about one and a half months.&lt;/p>
&lt;p>I kept records of every resume submission, with a total of at least 81 positions applied to.&lt;/p>
&lt;p>Below are records of some positions where I received interview invitations:&lt;/p>
&lt;hr>
&lt;h3 id="google-internship-position">&lt;strong>Google Internship Position&lt;/strong>
&lt;/h3>&lt;p>Two rounds of video interviews:&lt;/p>
&lt;ul>
&lt;li>First round: One LeetCode medium-difficulty algorithm problem;&lt;/li>
&lt;li>Second round: One simple problem, but performed poorly, guessing that&amp;rsquo;s why I didn&amp;rsquo;t pass.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="apple-testing-position">&lt;strong>Apple Testing Position&lt;/strong>
&lt;/h3>&lt;ul>
&lt;li>
&lt;p>First round video interview: Testing SQL and some basic algorithms;&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Second round covered various topics, from resume discussion to testing theory, to Python&amp;rsquo;s requests package;&lt;/p>
&lt;p>Overall felt my performance was average, guessing the reason for not passing might be slight inadequacy, as well as the position not matching well with my development background.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="apple-full-stack-position">&lt;strong>Apple Full Stack Position&lt;/strong>
&lt;/h3>&lt;p>Although I received an interview invitation, because the position was in Cork and I had already decided to develop in Dublin and rented a house, I directly declined this opportunity, feeling slightly regretful.&lt;/p>
&lt;hr>
&lt;h3 id="meta-sse-position">&lt;strong>Meta SSE Position&lt;/strong>
&lt;/h3>&lt;p>It was during exam week, so I negotiated with the Recruiter to postpone for a month, but after that, they never contacted me again, which should be considered being ghosted. However, I don&amp;rsquo;t think it was my own issue, as I have friends who performed very well in interviews but still didn&amp;rsquo;t pass, possibly related to Meta&amp;rsquo;s HC (headcount) reduction this year.&lt;/p>
&lt;hr>
&lt;h3 id="amazon-swe-position">&lt;strong>Amazon SWE Position&lt;/strong>
&lt;/h3>&lt;p>First round interview content included:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>BQ (Behavioral Questions) section: Answers were acceptable;&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Technical section asked about OOP design rather than algorithm problems, but I was inadequately prepared and not familiar with OOP;&lt;/p>
&lt;p>Additionally, Amazon mostly has Java positions, and I have limited interest in Java, so not passing was somewhat natural.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="ptc-swe-position">&lt;strong>PTC SWE Position&lt;/strong>
&lt;/h3>&lt;p>At the beginning of the phone call, they clearly stated they don&amp;rsquo;t provide work visas. I mentioned I could start with a 1G status, making the situation slightly awkward. Although we still went through the process, it ultimately didn&amp;rsquo;t progress.&lt;/p>
&lt;hr>
&lt;h3 id="ebay-swe">&lt;strong>eBay SWE&lt;/strong>
&lt;/h3>&lt;p>The Recruiter had preliminary communication, asked some basic questions, but didn&amp;rsquo;t proceed to the next round. Presumably, they already had more suitable candidates.&lt;/p>
&lt;hr>
&lt;h3 id="workday">&lt;strong>Workday&lt;/strong>
&lt;/h3>&lt;p>Preliminary communication with the Recruiter went smoothly, entered the waiting process stage. The originally scheduled interview time was temporarily cancelled, then rescheduled. Because I was already waiting for Arista&amp;rsquo;s formal offer at that time, I actively stopped the process.&lt;/p>
&lt;hr>
&lt;h3 id="arista">&lt;strong>Arista&lt;/strong>
&lt;/h3>&lt;p>The Recruiter first contacted me when I was at the gym, the sound was too noisy, so we agreed to contact later. Afterwards, HR sent the time for the first interview.&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>First interview&lt;/strong>: Due to technical issues, it couldn&amp;rsquo;t start smoothly, after rescheduling, completed two algorithm problems, passed;&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Second round&lt;/strong>: On-site interview, continued with two algorithm problems, the second problem was quite difficult, took an hour to complete, passed;&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Third round&lt;/strong>: Also an on-site interview, with more in-depth questions;&lt;/p>
&lt;p>After passing the interviews, waited for headquarters approval for the formal offer, the entire process took about a month.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="experience-summary">&lt;strong>Experience Summary&lt;/strong>
&lt;/h2>&lt;ul>
&lt;li>&lt;strong>Both NG and experienced positions can be applied for&lt;/strong>, don&amp;rsquo;t self-limit because of being &amp;ldquo;overqualified&amp;rdquo;, it doesn&amp;rsquo;t actually have that much impact;&lt;/li>
&lt;li>&lt;strong>Video interview pressure is greater than expected&lt;/strong>: Problems you can usually solve, you might not even perform well on simple problems during interviews;&lt;/li>
&lt;li>&lt;strong>Systematic problem-solving practice&lt;/strong>: Recommend solving at least 100 LeetCode problems, 200 is a relatively safe level, beyond that depends on time and energy, pursuing cost-effectiveness;&lt;/li>
&lt;li>&lt;strong>Don&amp;rsquo;t be afraid of rejection&lt;/strong>: Each rejection is a screening process, helping you find positions more suitable for yourself; summarize experiences in time and continue with the next company;&lt;/li>
&lt;li>&lt;strong>Maintain stable mindset&lt;/strong>: Submitting resumes is essentially a probability problem, the key is continuous output + good accumulation.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;p>Wishing everyone success in landing their desired positions!&lt;/p></description></item><item><title>Learning Python uv Package</title><link>https://nansenli.com/post/2025/05/pythonuv/</link><pubDate>Sun, 11 May 2025 21:02:53 +0100</pubDate><guid>https://nansenli.com/post/2025/05/pythonuv/</guid><description>&lt;p>Recently, I&amp;rsquo;ve been looking into MCP-related content, and many sources mention using uv to manage Python package dependencies. After learning more about it, uv appears to be a very useful tool. Here&amp;rsquo;s a summary of its functionality.&lt;/p>
&lt;h3 id="what-exactly-is-uv">What exactly is uv?
&lt;/h3>&lt;p>First of all, uv is a package written in Rust for Python. After installation, you can use the uv command.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-javascript" data-lang="javascript">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">pip&lt;/span> &lt;span style="color:#a6e22e">install&lt;/span> &lt;span style="color:#a6e22e">uv&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="what-can-uv-do">What can uv do?
&lt;/h3>&lt;ol>
&lt;li>It can replace pip&lt;/li>
&lt;/ol>
&lt;p>Simply add uv before your regular pip commands. But why use uv instead of pip? The answer is speed - according to statistics, it&amp;rsquo;s 77 times faster than pip.&lt;/p>
&lt;ol start="2">
&lt;li>It can replace venv or virtualenv&lt;/li>
&lt;/ol>
&lt;p>You need one less package, and the commands are cleaner. uv is much faster than &lt;code>venv&lt;/code> and &lt;code>virtualenv&lt;/code> when creating virtual environments and installing packages.&lt;/p>
&lt;ol start="3">
&lt;li>It can replace poetry&lt;/li>
&lt;/ol>
&lt;p>You might ask, what is poetry? Poetry is a tool for more convenient management of Python dependencies. So why not just use pip + requirements? The answer is that it&amp;rsquo;s cumbersome. Additionally, requirements files cannot lock the dependencies of dependencies. For example, if I lock package A to a specific version, but A depends on B, C, and D, this often leads to the &amp;ldquo;works on my machine but not on others&amp;rdquo; problem. Now, with uv, poetry can also be replaced.&lt;/p>
&lt;ol start="4">
&lt;li>It can replace pipx&lt;/li>
&lt;/ol>
&lt;p>Another question: what is pipx? pipx is a tool for installing Python utilities without polluting your environment.&lt;/p>
&lt;p>For example, if I need to install a command-line tool, I would run:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-javascript" data-lang="javascript">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">pip&lt;/span> &lt;span style="color:#a6e22e">install&lt;/span> &lt;span style="color:#a6e22e">httpie&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>But this might pollute my environment, and uninstalling could have side effects. Now, I can run:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-javascript" data-lang="javascript">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">pipx&lt;/span> &lt;span style="color:#a6e22e">install&lt;/span> &lt;span style="color:#a6e22e">httpie&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This won&amp;rsquo;t pollute the environment.&lt;/p>
&lt;p>However, this command has also been replaced. Now you can run:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-javascript" data-lang="javascript">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">uv&lt;/span> &lt;span style="color:#a6e22e">tool&lt;/span> &lt;span style="color:#a6e22e">install&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>To summarize uv in one sentence: Python dependency management&amp;rsquo;s &amp;ldquo;next-generation de facto standard.&amp;rdquo;&lt;/p></description></item><item><title>Dublin City University MSc in Computing: Secure Software Engineering Exam Cookbook</title><link>https://nansenli.com/post/2025/04/dcu-secure-software-exam-cookbook/</link><pubDate>Sun, 04 May 2025 12:00:00 +0000</pubDate><guid>https://nansenli.com/post/2025/04/dcu-secure-software-exam-cookbook/</guid><description>&lt;p>I completed all the exams for the Secure Software Engineering stream of the MSc in Computing at DCU this semester. Here, I&amp;rsquo;d like to share my experience and some practical tips.&lt;/p>
&lt;p>&lt;strong>Update: My results are now out, and I achieved an average score of 75%, which meets the First Class Honours degree level. I wish everyone the best of luck with their results!&lt;/strong>&lt;/p>
&lt;p>The program consists of 8 modules: 4 in the first semester and 4 in the second. You must pass all exams to graduate, and the overall difficulty is not trivial.&lt;/p>
&lt;p>To pass the exams, the most important points are:&lt;/p>
&lt;ol>
&lt;li>Attend classes. Try to attend every lecture. Being present helps reinforce your learning, and sometimes the lecturer will clearly indicate the focus or direction of the exam. For example, this year, a specific type of question was highlighted in class, which was very helpful for exam preparation.&lt;/li>
&lt;li>Practice with recent exam papers. Download past papers from &lt;a class="link" href="dcu.guruarchive.com" >dcu.guruarchive.com&lt;/a>. Study the question types carefully. If the module content hasn&amp;rsquo;t changed much, questions from the last two years may not appear again. If there have been changes, the last two years&amp;rsquo; papers are likely to be the main focus. I recommend practicing at least 2 years, up to 5 years of past papers.&lt;/li>
&lt;li>Complete all assignments carefully. Each assignment is directly graded and contributes to your final mark—typically 25%. Since assignments are done offline and don&amp;rsquo;t require memorization, you can achieve a high coursework score with effort, which greatly reduces the pressure to pass the final exam.&lt;/li>
&lt;/ol>
&lt;h1 id="exam-overview">Exam Overview
&lt;/h1>&lt;h2 id="semester-1-modules">Semester 1 Modules
&lt;/h2>&lt;ol>
&lt;li>Cryptography and Number Theory&lt;/li>
&lt;li>System Software/BlockChain&lt;/li>
&lt;li>Secure Programming&lt;/li>
&lt;li>Professional &amp;amp; Research Practice&lt;/li>
&lt;/ol>
&lt;h2 id="semester-2-modules">Semester 2 Modules
&lt;/h2>&lt;ol>
&lt;li>Formal Programming&lt;/li>
&lt;li>Concurrent Programming&lt;/li>
&lt;li>Software Process Quality&lt;/li>
&lt;li>Network Security&lt;/li>
&lt;/ol>
&lt;p>The first semester is relatively manageable. The key modules are Secure Programming and Cryptography and Number Theory. There is also System Software/BlockChain, depending on the university&amp;rsquo;s arrangement. If it&amp;rsquo;s BlockChain, it&amp;rsquo;s easier; if it&amp;rsquo;s System Software, it&amp;rsquo;s more challenging. Professional &amp;amp; Research Practice is relatively light. Overall, the first semester is less stressful, with two main exam-heavy modules.&lt;/p>
&lt;p>The second semester is more intense. Almost all modules are challenging, and even the slightly easier Software Process Quality is not easy to score high in. The academic pressure is noticeably higher.&lt;/p>
&lt;p>Below are details for each exam.&lt;/p>
&lt;h1 id="cryptography-and-number-theory">Cryptography and Number Theory
&lt;/h1>&lt;ul>
&lt;li>Module difficulty: ⭐️⭐️⭐️⭐️⭐️&lt;/li>
&lt;li>Exam difficulty: ⭐️⭐️⭐️⭐️⭐️&lt;/li>
&lt;/ul>
&lt;p>The first few lectures are manageable, but after hash functions, the difficulty spikes—especially in Cryptanalysis and Number Theory. Unfortunately, these tough topics are the main focus of the exam.&lt;/p>
&lt;p>To pass and score well with minimal time investment, I recommend:&lt;/p>
&lt;ol>
&lt;li>Attend all lectures.&lt;/li>
&lt;li>Spend 1 hour previewing before class; for Cryptanalysis and Number Theory, spend 2 hours.&lt;/li>
&lt;li>Don&amp;rsquo;t worry if you don&amp;rsquo;t fully understand the cryptanalysis and number theory parts in class—just keep up with the pace.&lt;/li>
&lt;li>Complete the major assignment carefully, usually on linear or differential cryptanalysis. The assignment is always examined in the final. If you didn&amp;rsquo;t do it yourself, the exam will be tough. The assignment is worth 25% and is quite challenging. To get full marks, expect to spend at least 4 days coding and testing. My topic was &lt;strong>Differential Cryptanalysis of Feistel 4&lt;/strong>.&lt;/li>
&lt;li>Practice the past four years&amp;rsquo; papers and make sure you understand any cryptography content you missed in class.&lt;/li>
&lt;/ol>
&lt;h1 id="system-softwareblockchain">System Software/BlockChain
&lt;/h1>&lt;p>This module depends on the university&amp;rsquo;s arrangement. I took BlockChain.&lt;/p>
&lt;ul>
&lt;li>Module difficulty: ⭐️⭐️&lt;/li>
&lt;li>Exam difficulty: ⭐️⭐️⭐️&lt;/li>
&lt;/ul>
&lt;p>BlockChain is mainly delivered via video lectures, so you don&amp;rsquo;t need to attend in-person classes, making the first semester lighter.&lt;/p>
&lt;p>Assignments include a report and a group (2-3 people) presentation on cryptography.&lt;/p>
&lt;p>Overall, this module doesn&amp;rsquo;t require much time to understand, and the exam isn&amp;rsquo;t too hard. However, while passing is easy, getting a high mark is tough. To excel, you need to deeply understand the exam content, prepare assignments thoroughly, memorize a lot, and manage your time well in the exam to write as much as possible.&lt;/p>
&lt;p>How to pass:&lt;/p>
&lt;ul>
&lt;li>Memorize questions from the past 4 years. Most are short-answer questions about blockchain, so you can prepare template answers.&lt;/li>
&lt;li>Prepare your presentation and report thoroughly. The report should have some depth.&lt;/li>
&lt;/ul>
&lt;h1 id="secure-programming">Secure Programming
&lt;/h1>&lt;ul>
&lt;li>Module difficulty: ⭐️⭐️⭐️⭐️⭐️&lt;/li>
&lt;li>Exam difficulty: ⭐️⭐️⭐️⭐️⭐️&lt;/li>
&lt;/ul>
&lt;p>This is, in my opinion, the most valuable module.&lt;/p>
&lt;p>It covers a wide range of topics from high-level to low-level computing: Linux, operating systems, C programming, reverse engineering, data structures, GDB debugging, and more.&lt;/p>
&lt;p>There are no assignments; instead, there are two in-class tests. Including the final, there are three exams in total.&lt;/p>
&lt;p>The first test is a rehearsal for the final, with similar question types. The second is a practical security attack test, where you must research and code an attack.&lt;/p>
&lt;p>How to pass:&lt;/p>
&lt;ol>
&lt;li>Attend all classes and try to remember everything covered. You need a solid foundation in computer science, especially low-level principles.&lt;/li>
&lt;li>Score well in the in-class tests. The lecturer is excellent and will rehearse key exam topics in advance. All questions are variants of previously tested material, so preparing the revision materials provided is key.&lt;/li>
&lt;li>Practice past papers thoroughly, especially Linux Permissions.&lt;/li>
&lt;li>In the final, you can choose 5 out of 6 questions. You can skip the security attack question (already tested in class and hard to revise), or skip another based on your strengths.&lt;/li>
&lt;/ol>
&lt;h1 id="professional--research-practice">Professional &amp;amp; Research Practice
&lt;/h1>&lt;ul>
&lt;li>Module difficulty: ⭐️⭐️⭐️&lt;/li>
&lt;li>Exam difficulty: ⭐️⭐️⭐️&lt;/li>
&lt;/ul>
&lt;p>Coursework counts for 50%: a literature review, a video presentation, and a paper.&lt;/p>
&lt;p>The exam is also 50%.&lt;/p>
&lt;p>You can attend classes or not; they are mainly for Q&amp;amp;A.&lt;/p>
&lt;p>How to pass:&lt;/p>
&lt;ol>
&lt;li>Take assignments seriously to get a good coursework mark. For high marks, pay attention to formatting in the paper and make the video engaging.&lt;/li>
&lt;li>Memorize the past 3 years&amp;rsquo; exam questions. Even though you can choose which to answer, you still need to prepare all topics.&lt;/li>
&lt;li>Prepare model answers for all past questions. Get a general understanding of the syllabus, then check each exam question and use your materials to generate answers to memorize.&lt;/li>
&lt;li>Class attendance is optional, as it&amp;rsquo;s mainly Q&amp;amp;A. Occasionally, there&amp;rsquo;s info about the practicum or job hunting.&lt;/li>
&lt;/ol>
&lt;h1 id="formal-programming">Formal Programming
&lt;/h1>&lt;ul>
&lt;li>Module difficulty: ⭐️⭐️⭐️⭐️⭐️&lt;/li>
&lt;li>Exam difficulty: ⭐️⭐️⭐️⭐️&lt;/li>
&lt;/ul>
&lt;p>This module is very challenging, involving complex logical reasoning. Fortunately, the exam is still manageable.&lt;/p>
&lt;p>How to pass:&lt;/p>
&lt;ol>
&lt;li>Attend every class, listen carefully, and try to follow the logic, formulas, and derivations.&lt;/li>
&lt;li>Complete the third week&amp;rsquo;s assignment (Event-B modeling) carefully.&lt;/li>
&lt;li>Don&amp;rsquo;t worry if you don&amp;rsquo;t understand everything in class—just make sure you get it during revision, especially the various Laws.&lt;/li>
&lt;li>Before the exam, do the past 3 years&amp;rsquo; papers and make sure you understand everything you missed in class.&lt;/li>
&lt;/ol>
&lt;h1 id="concurrent-programming">Concurrent Programming
&lt;/h1>&lt;ul>
&lt;li>Module difficulty: ⭐️⭐️⭐️&lt;/li>
&lt;li>Exam difficulty: ⭐️⭐️⭐️⭐️⭐️&lt;/li>
&lt;/ul>
&lt;p>This module isn&amp;rsquo;t too hard, but covers a lot of content.&lt;/p>
&lt;p>The exam requires a lot of memorization, code snippets, API usage, and design/prototyping.&lt;/p>
&lt;p>How to pass:&lt;/p>
&lt;ol>
&lt;li>Complete all assignments carefully.&lt;/li>
&lt;li>For the exam, practice at least 5 years of past papers. Although you can choose 5 out of 6 questions, the topics vary, so revise all types rather than skipping any.&lt;/li>
&lt;li>Attend every class and try to remember all content, as anything covered could be examined.&lt;/li>
&lt;li>Since the exam involves a lot of memorization, check in class whether the current topic is examinable and remember it if so.&lt;/li>
&lt;li>Build a database of past questions and ask AI if a topic is likely to be examined. If yes, pay close attention in class.&lt;/li>
&lt;li>I highly recommend browsing past papers first to understand the exam focus. Ask AI about trends in the past 3 years. Since this module is mainly about memorization, class absorption is key.&lt;/li>
&lt;/ol>
&lt;h1 id="software-process-quality">Software Process Quality
&lt;/h1>&lt;ul>
&lt;li>Module difficulty: ⭐️⭐️&lt;/li>
&lt;li>Exam difficulty: ⭐️⭐️⭐️&lt;/li>
&lt;/ul>
&lt;p>This module isn&amp;rsquo;t hard, but the exam covers a wide range of topics.&lt;/p>
&lt;ol>
&lt;li>When revising, focus first on topics that appear repeatedly in past papers, such as Karnaugh and Clause Coverage. Since the question types vary, even though you can choose 5 out of 6, I still recommend revising all types.&lt;/li>
&lt;li>You can attend classes or self-study.&lt;/li>
&lt;li>Assignments aren&amp;rsquo;t difficult—choose suitable topics and you can score well.&lt;/li>
&lt;/ol></description></item><item><title>April 2025 Tech Notes 2</title><link>https://nansenli.com/post/2025/04/tech250402/</link><pubDate>Sun, 20 Apr 2025 12:58:30 +0100</pubDate><guid>https://nansenli.com/post/2025/04/tech250402/</guid><description>&lt;h1 id="github-copilot">Github Copilot
&lt;/h1>&lt;p>&lt;img src="https://nansenli.com/img/rescued/6f49f8b9f1ec.png"
loading="lazy"
alt="Image.png"
>&lt;/p>
&lt;p>No more fear when facing unfamiliar Github projects. Just click the Copilot icon on the project homepage to start a conversation, and you can even use GPT-4.1 for communication, with up to a 1M context window. From questions like &amp;ldquo;What can this project be used for?&amp;rdquo; to &amp;ldquo;How to deploy and handle bugs?&amp;rdquo;, it can answer them all well.&lt;/p>
&lt;h1 id="dia-browser">Dia Browser
&lt;/h1>&lt;p>&lt;img src="https://nansenli.com/img/rescued/ba4da2cf2850.png"
loading="lazy"
alt="Image.png"
>&lt;/p>
&lt;p>&lt;a class="link" href="https://www.diabrowser.com/" target="_blank" rel="noopener"
>https://www.diabrowser.com/&lt;/a>&lt;/p>
&lt;p>It&amp;rsquo;s not yet in public beta, but you can submit your email via the link above to join the waitlist.&lt;/p>
&lt;p>The Arc browser is already great, offering a stable, mature, and consistent experience—it&amp;rsquo;s much more than just a sidebar tab innovation.&lt;/p>
&lt;p>Dia is the next-generation product from the Arc browser team. Honestly, I don&amp;rsquo;t quite understand why they need to launch a new product instead of integrating these features directly into Arc. Now, the founder seems to have put Arc on hold.&lt;/p>
&lt;p>From a product experience perspective, Dia browser seems to consist of the following parts:&lt;/p>
&lt;ul>
&lt;li>A built-in AI assistant for daily conversations&lt;/li>
&lt;li>A browser operation assistant similar to nanobrowser or browser-use, helping plan and operate the browser&lt;/li>
&lt;li>An AI writing assistant that can generate text at the cursor&lt;/li>
&lt;/ul>
&lt;p>A product with great potential.&lt;/p>
&lt;h1 id="release-of-gpt-o3--o4-mini--o4-mini-high--41--41-mini--41-nano">Release of GPT o3 / o4-mini / o4-mini-high / 4.1 / 4.1 mini / 4.1 nano
&lt;/h1>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;strong>Model&lt;/strong>&lt;/th>
&lt;th>&lt;strong>Context Window&lt;/strong>&lt;/th>
&lt;th>&lt;strong>Input Price&lt;/strong>&lt;/th>
&lt;th>&lt;strong>Output Price&lt;/strong>&lt;/th>
&lt;th>&lt;strong>Image Price&lt;/strong>&lt;/th>
&lt;th>&lt;strong>Main Features&lt;/strong>&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>o4-mini-high&lt;/td>
&lt;td>200K tokens&lt;/td>
&lt;td>$1.10/M&lt;/td>
&lt;td>$4.40/M&lt;/td>
&lt;td>$0.842/K image input&lt;/td>
&lt;td>High-reasoning version of o4-mini; high throughput, low latency; supports chain calls and structured output&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>o4-mini&lt;/td>
&lt;td>200K tokens&lt;/td>
&lt;td>$1.10/M&lt;/td>
&lt;td>$4.40/M&lt;/td>
&lt;td>$0.842/K image input&lt;/td>
&lt;td>Compact reasoning; AIME 99.5%, high SWE-bench score; excellent STEM, vision, and code editing capabilities&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>o3&lt;/td>
&lt;td>200K tokens&lt;/td>
&lt;td>$10/M&lt;/td>
&lt;td>$40/M&lt;/td>
&lt;td>$7.65/K image input&lt;/td>
&lt;td>General strong model; top math, science, coding, and vision reasoning; BYOK required&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GPT-4.1&lt;/td>
&lt;td>1.05M tokens&lt;/td>
&lt;td>$2/M&lt;/td>
&lt;td>$8/M&lt;/td>
&lt;td>N/A&lt;/td>
&lt;td>Flagship model; million-token context; excellent instruction following, enterprise-grade retrieval, and multimodal understanding&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GPT-4.1 Mini&lt;/td>
&lt;td>1.05M tokens&lt;/td>
&lt;td>$0.40/M&lt;/td>
&lt;td>$1.60/M&lt;/td>
&lt;td>N/A&lt;/td>
&lt;td>Medium version; close to GPT-4o performance; low latency, low cost; strong coding and vision understanding&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GPT-4.1 Nano&lt;/td>
&lt;td>1.05M tokens&lt;/td>
&lt;td>$0.10/M&lt;/td>
&lt;td>$0.40/M&lt;/td>
&lt;td>N/A&lt;/td>
&lt;td>Fastest and cheapest in the series; million-token context; suitable for classification and autocomplete tasks&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>GPT-4.1 Nano is naturally suited for translation and is extremely cost-effective. For example, when used for immersive translation with bilingual subtitles enabled, translating an entire season of Black Mirror costs only about $0.005 per episode, and all 7 seasons just $0.25—very low cost.&lt;/p>
&lt;p>The context window for GPT-4.1 has finally reached 1M. Previously, GPT was far behind in code writing, but now it has caught up.&lt;/p>
&lt;p>o3, o4-mini, and o4-mini-high are all surprisingly impressive, giving a bit of a &amp;ldquo;mini deep research&amp;rdquo; feel.&lt;/p>
&lt;p>Currently, my daily usage has basically become:&lt;/p>
&lt;ul>
&lt;li>o4-mini: Simple search and summarization, tolerant of mistakes&lt;/li>
&lt;li>o4-mini-high: Complex search, summarization, multi-step reasoning&lt;/li>
&lt;li>o3: Complex search, summarization, multi-step reasoning where high accuracy is required&lt;/li>
&lt;li>4o: Intuitive and knowledge-based answers&lt;/li>
&lt;/ul>
&lt;p>I&amp;rsquo;ve also tracked the usage frequency of each model, and I use almost all of them—none are useless, unlike o3-mini, which is almost unusable due to high hallucinations.&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/018788a20986.png"
loading="lazy"
alt="Image.png"
>&lt;/p>
&lt;h1 id="yuanbao-integrates-with-wechat-contacts">Yuanbao Integrates with WeChat Contacts
&lt;/h1>&lt;p>Yuanbao WeChat can directly add contacts and send messages, supporting both voice and image messages. It&amp;rsquo;s a competitor to Doubao. The advantage is that no extra app installation is required. I previously recommended Doubao to my parents, and they really liked it. If Yuanbao could also support voice calls or even video chat, it would be even more valuable.&lt;/p>
&lt;h1 id="openai-free-token-giveaway">OpenAI Free Token Giveaway
&lt;/h1>&lt;p>&lt;img src="https://nansenli.com/img/rescued/7c4b97a270c2.png"
loading="lazy"
alt="Image.png"
>&lt;/p>
&lt;p>OpenAI is now offering a free token giveaway. You can enable sharing logs in Data controls to claim them. Unfortunately, the duration is too short—just a week or two—so it&amp;rsquo;s hard to use up all the quota.&lt;/p>
&lt;h1 id="other-github-dailyweekly-trending-projects">Other Github Daily/Weekly Trending Projects
&lt;/h1>&lt;p>&lt;a class="link" href="https://github.com/kestra-io/kestra" target="_blank" rel="noopener"
>https://github.com/kestra-io/kestra&lt;/a>&lt;/p>
&lt;p>An automation platform, with competitors including: Airflow (data pipelines), Zapier (low-code marketing automation), Rundeck (ops automation), Camunda (e-commerce business automation)&lt;/p>
&lt;p>&lt;a class="link" href="https://github.com/Zackriya-Solutions/meeting-minutes" target="_blank" rel="noopener"
>https://github.com/Zackriya-Solutions/meeting-minutes&lt;/a>&lt;/p>
&lt;p>A speech-to-text software, a competitor to Apple Voice Memos.&lt;/p>
&lt;p>&lt;a class="link" href="https://github.com/maybe-finance/maybe" target="_blank" rel="noopener"
>https://github.com/maybe-finance/maybe&lt;/a>&lt;/p>
&lt;p>Personal financial report management, similar to Youzhi Youxing.&lt;/p>
&lt;p>&lt;a class="link" href="https://github.com/supabase-community/supabase-mcp" target="_blank" rel="noopener"
>https://github.com/supabase-community/supabase-mcp&lt;/a>&lt;/p>
&lt;p>A tool for connecting to databases when developing software with AI locally, based on the MCP protocol. If MCP becomes a real future standard, databases may natively support MCP, creating new competition.&lt;/p>
&lt;p>&lt;a class="link" href="https://github.com/langgenius/dify" target="_blank" rel="noopener"
>https://github.com/langgenius/dify&lt;/a>&lt;/p>
&lt;p>A low-code LLM platform, a competitor to LangChain, and can partially replace LangChain in current application scenarios.&lt;/p></description></item><item><title>April 2025 Tech Notes</title><link>https://nansenli.com/post/2025/04/tech2504/</link><pubDate>Tue, 15 Apr 2025 20:45:20 +0100</pubDate><guid>https://nansenli.com/post/2025/04/tech2504/</guid><description>&lt;h1 id="introduction">Introduction
&lt;/h1>&lt;p>I plan to record some weekly tech-related notes and insights starting from this issue. I&amp;rsquo;ll probably update every two to four weeks.&lt;/p>
&lt;h1 id="migrating-personal-blog-from-hexo-to-hugo">Migrating Personal Blog from Hexo to Hugo
&lt;/h1>&lt;p>This week, I migrated my blog from Hexo to Hugo, primarily because:&lt;/p>
&lt;p>Hugo has better i18n support. Hexo doesn&amp;rsquo;t support i18n very well; I previously modified the template to implement Chinese and English switching, but the maintenance cost was high. I discovered that Hugo natively supports multiple languages.&lt;/p>
&lt;p>After switching, I also found the following advantages:&lt;/p>
&lt;p>Theme configuration is simpler. I&amp;rsquo;m using the &lt;a class="link" href="https://github.com/CaiJimmy/hugo-theme-stack" target="_blank" rel="noopener"
>Stack&lt;/a> theme, which has been mostly problem-free. The official example configuration works well right out of the box. It also comes with Google Analytics integration and RSS feeds that automatically separate by language, making it very convenient.&lt;/p>
&lt;p>During the migration process, I didn&amp;rsquo;t encounter any major obstacles. You just need your old Markdown files, copy them to the new project directory, and force push to your original GitHub Pages repository. After force pushing, you&amp;rsquo;ll need to reconfigure your custom domain in GitHub, and the upgrade will be seamless, including comments and other features.&lt;/p>
&lt;p>The only difference is that Hugo uses lowercase links by default, while Hexo preserves case sensitivity, which might cause some old links to become invalid, requiring search engines to rebuild their indices.&lt;/p>
&lt;h1 id="mysterious-optimus-alpha-model-appears-on-httpsopenrouteraihttpsopenrouterai-and-dominates-rankings">Mysterious Optimus Alpha Model Appears on &lt;a class="link" href="https://openrouter.ai/" target="_blank" rel="noopener"
>https://openrouter.ai/&lt;/a> and Dominates Rankings
&lt;/h1>&lt;p>&lt;img src="https://nansenli.com/img/rescued/a7835acf9e85.png"
loading="lazy"
alt="Image.png"
>&lt;/p>
&lt;p>Because this model was free and powerful, it was chosen as OpenRouter&amp;rsquo;s primary traffic handling model after launch.&lt;/p>
&lt;p>After OpenAI officially announced GPT-4.1 on the 14th, the model was removed from OpenRouter. Obviously, this model was GPT-4.1.&lt;/p>
&lt;p>GPT-4.1 is a model specialized in programming capabilities, with a 1M context window. Indeed, in the programming field, OpenAI has been struggling against Claude 3.7 and Gemini 2.5 Pro. Neither o1 nor o3 models have achieved a leading position in programming. Hopefully, OpenAI will keep improving.&lt;/p>
&lt;blockquote>
&lt;p>OpenRouter is a middleware service platform that allows users to &lt;strong>access multiple large language model APIs&lt;/strong> through a unified interface. You can think of it as a &amp;ldquo;proxy&amp;rdquo; or &amp;ldquo;router&amp;rdquo; that manages API calls to different large model providers (OpenAI, Anthropic, Mistral, Google Gemini, Cohere, etc.).&lt;/p>
&lt;/blockquote>
&lt;p>It seems that this time, OpenAI chose OpenRouter as its test release platform, rather than &lt;a class="link" href="https://lmarena.ai/" target="_blank" rel="noopener"
>https://lmarena.ai/&lt;/a>.&lt;/p>
&lt;h1 id="openrouterai">OpenRouter.ai
&lt;/h1>&lt;p>&lt;a class="link" href="https://openrouter.ai/" target="_blank" rel="noopener"
>OpenRouter&lt;/a>&lt;/p>
&lt;p>This is an LLM platform that includes many free/paid high-quality models.&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/def8fd9ff726.png"
loading="lazy"
alt="Image.png"
>&lt;/p>
&lt;p>Select &amp;ldquo;Prompt pricing&amp;rdquo; as 0, then choose &amp;ldquo;Top Weekly&amp;rdquo; to see the best free models currently available.&lt;/p>
&lt;p>However, when using it, you need to enable logging and data usage for training.&lt;/p>
&lt;p>Currently, Gemini 2.5 Pro is an option.&lt;/p>
&lt;p>The Ranking feature allows you to view real-time traffic distribution across all models on the platform, making it easy to see which models perform best.&lt;/p>
&lt;h1 id="nanobrowser">nanobrowser
&lt;/h1>&lt;p>&lt;img src="https://nansenli.com/img/rescued/8d34bb10e9ba.png"
loading="lazy"
alt="Image.png"
>&lt;/p>
&lt;p>A browser plugin for Chrome that can control browser automation operations.&lt;/p>
&lt;p>I tested it and found it doesn&amp;rsquo;t support Arc browser. The experience was limited - page scrolling occasionally fails, it can&amp;rsquo;t summarize entire page content (only partial content is visible), and operations are sometimes incorrect.&lt;/p>
&lt;p>Although I don&amp;rsquo;t particularly recommend this product, it might represent a future direction. It seems we&amp;rsquo;ll see many AI agents emerging.&lt;/p>
&lt;p>Gray market industries like ticket scalping and point farming will probably be delighted.&lt;/p>
&lt;h1 id="markitdown">markitdown
&lt;/h1>&lt;p>&lt;img src="https://nansenli.com/img/rescued/e37c2c0e7d7a.png"
loading="lazy"
alt="Image.png"
>&lt;/p>
&lt;p>A tool for converting content in different formats to Markdown.&lt;/p>
&lt;p>Offline, it can process different files into Markdown format. I tried it, and the conversion is fairly basic.&lt;/p>
&lt;p>It also supports integration with LLMs to improve conversion results and handle images in documents.&lt;/p>
&lt;p>This could potentially be a good MCP service in the future.&lt;/p>
&lt;h1 id="pdf-search">PDF search
&lt;/h1>&lt;p>&lt;img src="https://nansenli.com/img/rescued/d1310e598823.png"
loading="lazy"
alt="Image.png"
>&lt;/p>
&lt;p>An application that can quickly search content across multiple PDFs.&lt;/p>
&lt;p>It uses a fuzzy search approach. The principle is to split PDFs and perform embedding vector calculations, then match them with search terms.&lt;/p>
&lt;p>Suitable for searching documents during exam periods.&lt;/p>
&lt;p>The downside is that indexing is done by page, so after searching, you need to find the corresponding part on the page yourself.&lt;/p></description></item><item><title>Incomplete Guide to Studying in Ireland</title><link>https://nansenli.com/post/2025/04/ireland-study-guide/</link><pubDate>Thu, 10 Apr 2025 18:12:52 +0000</pubDate><guid>https://nansenli.com/post/2025/04/ireland-study-guide/</guid><description>&lt;p>This isn&amp;rsquo;t really a comprehensive guide, just some personal supplementary information to Chen He&amp;rsquo;s content.&lt;/p>
&lt;p>Chen He&amp;rsquo;s link: &lt;a class="link" href="https://chenhe.me/post/ireland" target="_blank" rel="noopener"
>https://chenhe.me/post/ireland&lt;/a>&lt;/p>
&lt;p>Chen He&amp;rsquo;s content is excellent and detailed, basically answering many of my initial questions about coming to Ireland.&lt;/p>
&lt;p>The article is incomplete and will be updated periodically.&lt;/p>
&lt;h1 id="background">Background
&lt;/h1>&lt;p>Location is in Dublin, admitted to DCU.&lt;/p>
&lt;h2 id="before-departure">Before Departure
&lt;/h2>&lt;ul>
&lt;li>Let me specify the model directly: Supor Little Green Pressure Cooker. Why this specific one and not others? Because: 1. Pressure-cooked rice tastes better. 2. It&amp;rsquo;s more convenient for stewing dishes like beef brisket or braised chicken legs. I also brought a Xiaomi rice cooker, but found that the rice cooked in it wasn&amp;rsquo;t as tasty - only pressure-cooked rice tastes good😋. Also, this model seems to be the only one that can cook rice in just 10 minutes under pressure. Looking at other models, none were this fast, possibly due to its smaller capacity.&lt;/li>
&lt;li>Medications: Strongly recommend bringing Gan Kang (cold medicine) - one pill makes you feel much better when you have a cold. Also bring some transparent waterproof band-aids.&lt;/li>
&lt;li>Water bottle: After arriving, I found it difficult to buy a water bottle I liked. If you&amp;rsquo;re still in China, it&amp;rsquo;s important to buy a suitable water bottle from Taobao.&lt;/li>
&lt;li>Xiaohongshu: Previously thought it was just a beauty app, barely used it in China. After going abroad, I discovered it&amp;rsquo;s actually an overseas life app. There are many guides on the following topics on Xiaohongshu, you can browse as needed. Of course, some people dislike it and look down upon it - to each their own (don&amp;rsquo;t hit me, Chen He).&lt;/li>
&lt;li>Driver&amp;rsquo;s license: I brought mine, but it expired after arrival, so I haven&amp;rsquo;t used it much. If it&amp;rsquo;s still valid, it can reduce the time needed for getting a license here.&lt;/li>
&lt;li>Cash: Actually, you don&amp;rsquo;t need to exchange too much Euro. Having a dual-currency credit card like Visa or Mastercard set up in China is sufficient - you can use it directly here. Cash transactions are relatively rare here.&lt;/li>
&lt;li>Clothes: Recommend bringing layered clothing. Due to the variable weather and large temperature differences between indoor and outdoor, you might need to frequently add or remove layers. Winters are quite cold, you can bring two down jackets or similar thickness coats to alternate.&lt;/li>
&lt;li>Luggage: Hainan Airlines allows student verification, permitting up to two 28-inch suitcases. However, that was too much for me - I ended up bringing one 20-inch and one 28-inch. Also, a 28-inch suitcase can easily exceed weight limits when full. A 26-inch would also work.&lt;/li>
&lt;li>Medical check-ups, dental work, surgeries, etc.: These are expensive here, try to get them done in China. Things like dental fillings or laser surgeries.&lt;/li>
&lt;li>Power adapters: A high-quality power adapter will be very convenient. Consider preparing some simple adapters and buying one with multiple charging ports. Also consider purchasing a special power strip that has a UK standard plug but Chinese standard sockets.&lt;/li>
&lt;li>Living expenses: Rent might be €600-1200, if you cook your own meals, food costs about €500 per month, total living expenses might be €1000-1800 per month, depending on personal spending habits.&lt;/li>
&lt;li>Flights: Hainan Airlines&amp;rsquo; direct flights to Dublin are good, especially if you transit through Shanghai rather than flying from Beijing - the tickets are cheaper. So taking an extra flight actually saves money.&lt;/li>
&lt;li>Temporary accommodation: Can be booked on Airbnb or Booking.&lt;/li>
&lt;li>Chinese cooking ingredients: After arriving, you&amp;rsquo;ll find that a large part of daily life is grocery shopping and cooking. However, basically all seasonings are available at local Chinese supermarkets, so you don&amp;rsquo;t need to bring too many seasonings. For a specific list of items sold, you can search the Rong Xing Hang WeChat mini-program &amp;ldquo;353 Gou&amp;rdquo;.&lt;/li>
&lt;/ul>
&lt;h1 id="immigration">Immigration
&lt;/h1>&lt;p>Just show the following:&lt;/p>
&lt;ul>
&lt;li>Passport&lt;/li>
&lt;li>Insurance&lt;/li>
&lt;li>Admission letter&lt;/li>
&lt;/ul>
&lt;h1 id="after-arriving-in-ireland">After Arriving in Ireland
&lt;/h1>&lt;ul>
&lt;li>Arrive at accommodation: Just hail a taxi at the airport.&lt;/li>
&lt;li>Get a phone card: You can get a student plan, €20 for unlimited data. I got &lt;strong>Vodafone&lt;/strong>. Remember to set up automatic 20-day top-up, otherwise you&amp;rsquo;ll need to manually top up each time.&lt;/li>
&lt;li>Get a transit card: First get an adult card, then exchange it for a student card in the city center. Student cards are half price. You can then cancel the adult card on the website and get a refund.&lt;/li>
&lt;li>Apply for IRP (Irish Residence Permit): Book in advance, if you wait until September when school starts, you&amp;rsquo;ll be queuing until November. With IRP, you can leave Ireland, like going back home or visiting the UK or Europe.&lt;/li>
&lt;li>Apply for a bank card: Can be done through AIB, no need to visit in person, can be done via phone. Requires student ID, student bank cards are free of annual fees. Apply for the bank card early, as the address proof it provides is important for other procedures.&lt;/li>
&lt;li>Apply for &lt;strong>PPSN&lt;/strong>: Although not always necessary, better to have it just in case. Get it done early. It&amp;rsquo;s needed for tax purposes if you find an internship. It&amp;rsquo;s also needed for driver&amp;rsquo;s license applications.&lt;/li>
&lt;/ul>
&lt;h1 id="payments">Payments
&lt;/h1>&lt;p>Most daily scenarios use card payments - just tap your card on the wireless symbol to pay. You can also set up Apple Pay on your phone for payments.&lt;/p>
&lt;h1 id="housing">Housing
&lt;/h1>&lt;p>There are two apartments near DCU, highly recommended, on Shanowen Road: Shanowen Square and Shanowen Hall. Best to book several months in advance, otherwise there might not be available rooms. In the city center, Beckett House is also good, suitable for TCD students. Of course, if you get lucky with the school dorm lottery, that&amp;rsquo;s the best. In summary, prioritize school dorms, student apartments, then look for private rentals. As for homestays or living with landlords, it varies by person - for me, I&amp;rsquo;ve experienced it, not a nightmare but not a dream either, but having the experience is enough, I wouldn&amp;rsquo;t want to do it again. Some classmates said their homestay environment was good though.&lt;/p>
&lt;h1 id="home-and-bedding">Home and Bedding
&lt;/h1>&lt;p>You can order from IKEA, they&amp;rsquo;ll deliver to your room. But note that delivery times aren&amp;rsquo;t always accurate, might arrive the next day. Remember to use student discounts - I realized after checkout that there was a discount, contacted staff, and they helped cancel and re-settle the payment. Homebase also has good stuff, their electric heaters are great - dozens of euros for a large but light one, useful when heating stops at night in winter.&lt;/p>
&lt;h1 id="pharmacies">Pharmacies
&lt;/h1>&lt;p>Rarely visited pharmacies in China, but pharmacies here sell many things besides medicine, like cosmetics, health products, skincare, and bath products. Basically, Boots can handle everything. There&amp;rsquo;s a large Boots in the city center with a photo booth on the second floor, very versatile.&lt;/p>
&lt;h1 id="dining">Dining
&lt;/h1>&lt;p>Basically cook for yourself. You can buy ingredients from Rong Xing Hang Chinese supermarket in the city center. Rong Xing Hang&amp;rsquo;s mini-program offers city-wide delivery, free delivery for orders over €30 with the note &amp;ldquo;Rong Yi Life&amp;rdquo;, orders placed in the morning arrive in the afternoon, very convenient!!!&lt;/p>
&lt;p>I don&amp;rsquo;t use other supermarkets much. There&amp;rsquo;s LIDL and Tesco near DCU with some fresh produce, I mainly use them for meat.&lt;/p>
&lt;p>As for meat, the pork here has such a strong smell. Basically haven&amp;rsquo;t eaten it. Only eat beef.&lt;/p>
&lt;p>LIDL&amp;rsquo;s Rib eye steak is quite good, well-marbled, €8+ for 400g, can last two meals. Also LIDL&amp;rsquo;s chicken legs with skin and bone are very cost-effective.&lt;/p>
&lt;p>Rong Xing Hang sells beef brisket, makes great stewed beef.&lt;/p>
&lt;p>The stoves here aren&amp;rsquo;t great to use, mostly electric ceramic hobs. Might need some time to get used to the heat levels.&lt;/p>
&lt;h1 id="public-transit">Public Transit
&lt;/h1>&lt;p>Ireland&amp;rsquo;s public transit system is well-developed, basically no need for taxis. TFI Live can show real-time arrival times for the next bus at any stop, more accurate than Google, very useful at stops without electronic displays, recommended to download and use. Mainly buses, occasionally Luas, Dart, etc.&lt;/p>
&lt;h1 id="online-shopping">Online Shopping
&lt;/h1>&lt;p>Recommend using Shein, free shipping above certain amounts. Usually takes about 10 days for delivery. Can buy daily necessities, etc. No food items.&lt;/p>
&lt;p>Also students can get free Amazon student membership for six months. Greatly reduces shipping costs.&lt;/p>
&lt;p>If not using Shein and wanting to shop like on Taobao, then use international shipping companies - air freight takes 10 days but shipping costs are extremely high, about 200-300 RMB for 2kg. Sea freight is cheaper, about 60-80 RMB for 2kg. So if you can buy daily necessities on Shein, try to buy there, shipping is free.&lt;/p>
&lt;p>Ochama has more comprehensive products, including various Chinese foods and some Jingzao products (JD&amp;rsquo;s European company), use invitation code (7WYM5K) at checkout for €10 off. But shipping is expensive, rarely used without discounts.&lt;/p>
&lt;h1 id="clothing">Clothing
&lt;/h1>&lt;p>Dundrum Town Centre in the south has lots of clothing stores. Henry St in the city center also has many clothing stores.&lt;/p>
&lt;h1 id="domestic-travel">Domestic Travel
&lt;/h1>&lt;p>If you&amp;rsquo;ve signed up for a language course (I did DCU&amp;rsquo;s 5-week language course, quite good, otherwise the start of school would be too overwhelming, it&amp;rsquo;s like getting to know the school in advance), July-August are good for traveling. Can visit Galway and Cork. But if you haven&amp;rsquo;t signed up for a language course and only arrive in September, the weather isn&amp;rsquo;t great, it gets cold and daylight hours decrease significantly, so September when school starts is okay for travel, but after October isn&amp;rsquo;t suitable. The next good time for travel would be after April next year, when daylight returns and temperatures rise.&lt;/p>
&lt;h1 id="visas">Visas
&lt;/h1>&lt;ul>
&lt;li>Try to apply for UK visa in China, if applying in Ireland, they only give 6 months, but in China, they give 2 years. This makes it convenient whether going to Northern Ireland or the UK.&lt;/li>
&lt;li>Courses here are short. After winter break and second semester, there&amp;rsquo;s lots of free time. If you don&amp;rsquo;t have plans, it can be very boring. Especially in winter when daylight is short. Since visa applications here basically require using scalpers to queue, recommend starting visa applications right after arrival. To be safe, pick an embassy and start applying for visas. Make one round trip to the destination country during the visa period. This process will also cost lots of time and money, but there&amp;rsquo;s no way around it - to get a long-term Schengen visa, you need to accumulate entry/exit records. I applied for a French visa. Usually they issue visas for these durations: 1 month, 3 months, 6 months, 1 year, multiple years. Single or multiple entry doesn&amp;rsquo;t matter, as visas over 3 months are unlikely to be single entry.&lt;/li>
&lt;/ul>
&lt;h1 id="job-hunting">Job Hunting
&lt;/h1>&lt;p>This is also crucial. Came here clueless, only got a clear understanding after arrival. This section will be updated after I complete the process since I&amp;rsquo;m still job hunting.&lt;/p>
&lt;ul>
&lt;li>NG positions: Also known as campus recruitment positions, application period is September to November after arrival. Most NG positions close after November. So need to apply early. Regardless of skill level, recommend applying for NG positions. However, for those with over 2 years of experience, it&amp;rsquo;s difficult to get NG positions at big companies. But going through the process, if you get interviews, it&amp;rsquo;s good experience.&lt;/li>
&lt;li>Experienced positions: Application period starts in late April next year. Since I&amp;rsquo;m just starting to apply, I can&amp;rsquo;t really say much about it yet. Also, job hunting is somewhat up to fate. Many companies require you to have a 1G visa before they&amp;rsquo;ll even consider giving you an interview opportunity. If you apply too early, companies won&amp;rsquo;t process your application.&lt;/li>
&lt;li>Internship positions: These are hit or miss, since the master&amp;rsquo;s is only one year, many internship positions aren&amp;rsquo;t for us but for undergraduates. Can only say, try applying.&lt;/li>
&lt;/ul>
&lt;h1 id="contact">Contact
&lt;/h1>&lt;p>If you have questions, you can also contact me through these channels.&lt;/p>
&lt;p>Xiaohongshu:&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/91d9a6d2d744.jpeg"
loading="lazy"
alt="Image.jpeg"
>&lt;/p>
&lt;p>Whatsapp：&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/6d28c3183067.png"
loading="lazy"
alt="Image.heic.png"
>&lt;/p></description></item><item><title>Summary of LLM Tools Usage Experience</title><link>https://nansenli.com/post/2025/04/aitest/</link><pubDate>Tue, 08 Apr 2025 19:47:48 +0000</pubDate><guid>https://nansenli.com/post/2025/04/aitest/</guid><description>&lt;h1 id="chatgpt-o4-mini">ChatGPT o4-mini
&lt;/h1>&lt;p>&lt;a class="link" href="day://2025.04.17" >&lt;code>Thu, Apr 17&lt;/code>&lt;/a> The latest model released in the past two days, with significant reasoning capabilities and excellent multi-step execution. It appears that o4-mini has completely surpassed o3-mini in practical task execution performance.&lt;/p>
&lt;p>The following image shows an example of my usage. The question was: &amp;ldquo;In Black Mirror Season 7 Episode 3, there are many Asian characters. Are these Asians Chinese, Korean, Japanese, Malaysian, or Singaporean?&amp;rdquo; The model successfully called the search engine multiple times and obtained the correct answer:&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/51ec0e03d882.png"
loading="lazy"
alt="Image.png"
>&lt;/p>
&lt;p>Additionally, Projects can now be normally called and used in this mode. Since the model can search autonomously rather than using RAG methods, the efficiency of file searching has significantly improved.&lt;/p>
&lt;h1 id="chatgpt-excluding-o3-o4-mini">ChatGPT (excluding o3, o4-mini)
&lt;/h1>&lt;ul>
&lt;li>
&lt;p>GPT-4o: Versatile. Excellent for daily communication and learning, with sufficient context window to easily handle code and document retrieval tasks. Supports multiple attachment formats and online code execution. Drawback: generates limited context length, not suitable for very long content. For lengthy conversations, context compression is severe and may forget earlier content.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Projects: Somewhat redundant. Suitable for scenarios requiring frequent searching and text retrieval across multiple files.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>GPT-4o mini: Weaker than GPT-4o, but has a massive context window, recommended for translating very long texts.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>o1: A decent chain-of-thought model, suitable for solving complex code and mathematical problems. Not good at handling emotional or intuitive problems. However, the thought chain is too short and quality is not high. It&amp;rsquo;s recommended to use the output as input for Gemini-2.5pro or DeepSeek-R1 to optimize quality.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>o3-mini: Severe hallucinations, inferior to o1, but extremely fast reasoning speed.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Search: Slightly redundant. New version&amp;rsquo;s search results are heavily restricted, even inferior to model output without search. Suitable as a light search alternative.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Deep Research: Excellent tool. Best quality among similar features, most comprehensive output results.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>GPT-4.5-preview: Excellent. Massive context window, strong long-text comprehension. Has vast memory and strongest intuition. Not actually good at reasoning, but performs best due to low hallucinations.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Canvas: Overall inferior to Cursor. Context output too small, text length limited. Suitable for short-text scenarios with real-time editing.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Work with Apps on macOS: Functionality experience crushed by Cursor, essentially a simplified version of Cursor.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h1 id="claude">Claude
&lt;/h1>&lt;ul>
&lt;li>Claude 3.7 Sonnet: Excellent, suitable for generating various code. Web version supports massive context, almost matching API&amp;rsquo;s context history message retention. Model very suitable for writing communication texts like emails, with accurate, concise, and unpretentious wording. Drawback: limited usage in free version.&lt;/li>
&lt;/ul>
&lt;h1 id="deepseek">DeepSeek
&lt;/h1>&lt;ul>
&lt;li>
&lt;p>R1: Severe hallucinations, unstable performance, occasionally affecting usability, requires very high quality prompts. When prompts are correct or context is complete, it outperforms o1. Can be used as input for o1&amp;rsquo;s output to optimize quality.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>V3: Very excellent, alternative to GPT-4o.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h1 id="grok">Grok
&lt;/h1>&lt;ul>
&lt;li>
&lt;p>Grok-3: Generates text fluently and naturally, less AI-like, suitable for natural writing and novel creation. Overall mediocre, fewer productivity tools than GPT-4o.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Grok-3 + search: Excellent. Leverages English social media data, quickly analyzes news and current events. Outputs long content, barely filters search source content, stronger search capabilities than GPT-4o.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Grok-Deep Search: Slightly redundant, actually inferior to Grok-3 + search. Generated content heavily templated, affecting quality.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h1 id="gemini">Gemini
&lt;/h1>&lt;ul>
&lt;li>
&lt;p>2.5 Pro: Excellent model, alternative to GPT-4o, minimal hallucinations, high-quality search results, complete and clear thought chains, strong logical reasoning. Supports integration with Google tools, very useful in specific scenarios, such as uploading screenshots or text to automatically create events in Google Calendar. Drawback: often claims to have performed searches when it actually hasn&amp;rsquo;t.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Deep Research: Average, slightly better than old GPT Search. Occasional comprehension deviations, search and document generation executed in stages, process fragmented, heavily templated. Currently the only product that can replace GPT Deep Research.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h1 id="v0dev">v0.dev
&lt;/h1>&lt;ul>
&lt;li>Suitable for writing and previewing frontend components online.&lt;/li>
&lt;/ul>
&lt;h1 id="chatqwenai">chat.qwen.ai
&lt;/h1>&lt;ul>
&lt;li>Qwen2.5-Max: Very excellent model, alternative to GPT-4o, fast generation speed, supports thinking mode.&lt;/li>
&lt;/ul>
&lt;h1 id="zhihu-direct-answer">Zhihu Direct Answer
&lt;/h1>&lt;ul>
&lt;li>Supports searching Zhihu&amp;rsquo;s entire network content. Possible competitor is Xiaohongshu Direct Answer.&lt;/li>
&lt;/ul>
&lt;h1 id="perplexity">Perplexity
&lt;/h1>&lt;ul>
&lt;li>Supports basic search, mediocre quality, slightly redundant.&lt;/li>
&lt;/ul>
&lt;h1 id="mistralai">Mistral.ai
&lt;/h1>&lt;ul>
&lt;li>Fast speed, large context window, alternative to GPT4o-mini.&lt;/li>
&lt;/ul>
&lt;h1 id="cursor">Cursor
&lt;/h1>&lt;ul>
&lt;li>Excellent tool. Can index entire code repositories, suitable for large project development. Supports multiple model switching, local command line execution and terminal takeover, suitable for the following tasks:
&lt;ul>
&lt;li>Writing LaTeX documents, replacing Overleaf&lt;/li>
&lt;li>Assisting with various assignments&lt;/li>
&lt;li>Code repository search and specific feature location&lt;/li>
&lt;li>Creating unit tests&lt;/li>
&lt;li>Executing git operations&lt;/li>
&lt;li>Executing deployment tasks&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Can execute almost everything involving documents and command lines, helpful in development, debugging, debugging, and document writing&lt;/li>
&lt;li>Truly the most Agent-like product at the current stage (though this claim no longer holds after the release of o3 and o4-mini, as they can also perform multi-step planning and task execution. However, o3 and o4-mini can only operate within their own limited environment, while Cursor can execute on the user&amp;rsquo;s computer, which is an advantage.)&lt;/li>
&lt;/ul>
&lt;h1 id="example-leetcode-tracker">Example: Leetcode Tracker
&lt;/h1>&lt;p>There are many shared Leetcode premium accounts on Taobao, but sometimes we still need to add our problem-solving records to our own accounts. After purchasing a shared premium account, we can export a company&amp;rsquo;s problems as a CSV file, then export our own problem-solving records as another CSV file. By comparing these two files, we can easily track our current progress. Cursor is very good at writing such small tools:&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/1a7bea6f828c.png"
loading="lazy"
alt="Image.png"
>&lt;/p>
&lt;p>We just need to copy elements from the webpage, and Cursor can write complete CSV files for us and automatically generate a perfect frontend interface, greatly improving our problem-solving efficiency.&lt;/p></description></item><item><title>How to install Rodin software on a MacBook with an Apple chip</title><link>https://nansenli.com/post/2025/02/rodin/</link><pubDate>Sat, 01 Feb 2025 13:01:39 +0000</pubDate><guid>https://nansenli.com/post/2025/02/rodin/</guid><description>&lt;h3 id="steps-to-install-rodin-on-macos-m1-arm-architecture">&lt;strong>Steps to Install Rodin on macOS (M1 ARM Architecture):&lt;/strong>
&lt;/h3>&lt;ol>
&lt;li>&lt;strong>Install x86 JDK&lt;/strong>
&lt;ul>
&lt;li>
&lt;p>Download the &lt;strong>Intel x86 JDK 18&lt;/strong> (macOS .dmg) from Oracle:&lt;/p>
&lt;p>&lt;a class="link" href="https://download.oracle.com/java/18/archive/jdk-18.0.2.1_macos-x64_bin.dmg" target="_blank" rel="noopener"
>jdk-18.0.2.1_macos-x64_bin.dmg&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Run the installer; it automatically configures the Java environment.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Download and Install Rodin&lt;/strong>
&lt;ul>
&lt;li>
&lt;p>Get &lt;strong>Rodin 3.8/3.9&lt;/strong> (x86_64 version) from SourceForge:&lt;/p>
&lt;p>&lt;a class="link" href="https://sourceforge.net/projects/rodin-b-sharp/files/Core_Rodin_Platform/3.8/rodin-3.8.0.202304051545-af2f57e1e-macosx.cocoa.x86_64.tar.gz/download" target="_blank" rel="noopener"
>rodin-3.8.0.202304051545- macosx.cocoa.x86_64.tar.gz&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Extract the &lt;code>.tar.gz&lt;/code> file and move &lt;code>Rodin.app&lt;/code> to the &lt;code>/Applications&lt;/code> folder.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Fix macOS Security Permissions&lt;/strong>&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>Run this command to bypass &amp;ldquo;app is damaged&amp;rdquo; errors:&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>sudo xattr -cr /Applications/Rodin.app
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Rodin should be able to run without any additional steps (e.g., configuring the Java VM path).&lt;/p></description></item><item><title>Study of Differential Attack on FEAL-4</title><link>https://nansenli.com/post/2025/01/feal-4/</link><pubDate>Wed, 01 Jan 2025 13:15:04 +0000</pubDate><guid>https://nansenli.com/post/2025/01/feal-4/</guid><description>&lt;p>This article primarily introduces how to perform differential cryptanalysis on the traditional block cipher algorithm FEAL-4, and demonstrates the attack process and key implementation details. The text also includes some experimental data and attack results for interested readers to reference or reproduce.&lt;/p>
&lt;hr>
&lt;h2 id="two-important-images">Two Important Images
&lt;/h2>&lt;p>Before delving into the analysis, here are two images closely related to FEAL-4 to provide an intuitive understanding of differential propagation and the overall encryption process.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Differential Propagation Diagram&lt;/strong>&lt;br>
The figure below shows how plaintext differences (in XOR form) propagate through the encryption rounds in FEAL-4 and ultimately reflect in the ciphertext.&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/d9f08a73ad65.png"
loading="lazy"
>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>FEAL-4 Workflow Diagram&lt;/strong>&lt;br>
The following figure illustrates the overall encryption framework of FEAL-4, including the left and right blocks of the plaintext, the injection of round subkeys, and the usage flow of the F function.&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/73e0b1400fd1.png"
loading="lazy"
>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="characteristics-of-the-f-function">Characteristics of the F Function
&lt;/h2>&lt;p>The round function of FEAL-4 (i.e., the F function) can be viewed as some form of random permutation on a 32-bit input. In differential analysis, we do not need the specific implementation of F, but rather focus on its two key differential properties:&lt;/p>
&lt;ul>
&lt;li>If $X \oplus Y = 0$, then $F(X) = F(Y)$.&lt;/li>
&lt;li>If $X \oplus Y = 0x80800000$, then $F(X) \oplus F(Y) = 0x02000000$.&lt;/li>
&lt;/ul>
&lt;p>With these two conclusions, we can infer the output behavior under specific differential inputs in subsequent analyses, thereby aiding in locating the subkeys.&lt;/p>
&lt;hr>
&lt;h2 id="detailed-analysis-of-differential-paths">Detailed Analysis of Differential Paths
&lt;/h2>&lt;p>Suppose we choose a pair of special plaintexts $P_0$ and $P_1$ such that:
$$
P_0 \oplus P_1 = 0x8080000080800000 = P^{\prime}.
$$
This specific differential vector helps simplify the differential computations in several rounds of FEAL-4 outputs.&lt;/p>
&lt;h3 id="1-derivation-of-initial-round-differences">1. Derivation of Initial Round Differences
&lt;/h3>&lt;ul>
&lt;li>
&lt;p>Let $(L0_0, R0_0)$ denote the left and right blocks of $P_0$ (similarly, $(L0_1, R0_1)$ denotes those of $P_1$).&lt;/p>
&lt;/li>
&lt;li>
&lt;p>After the XOR operation in the first round:
$$
L1_0 \oplus L1_1 = (L0_0 \oplus K4) \oplus (L0_1 \oplus K4)
= L0_0 \oplus L0_1
= 0x80800000.
$$
Similarly, the differential of the right half can be derived.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Furthermore, due to the &amp;ldquo;additional XOR round&amp;rdquo; during encryption, we obtain:
$$
R2_0 \oplus R2_1
= (R1_0 \oplus L1_0) \oplus (R1_1 \oplus L1_1)
= 0x80800000 \oplus 0x80800000
= 0.
$$
This implies that in subsequent computations, the differences can be merged in a simpler form.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h3 id="2-backward-differential-calculation">2. Backward Differential Calculation
&lt;/h3>&lt;p>After obtaining the plaintext pair $(P_0, P_1)$ and the corresponding ciphertext pair $(C_0, C_1)$, further backward derivations can be made. Let the ciphertext be denoted as $(L, R)$, then:&lt;/p>
&lt;ul>
&lt;li>Compute $C^{\prime} = C_0 \oplus C_1$, obtaining $(L^{\prime}, R^{\prime})$.&lt;/li>
&lt;li>Based on the known differential properties, gradually restore intermediate variables such as $X^{\prime}$, $Y^{\prime}$, $Z^{\prime}$, etc., in the last round.&lt;/li>
&lt;li>Using these results, attacks or verifications on the subkeys can be performed.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="attacking-k3">Attacking K3
&lt;/h2>&lt;p>In differential analysis, one often starts with attacking an intermediate round subkey, such as $K_3$. The specific steps are as follows:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Select Plaintext Pairs:&lt;/strong> I generated 12 pairs of random plaintexts satisfying&lt;br>
$$
P_0 \oplus P_1 = 0x8080000080800000
$$&lt;/li>
&lt;li>&lt;strong>Encrypt and Extract Differences:&lt;/strong> Encrypt the above plaintext pairs to obtain the corresponding ciphertext pairs; combine with the differential path to derive intermediate values $Y_0$, $Y_1$, $Z^{\prime}$, etc.&lt;/li>
&lt;li>&lt;strong>Traverse $K_3$:&lt;/strong>&lt;br>
$$
Z^{\prime} = Z_0 \oplus Z_1 = F(Y_0 \oplus K_3) \oplus F(Y_1 \oplus K_3).
$$&lt;br>
Any candidate $K_3$ that satisfies the differential equation $= 0x02000000$ is retained.&lt;/li>
&lt;/ol>
&lt;p>Using this method, I successfully obtained four candidate subkeys:&lt;/p>
&lt;pre tabindex="0">&lt;code>cfa38976, cfa309f6, 4f238976, 4f2309f6
&lt;/code>&lt;/pre>&lt;p>An example command to execute the above process is:&lt;/p>
&lt;pre tabindex="0">&lt;code>./main -mode=attackk3 -file=K3.txt
&lt;/code>&lt;/pre>&lt;p>This implementation is consistent with the method I introduced on &lt;a class="link" href="https://www.theamazingking.com" target="_blank" rel="noopener"
>www.theamazingking.com&lt;/a>.&lt;/p>
&lt;hr>
&lt;h2 id="attacking-k2">Attacking K2
&lt;/h2>&lt;p>After obtaining several candidates for $K_3$, we can proceed to deduce $K_2$. The core equation is:
$$
X&amp;rsquo; = X_0 \oplus X_1
= F(U_0 \oplus K_2) \oplus F(U_1 \oplus K_2),
$$
where $U$ comes from the previous round operations (including further computations on $Y$, $Z$, etc.). Similarly:&lt;/p>
&lt;ol>
&lt;li>Generate plaintext pairs that satisfy the differential condition
$$
P_0 \oplus P_1 = 0x0000000080800000.
$$&lt;/li>
&lt;li>Capture ciphertext differences after encryption, and by traversing all possible $K_2$ values, check if the differential constraint $=0x02000000$ is satisfied.&lt;/li>
&lt;li>Example command:
&lt;pre tabindex="0">&lt;code>./main -mode=attackk2 -file=K2.txt -k3=cfa38976,cfa309f6,4f238976,4f2309f6
&lt;/code>&lt;/pre>&lt;/li>
&lt;li>The final output example is as follows:
&lt;pre tabindex="0">&lt;code>Candidate K2: 8b722e41 (K3: cfa38976)
Candidate K2: 8b72aec1 (K3: cfa38976)
...
Candidate K2: 89722e43 (K3: 4f2309f6)
Candidate K2: 8972aec3 (K3: 4f2309f6)
&lt;/code>&lt;/pre>&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="attacking-k1">Attacking K1
&lt;/h2>&lt;p>Similarly, attacking $K_1$ can be done following the previous process.&lt;br>
To ensure the differential path consistently presents the expected results, I generated 12 plaintext pairs satisfying&lt;br>
$$
P_0 \oplus P_1 = 0x0000000002000000
$$&lt;br>
and verified their encryption outputs.&lt;/p>
&lt;p>Example command:&lt;/p>
&lt;pre tabindex="0">&lt;code>./main -mode=attackk1 -file=K1.txt -k3k2=&amp;#34;cfa38976,8b722e41; cfa38976,8b72aec1; ...&amp;#34;
&lt;/code>&lt;/pre>&lt;p>This allows you to input all previously obtained candidate combinations of $K_3$ and $K_2$ at once, and finally find the corresponding $K_1$ candidates.&lt;/p>
&lt;hr>
&lt;h2 id="attacking-k0-k4-k5">Attacking K0, K4, K5
&lt;/h2>&lt;p>Once $K_3$, $K_2$, and $K_1$ are all obtained, $K_0$, $K_4$, and $K_5$ required for the last round can be deduced.&lt;br>
Let the final encrypted left and right parts be $L_0$, $R_0$. The corresponding plaintext blocks are $PL$, $PR$. Then:&lt;/p>
&lt;ul>
&lt;li>$PL \oplus K_4 = LR_0$&lt;/li>
&lt;li>$PR \oplus K_5 = RR_0$&lt;/li>
&lt;li>$RR_0 \oplus LR_0 = R_0$&lt;/li>
&lt;li>$f(R_0 \oplus K_0) \oplus L_0 = LR_0$&lt;/li>
&lt;/ul>
&lt;p>By enumerating all possible $K_0$, the corresponding $K_4$ and $K_5$ can be solved through the above equations. Then, use other plaintext-ciphertext pairs to cross-validate correctness. If all are satisfied, the correct final key set is obtained.&lt;/p>
&lt;p>Example command:&lt;/p>
&lt;pre tabindex="0">&lt;code>./main -mode=attackk0k4k5 -k3k2k1=&amp;#34;...&amp;#34; -file=K1.txt -file2=K1_p.txt
&lt;/code>&lt;/pre>&lt;hr>
&lt;h2 id="implementation-optimization-and-verification">Implementation, Optimization, and Verification
&lt;/h2>&lt;h3 id="1-automated-generation-and-parallel-computing">1. Automated Generation and Parallel Computing
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>Automatically Generate Random Plaintext Pairs:&lt;/strong> Control the XOR differences in the code in advance to batch output plaintext files that meet the requirements (e.g., &lt;code>K3_p.txt&lt;/code>, &lt;code>K2_p.txt&lt;/code>, &lt;code>K1_p.txt&lt;/code>).&lt;/li>
&lt;li>&lt;strong>Parallelized Search:&lt;/strong> Use Go&amp;rsquo;s Goroutines to start 10 parallel tasks, maximizing CPU resource utilization on a 10-core Mac.&lt;/li>
&lt;li>&lt;strong>Progress Bars and Visualization:&lt;/strong> Use the &lt;code>progressbar&lt;/code> library to display search progress in real-time, making the experimental process more intuitive.&lt;/li>
&lt;/ul>
&lt;h3 id="2-verification-methods">2. Verification Methods
&lt;/h3>&lt;ul>
&lt;li>
&lt;p>&lt;strong>Local Custom Key Testing&lt;/strong>&lt;br>
First, select a set of known $K_0$–$K_5$ (e.g., &lt;code>0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, 0x00000020&lt;/code>), encrypt the previously generated plaintext pairs, and repeat the differential analysis process to see if this set of keys can be matched among the results.&lt;br>
Experiments confirmed the ability to correctly restore this set of test keys, thereby verifying the accuracy of the code logic.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Specific Plaintext-Ciphertext Pair Testing&lt;/strong>&lt;br>
Select a plaintext &lt;code>1234567890abcdef&lt;/code> and the ciphertext &lt;code>f43ae3eeb56e2bbf&lt;/code> generated in the Einstein Zone, and verify that each of the 256 candidate $K_0$–$K_5$ sets can reproduce this encryption mapping, further confirming the attack process is correct.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="final-results">Final Results
&lt;/h2>&lt;p>All deduced $K_0$–$K_5$ are stored in &lt;code>final_result.txt&lt;/code>. After deduplication, they include the following ranges:&lt;/p>
&lt;h3 id="possible-values-for-k0">Possible Values for K0
&lt;/h3>&lt;pre tabindex="0">&lt;code>890c2148 890ca1c8 098c2148 098ca1c8 ...
0b8c214a 0b8ca1ca 8b0c214a 8b0ca1ca
&lt;/code>&lt;/pre>&lt;h3 id="possible-values-for-k1">Possible Values for K1
&lt;/h3>&lt;pre tabindex="0">&lt;code>471f077e 471f87fe c79f077e c79f87fc ...
451f077c 451f87fc c59f077c c59f87fc
&lt;/code>&lt;/pre>&lt;h3 id="possible-values-for-k2">Possible Values for K2
&lt;/h3>&lt;pre tabindex="0">&lt;code>8b722e41 8b72aec1 8b722e43 8b72aec3 ...
89722e43 8972aec3
&lt;/code>&lt;/pre>&lt;h3 id="possible-values-for-k3">Possible Values for K3
&lt;/h3>&lt;pre tabindex="0">&lt;code>cfa38976 cfa309f6 4f238976 4f2309f6
&lt;/code>&lt;/pre>&lt;h3 id="possible-values-for-k4">Possible Values for K4
&lt;/h3>&lt;pre tabindex="0">&lt;code>89eb0024 89eb0026 8beb0024 8beb0026
&lt;/code>&lt;/pre>&lt;h3 id="possible-values-for-k5">Possible Values for K5
&lt;/h3>&lt;pre tabindex="0">&lt;code>b85e6bc0 b85e6bc2 ba5e6bc0 ba5e6bc2
&lt;/code>&lt;/pre>&lt;p>With the aid of differential analysis, we ultimately obtained a set of candidate subkeys. Compared to modern block ciphers (such as AES), FEAL-4 has fewer rounds and a simpler structure, making it very suitable for demonstrating the concept of differential analysis in teaching and research.&lt;/p>
&lt;hr>
&lt;h2 id="conclusion">Conclusion
&lt;/h2>&lt;p>This article detailed the complete process of performing a differential attack on FEAL-4, including designing differential paths, generating plaintext pairs, attacking intermediate subkeys ($K_3$, $K_2$, $K_1$), and finally reverse-solving for $K_0$, $K_4$, $K_5$. It also introduced several optimizations and verification methods during the code implementation process.&lt;br>
The FEAL-4 algorithm, due to its fewer rounds and simpler structure, provides convenience for differential analysis. However, for more secure and complex modern block algorithms, the same analytical approach remains crucial. This case study aims to inspire learners and researchers in cryptography. If you have any questions or improvement suggestions, feel free to discuss and exchange ideas in the comments section!&lt;/p></description></item><item><title>How to Quickly Solve Disassembly Problems</title><link>https://nansenli.com/post/2024/11/disassembly/</link><pubDate>Sun, 03 Nov 2024 14:21:43 +0000</pubDate><guid>https://nansenli.com/post/2024/11/disassembly/</guid><description>&lt;p>For DCU&amp;rsquo;s Secure Programming course, the disassembly problems have a certain pattern. Using a fixed approach to solving them can help achieve quick results.&lt;/p>
&lt;h1 id="prerequisite-skills">Prerequisite Skills
&lt;/h1>&lt;ol>
&lt;li>Familiarity with assembly commands &lt;a class="link" href="craftdocs://open?blockId=8DC79A21-6886-4C9D-BAF2-8CAB0A186B73&amp;amp;spaceId=c5e6e898-910c-6f27-fbc5-91f4c34a9511" >Assembly Instructions&lt;/a>&lt;/li>
&lt;li>Understanding &lt;code>%&lt;/code> and &lt;code>$&lt;/code> for registers and immediate values &lt;a class="link" href="craftdocs://open?blockId=624020ED-93E1-483B-8FA7-7D21D0B71036&amp;amp;spaceId=c5e6e898-910c-6f27-fbc5-91f4c34a9511" >$ and % Registers and Immediate Values&lt;/a>&lt;/li>
&lt;li>Knowledge of direct and indirect addressing &lt;a class="link" href="craftdocs://open?blockId=C4F438DF-5C27-4FAF-82DF-7D815FB93AD1&amp;amp;spaceId=c5e6e898-910c-6f27-fbc5-91f4c34a9511" >Direct and Indirect Addressing&lt;/a>&lt;/li>
&lt;li>Familiarity with one example &lt;a class="link" href="craftdocs://open?blockId=F8D3758A-2BBE-46A0-B06A-9CB8EBC9D23E&amp;amp;spaceId=c5e6e898-910c-6f27-fbc5-91f4c34a9511" >C Code to Assembly Example&lt;/a>&lt;/li>
&lt;/ol>
&lt;h1 id="approach-to-solving">Approach to Solving
&lt;/h1>&lt;ol>
&lt;li>&lt;a class="link" href="craftdocs://open?blockId=5FC17CD5-B366-4554-B226-D38877BAB7A4&amp;amp;spaceId=c5e6e898-910c-6f27-fbc5-91f4c34a9511" >Identify the number of parameters&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="craftdocs://open?blockId=8B96E2AA-7B13-4371-8DF9-37856DD028BD&amp;amp;spaceId=c5e6e898-910c-6f27-fbc5-91f4c34a9511" >Identify the number of local variables&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="craftdocs://open?blockId=F7BE55C0-B922-4724-8405-BD72D9F0C85A&amp;amp;spaceId=c5e6e898-910c-6f27-fbc5-91f4c34a9511" >Recognize the loop body&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="craftdocs://open?blockId=4E432226-5F3A-411D-8479-34D681002111&amp;amp;spaceId=c5e6e898-910c-6f27-fbc5-91f4c34a9511" >Analyze remaining code snippets&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="craftdocs://open?blockId=E0AD97BA-3EFF-4498-9847-0536A7B7DABD&amp;amp;spaceId=c5e6e898-910c-6f27-fbc5-91f4c34a9511" >Identify the return value&lt;/a>&lt;/li>
&lt;/ol>
&lt;h2 id="identify-the-number-of-parameters">Identify the Number of Parameters
&lt;/h2>&lt;p>The position of &lt;code>ebp&lt;/code> is the saved frame pointer, and &lt;code>ebp+4&lt;/code> is the return address. Since the problems typically assume all parameters are of type &lt;code>int&lt;/code> or &lt;code>int*&lt;/code>, &lt;code>ebp+8&lt;/code>, &lt;code>ebp+c&lt;/code>, and &lt;code>ebp+10&lt;/code> correspond to the first, second, and third parameters, respectively.&lt;/p>
&lt;p>By quickly scanning the code for occurrences of &lt;code>0x__(%ebp)&lt;/code> and identifying the largest offset, the number of parameters can be determined as &lt;code>(offset - 4) // 4&lt;/code>.&lt;/p>
&lt;p>For example:&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-assembly" data-lang="assembly">push %ebp &amp;lt;foo+0&amp;gt;
mov %esp, %ebp &amp;lt;foo+1&amp;gt;
sub $0x4, %esp &amp;lt;foo+3&amp;gt;
mov 0x8(%ebp), %eax &amp;lt;foo+6&amp;gt;
mov %eax, -0x4(%ebp) &amp;lt;foo+9&amp;gt;
mov -0x4(%ebp), %eax &amp;lt;foo+12&amp;gt;
cmp 0x10(%ebp), %eax &amp;lt;foo+15&amp;gt;
jge &amp;lt;foo+32&amp;gt; &amp;lt;foo+18&amp;gt;
mov 0xc(%ebp), %eax &amp;lt;foo+20&amp;gt;
incl (%eax) &amp;lt;foo+23&amp;gt;
lea -0x4(%ebp), %eax &amp;lt;foo+25&amp;gt;
incl (%eax) &amp;lt;foo+28&amp;gt;
jmp &amp;lt;foo+12&amp;gt; &amp;lt;foo+30&amp;gt;
mov $0x0, %eax &amp;lt;foo+32&amp;gt;
leave &amp;lt;foo+37&amp;gt;
ret &amp;lt;foo+38&amp;gt;
&lt;/code>&lt;/pre>&lt;p>Here, &lt;code>0x10(%ebp)&lt;/code> exists, so the parameter count is &lt;code>(16 - 4) / 4 = 3&lt;/code>.&lt;/p>
&lt;p>We can construct the framework of the code as:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-c" data-lang="c">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#a6e22e">foo&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> a, &lt;span style="color:#66d9ef">int&lt;/span> b, &lt;span style="color:#66d9ef">int&lt;/span> c) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>a&lt;/code>, &lt;code>b&lt;/code>, and &lt;code>c&lt;/code> correspond to &lt;code>ebp+8&lt;/code>, &lt;code>ebp+c&lt;/code>, and &lt;code>ebp+10&lt;/code>, respectively. Note that parameters are pushed onto the stack in reverse order, so the closer to &lt;code>ebp&lt;/code>, the earlier the parameter appears in the list.&lt;/p>
&lt;p>For now, assume all are &lt;code>int&lt;/code> types. Adjust later if inconsistencies are found.&lt;/p>
&lt;h2 id="identify-the-number-of-local-variables">Identify the Number of Local Variables
&lt;/h2>&lt;p>The number of local variables is determined by the third line of the code: &lt;code>sub $0x4, %esp&lt;/code>. The amount subtracted corresponds to the length of the allocated local variables.&lt;/p>
&lt;p>In this example, &lt;code>sub $0x4, %esp&lt;/code> indicates 4 bytes, so there is one local variable. Assume it is an &lt;code>int&lt;/code> and name it &lt;code>i&lt;/code>.&lt;/p>
&lt;p>The code expands to:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-c" data-lang="c">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#a6e22e">foo&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> a, &lt;span style="color:#66d9ef">int&lt;/span> b, &lt;span style="color:#66d9ef">int&lt;/span> c) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">int&lt;/span> i;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="recognize-the-loop-body">Recognize the Loop Body
&lt;/h2>&lt;p>Loops are typically &lt;code>while&lt;/code> or &lt;code>for&lt;/code> loops. To identify:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Judgment Entry:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Look for a comparison instruction (e.g., &lt;code>cmp&lt;/code>) followed by a jump instruction (e.g., &lt;code>jge&lt;/code> or &lt;code>jle&lt;/code>).&lt;/li>
&lt;li>These indicate the start of a condition check.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Loop Body:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Unconditional &lt;code>jmp&lt;/code> instructions signify loops. The jump target is the beginning of the condition check.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Condition:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>The judgment condition combines the comparison and preceding instructions into a complete condition.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h3 id="example">Example:
&lt;/h3>&lt;pre tabindex="0">&lt;code class="language-assembly" data-lang="assembly">push %ebp &amp;lt;foo+0&amp;gt;
mov %esp, %ebp &amp;lt;foo+1&amp;gt;
sub $0x4, %esp &amp;lt;foo+3&amp;gt;
mov 0x8(%ebp), %eax &amp;lt;foo+6&amp;gt;
mov %eax, -0x4(%ebp) &amp;lt;foo+9&amp;gt;
mov -0x4(%ebp), %eax &amp;lt;foo+12&amp;gt;
cmp 0x10(%ebp), %eax &amp;lt;foo+15&amp;gt;
jge &amp;lt;foo+32&amp;gt; &amp;lt;foo+18&amp;gt;
mov 0xc(%ebp), %eax &amp;lt;foo+20&amp;gt;
incl (%eax) &amp;lt;foo+23&amp;gt;
lea -0x4(%ebp), %eax &amp;lt;foo+25&amp;gt;
incl (%eax) &amp;lt;foo+28&amp;gt;
jmp &amp;lt;foo+12&amp;gt; &amp;lt;foo+30&amp;gt;
mov $0x0, %eax &amp;lt;foo+32&amp;gt;
leave &amp;lt;foo+37&amp;gt;
ret &amp;lt;foo+38&amp;gt;
&lt;/code>&lt;/pre>&lt;p>&lt;strong>Judgment Entry:&lt;/strong>&lt;/p>
&lt;p>The combination of &lt;code>cmp&lt;/code> and &lt;code>jge&lt;/code> indicates a judgment entry.&lt;/p>
&lt;p>&lt;strong>Loop:&lt;/strong>&lt;/p>
&lt;p>The &lt;code>jmp&lt;/code> command jumps to &lt;code>&amp;lt;foo+12&amp;gt;&lt;/code>, signifying the loop condition check.&lt;/p>
&lt;p>&lt;strong>Condition:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;code>mov -0x4(%ebp), %eax&lt;/code>: Assigns the value of &lt;code>i&lt;/code> to &lt;code>eax&lt;/code>.&lt;/li>
&lt;li>&lt;code>cmp 0x10(%ebp), %eax&lt;/code>: Compares &lt;code>eax&lt;/code> (value of &lt;code>i&lt;/code>) with &lt;code>c&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>This calculates &lt;code>i - c&lt;/code> and checks the condition with &lt;code>jge&lt;/code>. In assembly, conditions are reversed compared to C: &lt;code>jge&lt;/code> skips the loop if the condition is met. Thus, the C condition is &lt;code>i - c &amp;lt; 0&lt;/code>.&lt;/p>
&lt;p>The code updates to:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-c" data-lang="c">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#a6e22e">foo&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> a, &lt;span style="color:#66d9ef">int&lt;/span> b, &lt;span style="color:#66d9ef">int&lt;/span> c) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">int&lt;/span> i;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">while&lt;/span> (i &lt;span style="color:#f92672">-&lt;/span> c &lt;span style="color:#f92672">&amp;lt;&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="analyze-remaining-code-snippets">Analyze Remaining Code Snippets
&lt;/h2>&lt;p>&lt;strong>Before the Loop:&lt;/strong>&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-assembly" data-lang="assembly">mov 0x8(%ebp), %eax &amp;lt;foo+6&amp;gt;
mov %eax, -0x4(%ebp) &amp;lt;foo+9&amp;gt;
&lt;/code>&lt;/pre>&lt;p>These lines assign the value of &lt;code>a&lt;/code> to &lt;code>i&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-c" data-lang="c">&lt;span style="display:flex;">&lt;span>i &lt;span style="color:#f92672">=&lt;/span> a;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Inside the Loop:&lt;/strong>&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-assembly" data-lang="assembly">mov 0xc(%ebp), %eax &amp;lt;foo+20&amp;gt;
incl (%eax) &amp;lt;foo+23&amp;gt;
lea -0x4(%ebp), %eax &amp;lt;foo+25&amp;gt;
incl (%eax) &amp;lt;foo+28&amp;gt;
&lt;/code>&lt;/pre>&lt;ul>
&lt;li>&lt;code>mov 0xc(%ebp), %eax&lt;/code> and &lt;code>incl (%eax)&lt;/code> increment the value at the address stored in &lt;code>b&lt;/code>:&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-c" data-lang="c">&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#f92672">*&lt;/span>b)&lt;span style="color:#f92672">++&lt;/span>;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;code>lea -0x4(%ebp), %eax&lt;/code> and &lt;code>incl (%eax)&lt;/code> increment &lt;code>i&lt;/code>:&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-c" data-lang="c">&lt;span style="display:flex;">&lt;span>i&lt;span style="color:#f92672">++&lt;/span>;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The updated code becomes:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-c" data-lang="c">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#a6e22e">foo&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> a, &lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#f92672">*&lt;/span>b, &lt;span style="color:#66d9ef">int&lt;/span> c) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">int&lt;/span> i;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> i &lt;span style="color:#f92672">=&lt;/span> a;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">while&lt;/span> (i &lt;span style="color:#f92672">-&lt;/span> c &lt;span style="color:#f92672">&amp;lt;&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#f92672">*&lt;/span>b)&lt;span style="color:#f92672">++&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> i&lt;span style="color:#f92672">++&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="identify-the-return-value">Identify the Return Value
&lt;/h2>&lt;p>In x86 calling conventions, return values are stored in the &lt;code>eax&lt;/code> register.&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-assembly" data-lang="assembly">mov $0x0, %eax
&lt;/code>&lt;/pre>&lt;p>This indicates the function returns &lt;code>0&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-c" data-lang="c">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="final-code">Final Code
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-c" data-lang="c">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#a6e22e">foo&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> a, &lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#f92672">*&lt;/span>b, &lt;span style="color:#66d9ef">int&lt;/span> c) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">int&lt;/span> i;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> i &lt;span style="color:#f92672">=&lt;/span> a;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">while&lt;/span> (i &lt;span style="color:#f92672">-&lt;/span> c &lt;span style="color:#f92672">&amp;lt;&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#f92672">*&lt;/span>b)&lt;span style="color:#f92672">++&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> i&lt;span style="color:#f92672">++&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>How to handle the inconsistency in LaTeX formatting from ChatGPT between Obsidian and Craft.</title><link>https://nansenli.com/post/2024/10/raycast-craft/</link><pubDate>Sat, 26 Oct 2024 12:59:21 +0000</pubDate><guid>https://nansenli.com/post/2024/10/raycast-craft/</guid><description>&lt;p>In a previous article, I used Obsidian&amp;rsquo;s QuickAdd to create a script that automatically converts text copied from ChatGPT and fixes the LaTeX formatting. However, there is no suitable plugin available for the Craft app.&lt;/p>
&lt;p>We can use Raycast to achieve this functionality uniformly.&lt;/p>
&lt;hr>
&lt;h1 id="create-a-raycast-script">Create a Raycast Script
&lt;/h1>&lt;p>First, we need to create a script.&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/2abd177df831.png"
loading="lazy"
alt="image"
>&lt;/p>
&lt;p>Next, select the Bash template.&lt;/p>
&lt;p>Then, we edit the Bash script and enter the following code:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#!/bin/bash
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Required parameters:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># @raycast.schemaVersion 1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># @raycast.title Copy From ChatGPT&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># @raycast.mode silent&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Optional parameters:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># @raycast.icon 🤖&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># @raycast.packageName ChatGPT Utils&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Documentation:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># @raycast.description Copy From ChatGPT&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># @raycast.author Nansen Li&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># @raycast.authorURL nansenli.com&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Get clipboard content&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>clipboard_content&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#66d9ef">$(&lt;/span>pbpaste&lt;span style="color:#66d9ef">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Check if content was successfully retrieved&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">if&lt;/span> &lt;span style="color:#f92672">[&lt;/span> -z &lt;span style="color:#e6db74">&amp;#34;&lt;/span>$clipboard_content&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">]&lt;/span>; &lt;span style="color:#66d9ef">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> echo &lt;span style="color:#e6db74">&amp;#34;Clipboard is empty or inaccessible.&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exit &lt;span style="color:#ae81ff">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Process clipboard content&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>modified_content&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#66d9ef">$(&lt;/span>echo &lt;span style="color:#e6db74">&amp;#34;&lt;/span>$clipboard_content&lt;span style="color:#e6db74">&amp;#34;&lt;/span> | &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> sed &lt;span style="color:#e6db74">&amp;#39;s/\\\[/$$/g; s/\\\]/$$/g; s/\\( /$/g; s/ \\\)/$/g&amp;#39;&lt;/span>&lt;span style="color:#66d9ef">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Write the modified content back to the clipboard&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>echo &lt;span style="color:#e6db74">&amp;#34;&lt;/span>$modified_content&lt;span style="color:#e6db74">&amp;#34;&lt;/span> | pbcopy
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;p>After creating the script, we need to add the directory containing the script to Raycast.&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/60724508c66a.png"
loading="lazy"
alt="image"
>&lt;/p>
&lt;p>In this step, select the directory where the script was just created. At this point, we can see the newly created script in &lt;code>Script Commands&lt;/code>.&lt;/p>
&lt;hr>
&lt;h1 id="how-to-use">How to Use
&lt;/h1>&lt;p>After copying a formula from ChatGPT, open the Raycast panel, find the newly created script, and run it. The clipboard content will be automatically fixed. Then, simply paste it into Obsidian or Craft.&lt;/p></description></item><item><title>Summary of Huawei Ireland Research Center Tech Arena 2024 Competition</title><link>https://nansenli.com/post/2024/10/huawei2024/</link><pubDate>Thu, 24 Oct 2024 10:01:27 +0000</pubDate><guid>https://nansenli.com/post/2024/10/huawei2024/</guid><description>&lt;h2 id="background">Background
&lt;/h2>&lt;p>I&amp;rsquo;m Nansen, and I participated in the 2024 Huawei Ireland Research Center Server Cluster Management Optimization Competition. Here, I’d like to share my experience in this competition and summarize some key takeaways.&lt;/p>
&lt;p>Our algorithm code can be found here: &lt;a class="link" href="https://github.com/linanwx/huawei2024" target="_blank" rel="noopener"
>huawei2024&lt;/a>&lt;/p>
&lt;h2 id="competition-results">Competition Results
&lt;/h2>&lt;p>We achieved first place in the algorithm section, scoring approximately 4%-5% higher than the second to fourth places, giving us a significant advantage. However, we faced considerable challenges in the presentation segment. First, we recognized that there is room for improvement in our English fluency. Second, we found that our presentation slides could be more polished and visually appealing. Lastly, we encountered some challenges with time management. Nevertheless, despite these obstacles, we still managed to secure third place overall.&lt;/p>
&lt;h2 id="competition-process">Competition Process
&lt;/h2>&lt;p>The competition was divided into two stages. The first stage allowed ample preparation time. Once we settled on using the simulated annealing algorithm, we began developing it. The main difficulty in this stage was optimizing and understanding the requirements of the task. During development, we encountered numerous bugs, but after fixing them, our score improved significantly.&lt;/p>
&lt;p>In the second stage, as the problem was released on the day of the competition, I continued optimizing the algorithm from the first stage, successfully increasing the evaluation speed by 1000 times. This significantly boosted our performance in the second stage, providing us with enough strength to vie for first place.&lt;/p>
&lt;p>In the final round, our algorithm performed very consistently, and after some adjustments, we took a considerable lead over our competitors. However, because we didn&amp;rsquo;t put enough emphasis on making an effective presentation, we mistakenly believed that high algorithm performance alone would guarantee a top score, which proved to be wrong.&lt;/p>
&lt;h2 id="lessons-learned">Lessons Learned
&lt;/h2>&lt;h3 id="algorithm-choice">Algorithm Choice
&lt;/h3>&lt;p>Fortunately, I chose the right algorithm from the outset, and shortly after the problem was released, I devised a framework that suited the entire competition. However, I did take some wrong turns, such as attempting impractical algorithms like PPO. After initial trials failed, I should have moved on instead of wasting further effort. Given the limited time, we should focus on achievable optimal results within the shortest period rather than pursuing ideal but unrealistic solutions. It&amp;rsquo;s also crucial to recognize one&amp;rsquo;s limitations and concentrate on goals that are achievable in the available time.&lt;/p>
&lt;h3 id="team-collaboration">Team Collaboration
&lt;/h3>&lt;p>Luckily, our team division was reasonable this time, and I did my best to ensure every member could contribute their value. One area for improvement is communicating more with team members to understand their ideas and preferences. Since I mainly handled the algorithm part, I had relatively little interaction with teammates, which I will work to improve next time.&lt;/p>
&lt;h3 id="presentation-design">Presentation Design
&lt;/h3>&lt;p>We didn’t anticipate that the level of presentation skills from other teams would be so high. My teammates speculated that some competitors might have a business background, giving them an advantage in crafting presentations. Moreover, they had five members in their team while we only had three, which put us at a disadvantage regarding manpower. These were objective challenges, but if we had paid more attention to creating our presentation, the first prize could have been within our reach.&lt;/p>
&lt;h3 id="over-committing-leading-to-imbalance">Over-committing Leading to Imbalance
&lt;/h3>&lt;p>In the final round, our algorithm was already quite excellent, and our score surpassed the previously top-ranked team. However, I continued spending considerable time on further optimizations. Even though we were significantly ahead, this focus caused us to neglect the preparation of our presentation. In hindsight, I should have known when to stop and fully recognized the importance of balancing different aspects of the scoring criteria.&lt;/p>
&lt;h2 id="conclusion">Conclusion
&lt;/h2>&lt;p>Participating in the Huawei Tech Arena 2024 competition provided me with invaluable experience. The competition highlighted our strengths, but also revealed areas where we need to improve in terms of showcasing skills and team collaboration. Looking ahead, I will keep these lessons in mind and strive to continuously improve myself in future competitions. If you have any questions, feel free to leave them in the comments section.&lt;/p></description></item><item><title>Leetcode Notes</title><link>https://nansenli.com/post/2024/10/leetcode241020/</link><pubDate>Mon, 21 Oct 2024 19:39:42 +0000</pubDate><guid>https://nansenli.com/post/2024/10/leetcode241020/</guid><description>&lt;p>Today, I began the long journey of practicing Leetcode problems. Previously, I only did a few problems to maintain familiarity, but today, I officially started preparing for interviews.&lt;/p>
&lt;p>I have been thinking about how to efficiently solve Leetcode problems. In my opinion, to be efficient, one must memorize problems. Just as reading a book a hundred times reveals its meaning, training a language model through extensive practice hones its coding skills. Similarly, with Leetcode, through repeated practice, the answers will come naturally; quantity brings quality.&lt;/p>
&lt;h2 id="53-maximum-subarrayhttpsleetcodecomproblemsmaximum-subarray">&lt;a class="link" href="https://leetcode.com/problems/maximum-subarray/" target="_blank" rel="noopener"
>53. Maximum Subarray&lt;/a>
&lt;/h2>&lt;p>The solution can be approached using &lt;strong>Kadane&amp;rsquo;s Algorithm&lt;/strong>. The code is as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">def&lt;/span> &lt;span style="color:#a6e22e">maxSubArray&lt;/span>(nums):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> max_current &lt;span style="color:#f92672">=&lt;/span> max_global &lt;span style="color:#f92672">=&lt;/span> nums[&lt;span style="color:#ae81ff">0&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span> num &lt;span style="color:#f92672">in&lt;/span> nums[&lt;span style="color:#ae81ff">1&lt;/span>:]:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> max_current &lt;span style="color:#f92672">=&lt;/span> max(num, max_current &lt;span style="color:#f92672">+&lt;/span> num)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> max_global &lt;span style="color:#f92672">=&lt;/span> max(max_global, max_current)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> max_global
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is a classic dynamic programming problem, and the above algorithm actually hides the essence of dynamic programming.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Solution&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span>&lt;span style="color:#f92672">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">int&lt;/span> maxSubArray(vector&lt;span style="color:#f92672">&amp;lt;&lt;/span>&lt;span style="color:#66d9ef">int&lt;/span>&lt;span style="color:#f92672">&amp;gt;&amp;amp;&lt;/span> nums) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> vector&lt;span style="color:#f92672">&amp;lt;&lt;/span>&lt;span style="color:#66d9ef">int&lt;/span>&lt;span style="color:#f92672">&amp;gt;&lt;/span> dp(nums.size());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dp[&lt;span style="color:#ae81ff">0&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> nums[&lt;span style="color:#ae81ff">0&lt;/span>];
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span> (&lt;span style="color:#66d9ef">int&lt;/span> i &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>; i &lt;span style="color:#f92672">&amp;lt;&lt;/span> nums.size(); i&lt;span style="color:#f92672">++&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dp[i] &lt;span style="color:#f92672">=&lt;/span> max(dp[i &lt;span style="color:#f92672">-&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>] &lt;span style="color:#f92672">+&lt;/span> nums[i], nums[i]);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#f92672">*&lt;/span>&lt;span style="color:#a6e22e">max_element&lt;/span>(dp.begin(), dp.end());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>};
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This code better reflects the essence of dynamic programming.&lt;/p>
&lt;p>To understand the formula &lt;code>dp[i] = max(nums[i] + dp[i-1], nums[i])&lt;/code>, we can analyze it from a dynamic programming perspective. The core idea here is to make the optimal choice at each position. Here is a detailed explanation:&lt;/p>
&lt;h3 id="1-what-does-dpi-represent">1. What does &lt;code>dp[i]&lt;/code> represent?
&lt;/h3>&lt;ul>
&lt;li>&lt;code>dp[i]&lt;/code> represents the &lt;strong>maximum subarray sum ending at position &lt;code>i&lt;/code>&lt;/strong>.&lt;/li>
&lt;/ul>
&lt;h3 id="2-why-compare-numsi--dpi-1-and-numsi">2. Why compare &lt;code>nums[i] + dp[i-1]&lt;/code> and &lt;code>nums[i]&lt;/code>?
&lt;/h3>&lt;ul>
&lt;li>The key question is: &lt;strong>Should the current maximum subarray include the previous part (&lt;code>dp[i-1]&lt;/code>)?&lt;/strong>
&lt;ul>
&lt;li>&lt;strong>&lt;code>nums[i] + dp[i-1]&lt;/code>&lt;/strong>: If &lt;code>dp[i-1]&lt;/code> is positive, adding the current &lt;code>nums[i]&lt;/code> will increase the subarray sum, so we choose to add it.&lt;/li>
&lt;li>&lt;strong>&lt;code>nums[i]&lt;/code>&lt;/strong>: If &lt;code>dp[i-1]&lt;/code> is negative, we choose to start a new subarray from the current position, as a negative sum will only drag down the current sum.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="3-why-not-compare-subsequent-numbers">3. Why not compare subsequent numbers?
&lt;/h3>&lt;ul>
&lt;li>When making the comparison, we assume the subarray stops at position &lt;code>i&lt;/code>. In other words, we consider the maximum value within the range &lt;code>[0:i]&lt;/code>. At position &lt;code>i&lt;/code>, we either add the previous subarray or abandon it and only use the current number.&lt;/li>
&lt;li>We then traverse the entire array, finding the maximum value at each position, and finally return the largest value.&lt;/li>
&lt;/ul>
&lt;h2 id="57-insert-intervalhttpsleetcodecomproblemsinsert-interval">&lt;a class="link" href="https://leetcode.com/problems/insert-interval/" target="_blank" rel="noopener"
>57. Insert Interval&lt;/a>
&lt;/h2>&lt;p>This is a classic interval merging problem, where we need to merge a new interval into existing intervals.&lt;/p>
&lt;p>The solution can be broken down as follows:&lt;/p>
&lt;ul>
&lt;li>Step 1: Add all non-overlapping intervals that come before &lt;code>newInterval&lt;/code> to the result.&lt;/li>
&lt;li>Step 2: Merge all potentially overlapping intervals with &lt;code>newInterval&lt;/code>. Note the conditions for merging.&lt;/li>
&lt;li>Step 3: Add the remaining intervals to the result.&lt;/li>
&lt;/ul>
&lt;p>Note that the condition for merging intervals is that the start of the previous interval is less than or equal to the end of the subsequent interval, i.e., &lt;code>intervals[i][0] &amp;lt;= newInterval[1]&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Solution&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">def&lt;/span> &lt;span style="color:#a6e22e">insert&lt;/span>(self, intervals: List[List[int]], newInterval: List[int]) &lt;span style="color:#f92672">-&amp;gt;&lt;/span> List[List[int]]:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ret_list &lt;span style="color:#f92672">=&lt;/span> []
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> i &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">while&lt;/span> i &lt;span style="color:#f92672">&amp;lt;&lt;/span> len(intervals) &lt;span style="color:#f92672">and&lt;/span> intervals[i][&lt;span style="color:#ae81ff">1&lt;/span>] &lt;span style="color:#f92672">&amp;lt;&lt;/span> newInterval[&lt;span style="color:#ae81ff">0&lt;/span>]:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ret_list&lt;span style="color:#f92672">.&lt;/span>append(intervals[i])
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> i &lt;span style="color:#f92672">+=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">while&lt;/span> i &lt;span style="color:#f92672">&amp;lt;&lt;/span> len(intervals) &lt;span style="color:#f92672">and&lt;/span> intervals[i][&lt;span style="color:#ae81ff">0&lt;/span>] &lt;span style="color:#f92672">&amp;lt;=&lt;/span> newInterval[&lt;span style="color:#ae81ff">1&lt;/span>]:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> newInterval[&lt;span style="color:#ae81ff">0&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> min(intervals[i][&lt;span style="color:#ae81ff">0&lt;/span>], newInterval[&lt;span style="color:#ae81ff">0&lt;/span>])
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> newInterval[&lt;span style="color:#ae81ff">1&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> max(intervals[i][&lt;span style="color:#ae81ff">1&lt;/span>], newInterval[&lt;span style="color:#ae81ff">1&lt;/span>])
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> i &lt;span style="color:#f92672">+=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ret_list&lt;span style="color:#f92672">.&lt;/span>append(newInterval)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">while&lt;/span> i &lt;span style="color:#f92672">&amp;lt;&lt;/span> len(intervals):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ret_list&lt;span style="color:#f92672">.&lt;/span>append(intervals[i])
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> i &lt;span style="color:#f92672">+=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> ret_list
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>With careful attention to detail, this problem is not difficult.&lt;/p>
&lt;h2 id="300-longest-increasing-subsequencehttpsleetcodecomproblemslongest-increasing-subsequence">&lt;a class="link" href="https://leetcode.com/problems/longest-increasing-subsequence/" target="_blank" rel="noopener"
>300. Longest Increasing Subsequence&lt;/a>
&lt;/h2>&lt;p>This is obviously a dynamic programming problem.&lt;/p>
&lt;p>&lt;code>dp[i]&lt;/code> represents the length of the longest increasing subsequence ending with a certain number.&lt;/p>
&lt;p>Each time an element is added, we update the current &lt;code>dp&lt;/code> array. If the current number is greater than the previous one, we increment the result by 1.&lt;/p>
&lt;p>Note that the first &lt;code>dp[i]&lt;/code> starts from index 1.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Solution&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">def&lt;/span> &lt;span style="color:#a6e22e">lengthOfLIS&lt;/span>(self, nums: List[int]) &lt;span style="color:#f92672">-&amp;gt;&lt;/span> int:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dp &lt;span style="color:#f92672">=&lt;/span> [&lt;span style="color:#ae81ff">1&lt;/span>] &lt;span style="color:#f92672">*&lt;/span> len(nums)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span> i &lt;span style="color:#f92672">in&lt;/span> range(&lt;span style="color:#ae81ff">1&lt;/span>, len(nums)):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span> j &lt;span style="color:#f92672">in&lt;/span> range(i):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> nums[j] &lt;span style="color:#f92672">&amp;lt;&lt;/span> nums[i]:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dp[i] &lt;span style="color:#f92672">=&lt;/span> max(dp[i], dp[j] &lt;span style="color:#f92672">+&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> max(dp)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Complexity Analysis&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>Time Complexity: $O(n^2)$, due to the two nested loops.&lt;/li>
&lt;li>Space Complexity: $O(n)$, as we need a &lt;code>dp&lt;/code> array of length &lt;code>n&lt;/code>.&lt;/li>
&lt;/ul>
&lt;h2 id="674-longest-continuous-increasing-subsequencehttpsleetcodecomproblemslongest-continuous-increasing-subsequence">&lt;a class="link" href="https://leetcode.com/problems/longest-continuous-increasing-subsequence/" target="_blank" rel="noopener"
>674. Longest Continuous Increasing Subsequence&lt;/a>
&lt;/h2>&lt;p>This is a simple problem, but still worth understanding.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Solution&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">def&lt;/span> &lt;span style="color:#a6e22e">findLengthOfLCIS&lt;/span>(self, nums: List[int]) &lt;span style="color:#f92672">-&amp;gt;&lt;/span> int:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dp &lt;span style="color:#f92672">=&lt;/span> [&lt;span style="color:#ae81ff">1&lt;/span>] &lt;span style="color:#f92672">*&lt;/span> len(nums)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span> i &lt;span style="color:#f92672">in&lt;/span> range(&lt;span style="color:#ae81ff">1&lt;/span>, len(nums)):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> nums[i] &lt;span style="color:#f92672">&amp;gt;&lt;/span> nums[i&lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>]:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dp[i] &lt;span style="color:#f92672">=&lt;/span> max(dp[i], dp[i&lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>] &lt;span style="color:#f92672">+&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> max(dp)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="392-is-subsequencehttpsleetcodecomproblemsis-subsequence">&lt;a class="link" href="https://leetcode.com/problems/is-subsequence/" target="_blank" rel="noopener"
>392. Is Subsequence&lt;/a>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Solution&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">def&lt;/span> &lt;span style="color:#a6e22e">isSubsequence&lt;/span>(self, s: str, t: str) &lt;span style="color:#f92672">-&amp;gt;&lt;/span> bool:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> i &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> j &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">while&lt;/span> i &lt;span style="color:#f92672">&amp;lt;&lt;/span> len(s) &lt;span style="color:#f92672">and&lt;/span> j &lt;span style="color:#f92672">&amp;lt;&lt;/span> len(t):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> s[i] &lt;span style="color:#f92672">==&lt;/span> t[j]:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> i &lt;span style="color:#f92672">+=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> j &lt;span style="color:#f92672">+=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> i &lt;span style="color:#f92672">==&lt;/span> len(s)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This problem can be solved using a two-pointer technique, with &lt;code>t&lt;/code> as the base. If &lt;code>s&lt;/code> contains matching characters, we move forward; if we reach the end of &lt;code>s&lt;/code>, it means the match is complete.&lt;/p>
&lt;h2 id="115-distinct-subsequenceshttpsleetcodecomproblemsdistinct-subsequences">&lt;a class="link" href="https://leetcode.com/problems/distinct-subsequences/" target="_blank" rel="noopener"
>115. Distinct Subsequences&lt;/a>
&lt;/h2>&lt;p>This problem asks us to find how many distinct subsequences of string &lt;code>s&lt;/code> equal string &lt;code>t&lt;/code>.&lt;/p>
&lt;p>First, we need to define &lt;code>dp&lt;/code>, where &lt;code>dp[i][j]&lt;/code> represents the number of distinct subsequences that can be formed from the first &lt;code>i&lt;/code> characters of &lt;code>s&lt;/code> to form the first &lt;code>j&lt;/code> characters of &lt;code>t&lt;/code>.&lt;/p>
&lt;ul>
&lt;li>&lt;code>dp[i][0]&lt;/code> represents when &lt;code>t&lt;/code> is an empty string, the result is 1.&lt;/li>
&lt;li>&lt;code>dp[0][j]&lt;/code> represents forming &lt;code>t&lt;/code> from an empty string, which results in 0.&lt;/li>
&lt;/ul>
&lt;p>For &lt;code>dp[i][j]&lt;/code>, the result depends on the characters at positions &lt;code>i&lt;/code> and &lt;code>j&lt;/code>:&lt;/p>
&lt;ul>
&lt;li>If they are equal, the result is the sum of the cases where &lt;code>s[i-1]&lt;/code> is not matched (&lt;code>dp[i-1][j]&lt;/code>) and the cases where it is matched (&lt;code>dp[i-1][j-1]&lt;/code>).&lt;/li>
&lt;li>If they are not equal, the result is equal to &lt;code>dp[i-1][j]&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>Note that &lt;code>i, j&lt;/code> refer to the first &lt;code>i&lt;/code> and &lt;code>j&lt;/code> characters.&lt;/p>
&lt;p>Additionally, &lt;code>dp[0][0]&lt;/code> is initialized to 1, as an empty string is a subsequence of any string.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Solution&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">def&lt;/span> &lt;span style="color:#a6e22e">numDistinct&lt;/span>(self, s: str, t: str) &lt;span style="color:#f92672">-&amp;gt;&lt;/span> int:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dp &lt;span style="color:#f92672">=&lt;/span> [[&lt;span style="color:#ae81ff">0&lt;/span> &lt;span style="color:#66d9ef">for&lt;/span> _ &lt;span style="color:#f92672">in&lt;/span> range(len(t) &lt;span style="color:#f92672">+&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>)] &lt;span style="color:#66d9ef">for&lt;/span> _ &lt;span style="color:#f92672">in&lt;/span> range(len(s) &lt;span style="color:#f92672">+&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>)]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span> i &lt;span style="color:#f92672">in&lt;/span> range(len(s) &lt;span style="color:#f92672">+&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dp[i][&lt;span style="color:#ae81ff">0&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span> j &lt;span style="color:#f92672">in&lt;/span> range(&lt;span style="color:#ae81ff">1&lt;/span>, len(t) &lt;span style="color:#f92672">+&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span> i &lt;span style="color:#f92672">in&lt;/span> range(&lt;span style="color:#ae81ff">1&lt;/span>, len(s) &lt;span style="color:#f92672">+&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> s[i&lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>] &lt;span style="color:#f92672">==&lt;/span> t[j&lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>]:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dp[i][j] &lt;span style="color:#f92672">=&lt;/span> dp[i&lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>][j] &lt;span style="color:#f92672">+&lt;/span> dp[i&lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>][j&lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">else&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dp[i][j] &lt;span style="color:#f92672">=&lt;/span> dp[i&lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>][j]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> dp[&lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>][&lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Off-by-One Overflow Attack Study</title><link>https://nansenli.com/post/2024/10/oboattact-md/</link><pubDate>Tue, 15 Oct 2024 14:18:15 +0000</pubDate><guid>https://nansenli.com/post/2024/10/oboattact-md/</guid><description>&lt;h1 id="off-by-one-overflow-attack-analysis">Off-by-One Overflow Attack Analysis
&lt;/h1>&lt;h2 id="background">Background
&lt;/h2>&lt;p>Last week, I attended a security course that included an example of an off-by-one overflow vulnerability. Here is the original code:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-c" data-lang="c">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/* Simple off-by-one overflow example */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;stdio.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;stdlib.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;string.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">foo&lt;/span>(&lt;span style="color:#66d9ef">char&lt;/span> &lt;span style="color:#f92672">*&lt;/span>input) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">char&lt;/span> buf[&lt;span style="color:#ae81ff">1024&lt;/span>];
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">strncpy&lt;/span>(buf, input, &lt;span style="color:#66d9ef">sizeof&lt;/span>(buf));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> buf[&lt;span style="color:#66d9ef">sizeof&lt;/span>(buf)] &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;\0&amp;#39;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">bar&lt;/span>(&lt;span style="color:#66d9ef">void&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">printf&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;I&amp;#39;ve been hacked&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> argc, &lt;span style="color:#66d9ef">char&lt;/span> &lt;span style="color:#f92672">**&lt;/span>argv) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (argc &lt;span style="color:#f92672">!=&lt;/span> &lt;span style="color:#ae81ff">2&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">printf&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Usage: %s input_string&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>, argv[&lt;span style="color:#ae81ff">0&lt;/span>]);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">exit&lt;/span>(EXIT_FAILURE);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">foo&lt;/span>(argv[&lt;span style="color:#ae81ff">1&lt;/span>]);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The answer provided for exploiting this vulnerability is:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>perl -e &lt;span style="color:#e6db74">&amp;#39;system &amp;#34;./obo&amp;#34;, &amp;#34;\x38\x84\x04\x08&amp;#34;x256&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The result of running this command is that multiple lines of &lt;code>I've been hacked&lt;/code> are printed on the screen.&lt;/p>
&lt;h2 id="analysis">Analysis
&lt;/h2>&lt;p>When the program enters the &lt;code>foo&lt;/code> function, the memory layout looks like this (as observed using GDB):&lt;/p>
&lt;p>From top to bottom, the layout contains the return address, the saved frame pointer, and the buffer (&lt;code>buf&lt;/code>).&lt;/p>
&lt;p>When the line &lt;code>buf[sizeof(buf)] = '\0';&lt;/code> is executed, the least significant byte of the saved frame pointer (&lt;code>ebp&lt;/code>) is set to &lt;code>0&lt;/code>. To ensure that &lt;code>ebp&lt;/code> still points within the &lt;code>buf&lt;/code> region after being partially overwritten, a buffer of at least 1024 bytes is required. Specifically, &lt;code>ebp&lt;/code> needs to be overwritten such that it remains within a reasonable range (— up to &lt;code>0xff&lt;/code>), which is why the buffer is set to &lt;code>0xff * 4&lt;/code> bytes.&lt;/p>
&lt;h2 id="understanding-assembly-commands-on-foo-return">Understanding Assembly Commands on &lt;code>foo&lt;/code> Return
&lt;/h2>&lt;p>When the &lt;code>foo&lt;/code> function returns, it typically executes the following key assembly instructions:&lt;/p>
&lt;h3 id="1-leave-instruction">1. &lt;code>leave&lt;/code> Instruction
&lt;/h3>&lt;p>The &lt;code>leave&lt;/code> instruction is equivalent to:&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-assembly" data-lang="assembly">mov esp, ebp
pop ebp
&lt;/code>&lt;/pre>&lt;ul>
&lt;li>&lt;code>mov esp, ebp&lt;/code>: This sets the stack pointer (&lt;code>esp&lt;/code>) to the value of the frame pointer (&lt;code>ebp&lt;/code>), restoring the stack pointer to the top of the current stack frame and effectively releasing the space occupied by the current function.&lt;/li>
&lt;li>&lt;code>pop ebp&lt;/code>: This pops the value at the top of the stack and assigns it to the frame pointer (&lt;code>ebp&lt;/code>), thereby restoring the caller&amp;rsquo;s frame pointer. Essentially, it writes the return address into &lt;code>ebp&lt;/code>, meaning it assigns the stack value (usually the caller&amp;rsquo;s frame address) to &lt;code>ebp&lt;/code>, restoring the caller&amp;rsquo;s stack frame.&lt;/li>
&lt;/ul>
&lt;p>The effect of &lt;code>leave&lt;/code> is to restore &lt;code>esp&lt;/code> to its state before the function was called and to pop the saved &lt;code>ebp&lt;/code>. If &lt;code>ebp&lt;/code> has been overwritten to point to a special address (such as an address within the buffer), it can result in an incorrect stack pointer location during function return.&lt;/p>
&lt;h3 id="2-ret-instruction">2. &lt;code>ret&lt;/code> Instruction
&lt;/h3>&lt;p>The &lt;code>ret&lt;/code> instruction pops an address off the top of the stack and jumps to that address:&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-assembly" data-lang="assembly">pop eip
&lt;/code>&lt;/pre>&lt;p>If the return address has been overwritten with the address of the &lt;code>bar&lt;/code> function, the execution flow will jump to &lt;code>bar&lt;/code>, allowing an attacker to run arbitrary code. Essentially, &lt;code>ret&lt;/code> pops an address into the instruction pointer (&lt;code>eip&lt;/code>) and jumps to that address to continue execution.&lt;/p>
&lt;h2 id="attack-steps">Attack Steps
&lt;/h2>&lt;ul>
&lt;li>When the command &lt;code>perl -e 'system &amp;quot;./obo&amp;quot;, &amp;quot;\x38\x84\x04\x08&amp;quot;x256'&lt;/code> is executed, the program takes these repeated bytes as the input to &lt;code>./obo&lt;/code>.&lt;/li>
&lt;li>As the &lt;code>foo&lt;/code> function returns, the &lt;code>leave&lt;/code> and &lt;code>ret&lt;/code> instructions are executed, leading to the return address being overwritten. This causes the program to jump to the &lt;code>bar&lt;/code> function, printing the success message multiple times.&lt;/li>
&lt;/ul>
&lt;h2 id="further-analysis-determining-effective-overwrite-locations">Further Analysis: Determining Effective Overwrite Locations
&lt;/h2>&lt;h2 id="stack-frame-layout-explanation">Stack Frame Layout Explanation
&lt;/h2>&lt;p>During the GDB debugging session, the memory layout for the stack frame of the &lt;code>foo&lt;/code> function looks like this:&lt;/p>
&lt;pre tabindex="0">&lt;code>0xbfffed10 return address
0xbfffed0c saved frame pointer (ebp)
0xbfffed0b buf[1023]
...
0xbfffed03 buf[1015]
0xbfffed02 buf[1014]
0xbfffed01 buf[1013]
0xbfffed00 buf[1012]
...
0xbfffe90c buf[0]
&lt;/code>&lt;/pre>&lt;ul>
&lt;li>
&lt;p>&lt;strong>Return address&lt;/strong>: Located at &lt;code>0xbfffed10&lt;/code>, this is the address that the program will jump to after the &lt;code>foo&lt;/code> function finishes executing. Overwriting this address can control the flow of the program and potentially redirect it to malicious code (e.g., the &lt;code>bar&lt;/code> function).&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Saved frame pointer (&lt;code>ebp&lt;/code>)&lt;/strong>: Stored at &lt;code>0xbfffed0c&lt;/code>, this value is used to restore the calling function&amp;rsquo;s stack frame after &lt;code>foo&lt;/code> finishes. In this example, we can see how the off-by-one overflow can overwrite the least significant byte of &lt;code>ebp&lt;/code>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Buffer (&lt;code>buf&lt;/code>)&lt;/strong>: The buffer starts at address &lt;code>0xbfffe90c&lt;/code> and extends to &lt;code>0xbfffed0b&lt;/code>, with &lt;code>buf[0]&lt;/code> located at &lt;code>0xbfffe90c&lt;/code> and &lt;code>buf[1023]&lt;/code> at &lt;code>0xbfffed0b&lt;/code>. The vulnerable line in the code, &lt;code>buf[sizeof(buf)] = '\0';&lt;/code>, writes a null terminator (&lt;code>\0&lt;/code>) just outside the bounds of this buffer, affecting the saved frame pointer.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>In the off-by-one overflow scenario, the write operation overwrites the least significant byte of &lt;code>ebp&lt;/code>, which is stored at &lt;code>0xbfffed0c&lt;/code>. By manipulating the value of &lt;code>ebp&lt;/code>, we can influence the stack behavior when the &lt;code>leave&lt;/code> and &lt;code>ret&lt;/code> instructions are executed, eventually allowing us to control the program flow and redirect execution to the &lt;code>bar&lt;/code> function.&lt;/p>
&lt;p>To perform a successful attack, it&amp;rsquo;s crucial to determine precisely which bytes need to be overwritten in order to manipulate the control flow effectively. In this example, the overflow occurs when &lt;code>buf[sizeof(buf)] = '\0'&lt;/code> is executed, causing the least significant byte of the saved frame pointer (&lt;code>ebp&lt;/code>) to be set to &lt;code>0&lt;/code>. Thus, the value of &lt;code>ebp&lt;/code> needs to be adjusted to ensure it points back into the buffer area, allowing the execution to proceed in the desired way and eventually jump to the &lt;code>bar&lt;/code> function.&lt;/p>
&lt;p>Based on further analysis and testing, the following insights were obtained:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>To accurately determine the overwrite location, the value of &lt;code>ebp&lt;/code> is crucial. However, obtaining this value is challenging because:&lt;/p>
&lt;ol>
&lt;li>GDB debugging affects address layout.&lt;/li>
&lt;li>The length of the input parameter affects the address layout.&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>
&lt;p>Under GDB debugging, the layout within the &lt;code>foo&lt;/code> function looks like this:&lt;/p>
&lt;/li>
&lt;li>
&lt;p>After executing &lt;code>buf[sizeof(buf)] = '\0';&lt;/code>, &lt;code>ebp&lt;/code> is modified such that the return address effectively takes the value at &lt;code>ebp + 1&lt;/code>, which is the address &lt;code>0xbfffed00 + 1&lt;/code>, or &lt;code>0xbfffed04&lt;/code>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>The corresponding offset is at position 255 in &lt;code>buf&lt;/code>, meaning the attack can be constructed by filling in the return address only at that specific location. The following command was used for verification in GDB:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>r &lt;span style="color:#66d9ef">$(&lt;/span>perl -e &lt;span style="color:#e6db74">&amp;#39;print &amp;#34;\x01\x01\x01\x01&amp;#34;x254 . &amp;#34;\x38\x84\x04\x08&amp;#34;x1 . &amp;#34;\x01\x01\x01\x01&amp;#34;x1&amp;#39;&lt;/span>&lt;span style="color:#66d9ef">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>This was verified to work under GDB debugging, with some details to note:&lt;/p>
&lt;ol>
&lt;li>The input parameter length must always be 256 bytes; otherwise, the value of &lt;code>ebp&lt;/code> will change, as the input parameter occupies stack space, affecting the starting position of the frame and thereby affecting the value of &lt;code>ebp&lt;/code>.&lt;/li>
&lt;li>Padding must use non-zero values such as &lt;code>0x01&lt;/code>, because &lt;code>strncpy&lt;/code> will terminate early if it encounters a &lt;code>0&lt;/code> value.&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>
&lt;p>When executing the program directly (i.e., without GDB), the memory layout differs, resulting in a different offset position. Through experimentation, it was found that the offset is at position 235. The corresponding attack command is:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>./obo &lt;span style="color:#66d9ef">$(&lt;/span>perl -e &lt;span style="color:#e6db74">&amp;#39;print &amp;#34;\x01\x01\x01\x01&amp;#34;x234 . &amp;#34;\x38\x84\x04\x08&amp;#34;x1 . &amp;#34;\x01\x01\x01\x01&amp;#34;x21&amp;#39;&lt;/span>&lt;span style="color:#66d9ef">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;p>This achieves the desired effect of accurately finding the overwrite location and successfully executing the attack.&lt;/p></description></item><item><title>Convert ChatGPT Formula Format to Obsidian-Compatible Format</title><link>https://nansenli.com/post/2024/10/obsidian-latex-fix/</link><pubDate>Mon, 14 Oct 2024 15:24:21 +0000</pubDate><guid>https://nansenli.com/post/2024/10/obsidian-latex-fix/</guid><description>&lt;p>The formulas generated by ChatGPT use the following format:&lt;/p>
&lt;pre tabindex="0">&lt;code>\[
Formula Content
\]
&lt;/code>&lt;/pre>&lt;p>However, Obsidian renders formulas using the following format:&lt;/p>
&lt;pre tabindex="0">&lt;code>$$
Formula Content
$$
&lt;/code>&lt;/pre>&lt;p>When copying a formula from ChatGPT to Obsidian, this difference prevents proper rendering.&lt;/p>
&lt;h2 id="solution">Solution
&lt;/h2>&lt;p>We can create a script for Obsidian to automatically replace the formula format when pasting.&lt;/p>
&lt;h3 id="1-create-the-script">1. Create the Script
&lt;/h3>&lt;p>We can solve this issue using a plugin in Obsidian.&lt;/p>
&lt;p>In your vault, create a file named &lt;code>fixlatex.js&lt;/code> under the &lt;code>template&lt;/code> directory, and input the following content:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-js" data-lang="js">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">module&lt;/span>.&lt;span style="color:#a6e22e">exports&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">async&lt;/span> (&lt;span style="color:#a6e22e">params&lt;/span>) =&amp;gt; {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">const&lt;/span> { &lt;span style="color:#a6e22e">quickAddApi&lt;/span> } &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#a6e22e">params&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Get clipboard content
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#a6e22e">clipboardContent&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">await&lt;/span> &lt;span style="color:#a6e22e">quickAddApi&lt;/span>.&lt;span style="color:#a6e22e">utility&lt;/span>.&lt;span style="color:#a6e22e">getClipboard&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Check if content was successfully retrieved
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#f92672">!&lt;/span>&lt;span style="color:#a6e22e">clipboardContent&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">new&lt;/span> &lt;span style="color:#a6e22e">Notice&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Clipboard is empty or inaccessible.&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#a6e22e">modifiedContent&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#a6e22e">clipboardContent&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .&lt;span style="color:#a6e22e">replace&lt;/span>(&lt;span style="color:#e6db74">/\\\[|\\\]/g&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;$$$$&amp;#39;&lt;/span>) &lt;span style="color:#75715e">// Convert \[ \] to $$ $$
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> .&lt;span style="color:#a6e22e">replace&lt;/span>(&lt;span style="color:#e6db74">/\\\(\s*|\s*\\\)/g&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;$$&amp;#39;&lt;/span>); &lt;span style="color:#75715e">// Convert \( \) to $
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Write the modified content back to the clipboard
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">await&lt;/span> &lt;span style="color:#a6e22e">navigator&lt;/span>.&lt;span style="color:#a6e22e">clipboard&lt;/span>.&lt;span style="color:#a6e22e">writeText&lt;/span>(&lt;span style="color:#a6e22e">modifiedContent&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">new&lt;/span> &lt;span style="color:#a6e22e">Notice&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Clipboard content has been processed and modified!&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>};
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="2-set-up-the-script-in-quickadd">2. Set Up the Script in QuickAdd
&lt;/h3>&lt;p>Install the QuickAdd plugin and create a &lt;code>Macro&lt;/code>, configuring it as shown below, then save it. The first step in the Macro is to execute our user script &lt;code>fixlatex.js&lt;/code>, the second step is to wait for 100 milliseconds, and the third step is to execute the paste action.&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/ec99ec1deab8.png"
loading="lazy"
>&lt;/p>
&lt;h3 id="3-set-sidebar-shortcut-in-commander">3. Set Sidebar Shortcut in Commander
&lt;/h3>&lt;p>Install the Commander plugin and set up the QuickAdd action we just created as a sidebar shortcut. You can also skip this step and directly use an Obsidian command to execute this action.&lt;/p>
&lt;h3 id="4-verify-the-effect">4. Verify the Effect
&lt;/h3>&lt;p>Now, on the ChatGPT webpage (currently there seems to be an issue when clicking the copy button in the app), click the copy button, then in Obsidian, click the sidebar shortcut or manually execute the QuickAdd command. This will copy the content from ChatGPT to Obsidian and automatically convert the LaTeX format.&lt;/p></description></item><item><title>About Me</title><link>https://nansenli.com/post/2024/10/aboutme/</link><pubDate>Fri, 11 Oct 2024 20:55:23 +0000</pubDate><guid>https://nansenli.com/post/2024/10/aboutme/</guid><description>&lt;h1 id="about-me">About Me
&lt;/h1>&lt;p>Written on October 11, 2024, in Dublin, Ireland.&lt;/p>
&lt;hr>
&lt;h2 id="self-introduction">Self Introduction
&lt;/h2>&lt;p>Hello everyone, my name is Nansen Li, previously known as Nan Li. I&amp;rsquo;m from China and a technology-loving engineer. I hold a bachelor&amp;rsquo;s degree in Mechatronic Engineering and two master&amp;rsquo;s degrees in Computer Science. I previously worked as a Go Language Engineer at a major internet company in China, and I am currently pursuing a master&amp;rsquo;s degree at a university in Dublin. I plan to develop my career in Ireland afterward.&lt;/p>
&lt;p>I am deeply interested in frontend and backend development, system architecture, algorithms, game development, and generative AI. I&amp;rsquo;ve always been dedicated to improving my programming skills and enjoy experimenting with new technologies, keeping myself sensitive to industry trends.&lt;/p>
&lt;p>In addition to software development and design, I have a diverse background in various fields. I have participated in embedded system design and mechanical software design, soldered circuit boards, and designed electronic components, including amplifiers for detection circuits and functional components like analog or digital cameras. I am familiar with control systems and processes, such as PID. I have also drawn engineering diagrams, such as models of car cylinders, and used AutoCAD for modeling. Additionally, I have been involved in embedded firewall design. I also have experience leading different teams in programming or electronic design competitions.&lt;/p>
&lt;p>If you share similar interests in technology or lifestyle, feel free to reach out to me. We could discuss and exchange ideas together.&lt;/p></description></item><item><title>Classic Kubernetes Installation Process</title><link>https://nansenli.com/post/jianshu/k8s/k8s%E7%BB%8F%E5%85%B8%E5%AE%89%E8%A3%85%E6%B5%81%E7%A8%8B/</link><pubDate>Sun, 20 Jun 2021 12:21:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/k8s/k8s%E7%BB%8F%E5%85%B8%E5%AE%89%E8%A3%85%E6%B5%81%E7%A8%8B/</guid><description>&lt;h1 id="run-on-all-machines">Run on All Machines
&lt;/h1>&lt;p>All machines are running Debian.
First, install Docker:&lt;/p>
&lt;pre tabindex="0">&lt;code> sudo apt-get update
sudo apt-get install -y \
apt-transport-https \
ca-certificates \
curl \
gnupg \
lsb-release
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
&amp;#34;deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \
$(lsb_release -cs) stable&amp;#34; | sudo tee /etc/apt/sources.list.d/docker.list &amp;gt; /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
sudo mkdir /etc/docker
cat &amp;lt;&amp;lt;EOF | sudo tee /etc/docker/daemon.json
{
&amp;#34;exec-opts&amp;#34;: [&amp;#34;native.cgroupdriver=systemd&amp;#34;],
&amp;#34;log-driver&amp;#34;: &amp;#34;json-file&amp;#34;,
&amp;#34;log-opts&amp;#34;: {
&amp;#34;max-size&amp;#34;: &amp;#34;100m&amp;#34;
},
&amp;#34;storage-driver&amp;#34;: &amp;#34;overlay2&amp;#34;
}
EOF
sudo usermod -aG docker ${USER}
sudo systemctl restart docker
sudo su ${USER}
&lt;/code>&lt;/pre>&lt;p>At this point, running &lt;code>docker ps -a&lt;/code> should work without errors, indicating Docker was installed successfully.&lt;/p>
&lt;p>Next, install Kubernetes components:&lt;/p>
&lt;pre tabindex="0">&lt;code>cat &amp;lt;&amp;lt;EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF
cat &amp;lt;&amp;lt;EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sudo sysctl --system
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl
sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
echo &amp;#34;deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main&amp;#34; | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
&lt;/code>&lt;/pre>&lt;h1 id="run-on-the-master-node">Run on the Master Node
&lt;/h1>&lt;p>Initialize the control plane:&lt;/p>
&lt;pre tabindex="0">&lt;code>sudo kubeadm init --apiserver-advertise-address masterip --control-plane-endpoint masterdns --pod-network-cidr 10.244.0.0/16
&lt;/code>&lt;/pre>&lt;p>Save the last part of the output for later use, including instructions on how to configure .kube and the join command for nodes. Follow the instructions to configure the .kube file.&lt;/p>
&lt;p>Then initialize the network plugin:&lt;/p>
&lt;pre tabindex="0">&lt;code>wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
kubectl apply -f kube-flannel.yml
&lt;/code>&lt;/pre>&lt;p>Check if pods are running properly:&lt;/p>
&lt;pre tabindex="0">&lt;code>kubectl get pods -A
&lt;/code>&lt;/pre>&lt;p>On another node, install Kubernetes components, then run the join command. This command is printed at the end of the kubeadm init process.&lt;/p>
&lt;p>Afterwards, check if the pods are running properly.&lt;/p>
&lt;p>Download the dashboard:&lt;/p>
&lt;pre tabindex="0">&lt;code>wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.2.0/aio/deploy/recommended.yaml -o dashboard.yaml
&lt;/code>&lt;/pre>&lt;p>Edit dashboard.yaml:
Add nodePort: 31001 below port 8443, and change the type to NodePort above it. Find the namespace= line and add a line below it: - &amp;ndash;token-ttl=43200
For details, refer to:
&lt;a class="link" href="https://www.huaweicloud.com/articles/dc1dcb0c48cc785a9193c9ce709c8b35.html" target="_blank" rel="noopener"
>https://www.huaweicloud.com/articles/dc1dcb0c48cc785a9193c9ce709c8b35.html&lt;/a>&lt;/p>
&lt;p>Create an admin role:&lt;/p>
&lt;pre tabindex="0">&lt;code>kubectl create -f https://raw.githubusercontent.com/rootsongjc/kubernetes-handbook/master/manifests/dashboard-1.7.1/admin-role.yaml
&lt;/code>&lt;/pre>&lt;p>Apply the pod:&lt;/p>
&lt;pre tabindex="0">&lt;code>kubectl apply -f dashboard.yaml
&lt;/code>&lt;/pre>&lt;p>Check the port:&lt;/p>
&lt;pre tabindex="0">&lt;code>sudo lsof -i:31001
&lt;/code>&lt;/pre>&lt;p>Get the token:&lt;/p>
&lt;pre tabindex="0">&lt;code>kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin-token | awk &amp;#39;{print $1}&amp;#39;)
&lt;/code>&lt;/pre>&lt;p>Visit https://masterip:31001 and enter &amp;rsquo;thisisunsafe&amp;rsquo; in Chrome, then input the token.&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/6e04c88e778d.png"
loading="lazy"
alt="dashboard"
>&lt;/p></description></item><item><title>Socket.io Learning</title><link>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/socket-io%E5%AD%A6%E4%B9%A0/</link><pubDate>Tue, 11 Dec 2018 10:50:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/socket-io%E5%AD%A6%E4%B9%A0/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>In a hurry, will write more later&lt;/p>
&lt;h2 id="core-concepts">Core Concepts
&lt;/h2>&lt;p>Both the server and client have &lt;code>emit&lt;/code> and &lt;code>on&lt;/code> functions, which can be considered the core of socket.io. Through &lt;code>emit&lt;/code> and &lt;code>on&lt;/code>, bidirectional communication between server and client can be easily implemented.&lt;/p>
&lt;p>emit: Used to emit or trigger an event. The first parameter is the event name, the second parameter is the data to be sent, and the third parameter is a callback function (generally omitted, but needed when immediate confirmation is required after the other party receives the information).
on: Used to listen for an event emitted by &lt;code>emit&lt;/code>. The first parameter is the name of the event to listen for, and the second parameter is an anonymous function used to receive data from the other party. The first parameter of this anonymous function is the received data, and if there is a second parameter, it is the function to be returned.
socket.io provides three default events (both client and server have them): connect, message, and disconnect. The connect event is automatically triggered when a connection is established with the other party, the message event is triggered when data is received from the other party (usually triggered by socket.send()), and the disconnect event is triggered when the other party closes the connection.&lt;/p>
&lt;p>On the server side, distinguish between these three situations:&lt;/p>
&lt;p>socket.emit(): Broadcasts to the client that established the connection
socket.broadcast.emit(): Broadcasts to all clients except the one that established the connection
io.sockets.emit(): Broadcasts to all clients, equivalent to the sum of the above two&lt;/p></description></item><item><title>Go Types Official Reference</title><link>https://nansenli.com/post/jianshu/go/go%E7%B1%BB%E5%9E%8B%E5%AE%98%E6%96%B9%E5%8F%82%E8%80%83-%E4%B8%AD%E8%8B%B1%E6%96%87%E5%AF%B9%E7%85%A7/</link><pubDate>Wed, 14 Nov 2018 08:14:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/go/go%E7%B1%BB%E5%9E%8B%E5%AE%98%E6%96%B9%E5%8F%82%E8%80%83-%E4%B8%AD%E8%8B%B1%E6%96%87%E5%AF%B9%E7%85%A7/</guid><description>&lt;h2 id="official-reference">Official Reference
&lt;/h2>&lt;p>&lt;a class="link" href="https://golang.org/ref/spec#Types" target="_blank" rel="noopener"
>https://golang.org/ref/spec#Types&lt;/a>&lt;/p>
&lt;p>The types include:&lt;/p>
&lt;p>Method sets
Boolean types
Numeric types
String types
Array types
Slice types
Struct types
Pointer types
Function types
Interface types
Map types
Channel types&lt;/p>
&lt;h2 id="method-sets">Method sets
&lt;/h2>&lt;p>A type may have a &lt;em>method set&lt;/em> associated with it. The method set of an &lt;a class="link" href="https://golang.org/ref/spec#Interface_types" target="_blank" rel="noopener"
>interface type&lt;/a> is its interface. The method set of any other type &lt;code>T&lt;/code> consists of all &lt;a class="link" href="https://golang.org/ref/spec#Method_declarations" target="_blank" rel="noopener"
>methods&lt;/a> declared with receiver type &lt;code>T&lt;/code>. The method set of the corresponding &lt;a class="link" href="https://golang.org/ref/spec#Pointer_types" target="_blank" rel="noopener"
>pointer type&lt;/a> &lt;code>*T&lt;/code> is the set of all methods declared with receiver &lt;code>*T&lt;/code> or &lt;code>T&lt;/code> (that is, it also contains the method set of &lt;code>T&lt;/code>). Further rules apply to structs containing embedded fields, as described in the section on &lt;a class="link" href="https://golang.org/ref/spec#Struct_types" target="_blank" rel="noopener"
>struct types&lt;/a>. Any other type has an empty method set. In a method set, each method must have a &lt;a class="link" href="https://golang.org/ref/spec#Uniqueness_of_identifiers" target="_blank" rel="noopener"
>unique&lt;/a> non-&lt;a class="link" href="https://golang.org/ref/spec#Blank_identifier" target="_blank" rel="noopener"
>blank&lt;/a>&lt;a class="link" href="https://golang.org/ref/spec#MethodName" target="_blank" rel="noopener"
>method name&lt;/a>.&lt;/p>
&lt;p>The method set of a type determines the interfaces that the type &lt;a class="link" href="https://golang.org/ref/spec#Interface_types" target="_blank" rel="noopener"
>implements&lt;/a> and the methods that can be &lt;a class="link" href="https://golang.org/ref/spec#Calls" target="_blank" rel="noopener"
>called&lt;/a> using a receiver of that type.&lt;/p>
&lt;h2 id="boolean-types">Boolean types
&lt;/h2>&lt;p>A &lt;em>boolean type&lt;/em> represents the set of Boolean truth values denoted by the predeclared constants &lt;code>true&lt;/code> and &lt;code>false&lt;/code>. The predeclared boolean type is &lt;code>bool&lt;/code>; it is a &lt;a class="link" href="https://golang.org/ref/spec#Type_definitions" target="_blank" rel="noopener"
>defined type&lt;/a>.&lt;/p>
&lt;h2 id="numeric-types">Numeric types
&lt;/h2>&lt;p>A numeric type represents sets of integer or floating-point values. The predeclared architecture-independent numeric types are:&lt;/p>
&lt;pre tabindex="0">&lt;code>uint8 the set of all unsigned 8-bit integers (0 to 255)
uint16 the set of all unsigned 16-bit integers (0 to 65535)
uint32 the set of all unsigned 32-bit integers (0 to 4294967295)
uint64 the set of all unsigned 64-bit integers (0 to 18446744073709551615)
int8 the set of all signed 8-bit integers (-128 to 127)
int16 the set of all signed 16-bit integers (-32768 to 32767)
int32 the set of all signed 32-bit integers (-2147483648 to 2147483647)
int64 the set of all signed 64-bit integers (-9223372036854775808 to 9223372036854775807)
float32 the set of all IEEE-754 32-bit floating-point numbers
float64 the set of all IEEE-754 64-bit floating-point numbers
complex64 the set of all complex numbers with float32 real and imaginary parts
complex128 the set of all complex numbers with float64 real and imaginary parts
byte alias for uint8
rune alias for int32
&lt;/code>&lt;/pre>&lt;p>The value of an &lt;em>n&lt;/em>-bit integer is &lt;em>n&lt;/em> bits wide and represented using &lt;a class="link" href="https://en.wikipedia.org/wiki/Two%27s_complement" target="_blank" rel="noopener"
>two&amp;rsquo;s complement arithmetic&lt;/a>.&lt;/p>
&lt;p>There is also a set of predeclared numeric types with implementation-specific sizes:&lt;/p>
&lt;pre tabindex="0">&lt;code>uint either 32 or 64 bits
int same size as uint
uintptr an unsigned integer large enough to store the uninterpreted bits of a pointer value
&lt;/code>&lt;/pre>&lt;p>To avoid portability issues all numeric types are &lt;a class="link" href="https://golang.org/ref/spec#Type_definitions" target="_blank" rel="noopener"
>defined types&lt;/a> and thus distinct except &lt;code>byte&lt;/code>, which is an &lt;a class="link" href="https://golang.org/ref/spec#Alias_declarations" target="_blank" rel="noopener"
>alias&lt;/a> for &lt;code>uint8&lt;/code>, and &lt;code>rune&lt;/code>, which is an alias for &lt;code>int32&lt;/code>. Conversions are required when different numeric types are mixed in an expression or assignment. For instance, &lt;code>int32&lt;/code> and &lt;code>int&lt;/code> are not the same type even though they may have the same size on a particular architecture.&lt;/p>
&lt;h2 id="string-types">String types
&lt;/h2>&lt;p>A &lt;em>string type&lt;/em> represents the set of string values. A string value is a (possibly empty) sequence of bytes. Strings are immutable: once created, it is impossible to change the contents of a string. The predeclared string type is &lt;code>string&lt;/code>; it is a &lt;a class="link" href="https://golang.org/ref/spec#Type_definitions" target="_blank" rel="noopener"
>defined type&lt;/a>.&lt;/p>
&lt;p>The length of a string &lt;code>s&lt;/code> (its size in bytes) can be discovered using the built-in function &lt;a class="link" href="https://golang.org/ref/spec#Length_and_capacity" target="_blank" rel="noopener"
>&lt;code>len&lt;/code>&lt;/a>. The length is a compile-time constant if the string is a constant. A string&amp;rsquo;s bytes can be accessed by integer &lt;a class="link" href="https://golang.org/ref/spec#Index_expressions" target="_blank" rel="noopener"
>indices&lt;/a> 0 through &lt;code>len(s)-1&lt;/code>. It is illegal to take the address of such an element; if &lt;code>s[i]&lt;/code> is the &lt;code>i&lt;/code>&amp;lsquo;th byte of a string, &lt;code>&amp;amp;s[i]&lt;/code> is invalid.&lt;/p>
&lt;h2 id="array-types">Array types
&lt;/h2>&lt;p>An array is a numbered sequence of elements of a single type, called the element type. The number of elements is called the length and is never negative.&lt;/p>
&lt;pre tabindex="0">&lt;code>ArrayType = &amp;#34;[&amp;#34; ArrayLength &amp;#34;]&amp;#34; ElementType .
ArrayLength = Expression .
ElementType = Type .
&lt;/code>&lt;/pre>&lt;p>The length is part of the array&amp;rsquo;s type; it must evaluate to a non-negative &lt;a class="link" href="https://golang.org/ref/spec#Constants" target="_blank" rel="noopener"
>constant&lt;/a> &lt;a class="link" href="https://golang.org/ref/spec#Representability" target="_blank" rel="noopener"
>representable&lt;/a> by a value of type &lt;code>int&lt;/code>. The length of array &lt;code>a&lt;/code> can be discovered using the built-in function &lt;a class="link" href="https://golang.org/ref/spec#Length_and_capacity" target="_blank" rel="noopener"
>&lt;code>len&lt;/code>&lt;/a>. The elements can be addressed by integer &lt;a class="link" href="https://golang.org/ref/spec#Index_expressions" target="_blank" rel="noopener"
>indices&lt;/a> 0 through &lt;code>len(a)-1&lt;/code>. Array types are always one-dimensional but may be composed to form multi-dimensional types.&lt;/p>
&lt;pre tabindex="0">&lt;code>[32]byte
[2*N] struct { x, y int32 }
[1000]*float64
[3][5]int
[2][2][2]float64 // same as [2]([2]([2]float64))
&lt;/code>&lt;/pre>&lt;h2 id="slice-types">Slice types
&lt;/h2>&lt;p>A slice is a descriptor for a contiguous segment of an &lt;em>underlying array&lt;/em> and provides access to a numbered sequence of elements from that array. A slice type denotes the set of all slices of arrays of its element type. The value of an uninitialized slice is &lt;code>nil&lt;/code>.&lt;/p>
&lt;pre tabindex="0">&lt;code>SliceType = &amp;#34;[&amp;#34; &amp;#34;]&amp;#34; ElementType .
&lt;/code>&lt;/pre>&lt;p>Like arrays, slices are indexable and have a length. The length of a slice &lt;code>s&lt;/code> can be discovered by the built-in function &lt;a class="link" href="https://golang.org/ref/spec#Length_and_capacity" target="_blank" rel="noopener"
>&lt;code>len&lt;/code>&lt;/a>; unlike with arrays it may change during execution. The elements can be addressed by integer &lt;a class="link" href="https://golang.org/ref/spec#Index_expressions" target="_blank" rel="noopener"
>indices&lt;/a> 0 through &lt;code>len(s)-1&lt;/code>. The slice index of a given element may be less than the index of the same element in the underlying array.&lt;/p>
&lt;p>A slice, once initialized, is always associated with an underlying array that holds its elements. A slice therefore shares storage with its array and with other slices of the same array; by contrast, distinct arrays always represent distinct storage.&lt;/p>
&lt;p>The array underlying a slice may extend past the end of the slice. The &lt;em>capacity&lt;/em> is a measure of that extent: it is the sum of the length of the slice and the length of the array beyond the slice; a slice of length up to that capacity can be created by &lt;a class="link" href="https://golang.org/ref/spec#Slice_expressions" target="_blank" rel="noopener"
>&lt;em>slicing&lt;/em>&lt;/a> a new one from the original slice. The capacity of a slice &lt;code>a&lt;/code> can be discovered using the built-in function &lt;a class="link" href="https://golang.org/ref/spec#Length_and_capacity" target="_blank" rel="noopener"
>&lt;code>cap(a)&lt;/code>&lt;/a>.&lt;/p>
&lt;p>A new, initialized slice value for a given element type &lt;code>T&lt;/code> is made using the built-in function &lt;a class="link" href="https://golang.org/ref/spec#Making_slices_maps_and_channels" target="_blank" rel="noopener"
>&lt;code>make&lt;/code>&lt;/a>, which takes a slice type and parameters specifying the length and optionally the capacity. A slice created with &lt;code>make&lt;/code> always allocates a new, hidden array to which the returned slice value refers. That is, executing&lt;/p>
&lt;pre tabindex="0">&lt;code>make([]T, length, capacity)
&lt;/code>&lt;/pre>&lt;p>produces the same slice as allocating an array and &lt;a class="link" href="https://golang.org/ref/spec#Slice_expressions" target="_blank" rel="noopener"
>slicing&lt;/a> it, so these two expressions are equivalent:&lt;/p>
&lt;pre tabindex="0">&lt;code>make([]int, 50, 100)
new([100]int)[0:50]
&lt;/code>&lt;/pre>&lt;p>Like arrays, slices are always one-dimensional but may be composed to construct higher-dimensional objects. With arrays of arrays, the inner arrays are, by construction, always the same length; however with slices of slices (or arrays of slices), the inner lengths may vary dynamically. Moreover, the inner slices must be initialized individually.&lt;/p>
&lt;h2 id="struct-types">Struct types
&lt;/h2>&lt;p>A struct is a sequence of named elements, called fields, each of which has a name and a type. Field names may be specified explicitly (IdentifierList) or implicitly (EmbeddedField). Within a struct, non-&lt;a class="link" href="https://golang.org/ref/spec#Blank_identifier" target="_blank" rel="noopener"
>blank&lt;/a> field names must be &lt;a class="link" href="https://golang.org/ref/spec#Uniqueness_of_identifiers" target="_blank" rel="noopener"
>unique&lt;/a>.&lt;/p>
&lt;pre tabindex="0">&lt;code>StructType = &amp;#34;struct&amp;#34; &amp;#34;{&amp;#34; { FieldDecl &amp;#34;;&amp;#34; } &amp;#34;}&amp;#34; .
FieldDecl = (IdentifierList Type | EmbeddedField) [ Tag ] .
EmbeddedField = [ &amp;#34;*&amp;#34; ] TypeName .
Tag = string_lit .
&lt;/code>&lt;/pre>&lt;pre tabindex="0">&lt;code>// An empty struct.
struct {}
// A struct with 6 fields.
struct {
x, y int
u float32
_ float32 // padding
A *[]int
F func()
}
&lt;/code>&lt;/pre>&lt;p>A field declared with a type but no explicit field name is called an embedded field. An embedded field must be specified as a type name T or as a pointer to a non-interface type name *T, and T itself may not be a pointer type. The unqualified type name acts as the field name.&lt;/p>
&lt;pre tabindex="0">&lt;code>// A struct with four embedded fields of types T1, *T2, P.T3 and *P.T4
struct {
T1 // field name is T1
*T2 // field name is T2
P.T3 // field name is T3
*P.T4 // field name is T4
x, y int // field names are x and y
}
&lt;/code>&lt;/pre>&lt;p>The following declaration is illegal because field names must be unique in a struct type:&lt;/p>
&lt;pre tabindex="0">&lt;code>struct {
T // conflicts with embedded field *T and *P.T
*T // conflicts with embedded field T and *P.T
*P.T // conflicts with embedded field T and *T
}
&lt;/code>&lt;/pre>&lt;p>A field or &lt;a class="link" href="https://golang.org/ref/spec#Method_declarations" target="_blank" rel="noopener"
>method&lt;/a> &lt;code>f&lt;/code> of an embedded field in a struct &lt;code>x&lt;/code> is called &lt;em>promoted&lt;/em> if &lt;code>x.f&lt;/code> is a legal &lt;a class="link" href="https://golang.org/ref/spec#Selectors" target="_blank" rel="noopener"
>selector&lt;/a> that denotes that field or method &lt;code>f&lt;/code>.&lt;/p>
&lt;p>Promoted fields act like ordinary fields of a struct except that they cannot be used as field names in &lt;a class="link" href="https://golang.org/ref/spec#Composite_literals" target="_blank" rel="noopener"
>composite literals&lt;/a> of the struct.&lt;/p>
&lt;p>Given a struct type &lt;code>S&lt;/code> and a &lt;a class="link" href="https://golang.org/ref/spec#Type_definitions" target="_blank" rel="noopener"
>defined type&lt;/a> &lt;code>T&lt;/code>, promoted methods are included in the method set of the struct as follows:&lt;/p>
&lt;ul>
&lt;li>If &lt;code>S&lt;/code> contains an embedded field &lt;code>T&lt;/code>, the &lt;a class="link" href="https://golang.org/ref/spec#Method_sets" target="_blank" rel="noopener"
>method sets&lt;/a> of &lt;code>S&lt;/code> and &lt;code>*S&lt;/code> both include promoted methods with receiver &lt;code>T&lt;/code>. The method set of &lt;code>*S&lt;/code> also includes promoted methods with receiver &lt;code>*T&lt;/code>.&lt;/li>
&lt;li>If &lt;code>S&lt;/code> contains an embedded field &lt;code>*T&lt;/code>, the method sets of &lt;code>S&lt;/code> and &lt;code>*S&lt;/code> both include promoted methods with receiver &lt;code>T&lt;/code> or&lt;code>*T&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>A field declaration may be followed by an optional string literal &lt;em>tag&lt;/em>, which becomes an attribute for all the fields in the corresponding field declaration. An empty tag string is equivalent to an absent tag. The tags are made visible through a &lt;a class="link" href="https://golang.org/pkg/reflect/#StructTag" target="_blank" rel="noopener"
>reflection interface&lt;/a> and take part in &lt;a class="link" href="https://golang.org/ref/spec#Type_identity" target="_blank" rel="noopener"
>type identity&lt;/a> for structs but are otherwise ignored.&lt;/p>
&lt;pre tabindex="0">&lt;code>struct {
x, y float64 &amp;#34;&amp;#34; // an empty tag string is like an absent tag
name string &amp;#34;any string is permitted as a tag&amp;#34;
_ [4]byte &amp;#34;ceci n&amp;#39;est pas un champ de structure&amp;#34;
}
// A struct corresponding to a TimeStamp protocol buffer.
// The tag strings define the protocol buffer field numbers;
// they follow the convention outlined by the reflect package.
struct {
microsec uint64 `protobuf:&amp;#34;1&amp;#34;`
serverIP6 uint64 `protobuf:&amp;#34;2&amp;#34;`
}
&lt;/code>&lt;/pre>&lt;h2 id="pointer-types">Pointer types
&lt;/h2>&lt;p>A pointer type denotes the set of all pointers to variables of a given type, called the base type of the pointer. The value of an uninitialized pointer is nil.&lt;/p>
&lt;pre tabindex="0">&lt;code>PointerType = &amp;#34;*&amp;#34; BaseType .
BaseType = Type .
*Point
*[4]int
&lt;/code>&lt;/pre>&lt;h2 id="function-types">Function types
&lt;/h2>&lt;p>A function type denotes the set of all functions with the same parameter and result types. The value of an uninitialized variable of function type is nil.&lt;/p>
&lt;pre tabindex="0">&lt;code>FunctionType = &amp;#34;func&amp;#34; Signature .
Signature = Parameters [ Result ] .
Result = Parameters | Type .
Parameters = &amp;#34;(&amp;#34; [ ParameterList [ &amp;#34;,&amp;#34; ] ] &amp;#34;)&amp;#34; .
ParameterList = ParameterDecl { &amp;#34;,&amp;#34; ParameterDecl } .
ParameterDecl = [ IdentifierList ] [ &amp;#34;...&amp;#34; ] Type .
&lt;/code>&lt;/pre>&lt;p>Within a list of parameters or results, the names (IdentifierList) must either all be present or all be absent. If present, each name stands for one item (parameter or result) of the specified type and all non-blank names in the signature must be unique. If absent, each type stands for one item of that type. Parameter and result lists are always parenthesized except that if there is exactly one unnamed result it may be written as an unparenthesized type.&lt;/p>
&lt;p>The final incoming parameter in a function signature may have a type prefixed with &amp;hellip;. A function with such a parameter is called variadic and may be invoked with zero or more arguments for that parameter.&lt;/p>
&lt;pre tabindex="0">&lt;code>func()
func(x int) int
func(a, _ int, z float32) bool
func(a, b int, z float32) (bool)
func(prefix string, values ...int)
func(a, b int, z float64, opt ...interface{}) (success bool)
func(int, int, float64) (float64, *[]int)
func(n int) func(p *T)
&lt;/code>&lt;/pre>&lt;h2 id="interface-types">Interface types
&lt;/h2>&lt;p>An interface type specifies a method set called its interface. A variable of interface type can store a value of any type with a method set that is any superset of the interface. Such a type is said to implement the interface. The value of an uninitialized variable of interface type is nil.&lt;/p>
&lt;pre tabindex="0">&lt;code>InterfaceType = &amp;#34;interface&amp;#34; &amp;#34;{&amp;#34; { MethodSpec &amp;#34;;&amp;#34; } &amp;#34;}&amp;#34; .
MethodSpec = MethodName Signature | InterfaceTypeName .
MethodName = identifier .
InterfaceTypeName = TypeName .
&lt;/code>&lt;/pre>&lt;p>As with all method sets, in an interface type, each method must have a unique non-blank name.&lt;/p>
&lt;pre tabindex="0">&lt;code>// A simple File interface
interface {
Read(b Buffer) bool
Write(b Buffer) bool
Close()
}
&lt;/code>&lt;/pre>&lt;p>More than one type may implement an interface. For instance, if two types S1 and S2 have the method set&lt;/p>
&lt;pre tabindex="0">&lt;code>func (p T) Read(b Buffer) bool { return … }
func (p T) Write(b Buffer) bool { return … }
func (p T) Close() { … }
&lt;/code>&lt;/pre>&lt;p>(where T stands for either S1 or S2) then the File interface is implemented by both S1 and S2, regardless of what other methods S1 and S2 may have or share.&lt;/p>
&lt;p>A type implements any interface comprising any subset of its methods and may therefore implement several distinct interfaces. For instance, all types implement the empty interface:&lt;/p>
&lt;p>interface{}
Similarly, consider this interface specification, which appears within a type declaration to define an interface called Locker:&lt;/p>
&lt;pre tabindex="0">&lt;code>type Locker interface {
Lock()
Unlock()
}
&lt;/code>&lt;/pre>&lt;p>If S1 and S2 also implement&lt;/p>
&lt;pre tabindex="0">&lt;code>func (p T) Lock() { … }
func (p T) Unlock() { … }
&lt;/code>&lt;/pre>&lt;p>they implement the Locker interface as well as the File interface.&lt;/p>
&lt;p>An interface T may use a (possibly qualified) interface type name E in place of a method specification. This is called embedding interface E in T; it adds all (exported and non-exported) methods of E to the interface T.&lt;/p>
&lt;pre tabindex="0">&lt;code>type ReadWriter interface {
Read(b Buffer) bool
Write(b Buffer) bool
}
type File interface {
ReadWriter // same as adding the methods of ReadWriter
Locker // same as adding the methods of Locker
Close()
}
type LockedFile interface {
Locker
File // illegal: Lock, Unlock not unique
Lock() // illegal: Lock not unique
}
&lt;/code>&lt;/pre>&lt;p>An interface type T may not embed itself or any interface type that embeds T, recursively.&lt;/p>
&lt;pre tabindex="0">&lt;code>// illegal: Bad cannot embed itself
type Bad interface {
Bad
}
// illegal: Bad1 cannot embed itself using Bad2
type Bad1 interface {
Bad2
}
type Bad2 interface {
Bad1
}
&lt;/code>&lt;/pre>&lt;h2 id="map-types">Map types
&lt;/h2>&lt;p>A map is an unordered group of elements of one type, called the element type, indexed by a set of unique keys of another type, called the key type. The value of an uninitialized map is nil.&lt;/p>
&lt;p>MapType = &amp;ldquo;map&amp;rdquo; &amp;ldquo;[&amp;rdquo; KeyType &amp;ldquo;]&amp;rdquo; ElementType .
KeyType = Type .
The comparison operators == and != must be fully defined for operands of the key type; thus the key type must not be a function, map, or slice. If the key type is an interface type, these comparison operators must be defined for the dynamic key values; failure will cause a run-time panic.&lt;/p>
&lt;p>map[string]int
map[*T]struct{ x, y float64 }
map[string]interface{}
The number of map elements is called its length. For a map m, it can be discovered using the built-in function len and may change during execution. Elements may be added during execution using assignments and retrieved with index expressions; they may be removed with the delete built-in function.&lt;/p>
&lt;p>A new, empty map value is made using the built-in function make, which takes the map type and an optional capacity hint as arguments:&lt;/p>
&lt;p>make(map[string]int)
make(map[string]int, 100)
The initial capacity does not bound its size: maps grow to accommodate the number of items stored in them, with the exception of nil maps. A nil map is equivalent to an empty map except that no elements may be added.&lt;/p>
&lt;h2 id="channel-types">Channel types
&lt;/h2>&lt;p>A channel provides a mechanism for concurrently executing functions to communicate by sending and receiving values of a specified element type. The value of an uninitialized channel is nil.&lt;/p>
&lt;p>ChannelType = ( &amp;ldquo;chan&amp;rdquo; | &amp;ldquo;chan&amp;rdquo; &amp;ldquo;&amp;lt;-&amp;rdquo; | &amp;ldquo;&amp;lt;-&amp;rdquo; &amp;ldquo;chan&amp;rdquo; ) ElementType .
The optional &amp;lt;- operator specifies the channel direction, send or receive. If no direction is given, the channel is bidirectional. A channel may be constrained only to send or only to receive by conversion or assignment.&lt;/p>
&lt;p>chan T // can be used to send and receive values of type T
chan&amp;lt;- float64 // can only be used to send float64s
&amp;lt;-chan int // can only be used to receive ints
The &amp;lt;- operator associates with the leftmost chan possible:&lt;/p>
&lt;p>chan&amp;lt;- chan int // same as chan&amp;lt;- (chan int)
chan&amp;lt;- &amp;lt;-chan int // same as chan&amp;lt;- (&amp;lt;-chan int)
&amp;lt;-chan &amp;lt;-chan int // same as &amp;lt;-chan (&amp;lt;-chan int)
chan (&amp;lt;-chan int)
A new, initialized channel value can be made using the built-in function make, which takes the channel type and an optional capacity as arguments:&lt;/p>
&lt;p>make(chan int, 100)
The capacity, in number of elements, sets the size of the buffer in the channel. If the capacity is zero or absent, the channel is unbuffered and communication succeeds only when both a sender and receiver are ready. Otherwise, the channel is buffered and communication succeeds without blocking if the buffer is not full (sends) or not empty (receives). A nil channel is never ready for communication.&lt;/p>
&lt;p>A channel may be closed with the built-in function close. The multi-valued assignment form of the receive operator reports whether a received value was sent before the channel was closed.&lt;/p>
&lt;p>A single channel may be used in send statements, receive operations, and calls to the built-in functions cap and len by any number of goroutines without further synchronization. Channels act as first-in-first-out queues. For example, if one goroutine sends values on a channel and a second goroutine receives them, the values are received in the order sent.&lt;/p></description></item><item><title>Legal Single-Line Dual Dial - Improving Dorm Router Bandwidth Utilization</title><link>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/%E5%90%88%E6%B3%95%E5%8D%95%E7%BA%BF%E5%8F%8C%E6%8B%A8%E6%8F%90%E9%AB%98%E5%AF%9D%E5%AE%A4%E8%B7%AF%E7%94%B1%E5%B8%A6%E5%AE%BD%E5%88%A9%E7%94%A8%E7%8E%87/</link><pubDate>Sat, 10 Nov 2018 18:08:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/%E5%90%88%E6%B3%95%E5%8D%95%E7%BA%BF%E5%8F%8C%E6%8B%A8%E6%8F%90%E9%AB%98%E5%AF%9D%E5%AE%A4%E8%B7%AF%E7%94%B1%E5%B8%A6%E5%AE%BD%E5%88%A9%E7%94%A8%E7%8E%87/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>My dormitory has two China Mobile broadband connections via fiber to the home. One is 20Mbps and the other is 30Mbps, both using PPPoE dial-up. I hope to combine these two bandwidths.&lt;/p>
&lt;h2 id="solution-1">Solution 1
&lt;/h2>&lt;p>Searched for multi-WAN port routers on JD.com, but they are quite expensive. Pass.&lt;/p>
&lt;h2 id="solution-2">Solution 2
&lt;/h2>&lt;p>Modify my Xiaomi Router Mini by flashing OpenWrt.&lt;/p>
&lt;h2 id="flashing-openwrt">Flashing OpenWrt
&lt;/h2>&lt;p>This isn&amp;rsquo;t the focus of this article. However, it&amp;rsquo;s worth noting that OpenWrt firmware can be found and downloaded from the official website. Its repositories are complete and up-to-date, so you don&amp;rsquo;t need to flash outdated firmware like Pandorabox with unreliable repositories. The router needs to be flashed with a developer version firmware and SSH firmware, then OpenWrt firmware can be flashed via command line. There&amp;rsquo;s no need to flash the Breed u-boot, which is too risky and can cause loss of the SN.&lt;/p>
&lt;h2 id="first-dial-up-setting-up-domestic-repositories">First Dial-up, Setting Up Domestic Repositories
&lt;/h2>&lt;p>First, turn on the router, plug in the network cable, access 192.168.1.1, and set the root password on the page.&lt;/p>
&lt;p>In the System Software page, click Configuration, replace downloads.openwrt.org with openwrt.proxy.ustclug.org, and submit. In the Network - Interface page, edit the WAN configuration, change the protocol, and configure PPPoE internet access.&lt;/p>
&lt;p>Connect to the router via SSH, type opkg update in the command line. Make sure it succeeds.
Then type opkg install kmod-macvlan mwan3 luci-app-mwan3. If it doesn&amp;rsquo;t succeed, try a few more times.&lt;/p>
&lt;h2 id="setting-up-a-virtual-network-card">Setting Up a Virtual Network Card
&lt;/h2>&lt;p>Check the VLAN binding of the WAN interface, which is eth0.2. In the terminal, enter ip link add link eth0.2 name veth0 type macvlan, and then ifconfig veth0 up.&lt;/p>
&lt;h2 id="binding-network-card-to-interface">Binding Network Card to Interface
&lt;/h2>&lt;p>For convenience, modify WAN6 as our second interface, set up the second account&amp;rsquo;s PPPoE internet account, and then modify its physical setting to veth0.&lt;/p>
&lt;p>If the two accounts cannot dial simultaneously at this point, it means single-line dual dial is not supported.&lt;/p>
&lt;h2 id="setting-hop-count">Setting Hop Count
&lt;/h2>&lt;p>Set the use gateway metric in both the WAN and WAN6 pages. Make sure they are different, for example, one at 10 and one at 11.&lt;/p>
&lt;h2 id="setting-up-load-balancing">Setting Up Load Balancing
&lt;/h2>&lt;p>Configure the load balance settings in sequence.&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/30ee28d04636.png"
loading="lazy"
alt="image.png"
>&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/d66fe1e72238.png"
loading="lazy"
alt="image.png"
>&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/9a3584c9d52a.png"
loading="lazy"
alt="image.png"
>&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/ec59a0a7332f.png"
loading="lazy"
alt="image.png"
>&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/f9733e8da055.png"
loading="lazy"
alt="image.png"
>&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/baabfb48f8bc.png"
loading="lazy"
alt="image.png"
>&lt;/p>
&lt;h2 id="restart-service">Restart Service
&lt;/h2>&lt;p>Click to restart the service&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/daf014ef974b.png"
loading="lazy"
alt="image.png"
>&lt;/p>
&lt;h2 id="testing">Testing
&lt;/h2>&lt;p>&lt;img src="https://nansenli.com/img/rescued/d2dbb6461264.png"
loading="lazy"
alt="image.png"
>&lt;/p>
&lt;blockquote>
&lt;p>Reference: &lt;a class="link" href="https://acris.me/2017/06/25/Load-balancing-multiple-PPPoE-on-LEDE/" target="_blank" rel="noopener"
>https://acris.me/2017/06/25/Load-balancing-multiple-PPPoE-on-LEDE/&lt;/a>&lt;/p>
&lt;/blockquote></description></item><item><title>2018-09-20 Go Learning Notes</title><link>https://nansenli.com/post/jianshu/go/2018-09-20-go%E5%AD%A6%E4%B9%A0%E6%8B%BE%E8%B4%9D/</link><pubDate>Wed, 07 Nov 2018 06:54:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/go/2018-09-20-go%E5%AD%A6%E4%B9%A0%E6%8B%BE%E8%B4%9D/</guid><description>&lt;h2 id="gos-pointers-and-values-behave-differently-when-used-as-receivers-and-parameters">Go&amp;rsquo;s pointers and values behave differently when used as receivers and parameters
&lt;/h2>&lt;p>Methods and pointer indirection
When comparing the first two programs, you might notice that functions with pointer parameters must accept a pointer:&lt;/p>
&lt;p>var v Vertex
ScaleFunc(v, 5) // Compile error!
ScaleFunc(&amp;amp;v, 5) // OK
However, methods with pointer receivers can be called with both values and pointers:&lt;/p>
&lt;p>var v Vertex
v.Scale(5) // OK
p := &amp;amp;v
p.Scale(10) // OK
For the statement v.Scale(5), even though v is a value and not a pointer, methods with pointer receivers can still be called directly. In other words, since the Scale method has a pointer receiver, for convenience, Go interprets the statement v.Scale(5) as (&amp;amp;v).Scale(5).&lt;/p>
&lt;p>Additionally, Go automatically interprets the . operation on pointers as (*).&lt;/p></description></item><item><title>Using OpenSSL AES Encryption Algorithm API</title><link>https://nansenli.com/post/jianshu/c++/openssl-aes-%E5%8A%A0%E5%AF%86%E7%AE%97%E6%B3%95-api%E4%BD%BF%E7%94%A8/</link><pubDate>Fri, 02 Nov 2018 06:49:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/c++/openssl-aes-%E5%8A%A0%E5%AF%86%E7%AE%97%E6%B3%95-api%E4%BD%BF%E7%94%A8/</guid><description>&lt;h2 id="example-using-aes-128-cbc-encryption-algorithm">Example: Using AES-128-CBC Encryption Algorithm
&lt;/h2>&lt;p>This example uses pkcs5_encode padding method, which is also compatible with pkcs7_encode&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-c++" data-lang="c++">&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;stdio.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;stdlib.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;string.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;openssl/aes.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;openssl/rand.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// a simple hex-print routine. could be modified to print 16 bytes-per-line
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">hex_print&lt;/span>(&lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span>&lt;span style="color:#f92672">*&lt;/span> pv, size_t len)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">unsigned&lt;/span> &lt;span style="color:#66d9ef">char&lt;/span> &lt;span style="color:#f92672">*&lt;/span> p &lt;span style="color:#f92672">=&lt;/span> (&lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">unsigned&lt;/span> &lt;span style="color:#66d9ef">char&lt;/span>&lt;span style="color:#f92672">*&lt;/span>)pv;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (NULL &lt;span style="color:#f92672">==&lt;/span> pv)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> printf(&lt;span style="color:#e6db74">&amp;#34;NULL&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> size_t i &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span> (; i&lt;span style="color:#f92672">&amp;lt;&lt;/span>len;&lt;span style="color:#f92672">++&lt;/span>i)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> printf(&lt;span style="color:#e6db74">&amp;#34;%02X &amp;#34;&lt;/span>, &lt;span style="color:#f92672">*&lt;/span>p&lt;span style="color:#f92672">++&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> printf(&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">pkcs7_encode&lt;/span>(&lt;span style="color:#66d9ef">uint8_t&lt;/span> &lt;span style="color:#f92672">*&lt;/span>in, &lt;span style="color:#66d9ef">uint8_t&lt;/span> &lt;span style="color:#f92672">*&lt;/span>out, &lt;span style="color:#66d9ef">int&lt;/span> inlen, &lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#f92672">*&lt;/span>outlen, &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> enc)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// uint8_t padchr[16] = {0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01};
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">if&lt;/span>(inlen &lt;span style="color:#f92672">&amp;lt;=&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>) &lt;span style="color:#66d9ef">return&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Add padding
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">if&lt;/span> (enc &lt;span style="color:#f92672">==&lt;/span> AES_ENCRYPT) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">*&lt;/span>outlen &lt;span style="color:#f92672">=&lt;/span> inlen &lt;span style="color:#f92672">+&lt;/span> &lt;span style="color:#ae81ff">16&lt;/span> &lt;span style="color:#f92672">-&lt;/span> inlen &lt;span style="color:#f92672">%&lt;/span> &lt;span style="color:#ae81ff">16&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span> (&lt;span style="color:#66d9ef">int&lt;/span> i &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>; i &lt;span style="color:#f92672">&amp;lt;&lt;/span> &lt;span style="color:#f92672">*&lt;/span>outlen; i&lt;span style="color:#f92672">++&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (i &lt;span style="color:#f92672">&amp;lt;&lt;/span> inlen)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> out[i] &lt;span style="color:#f92672">=&lt;/span> in[i];
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> out[i] &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">16&lt;/span> &lt;span style="color:#f92672">-&lt;/span> inlen &lt;span style="color:#f92672">%&lt;/span> &lt;span style="color:#ae81ff">16&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Remove padding
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">else&lt;/span> &lt;span style="color:#66d9ef">if&lt;/span> (enc &lt;span style="color:#f92672">==&lt;/span> AES_DECRYPT) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">*&lt;/span>outlen &lt;span style="color:#f92672">=&lt;/span> inlen &lt;span style="color:#f92672">-&lt;/span> (in[inlen&lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>]);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span> (&lt;span style="color:#66d9ef">int&lt;/span> i&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>; i&lt;span style="color:#f92672">&amp;lt;&lt;/span> &lt;span style="color:#f92672">*&lt;/span>outlen; i&lt;span style="color:#f92672">++&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> out[i] &lt;span style="color:#f92672">=&lt;/span> in[i];
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> argc, &lt;span style="color:#66d9ef">char&lt;/span> &lt;span style="color:#f92672">**&lt;/span>argv)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">int&lt;/span> keylength;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> printf(&lt;span style="color:#e6db74">&amp;#34;Give a key length [only 128 or 192 or 256!]:&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> scanf(&lt;span style="color:#e6db74">&amp;#34;%d&amp;#34;&lt;/span>, &lt;span style="color:#f92672">&amp;amp;&lt;/span>keylength);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">/* generate a key with a given length */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">unsigned&lt;/span> &lt;span style="color:#66d9ef">char&lt;/span> aes_key[&lt;span style="color:#ae81ff">16&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> {&lt;span style="color:#ae81ff">0x00&lt;/span>,&lt;span style="color:#ae81ff">0x01&lt;/span>,&lt;span style="color:#ae81ff">0x02&lt;/span>,&lt;span style="color:#ae81ff">0x03&lt;/span>,&lt;span style="color:#ae81ff">0x04&lt;/span>,&lt;span style="color:#ae81ff">0x05&lt;/span>,&lt;span style="color:#ae81ff">0x06&lt;/span>,&lt;span style="color:#ae81ff">0x07&lt;/span>,&lt;span style="color:#ae81ff">0x08&lt;/span>,&lt;span style="color:#ae81ff">0x09&lt;/span>,&lt;span style="color:#ae81ff">0x0a&lt;/span>,&lt;span style="color:#ae81ff">0x0b&lt;/span>,&lt;span style="color:#ae81ff">0x0c&lt;/span>,&lt;span style="color:#ae81ff">0x0d&lt;/span>,&lt;span style="color:#ae81ff">0x0e&lt;/span>,&lt;span style="color:#ae81ff">0x0f&lt;/span>};
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> size_t inputslength &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> printf(&lt;span style="color:#e6db74">&amp;#34;Give an input&amp;#39;s length:&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> scanf(&lt;span style="color:#e6db74">&amp;#34;%lu&amp;#34;&lt;/span>, &lt;span style="color:#f92672">&amp;amp;&lt;/span>inputslength);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">/* generate input with a given length */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">unsigned&lt;/span> &lt;span style="color:#66d9ef">char&lt;/span> &lt;span style="color:#f92672">*&lt;/span> aes_input &lt;span style="color:#f92672">=&lt;/span> malloc(inputslength);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">unsigned&lt;/span> &lt;span style="color:#66d9ef">char&lt;/span> &lt;span style="color:#f92672">*&lt;/span> aes_out &lt;span style="color:#f92672">=&lt;/span> malloc(inputslength);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> memset(aes_input, &lt;span style="color:#e6db74">&amp;#39;X&amp;#39;&lt;/span>, inputslength);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">/* init vector */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">unsigned&lt;/span> &lt;span style="color:#66d9ef">char&lt;/span> iv_enc[AES_BLOCK_SIZE] &lt;span style="color:#f92672">=&lt;/span> {&lt;span style="color:#ae81ff">0&lt;/span>}, iv_dec[AES_BLOCK_SIZE]&lt;span style="color:#f92672">=&lt;/span>{&lt;span style="color:#ae81ff">0&lt;/span>};
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">const&lt;/span> size_t encslength &lt;span style="color:#f92672">=&lt;/span> ((inputslength &lt;span style="color:#f92672">+&lt;/span> AES_BLOCK_SIZE) &lt;span style="color:#f92672">/&lt;/span> AES_BLOCK_SIZE) &lt;span style="color:#f92672">*&lt;/span> AES_BLOCK_SIZE;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">unsigned&lt;/span> &lt;span style="color:#66d9ef">char&lt;/span> &lt;span style="color:#f92672">*&lt;/span> enc_in &lt;span style="color:#f92672">=&lt;/span> malloc(encslength);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> pkcs7_encode(aes_input, enc_in, inputslength, &lt;span style="color:#f92672">&amp;amp;&lt;/span>encslength, AES_ENCRYPT);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">unsigned&lt;/span> &lt;span style="color:#66d9ef">char&lt;/span> &lt;span style="color:#f92672">*&lt;/span> enc_out &lt;span style="color:#f92672">=&lt;/span> malloc(encslength);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> AES_KEY enc_key;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> AES_set_encrypt_key(aes_key, keylength, &lt;span style="color:#f92672">&amp;amp;&lt;/span>enc_key);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> AES_cbc_encrypt(enc_in, enc_out, encslength, &lt;span style="color:#f92672">&amp;amp;&lt;/span>enc_key, iv_enc, AES_ENCRYPT);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> printf(&lt;span style="color:#e6db74">&amp;#34;original:&lt;/span>&lt;span style="color:#ae81ff">\t&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> hex_print(aes_input, inputslength);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> printf(&lt;span style="color:#e6db74">&amp;#34;padding:&lt;/span>&lt;span style="color:#ae81ff">\t&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> hex_print(enc_in, encslength);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> printf(&lt;span style="color:#e6db74">&amp;#34;encrypt:&lt;/span>&lt;span style="color:#ae81ff">\t&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> hex_print(enc_out, encslength);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">unsigned&lt;/span> &lt;span style="color:#66d9ef">char&lt;/span> &lt;span style="color:#f92672">*&lt;/span> dec_out &lt;span style="color:#f92672">=&lt;/span> malloc(encslength);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> AES_KEY dec_key;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> AES_set_decrypt_key(aes_key, keylength, &lt;span style="color:#f92672">&amp;amp;&lt;/span>dec_key);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> AES_cbc_encrypt(enc_out, dec_out, encslength, &lt;span style="color:#f92672">&amp;amp;&lt;/span>dec_key, iv_dec, AES_DECRYPT);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> pkcs7_encode(dec_out, aes_out, encslength, &lt;span style="color:#f92672">&amp;amp;&lt;/span>inputslength, AES_DECRYPT);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> printf(&lt;span style="color:#e6db74">&amp;#34;decrypt:&lt;/span>&lt;span style="color:#ae81ff">\t&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> hex_print(dec_out, encslength);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> printf(&lt;span style="color:#e6db74">&amp;#34;depadding:&lt;/span>&lt;span style="color:#ae81ff">\t&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> hex_print(aes_out, inputslength);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">int8_t&lt;/span> buff[&lt;span style="color:#ae81ff">100&lt;/span>];
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">long&lt;/span> bufflen;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Investigation and Solution of Windows 10 SSD Periodic Stuttering Problems</title><link>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/%E5%85%B3%E4%BA%8E-windows-10-%E4%BD%BF%E7%94%A8-ssd-%E6%AF%8F%E9%9A%94%E4%B8%80%E6%AE%B5%E6%97%B6%E9%97%B4%E5%8D%A1%E9%A1%BF%E9%97%AE%E9%A2%98%E7%9A%84%E6%8E%A2%E7%A9%B6%E4%B8%8E%E8%A7%A3%E5%86%B3/</link><pubDate>Wed, 24 Oct 2018 11:47:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/%E5%85%B3%E4%BA%8E-windows-10-%E4%BD%BF%E7%94%A8-ssd-%E6%AF%8F%E9%9A%94%E4%B8%80%E6%AE%B5%E6%97%B6%E9%97%B4%E5%8D%A1%E9%A1%BF%E9%97%AE%E9%A2%98%E7%9A%84%E6%8E%A2%E7%A9%B6%E4%B8%8E%E8%A7%A3%E5%86%B3/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>I was originally using an Intel i5-6500 CPU, but after switching to an AMD Ryzen 2700, frequent system freezes started occurring.&lt;/p>
&lt;h2 id="symptoms">Symptoms
&lt;/h2>&lt;p>During these freezes, I observed that the disk usage in Task Manager was at 100%, while both read and write rates were at 0. The SSD is a generic Lexar Hades 512 GB. In Event Viewer, there were numerous events with ID 129.&lt;/p>
&lt;h2 id="suspecting-hardware-issues">Suspecting Hardware Issues
&lt;/h2>&lt;p>After repeatedly changing SATA ports and reconnecting power cables, the freezes persisted.&lt;/p>
&lt;h2 id="searching-for-related-issues-online">Searching for Related Issues Online
&lt;/h2>&lt;p>Online searches mostly pointed to the root cause being Windows 10&amp;rsquo;s AHCI driver issues and SSD firmware problems.&lt;/p>
&lt;h2 id="solution-approach-1-replacing-the-ahci-driver---ineffective">Solution Approach 1: Replacing the AHCI Driver - Ineffective
&lt;/h2>&lt;p>To replace the AHCI driver, I tried downloading chipset drivers from both AMD&amp;rsquo;s official website and ASUS&amp;rsquo;s official website, and manually replaced the driver in Device Manager so that it showed as an AMD SATA driver. I also tried various motherboard drivers, but testing proved ineffective, and the problem remained unsolved.&lt;/p>
&lt;h2 id="solution-approach-2-converting-the-drive-to-raid-format---ineffective">Solution Approach 2: Converting the Drive to RAID Format - Ineffective
&lt;/h2>&lt;p>To convert to RAID format, I reinstalled Windows, but during the installation process, Windows could not recognize the SSD. According to AMD&amp;rsquo;s official English manual and official RAID drivers, I manually loaded the RAID drivers, but Windows still couldn&amp;rsquo;t see the SSD during installation. After multiple attempts, it remained undetected. This might be because volume mode RAID is not supported with only one drive. I abandoned this approach.&lt;/p>
&lt;h2 id="solution-approach-3-modifying-the-registry-disabling-msi---ineffective">Solution Approach 3: Modifying the Registry, Disabling MSI - Ineffective
&lt;/h2>&lt;p>&lt;a class="link" href="https://www.pconline.com.cn/win10/739/7395324.html" target="_blank" rel="noopener"
>https://www.pconline.com.cn/win10/739/7395324.html&lt;/a>&lt;/p>
&lt;blockquote>
&lt;p>In essence, when using the StorAHCI.sys driver in certain AHCI modes, some SSDs may not be able to complete input/output operations correctly in &amp;ldquo;Message Signaled Interrupt&amp;rdquo; (MSI) mode, ultimately resulting in system freezes with disk usage showing 100% in Task Manager. Of course, the solution is simple: just disable it (MSI Mode) directly in regedit.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>　　4. Click Cortana, type &amp;ldquo;regedit&amp;rdquo; to launch the Registry Editor, navigate to &amp;ldquo;HKEY_LOCAL_MACHINE\System\CurrentControlSet\Enum\PCI\xxxxx\Device Parameters\Interrupt Management\MessageSignaledInterruptProperties&amp;rdquo; (where &amp;ldquo;xxxxx&amp;rdquo; is the &amp;ldquo;device instance path&amp;rdquo; you just recorded);&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>　　5. Finally, change the &amp;ldquo;MSISupported&amp;rdquo; key value in the right pane from 1 to 0;&lt;/p>
&lt;/blockquote>
&lt;p>Testing proved this ineffective.&lt;/p>
&lt;h2 id="solution-approach-4-changing-power-settings---ineffective">Solution Approach 4: Changing Power Settings - Ineffective
&lt;/h2>&lt;p>Setting the PCI-Express setting to OFF, testing proved ineffective.&lt;/p>
&lt;h2 id="solution-approach-5-changing-lpm---effective">Solution Approach 5: Changing LPM - Effective
&lt;/h2>&lt;p>&lt;a class="link" href="http://www.sohu.com/a/204645459_493865" target="_blank" rel="noopener"
>http://www.sohu.com/a/204645459_493865&lt;/a>&lt;/p>
&lt;p>&lt;a class="link" href="https://answers.microsoft.com/en-us/windows/forum/windows_10-hardware-winpc/event-id-129-storahci-resetting-raidport0/7b30c512-6597-438b-80cb-22fb2f85d62e" target="_blank" rel="noopener"
>https://answers.microsoft.com/en-us/windows/forum/windows_10-hardware-winpc/event-id-129-storahci-resetting-raidport0/7b30c512-6597-438b-80cb-22fb2f85d62e&lt;/a>&lt;/p>
&lt;p>The content is as follows:&lt;/p>
&lt;p>Most SSDs we use today support some power-saving features, such as DIPM/HIPM/Device Sleep.&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/8ef2136fa9e4.png"
loading="lazy"
alt="image"
>&lt;/p>
&lt;p>When a state change request is initiated by the host, we call it HIPM (Host-Initiated LPM), and when it&amp;rsquo;s directly initiated by the device, we call it DIPM (Device-Initiated LPM). What are they requesting? Requesting to slow down, save energy, or enter Device Sleep.&lt;/p>
&lt;p>Both HIPM and DIPM rely on the computer&amp;rsquo;s LPM technology support. LPM, or Link Power Management, is a function at the physical layer of the SATA interface that can divide the link between the host and storage device into three states: Active state, Partial state, and Slumber state. The goal is to reduce the physical layer bus transmission speed, thus achieving power-saving effects.&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/beb05312d902.png"
loading="lazy"
alt="image"
>&lt;/p>
&lt;p>LPM is generally enabled by default.&lt;/p>
&lt;p>So is enabling LPM good for SSDs?&lt;/p>
&lt;p>1. The host sends an HIPM state change command, but due to differing SSD device standards, it may not be well-supported, causing compatibility issues leading to stuttering or drive disconnection;&lt;/p>
&lt;p>2. The host sends HIPM state change commands too frequently, causing the SSD device to not respond in time, resulting in stuttering;&lt;/p>
&lt;p>3. After LPM is enabled, it increases SATA bus transmission delays, causing high-speed SSDs to become blocked and stutter;&lt;/p>
&lt;p>4. Conflicts between the SSD&amp;rsquo;s internal DIPM and the host&amp;rsquo;s requested HIPM cause stuttering.&lt;/p>
&lt;p>Often, SSD-induced computer stuttering and sudden drive disconnections are caused by LPM.&lt;/p>
&lt;p>LPM can help SATA devices achieve power-saving effects. However, it also affects SSD performance.&lt;/p>
&lt;p>So, for the sake of power saving, are you willing to pay the price of losing SSD performance?&lt;/p>
&lt;p>HDD power consumption is around 10-25W. Perhaps LPM was originally only intended for HDDs.&lt;/p>
&lt;p>SSD startup power consumption: 0.1W&lt;del>1W (2W), maximum power consumption: 5&lt;/del>10W, average power consumption: around 4~5W.&lt;/p>
&lt;p>Does our power supply care about these few watts from the SSD? Do we care about this bit of electricity cost? Should we even care!&lt;/p>
&lt;p>If SSDs don&amp;rsquo;t need LPM, which harms SSD performance, can we turn it off?&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/e3137f899356.png"
loading="lazy"
alt="image"
>&lt;/p>
&lt;p>Save as &amp;ldquo;.reg&amp;rdquo; format.&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/bd0944771088.png"
loading="lazy"
alt="image"
>&lt;/p>
&lt;p>Click to run. It will automatically write to the registry, changing the LPM power settings.&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/cfc7398516b6.png"
loading="lazy"
alt="image"
>&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/cb2106c96417.png"
loading="lazy"
alt="image"
>&lt;/p>
&lt;p>If your SSD is experiencing severe stuttering or disconnections, you can try this method.&lt;/p>
&lt;p>The main reason for SSD stuttering and disconnections is that the SSD is too poor quality. Indeed, with SSD prices constantly rising today, it&amp;rsquo;s very difficult to buy a good SSD at a relatively low price.&lt;/p>
&lt;p>&lt;strong>After testing, this was effective&lt;/strong>&lt;/p>
&lt;h2 id="solution-approach-6-changing-bios-settings-enabling-sata-hot-plug-functionality---recommended">Solution Approach 6: Changing BIOS Settings, Enabling SATA Hot Plug Functionality - Recommended
&lt;/h2>&lt;p>&lt;a class="link" href="https://www.aiweibk.com/5795.html" target="_blank" rel="noopener"
>https://www.aiweibk.com/5795.html&lt;/a>&lt;/p>
&lt;blockquote>
&lt;p>After enabling the Hot Plug attribute in the motherboard BIOS settings, there will be a side effect: the SSD&amp;rsquo;s LPM power saving will be turned off as hot-plugging is enabled, thereby avoiding data loss during hot-plugging or loss of response when reconnecting.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>If your SSD and motherboard have compatibility issues causing intermittent stuttering, try enabling the Hot Plug option in the motherboard BIOS settings to disable LPM power saving, which might solve the problem.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;strong>After testing, this was effective&lt;/strong>&lt;/p>
&lt;h2 id="conclusion">Conclusion
&lt;/h2>&lt;p>Why was there no problem with Intel before, but problems arose after switching to AMD? I suspect it&amp;rsquo;s because the original Intel motherboard was inferior and didn&amp;rsquo;t support SATA LPM, while Windows 10 has LPM enabled by default. The generic SSD doesn&amp;rsquo;t support LPM well, so even though its firmware supports LPM, serious issues can occur. Since the motherboard didn&amp;rsquo;t support LPM, even with Windows enabling LPM, the problem didn&amp;rsquo;t manifest.&lt;/p>
&lt;p>Now with a new motherboard that supports LPM, compatibility issues with the SSD have emerged under default settings. LPM functionality needs to be manually disabled.&lt;/p></description></item><item><title>2018-09-15 NetEase Interactive Entertainment Interview</title><link>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/2018-09-15-%E7%BD%91%E6%98%93%E4%BA%92%E5%A8%B1%E9%9D%A2%E8%AF%95/</link><pubDate>Sat, 15 Sep 2018 15:59:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/2018-09-15-%E7%BD%91%E6%98%93%E4%BA%92%E5%A8%B1%E9%9D%A2%E8%AF%95/</guid><description>&lt;p>Interviewed at a hotel in Chengdu
Will update if I remember more&lt;/p>
&lt;h2 id="written-test">Written Test
&lt;/h2>&lt;p>Implement a hashmap using open addressing, with int as key and string as value, time limit 30 minutes&lt;/p>
&lt;h2 id="first-interview">First Interview
&lt;/h2>&lt;ul>
&lt;li>Purpose and principles of volatile&lt;/li>
&lt;li>Differences between dynamic_cast and static_cast&lt;/li>
&lt;li>Issues with legal conversions between parent and child classes using dynamic_cast&lt;/li>
&lt;li>Virtual destructors and vtables&lt;/li>
&lt;li>RTTI (Run-Time Type Information) and type_info&lt;/li>
&lt;li>Sizeof for structures&lt;/li>
&lt;li>Characteristics of red-black trees; given approximately 12 numbers, provide a possible red-black tree result and draw it&lt;/li>
&lt;li>API call process for establishing a server listen, how to implement non-blocking epoll&lt;/li>
&lt;li>Principles of epoll, differences in business code logic between edge-triggered and level-triggered&lt;/li>
&lt;li>TCP packet sticking (concatenation)&lt;/li>
&lt;li>Throwing two eggs from a 100-story building to determine the egg&amp;rsquo;s hardness, dynamic programming solution method&lt;/li>
&lt;li>1 billion players, each with a score ranging from 0 to 100,000, how to determine a player&amp;rsquo;s ranking&lt;/li>
&lt;li>Redis skip list&lt;/li>
&lt;li>How memory barriers are implemented in assembly&lt;/li>
&lt;li>Two things that every void func() assembly program needs to do at the beginning&lt;/li>
&lt;li>How to determine the cause of a memory leak&lt;/li>
&lt;li>How to print something before the main function. What other programs execute before the main function.&lt;/li>
&lt;/ul>
&lt;h2 id="second-interview">Second Interview
&lt;/h2>&lt;ul>
&lt;li>Differences between Python&amp;rsquo;s is and == operators, the id function&lt;/li>
&lt;li>Differences between Python&amp;rsquo;s xrange and range&lt;/li>
&lt;li>Python iterators&lt;/li>
&lt;li>Redis ziplist&lt;/li>
&lt;li>Redis TTL expiration implementation&lt;/li>
&lt;li>STL deque implementation (I answered list, which was incorrect. Then asked how I would implement a deque)&lt;/li>
&lt;li>What happens between running a program in shell and the program outputting &amp;ldquo;hello world&amp;rdquo;, in detail&lt;/li>
&lt;li>Draw a typical memory layout for a Linux program&lt;/li>
&lt;li>Can different threads access each other&amp;rsquo;s stack space?&lt;/li>
&lt;li>A matrix of length x and width y, with holes at the four corners. A ball starts from a certain point and moves indefinitely without friction. Which hole will it enter?&lt;/li>
&lt;li>What can be put inside the [] of a lambda expression, how do you think the compiler implements lambda expressions&lt;/li>
&lt;li>What is an rvalue (I confused rvalue with rvalue reference here)&lt;/li>
&lt;li>The implementation process of malloc, does malloc always call brk to adjust the heap boundary? Introduction to the buddy system&lt;/li>
&lt;li>How do HTTPS clients and servers implement protocol selection, what are the commonly used protocols now&lt;/li>
&lt;li>The process of client verification of the server in HTTPS&lt;/li>
&lt;/ul></description></item><item><title>A Discussion on Python Decorators</title><link>https://nansenli.com/post/jianshu/python/python-%E8%A3%85%E9%A5%B0%E5%99%A8%E6%BC%AB%E8%B0%88/</link><pubDate>Tue, 04 Sep 2018 08:54:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/python/python-%E8%A3%85%E9%A5%B0%E5%99%A8%E6%BC%AB%E8%B0%88/</guid><description>&lt;p>Reference: &lt;a class="link" href="http://python.jobbole.com/81683/" target="_blank" rel="noopener"
>http://python.jobbole.com/81683/&lt;/a>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Python&amp;rsquo;s Namespace
Any variable is created within its own namespace. If your own namespace doesn&amp;rsquo;t contain this name, a new one will be created, rather than overriding the upper namespace. When accessing, if your namespace doesn&amp;rsquo;t contain it, the upper namespace will be accessed.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Python&amp;rsquo;s Closures
Any function is an object. You can create a callable object. If you create a callable function that uses a local variable in its internal process, this involves a closure: a function nested and defined in a non-global scope can remember the enclosing namespace where it was defined.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>In simple terms: a child function can use local variables from its parent function, and this behavior is called a closure.&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Closure Details in C++ vs Python
In most languages, a parent function&amp;rsquo;s local variables end their lifecycle when the parent function exits. When lambda functions or function objects reference these local variables, problems can arise. If a lambda or function object can remember the current namespace during its definition, that&amp;rsquo;s a closure. In C++, local variable lifecycles are the same as their parent function, so what to do? Use the mutable modifier, which makes the variable&amp;rsquo;s lifecycle as long as the function object or lambda expression, otherwise capturing these variables might cause problems. Python captures all objects in the closure, preventing their release until the function object is collected.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Decorators
Sometimes we need to modify functions in similar ways. For example, we might want to calculate a function&amp;rsquo;s execution time by adding timestamps at the beginning and end, then printing at the end. Or we might need to validate parameters and return values. We could call validation functions at the start and end of each function.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>Decorators can accomplish this. For example, if we have a function:&lt;/p>
&lt;pre tabindex="0">&lt;code>def func():
time.sleep(1)
&lt;/code>&lt;/pre>&lt;p>We can use another function that returns a function object with closure to achieve this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">def&lt;/span> &lt;span style="color:#a6e22e">addTimePrint&lt;/span>(func):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">def&lt;/span> &lt;span style="color:#a6e22e">retFuncObj&lt;/span>(&lt;span style="color:#f92672">*&lt;/span>args, &lt;span style="color:#f92672">**&lt;/span>kwargs):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> starttime &lt;span style="color:#f92672">=&lt;/span> time()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> func(&lt;span style="color:#f92672">*&lt;/span>args, &lt;span style="color:#f92672">**&lt;/span>kwargs)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> print(time() &lt;span style="color:#f92672">-&lt;/span> starttime)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> retFuncObj
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Then we need to replace the original func function:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>func &lt;span style="color:#f92672">=&lt;/span> addTimePrint(func)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Python provides syntactic sugar: after defining addTimePrint, use @addTimePrint to complete both function definition and decoration:&lt;/p>
&lt;pre tabindex="0">&lt;code>@addTimePrint
def func():
time.sleep(1)
&lt;/code>&lt;/pre></description></item><item><title>Redis Cluster Learning</title><link>https://nansenli.com/post/jianshu/%E6%95%B0%E6%8D%AE%E5%BA%93/redis-%E9%9B%86%E7%BE%A4%E5%AD%A6%E4%B9%A0/</link><pubDate>Sun, 02 Sep 2018 14:25:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E6%95%B0%E6%8D%AE%E5%BA%93/redis-%E9%9B%86%E7%BE%A4%E5%AD%A6%E4%B9%A0/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>Let&amp;rsquo;s review the key concepts of Redis clusters:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Configuration&lt;/p>
&lt;ul>
&lt;li>A Redis database system has one master database and multiple slave databases&lt;/li>
&lt;li>The master database doesn&amp;rsquo;t require any special configuration; slave databases just need to add &amp;ndash;slaveof ip port to their configuration&lt;/li>
&lt;li>Slave databases are read-only by default, and even if changed to writable, it has no practical use&lt;/li>
&lt;li>Besides adding to the configuration file, you can also use the slaveof command at runtime to modify the master database&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Persistence Review&lt;/p>
&lt;ul>
&lt;li>RDB: This method forks the Redis process, the child process writes memory to a file, then replaces the previous RDB snapshot&lt;/li>
&lt;li>AOF: This method writes every command to a file. When the AOF file gets too large, it needs to be rewritten after a certain number of commands are appended because some commands are redundant. File writing is cached by the operating system and can be configured. Without configuration, data is written to disk every 30s. You can configure it to synchronize the cache with each command or once per second.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="principles">Principles
&lt;/h2>&lt;ul>
&lt;li>Initialization Replication
&lt;ul>
&lt;li>
&lt;ol>
&lt;li>When a slave database starts, it sends a SYNC command to the master database&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>
&lt;ol start="2">
&lt;li>The master database performs a snapshot save, caches commands during the snapshot, then sends both the snapshot and the commands to the slave database&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>
&lt;ol start="3">
&lt;li>If disconnected, Redis versions 2.6 and earlier would re-initialize replication. Redis 2.8 and later can just transmit commands during the disconnection period.&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>
&lt;ol start="4">
&lt;li>The slave database replaces the temporary snapshot with the RDB snapshot file specified in the configuration. Subsequent operations are consistent with the RDB persistence recovery process.&lt;/li>
&lt;/ol>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Replication Synchronization
&lt;ul>
&lt;li>Afterward, the master database sends commands that cause database changes to the slave database&lt;/li>
&lt;li>Redis uses optimistic replication strategy, with a window of inconsistency. The master database can be configured to be writable when there are multiple database connections, as well as the maximum allowed disconnection time for databases. This can reduce data inconsistency issues to some extent.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="readwrite-separation-for-performance-improvement">Read/Write Separation for Performance Improvement
&lt;/h2>&lt;p>Some time-consuming read operations can be accelerated by establishing multiple database slave nodes through replication&lt;/p>
&lt;h2 id="slave-database-persistence">Slave Database Persistence
&lt;/h2>&lt;p>When using slave databases, the master database&amp;rsquo;s persistence can be disabled. However, when the master database crashes, a role switch between master and slave databases is needed to recover the master database&amp;rsquo;s content. The slave database uses &amp;ldquo;slaveof no one&amp;rdquo;, and the master database uses the slaveof command to set itself as a slave database.&lt;/p>
&lt;p>In this case, if the master database crashes, it cannot be restarted immediately. Because if restarted immediately, since the master database has no persistence, it would clear the content in the slave database.&lt;/p>
&lt;h2 id="diskless-replication">Diskless Replication
&lt;/h2>&lt;p>Master-slave replication can use RDB persistence for initialization synchronization.
When the master database disables RDB snapshots but performs initialization replication, it will still generate an RDB snapshot. At this point, when the master database restarts, it will use the RDB snapshot for recovery. Since the synchronization time point is uncertain, and initialization synchronization may not have happened for a long time, this could lead to recovery at an arbitrary time point.&lt;/p>
&lt;p>Additionally, creating an RDB snapshot during initialization synchronization requires disk operations, which may affect synchronization efficiency if the disk is too slow.&lt;/p>
&lt;p>Starting from Redis 2.8.18, Redis introduced diskless replication. When this option is enabled, initialization replication will no longer create an RDB snapshot but will send the RDB snapshot content directly over the network.&lt;/p>
&lt;h2 id="incremental-replication">Incremental Replication
&lt;/h2>&lt;p>If the master and slave databases disconnect, using initialization replication for the next reconnection would be quite expensive.&lt;/p>
&lt;ul>
&lt;li>Databases generate a unique ID each time they run.&lt;/li>
&lt;li>During the replication synchronization phase, the master database places each command in a replay log and records the current command&amp;rsquo;s offset.&lt;/li>
&lt;li>The slave database also needs to record the command offset when receiving commands.&lt;/li>
&lt;li>When the master-slave connection is ready, the slave database sends psync to tell the master database it can use incremental replication.&lt;/li>
&lt;li>The master database checks if the ID sent by the slave database matches its own. If not, the master database may have restarted.&lt;/li>
&lt;li>Then it checks if the slave database&amp;rsquo;s command offset is in the backlog replay log. If so, it sends the commands from the replay log to the slave database.&lt;/li>
&lt;li>The backlog size can be configured, with a default of 1MB. A larger size allows for better tolerance after disconnection and reconnection.&lt;/li>
&lt;/ul>
&lt;h2 id="sentinel">Sentinel
&lt;/h2>&lt;p>The sentinel system was created to solve the inconvenience of maintaining master-slave databases. Previously, there were two problems: inability to detect when the master database goes down, and the master-slave conversion issue after the master database fails.&lt;/p>
&lt;p>When a sentinel process starts, it reads the configuration file. The configuration file includes master-name, ip, and port. When the master node fails, the sentinel will automatically convert one of the slave nodes to become the master node. When the previous master node recovers, it automatically becomes a slave node.&lt;/p>
&lt;p>After the sentinel starts, it will periodically perform the following three operations:&lt;/p>
&lt;ul>
&lt;li>Send INFO to master and slave every 10 seconds&lt;/li>
&lt;li>Send its own information to the &lt;code>__sentinel__:hello&lt;/code> channel of master and slave every 2 seconds&lt;/li>
&lt;li>Send PING to master database, slave database, and other sentinels every 1 second&lt;/li>
&lt;/ul>
&lt;p>INFO information allows the sentinel to obtain relevant information about the current database. Sending INFO to the master can obtain its slave databases.
By sending its information to the channel, all sentinels can receive information about other sentinels monitoring the same database.
Sentinels establish connections with each other to send ping information.
When a sentinel sends a ping command and receives no reply, it will consider the target subjectively down. If a master database is found to be subjectively down, the sentinel will ask other sentinels if they also consider it subjectively down. When a certain number is reached, exceeding the quorum set in the sentinel startup parameters, the sentinel considers it objectively down.&lt;/p>
&lt;p>RAFT Election:&lt;/p>
&lt;ul>
&lt;li>Sentinel A, which discovered the master database to be objectively down, sends a command to other sentinels requesting to be elected as the leader sentinel.&lt;/li>
&lt;li>If the requested sentinel hasn&amp;rsquo;t voted for anyone else, it will agree.&lt;/li>
&lt;li>If A finds that the number of agreements exceeds half of the sentinel count and the quorum parameter, then A becomes the leader sentinel.&lt;/li>
&lt;li>Otherwise, A waits a random time and reinitiates the request for the next round of elections.&lt;/li>
&lt;/ul>
&lt;p>The leader sentinel will perform the following operations:&lt;/p>
&lt;ul>
&lt;li>Check the priority of slave databases; higher priority ones are chosen&lt;/li>
&lt;li>Check the replication command offset of slave databases; those with larger offsets are chosen&lt;/li>
&lt;li>Choose the slave database with the smaller ID&lt;/li>
&lt;/ul>
&lt;p>The selected database will receive a &amp;ldquo;slaveof no one&amp;rdquo; command from the leader sentinel to become the master database, and other slave databases will receive slaveof commands to change their master database. Finally, the internal record is updated to mark the stopped database as a slave of the master database, so when it resumes service, it will recover as a slave database.&lt;/p>
&lt;h2 id="deployment">Deployment
&lt;/h2>&lt;p>Sentinels are typically deployed on each node. If the Redis cluster is very large, the number of connections between all sentinels and a slave database might be too many, affecting Redis&amp;rsquo;s response to all sentinels. Therefore, the number of deployed sentinels should not be too high.&lt;/p>
&lt;h2 id="cluster">Cluster
&lt;/h2>&lt;p>For horizontal scaling, I became familiar with Codis during my internship at Tencent. Codis consists of Redis proxies and slave nodes. The main principle is to hash the keys and place them in specific Redis slaves. During script execution, scripts are allocated to specific slaves, which might lead to situations where other keys cannot be found.&lt;/p>
&lt;p>Besides Codis, there&amp;rsquo;s also Redis Cluster.&lt;/p>
&lt;p>When starting a Redis instance, change cluster-enables to yes in the configuration to enable cluster configuration.&lt;/p>
&lt;p>Scripts can be used to conveniently create clusters. New nodes can be added using the cluster meet command with IP and port. After adding a new node, the node will use the gossip protocol to broadcast the node&amp;rsquo;s information to all nodes.&lt;/p>
&lt;p>Slot allocation: After a new node joins the cluster, it has two options: either use the cluster replicate command to replicate each master database and run as a slave database, or request slot allocation from the cluster to run as a master database.&lt;/p>
&lt;p>Keys are hashed using crc16. If {*} exists, that part is used for hashing, and then assigned to one of the 16384 slots. The cluster has a slot mapping table at the beginning of creation. Eventually, the data will be stored in the corresponding Redis node.&lt;/p></description></item><item><title>2018-08-30 Go Learning Notes</title><link>https://nansenli.com/post/jianshu/go/2018-08-30-go%E5%AD%A6%E4%B9%A0%E6%8B%BE%E8%B4%9D/</link><pubDate>Sat, 01 Sep 2018 13:40:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/go/2018-08-30-go%E5%AD%A6%E4%B9%A0%E6%8B%BE%E8%B4%9D/</guid><description>&lt;ul>
&lt;li>In functions, the short assignment statement := can replace var declarations where the type is clear. However, constants must be declared using const&lt;/li>
&lt;li>Outside of functions, every statement must start with a keyword (var, func, etc.), therefore := syntax cannot be used outside of functions.&lt;/li>
&lt;li>Why Go uses trailing type declarations: &lt;a class="link" href="https://blog.go-zh.org/gos-declaration-syntax" target="_blank" rel="noopener"
>https://blog.go-zh.org/gos-declaration-syntax&lt;/a>&lt;/li>
&lt;li>Go has the following types:&lt;/li>
&lt;/ul>
&lt;pre tabindex="0">&lt;code>bool
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte // alias for uint8
rune // alias for int32
// represents a Unicode code point
float32 float64
complex64 complex128
&lt;/code>&lt;/pre>&lt;ul>
&lt;li>When you need an integer value, you should use the int type, unless you have a specific reason to use a fixed size or unsigned integer type.&lt;/li>
&lt;li>Go does not support implicit type conversion&lt;/li>
&lt;/ul></description></item><item><title>2018-08-29 Tencent Timi Phone Interview</title><link>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/2018-08-29-%E8%85%BE%E8%AE%AF%E5%A4%A9%E7%BE%8E%E7%94%B5%E8%AF%9D%E9%9D%A2%E8%AF%95/</link><pubDate>Wed, 29 Aug 2018 04:32:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/2018-08-29-%E8%85%BE%E8%AE%AF%E5%A4%A9%E7%BE%8E%E7%94%B5%E8%AF%9D%E9%9D%A2%E8%AF%95/</guid><description>&lt;ul>
&lt;li>Three-way handshake and four-way termination&lt;/li>
&lt;li>Nagle algorithm&lt;/li>
&lt;li>What states does a TCP connection go through when disconnecting&lt;/li>
&lt;li>How to choose between multiple processes and multiple threads, considering business needs&lt;/li>
&lt;li>How much knowledge of MySQL master-slave clusters&lt;/li>
&lt;li>GDB debugging process in a multi-process environment&lt;/li>
&lt;li>Introduction to network frameworks like libevent&lt;/li>
&lt;li>Differences between select and epoll. Why Java uses select for connection instead of epoll&lt;/li>
&lt;li>Inter-process communication methods&lt;/li>
&lt;li>Understanding of locks. Implementation of lock-free data structures&lt;/li>
&lt;li>How to design shared memory mutex&lt;/li>
&lt;/ul></description></item><item><title>Solving Incorrect Network Card IP Issues When Building Cross-Provider Kubernetes Clusters on Tencent Cloud</title><link>https://nansenli.com/post/jianshu/k8s/%E8%A7%A3%E5%86%B3%E8%85%BE%E8%AE%AF%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%B7%A8%E8%BF%90%E8%90%A5%E5%95%86%E6%90%AD%E5%BB%BAk8s%E9%9B%86%E7%BE%A4%E7%BD%91%E5%8D%A1ip%E4%B8%8D%E6%AD%A3%E7%A1%AE%E7%9A%84%E9%97%AE%E9%A2%98/</link><pubDate>Sun, 22 Jul 2018 13:40:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/k8s/%E8%A7%A3%E5%86%B3%E8%85%BE%E8%AE%AF%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%B7%A8%E8%BF%90%E8%90%A5%E5%95%86%E6%90%AD%E5%BB%BAk8s%E9%9B%86%E7%BE%A4%E7%BD%91%E5%8D%A1ip%E4%B8%8D%E6%AD%A3%E7%A1%AE%E7%9A%84%E9%97%AE%E9%A2%98/</guid><description>&lt;p>Kubernetes requires fully connected networks for all nodes, so each node should have an accessible IP address. Although each node has a public IP, Tencent Cloud machines don&amp;rsquo;t show this IP on the network card. This causes nodes to use their network card IP when joining the cluster, making it impossible for other nodes to find them using this IP.&lt;/p>
&lt;p>To make the IP visible on the network card, you need to enable Tencent Cloud&amp;rsquo;s EIP direct pass-through feature. Note that the official script has issues with Debian 10 and doesn&amp;rsquo;t properly auto-start. The following changes are required:&lt;/p>
&lt;p>Add &lt;code>/etc/rc.d/rc.local&lt;/code> to the final line of &lt;code>/etc/rc.local&lt;/code>&lt;/p>
&lt;p>Although Tencent Cloud&amp;rsquo;s Debian 10 will start &lt;code>/etc/rc.local&lt;/code> at boot, it won&amp;rsquo;t start &lt;code>/etc/rc.d/rc.local&lt;/code>, but the provided script writes to the latter.&lt;/p>
&lt;p>Reference documentation: &lt;a class="link" href="https://cloud.tencent.com/document/product/1199/41709" target="_blank" rel="noopener"
>https://cloud.tencent.com/document/product/1199/41709&lt;/a>&lt;/p></description></item><item><title>SVN Learning Notes</title><link>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/svn%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</link><pubDate>Sun, 01 Jul 2018 15:01:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/svn%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>Learning SVN for work purposes. SVN is a C/S type version management system that heavily depends on servers. Most operations are related to the SVN server.&lt;/p>
&lt;h2 id="most-common-operation-flow">Most Common Operation Flow
&lt;/h2>&lt;p>Here we assume the server project address is svn://192.168.1.1/shop&lt;/p>
&lt;ul>
&lt;li>Checkout the project&lt;/li>
&lt;li>Update: update the server project to local&lt;/li>
&lt;li>Commit: submit local changes&lt;/li>
&lt;/ul>
&lt;h2 id="how-to-deploy-an-svn-server">How to Deploy an SVN Server
&lt;/h2>&lt;p>Since SVN is a C/S type software, you need to deploy a server to use it. So we need to set up a server for testing.
To keep it as simple as possible, we&amp;rsquo;ll install VisualSVN software on Windows to act as our server. Note that servers usually run on Linux, but we&amp;rsquo;re using Windows server software here just for ease of getting started. You can easily download VisualSVN Server software from the official website. I downloaded version 3.9. Use the latest stable version. During installation, there&amp;rsquo;s a step to select the standard edition; for other steps, just use the defaults. Pay attention to the port selection to avoid conflicts with ports on your machine. The default port is 443, but you can change it to 8443.&lt;/p>
&lt;h2 id="creating-a-test-project-on-the-server">Creating a Test Project on the Server
&lt;/h2>&lt;p>Since an SVN project is a repository, which is a folder, we need to create a folder. Open the D:\Repositories folder and run the command &lt;code>svnadmin create test1&lt;/code> in that directory to create a project. For other users to access this directory, you need to use the &lt;code>svnserve&lt;/code> command to start the server, which we&amp;rsquo;ll discuss later. You also need to configure permissions. Open the svnserve.conf file in the conf directory under test1, uncomment anon-access, and change its value to write. This allows anonymous access with write permissions.&lt;/p>
&lt;p>Then run the command in the D:\Repositories directory:
&lt;code>svnserve -d -r ./test1&lt;/code>
This step opens the SVN server&amp;rsquo;s listening state.&lt;/p>
&lt;h2 id="common-svn-client-operations">Common SVN Client Operations
&lt;/h2>&lt;p>Download and install TortoiseSVN.&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Checkout
Right-click in the directory you want to synchronize, select repo-browser from TortoiseSVN, enter svn://127.0.0.1, and confirm. Right-click on the folder icon and select checkout, then confirm. The checkout is now successful.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Commit
Add a txt file and write something in it. Then right-click and select commit. This will commit to the server.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Update
After developer A submits data and the server changes, developer B needs to synchronize. Right-click and select update.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Ignore
To exclude files from being committed, right-click on the file and select &amp;ldquo;add to ignore list&amp;rdquo;&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="svn-versions">SVN Versions
&lt;/h2>&lt;p>SVN versions start from 1 and increment. SVN can roll back versions. SVN can perform branch and merge operations, but these differ significantly from Git&amp;rsquo;s branch operations. It seems like it creates a new project. The final merge only combines conflicts. This is different from Git&amp;rsquo;s branch operations, which are based on diffs.&lt;/p></description></item><item><title>Using Let's Encrypt and Nginx to Configure HTTPS Access</title><link>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/%E4%BD%BF%E7%94%A8lets-encrypt%E5%92%8Cnginx%E9%85%8D%E7%BD%AEhttps%E8%AE%BF%E9%97%AE/</link><pubDate>Mon, 04 Jun 2018 09:12:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/%E4%BD%BF%E7%94%A8lets-encrypt%E5%92%8Cnginx%E9%85%8D%E7%BD%AEhttps%E8%AE%BF%E9%97%AE/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>To encrypt HTTP data transmission, we need to use HTTPS certificates. Let&amp;rsquo;s Encrypt provides free certificates.&lt;/p>
&lt;h2 id="configure-nginx-forwarding">Configure Nginx Forwarding
&lt;/h2>&lt;p>We need to configure Nginx on our server so that Let&amp;rsquo;s Encrypt can verify our domain.
Below is the configuration file for Nginx on my server:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-nginx" data-lang="nginx">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">server&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">listen&lt;/span> &lt;span style="color:#ae81ff">80&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">server_name&lt;/span> &lt;span style="color:#e6db74">www.unrealblue.cc&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">location&lt;/span> &lt;span style="color:#e6db74">/&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">return&lt;/span> &lt;span style="color:#ae81ff">301&lt;/span> &lt;span style="color:#e6db74">https://&lt;/span>$host$request_uri;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">location&lt;/span> ~ &lt;span style="color:#e6db74">/.well-known/acme-challenge&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">root&lt;/span> &lt;span style="color:#e6db74">/var/www/html&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">allow&lt;/span> &lt;span style="color:#e6db74">all&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Explanation: The first location directive forwards all HTTP requests on port 80 to HTTPS connections. The second location directive means that if /.well-known is accessed, use the /var/www/html directory we provided.&lt;/p>
&lt;p>Save this file as default.conf.&lt;/p>
&lt;p>Here I used an Nginx Docker container for deployment, so I didn&amp;rsquo;t install Nginx directly.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-docker" data-lang="docker">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">FROM&lt;/span>&lt;span style="color:#e6db74"> nginx&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">LABEL&lt;/span> MAINTAINER linanwx@gmail.com&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">RUN&lt;/span> apt update&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">RUN&lt;/span> apt install -y certbot&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">RUN&lt;/span> mkdir -p /var/www/html&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">COPY&lt;/span> ./default.conf /etc/nginx/conf.d/&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">EXPOSE&lt;/span>&lt;span style="color:#e6db74"> 80:80&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">EXPOSE&lt;/span>&lt;span style="color:#e6db74"> 443:443&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">VOLUME&lt;/span> [&lt;span style="color:#e6db74">&amp;#34;/etc/letsencrypt/&amp;#34;&lt;/span>]&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Save the first code segment as default.conf and the second code segment as Dockerfile, then run &lt;code>docker build -t nginx-server .&lt;/code>&lt;/p>
&lt;p>Run &lt;code>docker run -it --rm -d -p80:80 -p443:443 --net=server-net --name nginx-server -v $PWD/letsencrypt:/etc/letsencrypt nginx-server&lt;/code> to start the container in the background.&lt;/p>
&lt;h2 id="generate-certificates">Generate Certificates
&lt;/h2>&lt;p>Run &lt;code>docker exec -it nginx-server /bin/bash&lt;/code> to enter the container.
Run the following commands to generate the certificate:&lt;/p>
&lt;pre tabindex="0">&lt;code>openssl dhparam -out /etc/letsencrypt/live/dhparams.pem 2048
certbot certonly --agree-tos -a webroot --webroot-path=/var/www/html -d www.unrealblue.cc -m linanwx@gmail.com
&lt;/code>&lt;/pre>&lt;p>The certbot program will access your domain to confirm your domain ownership, which uses the Nginx configuration from the previous step. There are also some parameters that you can understand by checking the help documentation. Additionally, unless you are certain you want to deploy to a production server, use the &amp;ndash;test-cert parameter during the testing phase, as there are limits on the number of certificates that can be generated.
Another point is generating dhparam. This is a parameter for the Diffie-Hellman key exchange protocol, which takes relatively long to generate. We will use this in Nginx later.&lt;/p>
&lt;p>If everything goes well, you will obtain the certificates stored in the /etc/letsencrypt/ directory. Also, because we used a volume when starting the container, the certificates will appear on the host machine.&lt;/p>
&lt;h2 id="configure-nginx-https">Configure Nginx HTTPS
&lt;/h2>&lt;p>Create a new file named https.conf:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-nginx" data-lang="nginx">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># https://www.jianshu.com/p/f7f39cb24423
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">server&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">listen&lt;/span> &lt;span style="color:#ae81ff">443&lt;/span> &lt;span style="color:#e6db74">ssl&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">server_name&lt;/span> &lt;span style="color:#e6db74">www.unrealblue.cc&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">ssl_certificate&lt;/span> &lt;span style="color:#e6db74">/etc/letsencrypt/live/www.unrealblue.cc/fullchain.pem&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">ssl_trusted_certificate&lt;/span> &lt;span style="color:#e6db74">/etc/letsencrypt/live/www.unrealblue.cc/fullchain.pem&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">ssl_certificate_key&lt;/span> &lt;span style="color:#e6db74">/etc/letsencrypt/live/www.unrealblue.cc/privkey.pem&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">ssl_dhparam&lt;/span> &lt;span style="color:#e6db74">/etc/letsencrypt/live/dhparams.pem&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">ssl_ciphers&lt;/span> &lt;span style="color:#e6db74">&amp;#39;ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS&amp;#39;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">ssl_prefer_server_ciphers&lt;/span> &lt;span style="color:#66d9ef">on&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">ssl_protocols&lt;/span> &lt;span style="color:#e6db74">TLSv1&lt;/span> &lt;span style="color:#e6db74">TLSv1.1&lt;/span> &lt;span style="color:#e6db74">TLSv1.2&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">ssl_session_cache&lt;/span> &lt;span style="color:#e6db74">shared:SSL:50m&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">ssl_session_timeout&lt;/span> &lt;span style="color:#e6db74">1d&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">ssl_stapling&lt;/span> &lt;span style="color:#66d9ef">on&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">ssl_stapling_verify&lt;/span> &lt;span style="color:#66d9ef">on&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">add_header&lt;/span> &lt;span style="color:#e6db74">Strict-Transport-Security&lt;/span> &lt;span style="color:#e6db74">max-age=60&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">location&lt;/span> &lt;span style="color:#e6db74">/&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">root&lt;/span> &lt;span style="color:#e6db74">/usr/share/nginx/html&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">index&lt;/span> &lt;span style="color:#e6db74">index.html&lt;/span> &lt;span style="color:#e6db74">index.htm&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e"># location = /gfwlist {
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#75715e"># proxy_pass http://server-handy:8081;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#75715e"># }
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">error_page&lt;/span> &lt;span style="color:#ae81ff">500&lt;/span> &lt;span style="color:#ae81ff">502&lt;/span> &lt;span style="color:#ae81ff">503&lt;/span> &lt;span style="color:#ae81ff">504&lt;/span> &lt;span style="color:#e6db74">/50x.html&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">location&lt;/span> = &lt;span style="color:#e6db74">/50x.html&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">root&lt;/span> &lt;span style="color:#e6db74">/usr/share/nginx/html&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Save this file. Then modify the Dockerfile as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-dockerfile" data-lang="dockerfile">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">FROM&lt;/span>&lt;span style="color:#e6db74"> nginx&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">LABEL&lt;/span> MAINTAINER linanwx@gmail.com&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">RUN&lt;/span> apt update&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">RUN&lt;/span> apt install -y certbot&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">RUN&lt;/span> mkdir -p /var/www/html&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">COPY&lt;/span> ./default.conf /etc/nginx/conf.d/&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">COPY&lt;/span> ./https.conf /etc/nginx/conf.d/&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">EXPOSE&lt;/span>&lt;span style="color:#e6db74"> 80:80&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">EXPOSE&lt;/span>&lt;span style="color:#e6db74"> 443:443&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">VOLUME&lt;/span> [&lt;span style="color:#e6db74">&amp;#34;/etc/letsencrypt/&amp;#34;&lt;/span>]&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Here, the https.conf file is also copied in.&lt;/p>
&lt;p>Restart the container, visit https://127.0.0.1, and you can see the access is successful.&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/27b7400a5c27.png"
loading="lazy"
alt="image.png"
>&lt;/p>
&lt;p>When testing in a local environment, you will be prompted that the certificate is not correct.
Click continue.&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/52f75e932b3a.png"
loading="lazy"
alt="image.png"
>&lt;/p></description></item><item><title>Learning the socat Command</title><link>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/socat%E5%91%BD%E4%BB%A4%E5%AD%A6%E4%B9%A0/</link><pubDate>Wed, 23 May 2018 09:04:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/socat%E5%91%BD%E4%BB%A4%E5%AD%A6%E4%B9%A0/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>socat is a powerful and easy-to-use tool that can implement conversions between any types of sockets.&lt;/p>
&lt;h2 id="usage">Usage
&lt;/h2>&lt;p>&lt;code>socat [options] &amp;lt;address&amp;gt; &amp;lt;address&amp;gt;&lt;/code>&lt;/p>
&lt;p>You can check the help documentation for options.&lt;/p>
&lt;p>Address can take several forms:&lt;/p>
&lt;ul>
&lt;li>&lt;code>-&lt;/code> &lt;code>STDIN&lt;/code> &lt;code>STDOUT&lt;/code>: Represents standard input/output, can be replaced with just a dash&lt;/li>
&lt;li>&lt;code>/var/log/syslog&lt;/code>: Can also be any path (use ./ for relative paths), opens a file as a data stream&lt;/li>
&lt;li>&lt;code>TCP:127.0.0.1:1080&lt;/code>: Establishes a TCP connection as a data stream, TCP can also be replaced with UDP&lt;/li>
&lt;li>&lt;code>TCP-LISTEN:12345&lt;/code>: Creates a TCP listening port, TCP can also be replaced with UDP&lt;/li>
&lt;li>&lt;code>EXEC:/bin/bash&lt;/code>: Executes a program as a data stream&lt;/li>
&lt;/ul>
&lt;h2 id="scenario-1">Scenario 1
&lt;/h2>&lt;p>I have a local file that I want to display in the terminal&lt;/p>
&lt;p>&lt;code>socat - /etc/sysctl.conf&lt;/code>&lt;/p>
&lt;h2 id="scenario-2">Scenario 2
&lt;/h2>&lt;p>A TCP connection will connect, and I want to see what data will be received
&lt;code>socat TCP-LISTEN:12345 -&lt;/code>&lt;/p>
&lt;h2 id="scenario-3">Scenario 3
&lt;/h2>&lt;p>I&amp;rsquo;m a hacker and I want to create a shell proxy on the target machine
&lt;code>socat TCP-LISTEN:12345 EXEC:/bin/bash&lt;/code>&lt;/p>
&lt;h2 id="scenario-4">Scenario 4
&lt;/h2>&lt;p>I have a UNIX DOMAIN socket locally, and I want to convert it to a TCP SOCKET for use by machines in the local network. How do I do it?&lt;/p>
&lt;p>&lt;code>socat TCP-LISTEN:12345,reuseaddr,fork UNIX-CONNECT:/data/deCOREIDPS/unix.domain&lt;/code>&lt;/p>
&lt;p>When multiple TCP connections come in, it forks one to connect to the domain socket&lt;/p>
&lt;h2 id="scenario-5">Scenario 5
&lt;/h2>&lt;p>Forward local port 80 to a remote destination&lt;/p>
&lt;p>&lt;code>socat TCP-LISTEN:80,fork TCP:www.baidu.com:80&lt;/code>&lt;/p>
&lt;blockquote>
&lt;p>Reference: &lt;a class="link" href="http://brieflyx.me/2015/linux-tools/socat-introduction/" target="_blank" rel="noopener"
>http://brieflyx.me/2015/linux-tools/socat-introduction/&lt;/a>&lt;/p>
&lt;/blockquote></description></item><item><title>Understanding Google Protocol Buffer</title><link>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/google-protocol-buffer-%E4%BA%86%E8%A7%A3/</link><pubDate>Fri, 18 May 2018 11:10:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/google-protocol-buffer-%E4%BA%86%E8%A7%A3/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>Learning about Google Protocol Buffer before bed&lt;/p>
&lt;h2 id="installing-the-protobuf-compiler">Installing the protobuf compiler
&lt;/h2>&lt;p>Start the latest Ubuntu Docker image, run apt install protobuf-compiler&lt;/p>
&lt;h2 id="writing-a-proto-file">Writing a .proto file
&lt;/h2>&lt;p>nano lm.helloworld.proto&lt;/p>
&lt;p>Then input:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-protobuf" data-lang="protobuf">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">package&lt;/span> lm; &lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">message&lt;/span> &lt;span style="color:#a6e22e">helloworld&lt;/span> &lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>{ &lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span> &lt;span style="color:#66d9ef">required&lt;/span> &lt;span style="color:#66d9ef">int32&lt;/span> id &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>; &lt;span style="color:#75715e">// ID
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">required&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span> str &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">2&lt;/span>; &lt;span style="color:#75715e">// str
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">optional&lt;/span> &lt;span style="color:#66d9ef">int32&lt;/span> opt &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">3&lt;/span>; &lt;span style="color:#75715e">//optional field
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>}&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In the example above, the package name is lm, and a message called helloworld is defined. This message has three members: an int32 type id, a string type member str. opt is an optional member, meaning the message may not contain this member.&lt;/p>
&lt;h2 id="compiling-the-proto-file">Compiling the proto file
&lt;/h2></description></item><item><title>Epoll Code Example - Analysis of Handy Library's Built-in epoll.cc</title><link>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/epoll%E4%BB%A3%E7%A0%81%E7%A4%BA%E4%BE%8Bhandy%E5%BA%93%E8%87%AA%E5%B8%A6epoll-cc%E5%88%86%E6%9E%90/</link><pubDate>Wed, 16 May 2018 07:43:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/epoll%E4%BB%A3%E7%A0%81%E7%A4%BA%E4%BE%8Bhandy%E5%BA%93%E8%87%AA%E5%B8%A6epoll-cc%E5%88%86%E6%9E%90/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>There are many C++ network libraries. Handy is a network library with C++11 style that is very helpful for in-depth learning of C++.&lt;/p>
&lt;h2 id="code-analysis">Code Analysis
&lt;/h2>&lt;p>Below is the epoll.cc file from handy/raw_examples. It&amp;rsquo;s an example of level triggering. This HTTP server returns a static resource &amp;ldquo;123456&amp;rdquo; regardless of what kind of request it receives. Compilation: c++ -o epoll epoll.cc, execution: sudo ./epoll. The if (con.writeEnabled) statement in sendRes of the source code seems to have some issues, causing problems when sending large resources. I have modified it to correctly send large files.&lt;/p>
&lt;pre tabindex="0">&lt;code>/*
* Compilation: c++ -o epoll epoll.cc
* Execution: ./epoll
* Testing: curl -v localhost
*/
/*
Running Effect
Run the epoll program with sudo. This program listens on port 80 at 0.0.0.0 on the local machine, running as an HTTP server
Whenever a connection accesses, it returns the static resource httpRes
LT is the default mode
*/
#include &amp;lt;sys/socket.h&amp;gt;
#include &amp;lt;sys/epoll.h&amp;gt;
#include &amp;lt;netinet/in.h&amp;gt;
#include &amp;lt;arpa/inet.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;errno.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;signal.h&amp;gt;
#include &amp;lt;iostream&amp;gt;
using namespace std;
bool output_log = true;
// A macro for printing errors and exiting
#define exit_if(r, ...) if(r) {printf(__VA_ARGS__); printf(&amp;#34;%s:%d error no: %d error msg %s\n&amp;#34;, __FILE__, __LINE__, errno, strerror(errno)); exit(1);}
// This function is used to set the specified fd to non-blocking status
void setNonBlock(int fd) {
// First, we get the original flags of the file descriptor
int flags = fcntl(fd, F_GETFL, 0);
exit_if(flags&amp;lt;0, &amp;#34;fcntl failed&amp;#34;);
// Then add O_NONBLOCK and set it back
int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
exit_if(r&amp;lt;0, &amp;#34;fcntl failed&amp;#34;);
}
// A wrapper for epoll_ctl, putting events and fd into ev.
// And setting events to trigger when readable and writable
void updateEvents(int efd, int fd, int events, int op) {
struct epoll_event ev = {0};
ev.events = events;
ev.data.fd = fd;
printf(&amp;#34;%s fd %d events read %d write %d\n&amp;#34;,
op==EPOLL_CTL_MOD?&amp;#34;mod&amp;#34;:&amp;#34;add&amp;#34;, fd, ev.events &amp;amp; EPOLLIN, ev.events &amp;amp; EPOLLOUT);
int r = epoll_ctl(efd, op, fd, &amp;amp;ev);
exit_if(r, &amp;#34;epoll_ctl failed&amp;#34;);
}
// Try to perform accept operation on fd. If successful, add it to the monitoring list of epoll fd. Set epoll events to trigger when data is written.
void handleAccept(int efd, int fd) {
struct sockaddr_in raddr;
socklen_t rsz = sizeof(raddr);
int cfd = accept(fd,(struct sockaddr *)&amp;amp;raddr,&amp;amp;rsz);
exit_if(cfd&amp;lt;0, &amp;#34;accept failed&amp;#34;);
sockaddr_in peer, local;
socklen_t alen = sizeof(peer);
int r = getpeername(cfd, (sockaddr*)&amp;amp;peer, &amp;amp;alen);
exit_if(r&amp;lt;0, &amp;#34;getpeername failed&amp;#34;);
printf(&amp;#34;accept a connection from %s\n&amp;#34;, inet_ntoa(raddr.sin_addr));
setNonBlock(cfd);
updateEvents(efd, cfd, EPOLLIN, EPOLL_CTL_ADD);
}
// Represents a connection. Members include data read from the connection, data written
// Is it okay to use string to store binary content, what happens if \0 is encountered?
// No problem, see https://www.zhihu.com/question/33104941
struct Con {
string readed;
size_t written;
bool writeEnabled;
Con(): written(0), writeEnabled(false) {}
};
// Data structure used to map fd to con
map&amp;lt;int, Con&amp;gt; cons;
string httpRes;
// Send resources
void sendRes(int efd, int fd) {
// First get the connection information
Con&amp;amp; con = cons[fd];
// Request to write when no data is received
// This means that data sent last time might have been sent completely
// Its corresponding file descriptor has been deleted in cons
// Then the epoll signal was triggered
// At this time, close its last send flag
// Then close the buffer send trigger epoll flag
// Only keep it triggered when there is data to read
// Why not do this step when all data is written?
// if (!con.readed.length()) {
// if (con.writeEnabled) {
// updateEvents(efd, fd, EPOLLIN, EPOLL_CTL_MOD);
// con.writeEnabled = false;
// }
// return;
// }
// Calculate the length of data that still needs to be written
size_t left = httpRes.length() - con.written;
int wd = 0;
// Continuously write data until the kernel buffer can&amp;#39;t accept any more
while((wd=::write(fd, httpRes.data()+con.written, left))&amp;gt;0) {
con.written += wd;
left -= wd;
if(output_log) printf(&amp;#34;write %d bytes left: %lu\n&amp;#34;, wd, left);
};
// If there is no data to write, delete this connection. But don&amp;#39;t disconnect, just empty the connection information
if (left == 0) {
// close(fd); // Keepalive is used in testing, so don&amp;#39;t close the connection. The connection will be closed in the read event
if (con.writeEnabled) {
updateEvents(efd, fd, EPOLLIN, EPOLL_CTL_MOD);
con.writeEnabled = false;
}
cons.erase(fd);
return;
}
// If the kernel buffer is full, can&amp;#39;t write anymore
if (wd &amp;lt; 0 &amp;amp;&amp;amp; (errno == EAGAIN || errno == EWOULDBLOCK)) {
// Mark it as can continue writing
if (!con.writeEnabled) {
// Wait for it to be able to continue writing, or be readable
// Avoid repeated system calls, use con.writeEnabled flag
printf(&amp;#34;update it to EPOLLIN|EPOLLOUT\n&amp;#34;);
updateEvents(efd, fd, EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD);
con.writeEnabled = true;
}
return;
}
// If it&amp;#39;s other situations, such as returning 0 without completing data writing, or returning other errors
// It means an error occurred
if (wd&amp;lt;=0) {
printf(&amp;#34;write error for %d: %d %s\n&amp;#34;, fd, errno, strerror(errno));
close(fd);
cons.erase(fd);
}
}
// When loop_once processes read data, call this function
void handleRead(int efd, int fd) {
char buf[4096];
int n = 0;
// Read 4k bytes each time, loop to read out all the data already in the kernel (information may be incomplete due to packet splitting)
while ((n=::read(fd, buf, sizeof buf)) &amp;gt; 0) {
if(output_log) printf(&amp;#34;read %d bytes\n&amp;#34;, n);
// Here, use a map to get the connection information corresponding to the previous fd.
// When the index corresponding to fd does not exist, it will call the default constructor of con: Con(): written(0), writeEnabled(false) {}
string&amp;amp; readed = cons[fd].readed;
// Call the append method of the string class to add data to the connection information
// Note that parameter n needs to be passed to ensure binary safety
readed.append(buf, n);
std::cout &amp;lt;&amp;lt; &amp;#34;now info is&amp;#34; &amp;lt;&amp;lt; std::endl &amp;lt;&amp;lt; &amp;#34;---&amp;#34; &amp;lt;&amp;lt; readed &amp;lt;&amp;lt; endl &amp;lt;&amp;lt; &amp;#34;---&amp;#34; &amp;lt;&amp;lt; std::endl;
// Determine when an HTTP request is complete.
// Don&amp;#39;t judge the content of the HTTP request, just send static resources
if (readed.length()&amp;gt;4) {
if (readed.substr(readed.length()-2, 2) == &amp;#34;\n\n&amp;#34; || readed.substr(readed.length()-4, 4) == &amp;#34;\r\n\r\n&amp;#34;) {
// When a complete HTTP request is read, test sending a response
// After the TCP connection is established, the client starts transmitting the header, then uses \r\n\r\n to mark the end of the header and the beginning of the entity (of course, there will be the beginning of the entity only if the request contains an entity),
// Then the entity is transmitted, when the entity is transmitted, the client starts receiving data, the server knows, this request has ended,
// Then the entity is that segment of data from \r\n\r\n to stopping reception. Correspondingly, the client receives the response in the same way.
// If there is no entity, then \r\n\r\n is the end of http
// Start writing data. Note that it may fill the buffer, if it&amp;#39;s full, continue writing later
sendRes(efd, fd);
}
}
}
// If read cannot read, it will return -1. At this time, errno (errno belongs to the thread, it is thread-safe) is EAGAIN, which means it&amp;#39;s not all read. EWOULDBLOCK and EAGAIN are the same.
// Then return, and wait for the next read
if (n&amp;lt;0 &amp;amp;&amp;amp; (errno == EAGAIN || errno == EWOULDBLOCK)){
printf(&amp;#34;nothing to read from %d, return. \n&amp;#34;, fd);
return;
}
// In actual applications, n&amp;lt;0 should check various errors, such as EINTR
if (n &amp;lt; 0) {
printf(&amp;#34;read %d error: %d %s\n&amp;#34;, fd, errno, strerror(errno));
}
// Executing here, n is 0, indicating that the peer has closed the connection. At this time, we also close the connection
printf(&amp;#34;%d close the connection\n&amp;#34;, fd);
close(fd);
cons.erase(fd);
}
// When the buffer can be written in loop_once, simply write our prepared static resources
void handleWrite(int efd, int fd) {
sendRes(efd, fd);
}
// Perform one operation in a loop on an epoll handle
// Where l is the LISTEN fd
void loop_once(int efd, int lfd, int waitms) {
// At most copy 20 events out from the kernel
const int kMaxEvents = 20;
struct epoll_event activeEvs[100];
int n = epoll_wait(efd, activeEvs, kMaxEvents, waitms);
// n is how many events were returned
if(output_log) printf(&amp;#34;epoll_wait return %d\n&amp;#34;, n);
for (int i = 0; i &amp;lt; n; i ++) {
int fd = activeEvs[i].data.fd;
int events = activeEvs[i].events;
// EPOLLIN event or EPOLLERR event. EPOLLERR also means the pipe write ended.
// See: http://man7.org/linux/man-pages/man2/epoll_ctl.2.html
if (events &amp;amp; (EPOLLIN | EPOLLERR)) {
// The EPOLLIN event is only triggered when the peer has data written, so after triggering once, you need to keep reading all the data until you finish reading EAGAIN. Otherwise, the remaining data will only be taken out together the next time the peer writes.
// When the other party closes the connection, it is an EPOLLERR event
if (fd == lfd) {
printf(&amp;#34;this is accept\n&amp;#34;);
handleAccept(efd, fd);
} else {
printf(&amp;#34;this can read\n&amp;#34;);
handleRead(efd, fd);
}
} else if (events &amp;amp; EPOLLOUT) {
// This handles events if the file descriptor can be written
// The EPOLLOUT event is only triggered once during connection, indicating it can be written
// Afterwards, it indicates that the data in the buffer has been sent out and can continue to be written
// See https://www.zhihu.com/question/22840801
if(output_log) printf(&amp;#34;handling epollout\n&amp;#34;);
handleWrite(efd, fd);
} else {
exit_if(1, &amp;#34;unknown event&amp;#34;);
}
}
}
int main(int argc, const char* argv[]) {
if (argc &amp;gt; 1) { output_log = false; }
/*
Small Knowledge
signal (parameter 1, parameter 2);
Parameter 1: The signal we want to process. We can view the system signals (64 in total) by typing kill -l in the terminal. In fact, these signals are macros defined by the system.
Parameter 2: The way we handle it (system default, ignore, or capture). SIG_IGN: If the func parameter is set to SIG_IGN, the signal will be ignored.
*/
::signal(SIGPIPE, SIG_IGN);
// Set the content of the HTTP response
httpRes = &amp;#34;HTTP/1.1 200 OK\r\nConnection: Keep-Alive\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Length: 19048576\r\n\r\n123456&amp;#34;;
// Fill the rest of the content with 0. The final length of content is about 1024*1024
for(int i=0;i&amp;lt;19048570;i++) {
httpRes+=&amp;#39;\0&amp;#39;;
}
// Set the port to 80
short port = 80;
// Create an epoll handle
int epollfd = epoll_create(1);
exit_if(epollfd &amp;lt; 0, &amp;#34;epoll_create failed&amp;#34;);
// Create a socket
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
exit_if(listenfd &amp;lt; 0, &amp;#34;socket failed&amp;#34;);
struct sockaddr_in addr;
memset(&amp;amp;addr, 0, sizeof addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
// First bind the socket to the port
int r = ::bind(listenfd,(struct sockaddr *)&amp;amp;addr, sizeof(struct sockaddr));
// This step will report an error if you don&amp;#39;t have superuser permissions. Linux doesn&amp;#39;t allow non-root users to use ports below 1024
exit_if(r, &amp;#34;bind to 0.0.0.0:%d failed %d %s&amp;#34;, port, errno, strerror(errno));
/*
#include&amp;lt;sys/socket.h&amp;gt;
int listen(int sockfd, int backlog)
Return: 0──success, -1──failure
Parameter sockfd
The socket that the listen function acts on, sockfd was previously returned by the socket function. At the time when the socket function returns the socket fd, it is an active connection socket,
which means the system assumes the user will call the connect function on this socket, expecting it to actively connect with other processes, then in server programming, the user wants this socket to accept external connection requests,
that is, passively wait for users to connect. Since the system assumes by default that a socket is actively connected, it needs to be told in some way, and the user process completes this by making the system call listen.
Parameter backlog
This parameter involves some network details. While a process is handling one connection request, there may be other connection requests.
Because TCP connection is a process, there may be a half-connected state, and sometimes due to too many users trying to connect simultaneously, the server process cannot quickly complete the connection request.
If this situation occurs, how does the server process want the kernel to handle it?
The kernel will maintain a queue in its own process space to track these completed connections that the server process has not yet handled or is processing. Such a queue cannot be arbitrarily large in the kernel,
so there must be an upper limit to its size. This backlog tells the kernel to use this value as the upper limit.
Without a doubt, the server process cannot arbitrarily specify a value, the kernel has a permissible range. This range is implementation-related. It&amp;#39;s hard to have some standardization, usually this value will be less than 30.
The length of the queue used by the kernel to track these completed connections but not yet accepted by user code is set to 20 here. When the queue length is less than 20, the kernel will immediately complete the connection establishment.
But if the queue length is greater than 20, the connection will not be established before the user code calls accept, and the other party will be in a blocked state.
*/
r = listen(listenfd, 20);
exit_if(r, &amp;#34;listen failed %d %s&amp;#34;, errno, strerror(errno));
printf(&amp;#34;fd %d listening at %d\n&amp;#34;, listenfd, port);
// Next, set the file descriptor to non-blocking.
// Why set it to non-blocking? https://www.zhihu.com/question/23614342
setNonBlock(listenfd);
// Set it to trigger when readable, add to the epoll file descriptor pool
updateEvents(epollfd, listenfd, EPOLLIN, EPOLL_CTL_ADD);
for (;;) { // Actual applications should register signal handling functions and clean up resources when exiting
loop_once(epollfd, listenfd, 10000);
}
return 0;
}
&lt;/code>&lt;/pre>&lt;h2 id="running-effect">Running Effect
&lt;/h2>&lt;p>&lt;img src="https://nansenli.com/img/rescued/7d2d193858a6.png"
loading="lazy"
alt="image.png"
>&lt;/p>
&lt;pre tabindex="0">&lt;code>sudo ./epoll
fd 4 listening at 80
add fd 4 events read 1 write 0
epoll_wait return 1
this is accept
accept a connection from 127.0.0.1
add fd 5 events read 1 write 0
epoll_wait return 1
this can read
read 412 bytes
now info is
---GET / HTTP/1.1
Host: 127.0.0.1
Connection: keep-alive
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
Upgrade-Insecure-Requests: 1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,und;q=0.8,zh-TW;q=0.7,en;q=0.6,pl;q=0.5
---
write 4081834 bytes left: 14966851
update it to EPOLLIN|EPOLLOUT
mod fd 5 events read 1 write 4
nothing to read from 5, return.
epoll_wait return 1
handling epollout
write 2226422 bytes left: 12740429
epoll_wait return 1
handling epollout
write 2095456 bytes left: 10644973
epoll_wait return 1
handling epollout
write 1964490 bytes left: 8680483
epoll_wait return 1
handling epollout
write 1506109 bytes left: 7174374
epoll_wait return 1
handling epollout
write 1833524 bytes left: 5340850
epoll_wait return 1
handling epollout
write 1637075 bytes left: 3703775
write 130966 bytes left: 3572809
epoll_wait return 1
handling epollout
write 1571592 bytes left: 2001217
epoll_wait return 1
handling epollout
write 1440626 bytes left: 560591
epoll_wait return 1
handling epollout
write 560591 bytes left: 0
mod fd 5 events read 1 write 0
epoll_wait return 1
this can read
read 375 bytes
now info is
---GET /favicon.ico HTTP/1.1
Host: 127.0.0.1
Connection: keep-alive
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
Accept: image/webp,image/apng,image/*,*/*;q=0.8
Referer: http://127.0.0.1/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,und;q=0.8,zh-TW;q=0.7,en;q=0.6,pl;q=0.5
---
write 10477280 bytes left: 8571405
update it to EPOLLIN|EPOLLOUT
mod fd 5 events read 1 write 4
nothing to read from 5, return.
epoll_wait return 1
handling epollout
write 1440626 bytes left: 7130779
epoll_wait return 1
handling epollout
write 1768041 bytes left: 5362738
epoll_wait return 1
handling epollout
write 1571592 bytes left: 3791146
epoll_wait return 1
handling epollout
write 1637075 bytes left: 2154071
epoll_wait return 1
handling epollout
write 1702558 bytes left: 451513
epoll_wait return 1
handling epollout
write 451513 bytes left: 0
mod fd 5 events read 1 write 0
epoll_wait return 0
epoll_wait return 0
epoll_wait return 0
&lt;/code>&lt;/pre>&lt;p>Here I increased the size of the resource, changing it to the following value:&lt;/p>
&lt;pre tabindex="0">&lt;code> httpRes = &amp;#34;HTTP/1.1 200 OK\r\nConnection: Keep-Alive\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Length: 19048576\r\n\r\n123456&amp;#34;;
// Fill the rest of the content with 0. The final length of content is about 1024*1024
for(int i=0;i&amp;lt;19048570;i++) {
httpRes+=&amp;#39;\0&amp;#39;;
}
&lt;/code>&lt;/pre>&lt;p>You can see it was transmitted in multiple parts. Finally, the terminal page displays 123456, with \0 after it, which won&amp;rsquo;t be displayed.
You can see that the browser made two requests, one for the root directory and one for the page icon favicon.ico
&amp;lt;/rewritten_file&amp;gt;&lt;/p></description></item><item><title>2018-05-11 Essay</title><link>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/2018-05-11-%E9%9A%8F%E7%AC%94/</link><pubDate>Fri, 11 May 2018 09:50:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/2018-05-11-%E9%9A%8F%E7%AC%94/</guid><description>&lt;p>Started preparing to write the server side of the SS assistant. There are many issues.&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Language
If I want to pursue C++ backend development in the future, it&amp;rsquo;s probably best to stick with C++. But I also want to try other solutions like Python. I tried webpy in Python and found it very lightweight - I probably won&amp;rsquo;t use the Flask framework I used before. Another option is Go. Go&amp;rsquo;s syntax is also an issue as I haven&amp;rsquo;t learned it yet.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Framework
I&amp;rsquo;ve currently found C++&amp;rsquo;s handy library. I&amp;rsquo;m not particularly keen on using the muduo library. I still don&amp;rsquo;t want to use boost, even though Effective C++ recommends learning it. My first impression of handy is quite good. Still learning about it.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>The goal is to first write a static server using handy, then expand from there.&lt;/p></description></item><item><title>C++ Perfect Singleton Pattern</title><link>https://nansenli.com/post/jianshu/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/c++%E5%AE%8C%E7%BE%8E%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/</link><pubDate>Mon, 07 May 2018 06:15:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/c++%E5%AE%8C%E7%BE%8E%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/</guid><description>&lt;h2 id="original-singleton-pattern">Original Singleton Pattern
&lt;/h2>&lt;p>The singleton pattern needs to do the following:&lt;/p>
&lt;ul>
&lt;li>Cannot be constructed through a constructor, otherwise multiple instances could be created. The constructor needs to be declared private.&lt;/li>
&lt;li>Ensure only one instance can be produced.&lt;/li>
&lt;/ul>
&lt;p>Here is a simple implementation:&lt;/p>
&lt;pre tabindex="0">&lt;code>class Singleton
{
private:
static Singleton *local_instance;
Singleton(){};
public:
static Singleton *getInstance()
{
if (local_instance == nullptr)
{
local_instance = new Singleton();
}
return local_instance;
}
};
Singleton * Singleton::local_instance = nullptr;
int main()
{
Singleton * s = Singleton::getInstance();
return 0;
}
&lt;/code>&lt;/pre>&lt;h2 id="using-local-static-objects-to-solve-two-existing-problems">Using Local Static Objects to Solve Two Existing Problems
&lt;/h2>&lt;p>There are two problems in the code above. One is that in a multi-threaded situation, the new operation might be executed twice. The other is that the destructor is not called when the program exits.
The following solution uses a static object to solve these problems.&lt;/p>
&lt;pre tabindex="0">&lt;code>class Singleton
{
private:
static Singleton *local_instance;
Singleton(){
cout &amp;lt;&amp;lt; &amp;#34;Constructor&amp;#34; &amp;lt;&amp;lt; endl;
};
~Singleton(){
cout &amp;lt;&amp;lt; &amp;#34;Destructor&amp;#34; &amp;lt;&amp;lt; endl;
}
public:
static Singleton *getInstance()
{
static Singleton locla_s;
return &amp;amp;locla_s;
}
};
int main()
{
cout &amp;lt;&amp;lt; &amp;#34;Before first access to singleton&amp;#34; &amp;lt;&amp;lt; endl;
Singleton * s = Singleton::getInstance();
cout &amp;lt;&amp;lt; &amp;#34;After first access to singleton&amp;#34; &amp;lt;&amp;lt; endl;
cout &amp;lt;&amp;lt; &amp;#34;Before second access to singleton&amp;#34; &amp;lt;&amp;lt; endl;
Singleton * s2 = Singleton::getInstance();
cout &amp;lt;&amp;lt; &amp;#34;After second access to singleton&amp;#34; &amp;lt;&amp;lt; endl;
return 0;
}
&lt;/code>&lt;/pre>&lt;p>&lt;img src="https://nansenli.com/img/rescued/b616396efe3d.png"
loading="lazy"
alt="Output results"
>&lt;/p>
&lt;p>This code might cause multiple constructor calls in versions before C++11, so it can only be used with newer compilers.&lt;/p>
&lt;h2 id="if-using-pre-c11-versions-static-objects-will-not-be-thread-safe">If Using Pre-C++11 Versions, Static Objects Will Not Be Thread-Safe
&lt;/h2>&lt;p>The version below uses mutex and a static member to destruct the singleton. The disadvantage of this approach is that the lock causes slow speed and low efficiency. But at least it&amp;rsquo;s correct and can be used in versions before C++11. Sample code is as follows:&lt;/p>
&lt;pre tabindex="0">&lt;code>class Singleton
{
private:
static Singleton *local_instance;
static pthread_mutex_t mutex;
Singleton(){
cout &amp;lt;&amp;lt; &amp;#34;Constructor&amp;#34; &amp;lt;&amp;lt; endl;
};
~Singleton(){
cout &amp;lt;&amp;lt; &amp;#34;Destructor&amp;#34; &amp;lt;&amp;lt; endl;
}
class rememberFree{
public:
rememberFree(){
cout &amp;lt;&amp;lt; &amp;#34;Member constructor&amp;#34; &amp;lt;&amp;lt; endl;
}
~rememberFree(){
if(Singleton::local_instance != nullptr){
delete Singleton::local_instance;
}
}
};
static rememberFree remember;
public:
static Singleton *getInstance()
{
pthread_mutex_lock(&amp;amp;mutex);
if (local_instance == nullptr)
{
local_instance = new Singleton();
}
pthread_mutex_unlock(&amp;amp;mutex);
return local_instance;
}
};
Singleton * Singleton::local_instance = nullptr;
pthread_mutex_t Singleton::mutex = PTHREAD_MUTEX_INITIALIZER;
Singleton::rememberFree Singleton::remember;
&lt;/code>&lt;/pre>&lt;h2 id="double-checked-locking-causes-uninitialized-memory-access">Double-Checked Locking Causes Uninitialized Memory Access
&lt;/h2>&lt;p>Using the following code to implement direct return of an already initialized object will greatly improve the performance of the above code. But the same code has obvious problems in Java, where CPU out-of-order execution may lead to accessing a reference to an uninitialized object.
Does C++ have the same problem? See the following article: &lt;a class="link" href="http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf" target="_blank" rel="noopener"
>http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf&lt;/a>
The conclusion is the same - C++ has the same issue and may lead to undefined behavior causing segmentation faults. An example of double-checked locking code is as follows:&lt;/p>
&lt;pre tabindex="0">&lt;code>static Singleton *getInstance()
{
if(local_instance == nullptr){
pthread_mutex_lock(&amp;amp;mutex);
if (local_instance == nullptr)
{
local_instance = new Singleton();
}
pthread_mutex_unlock(&amp;amp;mutex);
}
return local_instance;
}
&lt;/code>&lt;/pre>&lt;p>If thread A enters the lock and allocates space for the object, but due to possible instruction reordering, local_instance is actually pointed to a block of unallocated memory first, and then initialization occurs on this memory block. But after pointing and before initialization, another thread B might get this pointer through getInstance.&lt;/p>
&lt;h2 id="attempting-to-use-local-variables-cannot-guarantee-instruction-execution-order">Attempting to Use Local Variables Cannot Guarantee Instruction Execution Order
&lt;/h2>&lt;p>When trying to use temporary variables to force the order of instruction execution, they may still be considered useless variables by the compiler and optimized away. The following code is a good idea but fails to achieve its purpose:&lt;/p>
&lt;pre tabindex="0">&lt;code> if(local_instance == nullptr){
static mutex mtx;
lock_guard&amp;lt;mutex&amp;gt; lock(mtx);
if (local_instance == nullptr)
{
auto tmp = new Singleton()
local_instance = tmp;
}
}
return local_instance;
&lt;/code>&lt;/pre>&lt;h2 id="inelegant-use-of-volatile-to-solve-instruction-reordering-problems-in-double-checked-locking">Inelegant Use of volatile to Solve Instruction Reordering Problems in Double-Checked Locking
&lt;/h2>&lt;p>Trying to use volatile to declare the internal pointer, the code is as follows:&lt;/p>
&lt;pre tabindex="0">&lt;code>class Singleton
{
private:
static Singleton * volatile local_instance;
Singleton(){
cout &amp;lt;&amp;lt; &amp;#34;Constructor&amp;#34; &amp;lt;&amp;lt; endl;
};
~Singleton(){
cout &amp;lt;&amp;lt; &amp;#34;Destructor&amp;#34; &amp;lt;&amp;lt; endl;
}
class rememberFree{
public:
rememberFree(){
cout &amp;lt;&amp;lt; &amp;#34;Member constructor&amp;#34; &amp;lt;&amp;lt; endl;
}
~rememberFree(){
if(Singleton::local_instance != nullptr){
delete Singleton::local_instance;
}
}
};
static rememberFree remember;
public:
static Singleton *getInstance()
{
if(local_instance == nullptr){
static mutex mtx;
lock_guard&amp;lt;mutex&amp;gt; lock(mtx);
if (local_instance == nullptr)
{
auto tmp = new Singleton();
local_instance = tmp;
}
}
return local_instance;
}
};
Singleton * volatile Singleton::local_instance = nullptr;
Singleton::rememberFree Singleton::remember;
int main()
{
cout &amp;lt;&amp;lt; &amp;#34;Before first access to singleton&amp;#34; &amp;lt;&amp;lt; endl;
Singleton * s = Singleton::getInstance();
cout &amp;lt;&amp;lt; &amp;#34;After first access to singleton&amp;#34; &amp;lt;&amp;lt; endl;
cout &amp;lt;&amp;lt; &amp;#34;Before second access to singleton&amp;#34; &amp;lt;&amp;lt; endl;
Singleton * s2 = Singleton::getInstance();
cout &amp;lt;&amp;lt; &amp;#34;After second access to singleton&amp;#34; &amp;lt;&amp;lt; endl;
return 0;
}
&lt;/code>&lt;/pre>&lt;p>In this code, although temp is volatile, *temp is not, nor are its members. So it may still be optimized. Try to declare *temp as volatile as well, and you&amp;rsquo;ll find that your code is full of volatile. But at least it&amp;rsquo;s correct:&lt;/p>
&lt;pre tabindex="0">&lt;code>class Singleton
{
private:
static volatile Singleton * volatile local_instance;
Singleton(){
cout &amp;lt;&amp;lt; &amp;#34;Constructor&amp;#34; &amp;lt;&amp;lt; endl;
};
~Singleton(){
cout &amp;lt;&amp;lt; &amp;#34;Destructor&amp;#34; &amp;lt;&amp;lt; endl;
}
class rememberFree{
public:
rememberFree(){
cout &amp;lt;&amp;lt; &amp;#34;Member constructor&amp;#34; &amp;lt;&amp;lt; endl;
}
~rememberFree(){
if(Singleton::local_instance != nullptr){
delete Singleton::local_instance;
}
}
};
static rememberFree remember;
public:
static volatile Singleton *getInstance()
{
if(local_instance == nullptr){
static mutex mtx;
lock_guard&amp;lt;mutex&amp;gt; lock(mtx);
if (local_instance == nullptr)
{
auto tmp = new Singleton();
local_instance = tmp;
}
}
return local_instance;
}
};
volatile Singleton * volatile Singleton::local_instance = nullptr;
Singleton::rememberFree Singleton::remember;
int main()
{
cout &amp;lt;&amp;lt; &amp;#34;Before first access to singleton&amp;#34; &amp;lt;&amp;lt; endl;
volatile Singleton * s = Singleton::getInstance();
cout &amp;lt;&amp;lt; &amp;#34;After first access to singleton&amp;#34; &amp;lt;&amp;lt; endl;
cout &amp;lt;&amp;lt; &amp;#34;Before second access to singleton&amp;#34; &amp;lt;&amp;lt; endl;
volatile Singleton * s2 = Singleton::getInstance();
cout &amp;lt;&amp;lt; &amp;#34;After second access to singleton&amp;#34; &amp;lt;&amp;lt; endl;
return 0;
}
&lt;/code>&lt;/pre>&lt;h2 id="the-ultimate-weapon--memory-barrier">The Ultimate Weapon — Memory Barrier
&lt;/h2>&lt;p>In the new standard, the atomic class implements memory barriers, making memory access controllable across multiple cores. This utilizes the controllable memory access order in C++11. Here is the code implementation:&lt;/p>
&lt;pre tabindex="0">&lt;code>class Singleton
{
private:
// static volatile Singleton * volatile local_instance;
static atomic&amp;lt;Singleton*&amp;gt; instance;
Singleton(){
cout &amp;lt;&amp;lt; &amp;#34;Constructor&amp;#34; &amp;lt;&amp;lt; endl;
};
~Singleton(){
cout &amp;lt;&amp;lt; &amp;#34;Destructor&amp;#34; &amp;lt;&amp;lt; endl;
}
class rememberFree{
public:
rememberFree(){
cout &amp;lt;&amp;lt; &amp;#34;Member constructor&amp;#34; &amp;lt;&amp;lt; endl;
}
~rememberFree(){
Singleton* local_instance = instance.load(std::memory_order_relaxed);
if(local_instance != nullptr){
delete local_instance;
}
}
};
static rememberFree remember;
public:
static Singleton *getInstance()
{
Singleton* tmp = instance.load(std::memory_order_relaxed);
atomic_thread_fence(memory_order_acquire);
if(tmp == nullptr){
static mutex mtx;
lock_guard&amp;lt;mutex&amp;gt; lock(mtx);
tmp = instance.load(memory_order_relaxed);
if (tmp == nullptr)
{
tmp = new Singleton();
atomic_thread_fence(memory_order_release);
instance.store(tmp, memory_order_relaxed);
}
}
return tmp;
}
};
atomic&amp;lt;Singleton*&amp;gt; Singleton::instance;
Singleton::rememberFree Singleton::remember;
int main()
{
cout &amp;lt;&amp;lt; &amp;#34;Before first access to singleton&amp;#34; &amp;lt;&amp;lt; endl;
Singleton * s = Singleton::getInstance();
cout &amp;lt;&amp;lt; &amp;#34;After first access to singleton&amp;#34; &amp;lt;&amp;lt; endl;
cout &amp;lt;&amp;lt; &amp;#34;Before second access to singleton&amp;#34; &amp;lt;&amp;lt; endl;
Singleton * s2 = Singleton::getInstance();
cout &amp;lt;&amp;lt; &amp;#34;After second access to singleton&amp;#34; &amp;lt;&amp;lt; endl;
return 0;
}
&lt;/code>&lt;/pre>&lt;p>The above code may be difficult to read. The two loads of instance can be executed out of order. But changes during this period cannot be observed by other CPU cores. In the Muduo book, memory barriers are also rated as the ultimate weapon.&lt;/p>
&lt;h2 id="using-atomic-operation-memory-order">Using Atomic Operation Memory Order
&lt;/h2>&lt;p>There are six memory sequence options that can be applied to operations on atomic types: memory_order_relaxed, memory_order_consume, memory_order_acquire, memory_order_release, memory_order_acq_rel, and memory_order_seq_cst. Unless you specify a sequence option for a specific operation, the memory sequence option for all atomic types defaults to memory_order_seq_cst. Although there are six options, they only represent three memory models: sequentially consistent, acquire-release sequence (memory_order_consume, memory_order_acquire, memory_order_release and memory_order_acq_rel), and relaxed sequence (memory_order_relaxed).&lt;/p>
&lt;p>The models that can be adopted here are: the default memory_order_seq_cst, which is sequentially consistent, and memory_order_acquire, memory_order_release, which is the acquire-release sequence. The latter may perform better.&lt;/p>
&lt;p>To be improved&lt;/p>
&lt;h2 id="using-pthread_once-or-call_once">Using pthread_once or call_once
&lt;/h2>&lt;p>The former comes from the pthread library. The latter comes from std::atomic.&lt;/p>
&lt;p>To be improved&lt;/p></description></item><item><title>Learning GDB from Scratch</title><link>https://nansenli.com/post/jianshu/c++/gdb-%E4%BB%8E%E9%9B%B6%E5%AD%A6%E4%B9%A0/</link><pubDate>Wed, 25 Apr 2018 16:45:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/c++/gdb-%E4%BB%8E%E9%9B%B6%E5%AD%A6%E4%B9%A0/</guid><description>&lt;p>Let&amp;rsquo;s start with an example.&lt;/p>
&lt;pre tabindex="0">&lt;code>#include &amp;lt;stdio.h&amp;gt;
void my_itoa(int n,char s[])
{
int i,j,sign;
if((sign=n)&amp;lt;0) // record sign
n=-n; // make n positive
i=0;
do{
s[i++]=n%10+&amp;#39;0&amp;#39;; // get the next digit
}while((n/=10)&amp;gt;0); // loop through division
if(sign&amp;lt;0)
s[i++]=&amp;#39;-&amp;#39;;
s[i]=&amp;#39;\0&amp;#39;;
for(j=i-1;j&amp;gt;=0;j--) // the generated numbers are in reverse order, so output them in reverse
printf(&amp;#34;%c&amp;#34;,s[j]);
}
void main()
{
int n;
char str[100];
my_itoa(-123,str);
printf(&amp;#34;\n&amp;#34;);
printf(&amp;#34;%d\n&amp;#34;,my_atoi(&amp;#34;123&amp;#34;));
system(&amp;#34;pause&amp;#34;);
}
&lt;/code>&lt;/pre>&lt;p>Save the above file to test.cpp, then compile and run it.&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/3132a6a25dab.gif"
loading="lazy"
alt="Peek 2018-04-16 17-25.gif"
>&lt;/p>
&lt;p>Now let&amp;rsquo;s debug it and see how to debug specifically. Debug the code following the method below.&lt;/p>
&lt;pre tabindex="0">&lt;code>linan@linan-PC:~$ gdb test
GNU gdb (Debian 7.12-6) 7.12.0.20161007-git
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later &amp;lt;http://gnu.org/licenses/gpl.html&amp;gt;
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type &amp;#34;show copying&amp;#34;
and &amp;#34;show warranty&amp;#34; for details.
This GDB was configured as &amp;#34;x86_64-linux-gnu&amp;#34;.
Type &amp;#34;show configuration&amp;#34; for configuration details.
For bug reporting instructions, please see:
&amp;lt;http://www.gnu.org/software/gdb/bugs/&amp;gt;.
Find the GDB manual and other documentation resources online at:
&amp;lt;http://www.gnu.org/software/gdb/documentation/&amp;gt;.
For help, type &amp;#34;help&amp;#34;.
Type &amp;#34;apropos word&amp;#34; to search for commands related to &amp;#34;word&amp;#34;...
Reading symbols from test...done.
(gdb) l
2
3 int func(int n)
4 {
5 int sum=0,i;
6 for(i=0; i&amp;lt;n; i++)
7 {
8 sum+=i;
9 }
10 return sum;
11 }
(gdb)
12
13 main()
14 {
15 int i;
16 long result = 0;
17 for(i=1; i&amp;lt;=100; i++)
18 {
19 result += i;
20 }
21
(gdb)
22 printf(&amp;#34;result[1-100] = %d \n&amp;#34;, result );
23 printf(&amp;#34;result[1-250] = %d \n&amp;#34;, func(250) );
24 }
(gdb)
Line number 25 out of range; test.cpp has 24 lines.
(gdb) break 15
Breakpoint 1 at 0x706: file test.cpp, line 15.
(gdb) break func
Breakpoint 2 at 0x6d7: file test.cpp, line 5.
(gdb) info break
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000000000000706 in main() at test.cpp:15
2 breakpoint keep y 0x00000000000006d7 in func(int) at test.cpp:5
(gdb) run
Starting program: /home/linan/test
Breakpoint 1, main () at test.cpp:16
16 long result = 0;
(gdb) next
17 for(i=1; i&amp;lt;=100; i++)
(gdb) next
19 result += i;
(gdb) next
17 for(i=1; i&amp;lt;=100; i++)
(gdb) continue
Continuing.
result[1-100] = 5050
Breakpoint 2, func (n=250) at test.cpp:5
5 int sum=0,i;
(gdb) p sum
$1 = 32767
(gdb) n
6 for(i=0; i&amp;lt;n; i++)
(gdb) p sum
$2 = 0
(gdb) p i
$3 = -134225552
(gdb) print i
$4 = -134225552
(gdb) n
8 sum+=i;
(gdb) print i
$5 = 0
(gdb) bt
#0 func (n=250) at test.cpp:8
#1 0x000055555555474c in main () at test.cpp:23
(gdb) help bt
Print backtrace of all stack frames, or innermost COUNT frames.
With a negative argument, print outermost -COUNT frames.
Use of the &amp;#39;full&amp;#39; qualifier also prints the values of the local variables.
Use of the &amp;#39;no-filters&amp;#39; qualifier prohibits frame filters from executing
on this backtrace.
(gdb) bt
#0 func (n=250) at test.cpp:8
#1 0x000055555555474c in main () at test.cpp:23
(gdb) finish
Run till exit from #0 func (n=250) at test.cpp:8
0x000055555555474c in main () at test.cpp:23
23 printf(&amp;#34;result[1-250] = %d \n&amp;#34;, func(250) );
Value returned is $6 = 31125
(gdb) c
Continuing.
result[1-250] = 31125
[Inferior 1 (process 11480) exited normally]
(gdb)
&lt;/code>&lt;/pre>&lt;p>&lt;img src="https://nansenli.com/img/rescued/32228d4f4a18.gif"
loading="lazy"
alt="Peek 2018-04-16 17-39.gif"
>&lt;/p>
&lt;h2 id="before-using-gdb">Before Using GDB
&lt;/h2>&lt;p>First, make sure your program was compiled with the -g parameter. This will insert relevant information into the executable program. This information doesn&amp;rsquo;t help the program but assists with debugging.&lt;/p>
&lt;h2 id="starting-debugging-with-gdb">Starting Debugging with GDB
&lt;/h2>&lt;p>There are several ways to launch GDB:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>gdb &lt;!-- raw HTML omitted -->
program is your executable file, usually in the current directory.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>gdb &lt;!-- raw HTML omitted --> core
Use gdb to simultaneously debug a running program and core file. The core file is generated after a program illegally executes and core dumps.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>gdb &lt;!-- raw HTML omitted --> &lt;!-- raw HTML omitted -->
If your program is a service program, you can specify the process ID of the running service program. gdb will automatically &lt;strong>attach&lt;/strong> to it and debug it. The program should be found in the PATH environment variable.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="notes-for-gdb-beginners">Notes for GDB Beginners
&lt;/h2>&lt;p>When debugging with GDB, you need to know where the source code files are located. In the example above, the source code and the running program are together. But this is not possible in practice. Add the source code directory using the -d parameter in the GDB command or using the directory command during runtime.&lt;/p>
&lt;p>Other notes:&lt;/p>
&lt;pre tabindex="0">&lt;code> -symbols &amp;lt;file&amp;gt;
-s &amp;lt;file&amp;gt;
Read symbols from the specified file.
-se file
Read symbol table information from the specified file and use it in the executable file.
-core &amp;lt;file&amp;gt;
-c &amp;lt;file&amp;gt;
The core file from a core dump during debugging.
&lt;/code>&lt;/pre></description></item><item><title>Effective C++ Notes</title><link>https://nansenli.com/post/jianshu/c++/effective-c++-%E7%AC%94%E8%AE%B0/</link><pubDate>Mon, 23 Apr 2018 10:40:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/c++/effective-c++-%E7%AC%94%E8%AE%B0/</guid><description>&lt;h2 id="chapter-1-from-c-to-c">Chapter 1: From C to C++
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>Item 1: C++ is a federation of languages
This means that C++ supports procedural, object-oriented, generic, functional, and meta-programming - five programming styles, which can be confusing. It is a comprehensive language.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 2: Prefer const, enum, inline to #define
#define is not part of the language, but part of the preprocessor. const, enum, and inline allow the compiler to detect errors.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 3: Use const to restrict possible errors
For example, return const reference objects to prevent users from modifying them. Use const member functions to prevent damage to classes.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 4: Make sure objects are initialized before use
Mainly use initialization lists to initialize members or parent classes. Additionally, if a class uses global objects, it&amp;rsquo;s impossible to know when those objects are initialized. Therefore, use static variables inside the class (which also have only one copy).&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="chapter-2-constructors-destructors-and-assignment-operators">Chapter 2: Constructors, Destructors, and Assignment Operators
&lt;/h2>&lt;ul>
&lt;li>Item 5: Know what functions the compiler silently generates
The compiler will generate default member functions as needed, including default constructor, copy constructor, destructor, and copy assignment operator.&lt;/li>
&lt;/ul>
&lt;p>The default copy assignment operator may not be able to copy reference members.&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Item 6: Use =delete to explicitly disallow auto-generated functions
This can prevent a class from being copied, blocking copying.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 7: Declare virtual destructors for polymorphic base classes
This is a basic function.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 8: Prevent exceptions from leaving destructors
This can lead to incomplete memory release or cause multiple exceptions to be thrown.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 9: Never call virtual functions during construction or destruction
This is also obvious - if a derived class constructor enters the base class constructor and calls a virtual function, the derived class&amp;rsquo;s virtual function isn&amp;rsquo;t ready yet.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 10: Have operator= return a reference to *this
This allows for continuous assignment.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 11: Handle self-assignment in operator=
It&amp;rsquo;s difficult to ensure your self-assignment code has no issues, and self-assignment is a time-wasting behavior.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 12: Copy all parts of an object when copying
This is obvious. If you implement an assignment function, you must copy all contents.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="chapter-3-resource-management">Chapter 3: Resource Management
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>Item 13: Objects are the basic unit of resource management
You need to be able to acquire resources and correctly release them. The key is to use shared_ptr to manage memory, which can automatically release held resources.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 14: Copying resource-managing objects
Either prohibit copying, use shared_ptr members, perform deep copying, or make the copying process transfer the resource.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 15: Provide access to raw resources in resource-managing classes
The world isn&amp;rsquo;t perfect, and many APIs require passing raw resource pointers. Using shared_ptr can interfere with how the object is used.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 16: Use delete[] for arrays allocated with new[]
To avoid this error, you can use vector containers to operate, avoiding the need for new arrays.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 17: Store newed objects in smart pointers in standalone statements
If an exception is thrown between creating a new object and passing it to a shared_ptr, it will lead to memory leaks.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="chapter-4-designs-and-declarations">Chapter 4: Designs and Declarations
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>Item 18: Make interfaces easy to use correctly
Interfaces can sometimes be difficult to express clearly. For example, if you pass in three int values, you need to clearly remember what each one represents. What if the parameters are passed in the wrong order? The book mentions passing in specific objects and using explicit to constrain parameters. You could also build a date object and then specify members, which I think is a bit better. Additionally, interfaces should have reasonable parameter settings and reasonable return values (such as returning smart pointers instead of raw pointers to force proper usage).&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 19: Designing a class is a major project
Object creation, destruction, initialization and assignment, whether passing objects by value causes exceptions, what are the legal values for the new type. Have type conversion functions been written? What about operators? Which functions should be deleted?&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 20: Prefer pass-by-reference-to-const to pass-by-value
Passing by value brings the call of the object&amp;rsquo;s constructor, consuming time. Passing by reference is like passing a pointer.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 21: Don&amp;rsquo;t return a reference to a local object
When returning an object created by a function, you cannot return a reference to that object. The compiler will make the correct optimization steps by itself.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 22: Declare data members private
If declared public, deleting a variable will cause all programs to be rewritten. If declared private, deleting a variable will cause inherited class programs to be rewritten. So declare them as private.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 23: Prefer non-member non-friend functions to member functions
If a method only calls a few other member functions, there&amp;rsquo;s no need to set it as a member function. Put it outside and wrap it with the same namespace.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 24: Declare non-member functions when type conversions should apply to all parameters
This is because member functions may not work in some cases. For example, operator * can only support that type as the first parameter. It will fail when a built-in type is used as the first parameter.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 25: Consider support for a non-throwing swap
Swap is a useful function. To implement an efficient swap, you need to design a swap member function to exchange with another object. Then design a non-member function in the namespace to call the above swap function. Finally, specialize std::swap to let the compiler choose your own swap.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="chapter-5-implementations">Chapter 5: Implementations
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>Item 26: Postpone variable definitions as long as possible
C++ cannot arbitrarily change the order of different lines, which means it&amp;rsquo;s better to define something only when it&amp;rsquo;s used. If all definitions are placed at the beginning, but the program returns right away, this would bring unnecessary construction and destruction processes.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 27: Minimize casting
Although the four types of C++ type conversions can be used, they all bring bigger problems. Old-style type conversions should be used even less.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 28: Avoid returning handles to object internals
Try to avoid returning pointers to internal data of objects outside the object, as this can cause big problems. The vector [] operator is such a case. This is a special case.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 29: Strive for exception-safe code
C++ exceptions are the most difficult thing to handle, but you still need to ensure exceptions.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 30: Understand the ins and outs of inlining
Inline is to make the compiler try to avoid stack expansion, not definitely. The 80-20 rule tells us that a program will only spend time on 20% of the code, so not all functions need to use inline.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 31: Minimize compilation dependencies: Separate definitions from declarations
Two ways to handle - implementation classes and interface classes. Implementation classes use another class to proxy the functionality of the class. Interface classes use object-oriented methods to design the class as an interface.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="chapter-6-inheritance-and-object-oriented-design">Chapter 6: Inheritance and Object-Oriented Design
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>Item 32: Make sure public inheritance models &amp;ldquo;is-a&amp;rdquo;
This means that derived classes can do everything base classes can do. But what derived classes can do, base classes may not be able to do.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 33: Avoid hiding inherited names
If you declare another overloaded version of a parent class function in a derived class, it will also override the original version of the parent class, making it impossible to correctly link to the parent class function. You can use &amp;ldquo;using&amp;rdquo; to indicate the use of the parent class&amp;rsquo;s function space.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 34: Differentiate between inheritance of interface and inheritance of implementation
Pure virtual functions only inherit the interface, virtual functions inherit the interface and default implementation, and non-virtual functions inherit the interface and forced implementation.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 35: Consider alternatives to virtual functions
Other design patterns can be used to replace the implementation of virtual functions, such as function objects.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 36: Never redefine an inherited non-virtual function
Otherwise, you won&amp;rsquo;t know which one is being called. Yes, I&amp;rsquo;m talking about destructors.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 37: Never redefine a function&amp;rsquo;s inherited default parameter value
This is because default parameters are statically determined, not dynamically determined. The one you define may not be useful.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 38: Model &amp;ldquo;has-a&amp;rdquo; or &amp;ldquo;is-implemented-in-terms-of&amp;rdquo; through composition
Public inheritance is an &amp;ldquo;is-a&amp;rdquo; relationship. Composition is a &amp;ldquo;has-a&amp;rdquo; relationship. For example, in the implementation of a set, inheriting from list would have some problems. Set should compose list.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 39: Use private inheritance judiciously
The frequency of using private inheritance is lower than composition. There are two cases where this implementation is needed: first, when the derived class needs to access the protected members of the base class; second, when virtual functions need to be redefined (shouldn&amp;rsquo;t a middle class be designed in this case?).&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 40: Use multiple inheritance judiciously
Multiple inheritance is rarely used. It&amp;rsquo;s best not to include any members in virtual inheritance. This is because the initialization of members in virtual inheritance is done in the derived class.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="chapter-7-templates-and-generic-programming">Chapter 7: Templates and Generic Programming
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>Item 41: Understand implicit interfaces and compile-time polymorphism
This is static polymorphism.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 42: Understand the two meanings of typename
Typename appears not only in the template line of a template but also in template processing. When we use member variables or member types, the compiler doesn&amp;rsquo;t know which one is being used. By default, it uses member variables. Add typename to remind the compiler that it&amp;rsquo;s a member type.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 43: Know how to access names in templatized base classes
This is because template classes can be specialized, and if specialized, some symbols may not exist. So you need to explicitly indicate symbols in the template class, such as using the this pointer or scope.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 44: Factor parameter-independent code out of templates
When a template passes a non-type parameter, it generates multiple copies of code. There are some ways to avoid generating duplicate code, but they&amp;rsquo;re not as fast as the original.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 45: Use member function templates to accept all compatible types
Classes can be templates, and member functions can also be templates.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 46: Define non-member functions inside templates when type conversions are desired
Not only define it as a non-member function but also as a friend function.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 47: Use traits classes for information about types
Traits use the properties of template programming and specialization to implement a traits class, through which you can get the data type you want in the generalized class.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 48: Be aware of template metaprogramming
Metaprogramming refers to programming methods where results are known at compile time. For example, processing input at compile time. Similar to factorial, which is like the Hello World program of C++ metaprogramming.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="chapter-8-customizing-new-and-delete">Chapter 8: Customizing new and delete
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>Item 49: Understand the behavior of the new-handler
It continuously tries to allocate memory. I&amp;rsquo;m curious if there will be a freeze if the new-handler keeps failing to allocate.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 50: Understand when it makes sense to replace new and delete
It seems that the performance of new and delete is very high now. No need to replace them.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 51: Adhere to convention when writing new and delete
Maintain compatibility when customizing new and delete.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 52: Write placement delete if you write placement new
Placement delete will automatically run when placement new encounters an error.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="chapter-9-modern-c11">Chapter 9: Modern C++11
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>Item 53: Pay attention to compiler warnings
Don&amp;rsquo;t ignore warnings.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 54: Familiarize yourself with the TR1 standard libraries&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Item 55: Familiarize yourself with Boost&lt;/p>
&lt;/li>
&lt;/ul></description></item><item><title>Tencent Campus Recruitment Essay</title><link>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/%E8%85%BE%E8%AE%AF%E6%A0%A1%E6%8B%9B%E9%9A%8F%E7%AC%94/</link><pubDate>Wed, 11 Apr 2018 08:39:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/%E8%85%BE%E8%AE%AF%E6%A0%A1%E6%8B%9B%E9%9A%8F%E7%AC%94/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>There were two phone interviews in the early selection phase, which I&amp;rsquo;ll skip here.
First published at: &lt;a class="link" href="https://www.jianshu.com/p/d5aa63a27172" target="_blank" rel="noopener"
>https://www.jianshu.com/p/d5aa63a27172&lt;/a>&lt;/p>
&lt;h2 id="first-interview">First Interview
&lt;/h2>&lt;p>Chengdu, April 11&lt;/p>
&lt;ul>
&lt;li>Introduce your projects, what were the difficulties, and how did you solve them&lt;/li>
&lt;li>Introduce C++11 features&lt;/li>
&lt;li>Explain the fork function&lt;/li>
&lt;li>Explain the TIME_WAIT state&lt;/li>
&lt;li>Why does a TCP connection handshake require three steps&lt;/li>
&lt;li>Explain iterator invalidation. Does push_back cause iterator invalidation?&lt;/li>
&lt;li>Characteristics of red-black trees, explain&lt;/li>
&lt;li>Methods to resolve hash collisions&lt;/li>
&lt;li>Differences between processes and threads&lt;/li>
&lt;li>What thread models do you use&lt;/li>
&lt;li>Explain coroutines&lt;/li>
&lt;li>Explain the quicksort algorithm&lt;/li>
&lt;li>What is a stable sort, is quicksort stable, and why&lt;/li>
&lt;li>Derive the formula for the worst-case scenario of quicksort&lt;/li>
&lt;li>Why should a destructor be virtual&lt;/li>
&lt;li>Why can&amp;rsquo;t a constructor be a virtual function&lt;/li>
&lt;li>Questions printed on paper, testing: new[] objects, static members, what exactly a subclass constructor calls, differences between copy constructors and assignment operators, when the equals sign appears in a copy constructor, when an assignment operator is needed, deep copy vs. shallow copy, when virtual functions are called.&lt;/li>
&lt;/ul>
&lt;p>Below is one of the questions, please write the output:&lt;/p>
&lt;pre tabindex="0">&lt;code>#include &amp;lt;iostream&amp;gt;
using namespace std;
struct A{
A(){
local_var++;
}
virtual void func(){
cout &amp;lt;&amp;lt; &amp;#34;A&amp;#34; &amp;lt;&amp;lt; endl;
}
static int local_var;
};
int A::local_var = 0;
struct B : A{
B(){
local_var+=2;
}
virtual void func(){
cout &amp;lt;&amp;lt; &amp;#34;B&amp;#34; &amp;lt;&amp;lt; endl;
}
};
int main(){
A* a1 = new B[3];
cout &amp;lt;&amp;lt; a1[0].local_var &amp;lt;&amp;lt; endl;
a1-&amp;gt;func();
A a2 = a1[0];
cout &amp;lt;&amp;lt; a2.local_var &amp;lt;&amp;lt; endl;
a2.func();
A a3;
a3 = a1[0];
cout &amp;lt;&amp;lt; a3.local_var &amp;lt;&amp;lt; endl;
a3.func();
}
&lt;/code>&lt;/pre>&lt;p>What problem will occur in the following code?&lt;/p>
&lt;pre tabindex="0">&lt;code>#include &amp;lt;iostream&amp;gt;
using namespace std;
struct A{
virtual void func1(){
cout &amp;lt;&amp;lt; a &amp;lt;&amp;lt; endl;
}
void func2(){
cout &amp;lt;&amp;lt; a &amp;lt;&amp;lt; endl;
}
void func3(){
cout &amp;lt;&amp;lt; &amp;#34;a&amp;#34; &amp;lt;&amp;lt; endl;
}
int a;
};
int main(){
A * a = (A*)malloc(sizeof(A));
a-&amp;gt;func1();
a-&amp;gt;func2();
a-&amp;gt;func3();
}
&lt;/code>&lt;/pre>&lt;ul>
&lt;li>The approach to a problem during the written test (previous Nowcoder test. The interviewer actually printed all my test answers and results, including multiple-choice and algorithm questions, how many times I submitted, how many times I failed). Testing when the virtual pointer is initialized.&lt;/li>
&lt;li>Randomly selected a question on paper. Given preorder traversal ABC and postorder traversal CBA, what is the inorder traversal? Draw out the two possible scenarios.&lt;/li>
&lt;li>The approach to the first algorithm question during the written test (previous Nowcoder test)&lt;/li>
&lt;li>There are 100 marbles, two players take turns, each person can only take 1-5 marbles, the person who cannot take any loses, what is the winning strategy?&lt;/li>
&lt;li>What have you been learning recently (server programming)? What did you learn before (TensorFlow)? How did you learn it? What do you think about TensorFlow&amp;rsquo;s powerful neural networks?&lt;/li>
&lt;li>Family situation.&lt;/li>
&lt;li>After leaving, wrote code in the lobby: find elements in char array a that are not in char array b, and put them in char array c. Here I forgot to convert char characters to unsigned char type.&lt;/li>
&lt;/ul>
&lt;h2 id="second-interview">Second Interview
&lt;/h2>&lt;p>Chengdu, April 13&lt;/p>
&lt;ul>
&lt;li>Self-introduction&lt;/li>
&lt;li>What are system calls? Which system calls have you used? Which system calls take a long time?&lt;/li>
&lt;li>Have you used gdb debugging? What is a conditional breakpoint?&lt;/li>
&lt;li>Difference between function pointers and pointer functions. Write an example.&lt;/li>
&lt;li>Write a fork call example by hand. In what situations have you used fork calls?&lt;/li>
&lt;li>Explain the connect function in UDP.&lt;/li>
&lt;li>What is an index? Is adding more indexes always better? (I admitted to looking at interview experiences for the index question, but came up with the follow-up question myself)&lt;/li>
&lt;li>Which questions from the last interview did you research afterward?&lt;/li>
&lt;li>There was a problem with your code in the last interview, do you know what it was?&lt;/li>
&lt;li>Where did you learn the answers to these questions? What have you been doing these two days?&lt;/li>
&lt;li>Some suggestions for you: look into gdb debugging, database knowledge, network programming, and practice more.&lt;/li>
&lt;/ul>
&lt;h2 id="aftermath">Aftermath
&lt;/h2>&lt;p>Received a phone call on April 20 confirming the offer from Tencent.&lt;/p></description></item><item><title>TCP UDP IP Maximum Message Length</title><link>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/tcp-udp-ip%E6%9C%80%E5%A4%A7%E6%8A%A5%E6%96%87%E9%95%BF%E5%BA%A6/</link><pubDate>Mon, 26 Mar 2018 16:41:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/tcp-udp-ip%E6%9C%80%E5%A4%A7%E6%8A%A5%E6%96%87%E9%95%BF%E5%BA%A6/</guid><description>&lt;h2 id="udp">UDP
&lt;/h2>&lt;p>For UDP protocol, the maximum length of the entire packet is 65535 bytes, and the payload length is 65535-20=65515 bytes.&lt;/p>
&lt;h2 id="tcp">TCP
&lt;/h2>&lt;p>For TCP protocol, the maximum length of the entire packet is determined by the Maximum Segment Size (MSS). MSS is the maximum data segment that a TCP packet can transmit each time. To achieve optimal transmission performance, the TCP protocol usually negotiates the MSS value between both parties when establishing a connection. When implementing the TCP protocol, the MTU value is often used as a substitute (minus the size of the IP packet header of 20 bytes and the TCP segment header of 20 bytes), so the MSS is often 1460 bytes. The communicating parties will determine the maximum MSS value for this connection based on the smaller of the two MSS values provided.&lt;/p>
&lt;h2 id="ip">IP
&lt;/h2>&lt;p>For IP protocol, the size of the IP packet is determined by the MTU (Maximum Transmission Unit).&lt;/p></description></item><item><title>TCP Message Format and HTTP Message Format</title><link>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/tcp-%E6%8A%A5%E6%96%87%E6%A0%BC%E5%BC%8F-http%E6%8A%A5%E6%96%87%E6%A0%BC%E5%BC%8F/</link><pubDate>Mon, 26 Mar 2018 16:37:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/tcp-%E6%8A%A5%E6%96%87%E6%A0%BC%E5%BC%8F-http%E6%8A%A5%E6%96%87%E6%A0%BC%E5%BC%8F/</guid><description>&lt;h2 id="tcp">TCP
&lt;/h2>&lt;pre tabindex="0">&lt;code>Source Port Destination Port
Sequence Number
Acknowledgment Number
Flags Window
Checksum Urgent Pointer
Options
Data
&lt;/code>&lt;/pre>&lt;h2 id="http">HTTP
&lt;/h2>&lt;pre tabindex="0">&lt;code>Request URL Protocol-Version\r\n
Field: Value\r\n
\r\n
Body
&lt;/code>&lt;/pre>&lt;pre tabindex="0">&lt;code>Protocol-Version Status-Code Status-Description\r\n
Field: Value\r\n
\r\n
Body
&lt;/code>&lt;/pre></description></item><item><title>TCP UDP Connection Establishment Process via Socket</title><link>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/tcp-udp-%E9%80%9A%E8%BF%87socket%E5%BB%BA%E7%AB%8B%E8%BF%9E%E6%8E%A5%E7%9A%84%E8%BF%87%E7%A8%8B/</link><pubDate>Mon, 26 Mar 2018 14:46:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/tcp-udp-%E9%80%9A%E8%BF%87socket%E5%BB%BA%E7%AB%8B%E8%BF%9E%E6%8E%A5%E7%9A%84%E8%BF%87%E7%A8%8B/</guid><description>&lt;h2 id="tcp">TCP
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Phase&lt;/th>
&lt;th style="text-align:right">Server&lt;/th>
&lt;th style="text-align:center">Client&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;/td>
&lt;td style="text-align:right">socket&lt;/td>
&lt;td style="text-align:center">socket&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;/td>
&lt;td style="text-align:right">bind&lt;/td>
&lt;td style="text-align:center">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;/td>
&lt;td style="text-align:right">listen&lt;/td>
&lt;td style="text-align:center">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;/td>
&lt;td style="text-align:right">accept&lt;/td>
&lt;td style="text-align:center">connect&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;/td>
&lt;td style="text-align:right">write&lt;/td>
&lt;td style="text-align:center">write&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;/td>
&lt;td style="text-align:right">read&lt;/td>
&lt;td style="text-align:center">read&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;/td>
&lt;td style="text-align:right">close&lt;/td>
&lt;td style="text-align:center">close&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="udp">UDP
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Phase&lt;/th>
&lt;th style="text-align:right">Server&lt;/th>
&lt;th style="text-align:center">Client&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;/td>
&lt;td style="text-align:right">socket&lt;/td>
&lt;td style="text-align:center">socket&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;/td>
&lt;td style="text-align:right">bind&lt;/td>
&lt;td style="text-align:center">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;/td>
&lt;td style="text-align:right">sendto&lt;/td>
&lt;td style="text-align:center">recvfrom&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;/td>
&lt;td style="text-align:right">recvfrom&lt;/td>
&lt;td style="text-align:center">sendto&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;/td>
&lt;td style="text-align:right">close&lt;/td>
&lt;td style="text-align:center">close&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table></description></item><item><title>HTTPS Two-way Encryption Process Analysis</title><link>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/https-%E5%8F%8C%E5%90%91%E5%8A%A0%E5%AF%86%E8%BF%87%E7%A8%8B%E5%88%86%E6%9E%90/</link><pubDate>Mon, 26 Mar 2018 14:42:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/https-%E5%8F%8C%E5%90%91%E5%8A%A0%E5%AF%86%E8%BF%87%E7%A8%8B%E5%88%86%E6%9E%90/</guid><description>&lt;p>HTTPS authentication process:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>① The browser sends a connection request to the secure server.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>② The server sends its certificate and certificate-related information to the client browser.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>③ The client browser checks whether the certificate sent by the server is issued by a trusted CA (Certificate Authority). If it is, the protocol continues execution; if not, the client browser gives the user a warning message: warning that this certificate is not trustworthy and asking whether the user wants to continue.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>④ Next, the client browser compares the information in the certificate, such as domain name and public key, with the relevant information just sent by the server. If they match, the client browser recognizes the server&amp;rsquo;s legal identity.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>⑤ The server requests the client to send its own certificate. After receiving it, the server verifies the client&amp;rsquo;s certificate. If it fails verification, the connection is refused; if it passes verification, the server obtains the user&amp;rsquo;s public key.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>⑥ The client browser informs the server of the symmetric encryption methods it supports.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>⑦ The server selects the encryption method with the highest level of encryption from the methods sent by the client, encrypts it with the client&amp;rsquo;s public key, and notifies the browser.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>⑧ The browser selects a session key for this encryption method, encrypts it with the server&amp;rsquo;s public key, and sends it to the server.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>⑨ The server receives the message sent by the browser, decrypts it with its own private key, and obtains the session key.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>⑩ The subsequent communication between the server and browser uses the symmetric encryption method, and the symmetric key is encrypted.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>The above describes the specific communication process of two-way authentication SSL protocol, which requires certificates from both the server and user. One-way authentication SSL protocol does not require the client to have a CA certificate. Compared to the steps above, it only needs to remove the process of server-side verification of the client certificate, and when negotiating the symmetric encryption method and session key, the server sends the unencrypted encryption method to the client (this does not affect the security of the SSL process). This way, the specific communication content between both parties is encrypted data. If a third party attacks and obtains the data, they only get encrypted information. For the third party to obtain useful information, they need to decrypt the encrypted data, and at this point, security depends on the security of the encryption method. Fortunately, the current encryption methods are sufficiently secure as long as the communication key is long enough. This is why we emphasize the requirement to use 128-bit encrypted communication.&lt;/p></description></item><item><title>Tencent Internal Referral Essay</title><link>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/%E8%85%BE%E8%AE%AF%E5%86%85%E6%8E%A8%E9%9A%8F%E7%AC%94/</link><pubDate>Mon, 26 Mar 2018 12:23:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/%E8%85%BE%E8%AE%AF%E5%86%85%E6%8E%A8%E9%9A%8F%E7%AC%94/</guid><description>&lt;p>This afternoon I was browsing Nowcoder. Suddenly I wanted to check on the progress of my Tencent internal referral, and the WeChat official account showed that &amp;ldquo;the current position may not be suitable for you.&amp;rdquo;&lt;/p>
&lt;p>I don&amp;rsquo;t have any special feelings about it.&lt;/p>
&lt;p>I still need to work hard and strive for a good outcome. Regardless, there are still opportunities.&lt;/p>
&lt;p>In the afternoon while doing practice problems, there were 3 out of 8 NetEase questions that I couldn&amp;rsquo;t solve, which made me feel that there are indeed many things I need to learn.&lt;/p>
&lt;p>I bought a lot of books, and I don&amp;rsquo;t know when I&amp;rsquo;ll be able to finish reading them.&lt;/p>
&lt;p>Anyway, I should stay positive.&lt;/p>
&lt;p>The interviewer said I was quite suitable, although that probably made me quite happy, but I won&amp;rsquo;t be sad because I was rejected, nor will I be proud because I was chosen.&lt;/p></description></item><item><title>Tencent Mock Exam: The Change-Making Problem</title><link>https://nansenli.com/post/jianshu/%E7%AE%97%E6%B3%95/%E8%85%BE%E8%AE%AF%E6%A8%A1%E6%8B%9F%E8%80%83%E8%AF%95%E4%B9%8B%E6%89%BE%E9%9B%B6%E9%97%AE%E9%A2%98/</link><pubDate>Mon, 26 Mar 2018 03:57:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E7%AE%97%E6%B3%95/%E8%85%BE%E8%AE%AF%E6%A8%A1%E6%8B%9F%E8%80%83%E8%AF%95%E4%B9%8B%E6%89%BE%E9%9B%B6%E9%97%AE%E9%A2%98/</guid><description>&lt;blockquote>
&lt;p>Little Q is very wealthy and owns many coins. Little Q&amp;rsquo;s coins follow a pattern: for all non-negative integers K, Little Q has exactly two coins with the value 2^k. So Little Q owns coins of values 1, 1, 2, 2, 4, 4, and so on. Little Q needs to pay n money for something and wants to know how many different combinations of coins he can use to make this payment.
Input: An integer n (1&amp;lt;=n&amp;lt;=10^18), representing the amount to be paid.
Output: The number of ways Little Q can make this payment.&lt;/p>
&lt;/blockquote>
&lt;p>Reference solution:&lt;/p>
&lt;pre tabindex="0">&lt;code>#coding=utf-8
import math
n = int(input())
def getMaxCoin(n):
tmp = int(math.log(n,2))
return 2**tmp
buff = {}
def dp(n, coin):
if (n, coin) in buff:
return buff[(n,coin)]
if n == 0: # Making change for 0 money is always possible
return 1
if coin == 1: # Only using 1-value coins
if n == 1 or n == 2:
return 1
return 0 # No solution with current coin value
if n &amp;gt; coin * 4 -2:
return 0
ret = 0
# Using one coin of current value
if n &amp;gt;= coin:
ret += dp(n-coin, coin//2)
if n &amp;gt;= coin*2:
# Using two coins of current value
ret += dp(n-coin*2, coin//2)
# Not using any coin of current value
ret += dp(n, coin//2)
buff[(n,coin)] = ret
return ret
print(dp(n,getMaxCoin(n)))
&lt;/code>&lt;/pre>&lt;p>Approach:
For the current amount, the maximum possible coin value that can be used is calculated by the getMaxCoin function.
For the problem amount n and the maximum coin value that can be used, we calculate dp(n, coin).
The coin value will continuously decrease. For coin value 1, there&amp;rsquo;s only 1 way to make amounts 1 or 2. For amount 0, no coins are needed for a solution. For amounts greater than coin*4-2, it exceeds the maximum possible value, so there&amp;rsquo;s no solution. For other cases, we consider three situations: using one coin of the current value, using two coins of the current value, or using zero coins of the current value. We calculate the corresponding dp values for each case. We use the buff cache to store already calculated values.&lt;/p></description></item><item><title>My First Program</title><link>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/%E6%88%91%E7%9A%84%E7%AC%AC%E4%B8%80%E4%B8%AA%E7%A8%8B%E5%BA%8F/</link><pubDate>Wed, 21 Mar 2018 08:06:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/%E6%88%91%E7%9A%84%E7%AC%AC%E4%B8%80%E4%B8%AA%E7%A8%8B%E5%BA%8F/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>Blank space&lt;/p>
&lt;h2 id="attempt">Attempt
&lt;/h2>&lt;p>During my freshman year, I remember participating in a college competition, which was my first exposure to programming concepts.&lt;/p>
&lt;p>The competition involved using programmable building blocks and toys provided by the laboratory to create a project. It was somewhat similar to the concept of Mi Bunny Robot, except it was much earlier.&lt;/p>
&lt;p>I built a smart car that could follow black tape tracks on the ground and perform corresponding actions. It had the following three functions:&lt;/p>
&lt;ul>
&lt;li>Following a single line of tape on the ground. This function was implemented using two infrared sensors.&lt;/li>
&lt;li>When encountering obstacles, it could bypass them according to pre-agreed actions. This was implemented with an infrared sensor.&lt;/li>
&lt;li>It could grab ping pong balls from the ground using a claw. This was driven by a motor.&lt;/li>
&lt;/ul>
&lt;p>I remember the programming interface was a graphical one, where you could drag different rectangles representing program bodies. Then you used connecting lines to indicate state transitions. After writing the code, it needed to be transmitted to the programmable blocks via an infrared device. The infrared transmission device looked a bit like an upright iron, and the block was quite large. It could be fitted with lithium batteries and had some gear-like objects that could be attached to other wheels or sliding devices. This way, the entire device could move as expected.&lt;/p>
&lt;p>I remember that during the judging, except for my own work, none of the other teams&amp;rsquo; projects could move (meaning they hadn&amp;rsquo;t written programs).&lt;/p>
&lt;p>When other teams were introducing their robots, they only described the functions, but unfortunately, they couldn&amp;rsquo;t move.&lt;/p>
&lt;p>When it was my turn to present, it truly amazed everyone. The judges were astonished to see my robot move.&lt;/p>
&lt;p>In reality, the built-in program would disappear when power was lost, and because the blocks were very old, the lithium batteries inside couldn&amp;rsquo;t last long. I wrote the program at the base and brought it to the competition venue. To prevent the program from being lost, I found a charger. Because when running, the lithium battery without a charging cable would instantly lose power, causing the program to disappear. So it was also lucky that it could move.&lt;/p>
&lt;p>I didn&amp;rsquo;t really expect anything; I felt like I was just implementing this little project according to my own ideas. Later, when they announced the first prize, surprisingly, it wasn&amp;rsquo;t me. I didn&amp;rsquo;t feel very disappointed, as it was fun anyway. But when the person who went up to receive the award wasn&amp;rsquo;t me, everyone present was quite astonished.&lt;/p>
&lt;p>Then the award presenter said there was a mistake, and I ended up winning first place after all.&lt;/p>
&lt;h2 id="failure">Failure
&lt;/h2>&lt;p>The university was holding an electronic design competition open to all students.&lt;/p>
&lt;p>I looked at the competition topics and thought I could give it a try. I found similar topics on Baidu. The content required a series of electronic components to be soldered together, and then the required program, which was provided, had to be loaded.&lt;/p>
&lt;p>For me at that time, that pile of code was just a bunch of gibberish.&lt;/p>
&lt;p>I went to the Hangzhou electronics market and navigated through countless shops, where the shop owners looked at me as if I were an alien.&lt;/p>
&lt;p>I awkwardly asked for the electronic components I wanted. A kind shop owner gave me some LED bulbs.&lt;/p>
&lt;p>She also said that I should use a flat-head soldering iron, as beginners can&amp;rsquo;t handle pointed ones well.&lt;/p>
&lt;p>They were very curious about what I was trying to do.&lt;/p>
&lt;p>After buying the components, I sat on the floor and, following the circuit diagram from that blog, soldered everything together on a chair.&lt;/p>
&lt;p>Oh, right, at that time I hadn&amp;rsquo;t learned about circuit diagrams either, so I was just soldering based on my intuition.&lt;/p>
&lt;p>I think I was quite naive then, trying to solder something without any basics. But I don&amp;rsquo;t have the right to mock him.&lt;/p>
&lt;p>Then I started loading the program. I put the single chip into the socket, connected the socket to the power supply, and started trying to download the program provided by that blog to the chip using software.&lt;/p>
&lt;p>I actually wanted to tell him, &amp;ldquo;Hey, are you sure the firmware program provided by this website matches your chip model? The chip is so hot; you actually inserted it backward, did you know? The chip has probably been burned out.&amp;rdquo;&lt;/p>
&lt;p>But I knew nothing. I stupidly took something soldered from unknown components and a chip that was actually burned out to participate in the evaluation. Needless to say, I was despised by the expert judges.&lt;/p>
&lt;p>What I didn&amp;rsquo;t know was that almost no one like me had registered for the competition. The university had an electronic design competition experimental class where you could learn everything you wanted to know about electronic design.&lt;/p>
&lt;p>Although the competition was open to the whole university, only those from this experimental class would register.&lt;/p>
&lt;h2 id="my-first-program">My First Program
&lt;/h2>&lt;p>Later, I understood the existence of the electronic design class. I signed up for it in my sophomore year. I remember there was an exam and an interview, and you had to pass to get in.&lt;/p>
&lt;p>I remember they asked me several questions, none of which I could answer. Later, they asked what my C language score was, and I said 97, then they said, &amp;ldquo;Okay, you&amp;rsquo;ve passed.&amp;rdquo;&lt;/p>
&lt;p>The first week mainly covered basic soldering knowledge. Although it seems the story is just beginning, the story I want to tell in this article is also about to end.&lt;/p>
&lt;p>The second week mainly covered knowledge about LED digital tubes. I received the distributed 51 development board. In the evening, I studied in my dorm how to make the digital tubes light up according to my ideas.&lt;/p>
&lt;p>I learned how to display numbers that were constantly lit. I understood that I needed to select position first and then segment. I suddenly understood a lot; this was the most primitive time-slicing method.&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/e8695ded8be9.png"
loading="lazy"
alt="image.png"
>&lt;/p>
&lt;p>I suddenly had an idea: I wanted to make the digital tubes achieve a marquee effect, with the outer ring continuously rotating.&lt;/p>
&lt;p>I wrote and wrote and wrote.&lt;/p>
&lt;p>I wrote it out.&lt;/p>
&lt;p>I burned it in, ran it, and amazingly, it was just as I had imagined. The outer ring kept turning and turning, which was really beautiful. I was very happy.&lt;/p>
&lt;h2 id="afterword">Afterword
&lt;/h2>&lt;p>Perhaps that wasn&amp;rsquo;t my first program, after all, before that, the C language course required designing programs, so I should have written programs already.&lt;/p>
&lt;p>However, for me, that marquee display was truly my first program in a real sense, completely created by me.&lt;/p>
&lt;p>Even if I die someday, if the programs I write can continue to run, I can continue my life in another way.&lt;/p></description></item><item><title>Linux Multithreaded Server Programming Basics: C++11 Concurrent Programming</title><link>https://nansenli.com/post/jianshu/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/linux%E5%A4%9A%E7%BA%BF%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%BC%96%E7%A8%8B%E5%9F%BA%E7%A1%80-c++-11-%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0/</link><pubDate>Sun, 18 Mar 2018 08:29:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/linux%E5%A4%9A%E7%BA%BF%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%BC%96%E7%A8%8B%E5%9F%BA%E7%A1%80-c++-11-%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>Previous versions of C++ didn&amp;rsquo;t support threads natively. Instead, you had to use libraries like pthread. Using C++&amp;rsquo;s built-in thread support makes programs more unified and concise.&lt;/p>
&lt;h2 id="header-files">Header Files
&lt;/h2>&lt;ul>
&lt;li>&lt;code>&amp;lt;thread&amp;gt;&lt;/code>: This header file contains the std::thread and std::this_thread classes, along with functions for managing threads. It&amp;rsquo;s the main file for implementing threads.&lt;/li>
&lt;li>&lt;code>&amp;lt;atomic&amp;gt;&lt;/code>: This header file contains std::atomic and std::atomic_flag classes, which are the main files for implementing atomic operations.&lt;/li>
&lt;li>&lt;code>&amp;lt;mutex&amp;gt;&lt;/code>: Contains mutex-related classes and functions.&lt;/li>
&lt;li>&lt;code>&amp;lt;future&amp;gt;&lt;/code>: Contains the future class and related functions.&lt;/li>
&lt;li>&lt;code>&amp;lt;condition_variable&amp;gt;&lt;/code>: Contains condition variable classes.&lt;/li>
&lt;/ul>
&lt;p>These are the thread-related parts of C++11. Although there are many debates about pthread versus C++ thread, the cross-platform C++ thread seems to be more standard.&lt;/p>
&lt;p>Recommended book for learning C++11 threads:
&lt;a class="link" href="https://www.gitbook.com/book/chenxiaowei/cpp_concurrency_in_action/details" target="_blank" rel="noopener"
>https://www.gitbook.com/book/chenxiaowei/cpp_concurrency_in_action/details&lt;/a>&lt;/p>
&lt;h2 id="hello-world-thread">Hello World Thread
&lt;/h2>&lt;p>A simple introduction to the thread class:&lt;/p>
&lt;pre tabindex="0">&lt;code>#include &amp;lt;thread&amp;gt;
using namespace std;
// This function is the thread task we want to run
void hello()
{
printf(&amp;#34;%s&amp;#34;, &amp;#34;hello\n&amp;#34;);
}
// Using the thread class, we pass a function as our initial task
// We can also pass other parameters like classes
int main()
{
thread t(hello);
// Using join to wait for completion
t.join();
}
&lt;/code>&lt;/pre>&lt;p>Besides join to wait for completion, you can use detach to not wait for the thread to finish.&lt;/p>
&lt;pre tabindex="0">&lt;code>struct func
{
int&amp;amp; i;
func(int&amp;amp; i_) : i(i_) {}
void operator() ()
{
for (unsigned j=0 ; j&amp;lt;1000000 ; ++j)
{
do_something(i); // 1. Potential access hazard: dangling reference
}
}
};
void oops()
{
int some_local_state=0;
func my_func(some_local_state);
std::thread my_thread(my_func);
my_thread.detach(); // 2. Don&amp;#39;t wait for the thread to finish
} // 3. The new thread might still be running
&lt;/code>&lt;/pre>&lt;p>In this example, we&amp;rsquo;ve decided not to wait for the thread to finish (using detach() ②), so when the oops() function completes ③, the new thread might still be running. If the thread is still running, it will call the do_something(i) function ①, which accesses a variable that has already been destroyed. As with a single-threaded program—allowing pointers or references to local variables to persist after the function completes has never been a good idea—this situation isn&amp;rsquo;t obvious and makes multithreading more error-prone.&lt;/p>
&lt;h2 id="how-to-wait-for-a-thread-to-complete">How to Wait for a Thread to Complete?
&lt;/h2>&lt;p>If you need to wait for a thread, the corresponding std::thread instance needs to use join(). In example 2.1, replacing my_thread.detach() with my_thread.join() ensures that local variables are only destroyed after the thread completes. In this case, since the original thread doesn&amp;rsquo;t do much during its lifetime, running the function in a separate thread offers minimal benefit. However, in real programming, either the original thread has its own work to do, or it starts multiple child threads to do useful work and waits for these threads to complete.&lt;/p>
&lt;p>join() is a simple, direct way to wait for a thread to complete or not wait at all. When you need more flexible control over waiting threads, such as checking if a thread has finished or only waiting for a period of time (determining a timeout if exceeded), you need to use other mechanisms like condition variables and futures. Calling join() also cleans up the thread-related storage parts, so the std::thread object is no longer associated with the completed thread. This means you can only use join() once on a thread; once you&amp;rsquo;ve used join(), the std::thread object can&amp;rsquo;t be joined again. When joinable() is used on it, it will return false.&lt;/p>
&lt;h2 id="passing-parameters-to-threads">Passing Parameters to Threads
&lt;/h2>&lt;pre tabindex="0">&lt;code>void f(int i, std::string const&amp;amp; s);
std::thread t(f, 3, &amp;#34;hello&amp;#34;);
&lt;/code>&lt;/pre>&lt;p>The code creates a thread that calls f(3, &amp;ldquo;hello&amp;rdquo;). Note that function f needs a std::string object as its second parameter, but here a string literal is used, which is of type char const *. The conversion from the literal to a std::string object is completed in the thread&amp;rsquo;s context.&lt;/p>
&lt;p>It&amp;rsquo;s important to note that the constructor ignores the expected parameter types of the function and blindly copies the provided variables.&lt;/p>
&lt;p>Example 1:&lt;/p>
&lt;pre tabindex="0">&lt;code>void f(int i,std::string const&amp;amp; s);
void oops(int some_param)
{
char buffer[1024]; // 1
sprintf(buffer, &amp;#34;%i&amp;#34;,some_param);
std::thread t(f,3,buffer); // 2
t.detach();
}
&lt;/code>&lt;/pre>&lt;p>In this case, buffer ② is a pointer variable pointing to a local variable, and then the local variable is passed to the new thread through buffer ②. Furthermore, the function is very likely to crash (oops) before the literal is converted to a std::string object, resulting in undefined behavior. And even if you want to rely on implicit conversion to convert the literal to the std::string object that the function expects, since std::thread&amp;rsquo;s constructor copies the provided variables, it only copies the unconverted string literal.
The solution is to convert the literal to a std::string object before passing it to the std::thread constructor:&lt;/p>
&lt;pre tabindex="0">&lt;code>void f(int i,std::string const&amp;amp; s);
void not_oops(int some_param)
{
char buffer[1024];
sprintf(buffer,&amp;#34;%i&amp;#34;,some_param);
std::thread t(f,3,std::string(buffer)); // Use std::string to avoid dangling pointers
t.detach();
}
&lt;/code>&lt;/pre>&lt;p>Example 2:
You might also encounter the opposite situation: expecting to pass a reference, but the entire object is copied. This can happen when a thread updates a data structure passed by reference, such as:&lt;/p>
&lt;pre tabindex="0">&lt;code>void update_data_for_widget(widget_id w,widget_data&amp;amp; data); // 1
void oops_again(widget_id w)
{
widget_data data;
std::thread t(update_data_for_widget,w,data); // 2
display_status();
t.join();
process_widget_data(data); // 3
}
&lt;/code>&lt;/pre>&lt;h2 id="using-mutexes">Using Mutexes
&lt;/h2>&lt;p>In C++, you create a mutex by instantiating std::mutex and lock it by calling the member function lock(), and unlock it with unlock(). However, direct calling of member functions is not recommended in practice, as it means you must remember to call unlock() at every function exit, including exception cases. The C++ standard library provides a RAII syntax template class std::lock_guard for mutexes, which provides a locked mutex when constructed and unlocks it when destructed, ensuring that a locked mutex is always correctly unlocked. The following program listing shows how to use a std::lock_guard instance constructed with std::mutex to protect access to a list in a multithreaded program. Both std::mutex and std::lock_guard are declared in the &lt;!-- raw HTML omitted --> header file.&lt;/p>
&lt;pre tabindex="0">&lt;code>#include &amp;lt;list&amp;gt;
#include &amp;lt;mutex&amp;gt;
#include &amp;lt;algorithm&amp;gt;
std::list&amp;lt;int&amp;gt; some_list; // 1
std::mutex some_mutex; // 2
void add_to_list(int new_value)
{
std::lock_guard&amp;lt;std::mutex&amp;gt; guard(some_mutex); // 3
some_list.push_back(new_value);
}
bool list_contains(int value_to_find)
{
std::lock_guard&amp;lt;std::mutex&amp;gt; guard(some_mutex); // 4
return std::find(some_list.begin(),some_list.end(),value_to_find) != some_list.end();
}
&lt;/code>&lt;/pre>&lt;p>Protection of data breaks when one of the member functions returns a pointer or reference to the protected data. Pointers or references with access capability can access (and possibly modify) the protected data without being restricted by the mutex. The design of interfaces that involve mutex-protected data requires careful consideration to ensure that the mutex can lock any access to the protected data, leaving no backdoors.&lt;/p>
&lt;h2 id="using-locks">Using Locks
&lt;/h2>&lt;p>Imagine a toy that consists of two parts, both of which must be obtained to play with it. For example, a toy drum requires a drumstick and a drum to play. Now there are two children who both like to play with this toy. When one child has both the drum and the drumstick, they can play freely. When the other child wants to play, they have to wait until the first child is finished. Now imagine that the drum and drumstick are kept in different toy boxes, and both children want to play the drum at the same time. So they go to the toy boxes looking for the drum. One finds the drum, and the other finds the drumstick. Now there&amp;rsquo;s a problem: unless one child decides to let the other play first by giving up their part, if they both hold firmly to their parts without giving them up, neither can play the drum.&lt;/p>
&lt;p>Fortunately, the C++ standard library has a way to solve this problem: std::lock—it can lock multiple (two or more) mutexes at once without side effects (risk of deadlock).&lt;/p>
&lt;p>How to avoid deadlocks?&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Avoid nested locks
The first suggestion is often the simplest: don&amp;rsquo;t acquire a second lock when a thread already has one. If you can stick to this advice, there won&amp;rsquo;t be deadlocks on locks because each thread only holds one lock.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Use a fixed order to acquire locks
When hard conditions require you to acquire two or more locks, and you can&amp;rsquo;t use std::lock to acquire them in a single operation; it&amp;rsquo;s best to acquire them in a fixed order on each thread.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="synchronization-and-waiting">Synchronization and Waiting
&lt;/h2>&lt;p>Imagine you&amp;rsquo;re traveling on a night train. How do you get off at the right station at night? One way is to stay awake all night and pay attention to which station you&amp;rsquo;re at. This way, you won&amp;rsquo;t miss your destination, but it will make you very tired. Alternatively, you can look at the timetable, estimate when the train will arrive at your destination, and set an alarm a little earlier, then you can sleep soundly. This method sounds good and doesn&amp;rsquo;t involve missing your station, but when the train is late, you&amp;rsquo;ll be woken up too early. Of course, the battery in your alarm clock might also die, causing you to sleep through your station. The ideal way is, regardless of early or late, to have someone or something wake you up exactly when the train arrives at the station.&lt;/p>
&lt;p>The C++ standard library has two implementations for condition variables: std::condition_variable and std::condition_variable_any. Both implementations are declared in the &amp;lt;condition_variable&amp;gt; header file. Both need to work with a mutex (for synchronization); the former is limited to working with std::mutex, while the latter can work with any mutex that meets minimum standards, hence the _any suffix. Because std::condition_variable_any is more general, there may be additional overhead in terms of size, performance, and system resource usage, so std::condition_variable is generally the preferred type, and we only consider std::condition_variable_any when there are hard requirements for flexibility.&lt;/p></description></item><item><title>Excerpts from Nowcoder Interview Experiences</title><link>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/%E7%89%9B%E5%AE%A2%E7%BD%91%E9%9D%A2%E7%BB%8F%E9%A2%98%E7%9B%AE%E6%91%98%E6%8A%84/</link><pubDate>Sat, 17 Mar 2018 06:42:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/%E7%89%9B%E5%AE%A2%E7%BD%91%E9%9D%A2%E7%BB%8F%E9%A2%98%E7%9B%AE%E6%91%98%E6%8A%84/</guid><description>&lt;h2 id="source-alibaba-cainiao-java-internship-phone-interview-experience-failed">Source: Alibaba Cainiao Java Internship Phone Interview Experience, Failed~!~
&lt;/h2>&lt;p>&lt;a class="link" href="https://www.nowcoder.com/discuss/67251?type=2&amp;amp;order=0&amp;amp;pos=7&amp;amp;page=1" target="_blank" rel="noopener"
>https://www.nowcoder.com/discuss/67251?type=2&amp;order=0&amp;pos=7&amp;page=1&lt;/a>&lt;/p>
&lt;p>Java-specific questions removed&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Is a user request handled by one thread per request on the server?
After searching, I found there are two models: blocking and non-blocking. Nowadays, technology based on I/O multiplexing is generally used, like the Reactor pattern, which only needs one thread or a small number of threads to handle a large number of requests.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Server pressure handling strategies
This is an even bigger question. After searching on Baidu, there are two points: caching and distributed systems. A netizen answered with message queues, which can alleviate situations with sudden large concurrent volumes.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Finding the top ten numbers among many numbers
Can use a variant of quicksort&lt;/p>
&lt;/li>
&lt;li>
&lt;p>How is the time complexity of heap sort calculated?
n×log n, which is n times, each time extracting from the heap bottom to top takes log n times&lt;/p>
&lt;/li>
&lt;li>
&lt;p>HTTP and TCP
Not sure what specifically was being asked, perhaps the relationship?&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Why are four steps needed to close a TCP connection (four-way handshake)?
Two FIN+ACK, with unsent data inserted in the middle. In situations with delayed ACK.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="source-netease-internal-referral-cc-development-offer-journey">Source: NetEase Internal Referral (C++/C Development) Offer Journey
&lt;/h2>&lt;p>&lt;a class="link" href="https://www.nowcoder.com/discuss/3038?type=2&amp;amp;order=0&amp;amp;pos=15&amp;amp;page=4" target="_blank" rel="noopener"
>https://www.nowcoder.com/discuss/3038?type=2&amp;order=0&amp;pos=15&amp;page=4&lt;/a>&lt;/p>
&lt;ul>
&lt;li>What forms of polymorphism exist in C++?
Polymorphism is one interface with multiple implementations. Static polymorphism and dynamic polymorphism. Static polymorphism is implemented through generic programming, dynamic polymorphism is implemented through virtual functions.&lt;/li>
&lt;li>How is dynamic binding implemented?
Static binding: compile-time binding, called through objects
Dynamic binding: runtime binding, implemented through addresses
Dynamic binding is implemented through virtual tables.&lt;/li>
&lt;li>What types of type conversions are there?
C-style conversions, explicit type casting, implicit type conversion. C++ has four types of type conversions: static_cast, const_cast, dynamic_cast, reinterpret_cast. They are used for built-in static type conversion (int, float), removing the const property of a type, dynamic type conversion (must be parent-child classes), and reinterpreting types without substantive conversion (pointer forced conversion), respectively.&lt;/li>
&lt;li>Memory alignment principles
The basic data type members of a structure must start aligned at integer multiples of the member size, structures as members must align at integer multiples of the widest data type. The size of a structure is aligned at integer multiples of its largest internal member.&lt;/li>
&lt;li>How are templates implemented?
The compiler remembers the template name when encountering a template declaration. When called, it generates specific type code based on the parameter types passed to the template, then compiles it.&lt;/li>
&lt;li>Uses of pointers and const?
Const type pointers, pointers to const type, const type pointers to const type.&lt;/li>
&lt;li>Virtual functions, pure virtual functions, virtual functions and destructors? Why should destructors be defined as virtual functions?
&lt;a class="link" href="http://liaoxl.github.io/blog/20131205/virtual-destructor/" target="_blank" rel="noopener"
>http://liaoxl.github.io/blog/20131205/virtual-destructor/&lt;/a>
According to this article, if defined as a virtual function and the child class also implements it, both the child class destructor and the parent class destructor will be called.&lt;/li>
&lt;li>Inline functions&lt;/li>
&lt;li>const and typedef&lt;/li>
&lt;li>What sorting algorithms are there? How is quicksort implemented? Best-case time complexity, average time complexity.
Insertion, bubble, heap sort, merge sort, bucket sort, shell sort, quicksort.&lt;/li>
&lt;li>What is extern &amp;ldquo;C&amp;rdquo; used for?
Mainly used to call programs written in C.&lt;/li>
&lt;/ul>
&lt;h2 id="2017-summer-internship-c-interview-experience-alibaba-netease-games-tencent-etc">2017 Summer Internship C++ Interview Experience (Alibaba, NetEase Games, Tencent, etc.)
&lt;/h2>&lt;p>Source: &lt;a class="link" href="https://www.nowcoder.com/discuss/23512?type=2&amp;amp;order=0&amp;amp;pos=20&amp;amp;page=2" target="_blank" rel="noopener"
>https://www.nowcoder.com/discuss/23512?type=2&amp;order=0&amp;pos=20&amp;page=2&lt;/a>&lt;/p>
&lt;ul>
&lt;li>How is map implemented? What are the differences between red-black trees and AVL trees? Why do data structures like map and set use red-black trees for implementation? Have you heard of skip-list?&lt;/li>
&lt;li>Have you used vector.reserve()? How does the vector array grow?&lt;/li>
&lt;li>Are you familiar with map-reduce?&lt;/li>
&lt;li>Are you familiar with Linux? Directory permissions, what does 644 represent? What does executable permission for a directory represent?&lt;/li>
&lt;li>Do you regularly use vi? Are you familiar with common Linux commands? (like awk, grep, etc.) Have you used gdb for debugging?&lt;/li>
&lt;li>What are the differences between TCP/UDP? How is congestion control implemented? How is reliable transmission implemented? Applications of UDP?
Reliable/Unreliable, Byte stream/Datagram, Connection-oriented/Connectionless, High overhead/Low overhead, With congestion control/Without congestion control, Full-duplex/Simplex
The purpose of congestion control is to prevent sending data packets beyond the network&amp;rsquo;s capacity, involving all hosts and routers. Congestion control assumes packet loss occurs when data packets exceed the network&amp;rsquo;s capacity, so when sending data packets, it needs to start slowly, gradually reaching the network&amp;rsquo;s capacity, increasing the congestion window exponentially to the slow start threshold, then entering congestion avoidance state where the congestion window grows linearly until congestion occurs. If a data packet&amp;rsquo;s ACK is not received before the timeout timer expires, it&amp;rsquo;s assumed that congestion has occurred, then the slow start threshold is set to half the current window size, and the slow start algorithm is executed. If three duplicate ACK packets are received, it&amp;rsquo;s assumed the lost packet hasn&amp;rsquo;t been received, so the unacknowledged packet is retransmitted early rather than waiting for the timeout timer to expire - this is fast retransmit. In case of fast retransmit, the slow start algorithm isn&amp;rsquo;t executed; instead, the congestion window is set to the slow start threshold, then congestion avoidance is executed - this is fast recovery.&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>Author: Simon_
Link: &lt;a class="link" href="https://www.nowcoder.com/discuss/23512?type=2&amp;amp;order=0&amp;amp;pos=20&amp;amp;page=2" target="_blank" rel="noopener"
>https://www.nowcoder.com/discuss/23512?type=2&amp;amp;order=0&amp;amp;pos=20&amp;amp;page=2&lt;/a>
Source: Nowcoder&lt;/p>
&lt;p>Summary:&lt;/p>
&lt;p>I think Alibaba focuses more on projects, distributed systems, and multithreading knowledge. NetEase Games seems to focus more on algorithms and C++. Tencent asks about everything, from C++ memory structure, RTTI, to network programming, to algorithm implementation for brain teasers. You can prepare better based on the company you&amp;rsquo;re applying to. Let&amp;rsquo;s work hard together.&lt;/p>
&lt;p>Books I read before preparing for internships include &amp;ldquo;C++ Primer&amp;rdquo;, &amp;ldquo;Inside the C++ Object Model&amp;rdquo;, &amp;ldquo;Effective C++&amp;rdquo;, &amp;ldquo;The Annotated STL Sources&amp;rdquo;, &amp;ldquo;Coding Interviews&amp;rdquo;, plus some books on networking and operating systems. I also solved the first 120 problems on LeetCode.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;strong>Some advice for juniors and sophomores looking for C++ development internships:&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;ol>
&lt;li>It&amp;rsquo;s best to go through C++ Primer several times before looking for internships. Make notes on things you don&amp;rsquo;t understand for future reference. When you read &amp;ldquo;The Object Model&amp;rdquo;, you&amp;rsquo;ll find that concepts you couldn&amp;rsquo;t understand in Primer will become clear. (For Primer study methods, I followed the first answer in &lt;a class="link" href="https://www.zhihu.com/question/32087709" target="_blank" rel="noopener"
>https://www.zhihu.com/question/32087709&lt;/a>)&lt;/li>
&lt;li>It&amp;rsquo;s also good to read &amp;ldquo;The Annotated STL Sources&amp;rdquo;. Interviewers love to ask about the underlying implementation or dynamic growth of vector, map, etc. It might be difficult to understand these concepts just by reading blogs and interview experiences.&lt;/li>
&lt;li>&amp;ldquo;Coding Interviews&amp;rdquo; covers algorithm questions for most companies&amp;rsquo; first interviews. I think it&amp;rsquo;s best to understand each problem. The first 100 problems on LeetCode are indeed very classic, and you can reinforce your knowledge of binary search, hash, dynamic programming, binary trees, etc. through practice.&lt;/li>
&lt;li>For networking, TCP/IP Illustrated Volume 1 is essential, and if you have time, you can also read Unix Network Programming. If you have even more time, it&amp;rsquo;s best to read through Chen Shuo&amp;rsquo;s &amp;ldquo;Linux Multithreaded Server Programming&amp;rdquo; to gain an understanding of multithreading.&lt;/li>
&lt;li>The final piece of advice is to prepare as early as possible. During the resume submission and interview period, it&amp;rsquo;s only suitable to organize Nowcoder interview experiences and your own experiences, filling in knowledge gaps. After all, interview questions are random, and to perform better in interviews, it&amp;rsquo;s best to accumulate knowledge regularly.&lt;/li>
&lt;/ol>
&lt;/blockquote>
&lt;h2 id="cc-backend-development-tencent-internship-spring-recruitment-first-interview-failed">C/C++ Backend Development Tencent Internship Spring Recruitment First Interview, Failed~
&lt;/h2>&lt;p>&lt;a class="link" href="https://www.nowcoder.com/discuss/68508?type=0&amp;amp;order=0&amp;amp;pos=8&amp;amp;page=0" target="_blank" rel="noopener"
>https://www.nowcoder.com/discuss/68508?type=0&amp;order=0&amp;pos=8&amp;page=0&lt;/a>&lt;/p>
&lt;ul>
&lt;li>The underlying implementation of vector. I talked a lot about this, so he asked what specific actions insert performs, what resize function calls, and expansion situations.&lt;/li>
&lt;li>Inter-process communication mechanisms
Pipe, Named pipe (FIFO)
Signal
Message queue
Shared memory
Semaphore
Socket&lt;/li>
&lt;/ul>
&lt;h2 id="alibaba-cainiao-network-second-interview">Alibaba Cainiao Network Second Interview
&lt;/h2>&lt;p>&lt;a class="link" href="http://blog.csdn.net/caishenfans/article/details/44496581" target="_blank" rel="noopener"
>http://blog.csdn.net/caishenfans/article/details/44496581&lt;/a>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Difference between fill and memset
fill is an STL algorithm that calls the copy constructor; for basic types, it should directly copy bit by bit. memset is a C algorithm that copies byte by byte to the position pointed to by the original pointer.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>C++ memory management methods
RAII stands for Resource Acquisition Is Initialization
It utilizes the guarantee that destructors of stack objects will be automatically called during stack unwinding to correctly release previously acquired resources. RAII only works properly if stack unwinding executes normally. Both function calls and normal C++ exception handling flow (exceptions within try-catch blocks) involve stack unwinding.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Implementation of smart pointers
When another smart pointer copies the current smart pointer, the reference counter increments by 1.
When assignment occurs between two smart pointers, the reference count of the object pointed to by the left pointer decrements by 1, and the right one increments by 1.
At destruction, the reference count similarly decrements by 1.
When the reference count reaches 0, the object is reclaimed.
A set of operators is redefined: -&amp;gt; pointer, returns a pointer to the original pointer. Dereference *T, returns the dereference of the original pointer.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="alibaba-internship-phone-interview-experience-over-an-hour">Alibaba Internship Phone Interview Experience, Over an Hour
&lt;/h2>&lt;p>&lt;a class="link" href="https://www.nowcoder.com/discuss/3836" target="_blank" rel="noopener"
>https://www.nowcoder.com/discuss/3836&lt;/a>&lt;/p>
&lt;ul>
&lt;li>What is a binary balanced tree, how to insert nodes, delete nodes, describe the key steps.&lt;/li>
&lt;/ul>
&lt;h2 id="how-students-can-get-an-alibaba-technical-offer-alibaba-internship-interview-experience-successful">How Students Can Get an Alibaba Technical Offer: &amp;ldquo;Alibaba Internship Interview Experience (Successful)&amp;rdquo;
&lt;/h2>&lt;p>&lt;a class="link" href="https://yq.aliyun.com/articles/6395" target="_blank" rel="noopener"
>https://yq.aliyun.com/articles/6395&lt;/a>&lt;/p>
&lt;h2 id="alibaba-c-development-internship-second-and-third-interview-experience-failed">Alibaba C++ Development Internship Second and Third Interview Experience (Failed)
&lt;/h2>&lt;p>&lt;a class="link" href="https://www.nowcoder.com/discuss/27801?type=2&amp;amp;order=3&amp;amp;pos=55&amp;amp;page=1" target="_blank" rel="noopener"
>https://www.nowcoder.com/discuss/27801?type=2&amp;order=3&amp;pos=55&amp;page=1&lt;/a>&lt;/p>
&lt;h2 id="internship-interview-summary">Internship Interview Summary
&lt;/h2>&lt;p>&lt;a class="link" href="https://www.nowcoder.com/discuss/25302?type=2&amp;amp;order=3&amp;amp;pos=190&amp;amp;page=1" target="_blank" rel="noopener"
>https://www.nowcoder.com/discuss/25302?type=2&amp;order=3&amp;pos=190&amp;page=1&lt;/a>&lt;/p>
&lt;h2 id="zhihu-and-nowcoder-summary">Zhihu and Nowcoder Summary
&lt;/h2>&lt;p>&lt;a class="link" href="http://link.zhihu.com/?target=https%3A//www.nowcoder.com/discuss/27801%3Ftype%3D2%26order%3D3%26pos%3D55%26page%3D1" target="_blank" rel="noopener"
>Alibaba C++ Development Internship Second and Third Interview Experience (Failed)&lt;/a>&lt;/p>
&lt;p>&lt;a class="link" href="http://link.zhihu.com/?target=https%3A//www.nowcoder.com/discuss/27739%3Ftype%3D2%26order%3D3%26pos%3D57%26page%3D2" target="_blank" rel="noopener"
>30 Interview Experiences for [Alibaba, Tencent, ByteDance] and 12 Other Companies in [C++ Direction]&lt;/a>&lt;/p>
&lt;p>&lt;a class="link" href="http://link.zhihu.com/?target=https%3A//www.nowcoder.com/discuss/27616%3Ftype%3D2%26order%3D3%26pos%3D61%26page%3D1" target="_blank" rel="noopener"
>Alibaba Campus Recruitment Internship First Interview Experience&lt;/a>&lt;/p>
&lt;p>&lt;a class="link" href="http://link.zhihu.com/?target=https%3A//www.nowcoder.com/discuss/27349%3Ftype%3D2%26order%3D3%26pos%3D71%26page%3D1" target="_blank" rel="noopener"
>Goodbye to Alibaba Autumn Recruitment&lt;/a>&lt;/p>
&lt;p>&lt;a class="link" href="http://link.zhihu.com/?target=https%3A//www.nowcoder.com/discuss/26107%3Ftype%3D2%26order%3D3%26pos%3D125%26page%3D1" target="_blank" rel="noopener"
>Baidu Three Interviews (Offer) &amp;amp;&amp;amp; Alibaba Four Interviews (In Progress)&lt;/a>&lt;/p>
&lt;p>&lt;a class="link" href="http://link.zhihu.com/?target=https%3A//www.nowcoder.com/discuss/25923%3Ftype%3D2%26order%3D3%26pos%3D143%26page%3D1" target="_blank" rel="noopener"
>Non-Key University Junior Spring Recruitment Internship Interview Experience&lt;/a>&lt;/p>
&lt;p>&lt;a class="link" href="http://link.zhihu.com/?target=https%3A//www.nowcoder.com/discuss/25302%3Ftype%3D2%26order%3D3%26pos%3D190%26page%3D1" target="_blank" rel="noopener"
>Internship Interview Summary&lt;/a>&lt;/p>
&lt;p>&lt;a class="link" href="http://link.zhihu.com/?target=https%3A//www.nowcoder.com/discuss/24900%3Ftype%3D2%26order%3D3%26pos%3D230%26page%3D1" target="_blank" rel="noopener"
>Alibaba, Morgan, Nomura, Tencent, Microsoft, TuDuck Technology Interview Experience Sharing&lt;/a>&lt;/p>
&lt;p>&lt;a class="link" href="http://link.zhihu.com/?target=http%3A//www.nowcoder.com/discuss/8343%3Ftype%3D0%26order%3D3%26pos%3D3950%26page%3D1%26from%3Dmnks201" target="_blank" rel="noopener"
>Alibaba Four Interview Experiences + Successfully Received Offer&lt;/a>&lt;/p>
&lt;p>&lt;a class="link" href="http://link.zhihu.com/?target=https%3A//www.nowcoder.com/discuss/18460%3Ftype%3D2%26order%3D3%26pos%3D57%26page%3D2%26from%3Dmnks201" target="_blank" rel="noopener"
>My 2016 Written Test and Interview Experience (For Next Year&amp;rsquo;s Juniors and Sophomores)&lt;/a>&lt;/p>
&lt;h2 id="alibaba-2017-internship-interview-sharing-three-technical-interviews--hr-interview">Alibaba 2017 Internship Interview Sharing (Three Technical Interviews + HR Interview)
&lt;/h2>&lt;p>&lt;a class="link" href="https://www.nowcoder.com/discuss/25446" target="_blank" rel="noopener"
>https://www.nowcoder.com/discuss/25446&lt;/a>&lt;/p></description></item><item><title>C++ new and delete</title><link>https://nansenli.com/post/jianshu/c++/c++-new-%E4%B8%8E-delete/</link><pubDate>Wed, 14 Mar 2018 03:52:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/c++/c++-new-%E4%B8%8E-delete/</guid><description>&lt;p>Source:&lt;/p>
&lt;p>&lt;a class="link" href="https://kelvinh.github.io/blog/2014/04/19/research-on-operator-new-and-delete/" target="_blank" rel="noopener"
>https://kelvinh.github.io/blog/2014/04/19/research-on-operator-new-and-delete/&lt;/a>&lt;/p>
&lt;h2 id="what-do-new-and-delete-code-mean">What do new and delete code mean
&lt;/h2>&lt;pre tabindex="0">&lt;code>Class *pc = new Class;
// ...
delete pc;
&lt;/code>&lt;/pre>&lt;p>The first line above is the new operator, and the third line is the delete operator. The code is simple, but for the compiler, it needs to do additional work, translating the above code into something approximate to the following code:&lt;/p>
&lt;pre tabindex="0">&lt;code>void *p = operator new(sizeof(Class));
// Call the constructor of Class on the memory pointed to by p, this can&amp;#39;t be shown with straightforward code
Class *pc = static_cast&amp;lt;Class*&amp;gt;(p);
// ...
pc-&amp;gt;~Class();
operator delete(pc);
&lt;/code>&lt;/pre>&lt;h2 id="what-does-the-operator-new-actually-do-and-what-does-the-operator-delete-do">What does the operator new actually do, and what does the operator delete do?
&lt;/h2>&lt;pre tabindex="0">&lt;code>void * operator new(std::size_t size) throw(std::bad_alloc) {
if (size == 0)
size = 1;
void* p;
while ((p = ::malloc(size)) == 0) {
std::new_handler nh = std::get_new_handler();
if (nh)
nh();
else
throw std::bad_alloc();
}
return p;
}
void operator delete(void* ptr) {
if (ptr)
::free(ptr);
}
&lt;/code>&lt;/pre>&lt;p>It&amp;rsquo;s actually a wrapper for malloc, but it allocates at least one byte, and in case of memory failure, it attempts to get the user-set new_handler. If the user has previously set a new_handler, then it will call the new_handler; otherwise, it will throw a bad_alloc exception.&lt;/p>
&lt;h2 id="what-is-placement-new">What is placement new
&lt;/h2>&lt;pre tabindex="0">&lt;code>inline _LIBCPP_INLINE_VISIBILITY void* operator new (std::size_t, void* __p) _NOEXCEPT {return __p;}
&lt;/code>&lt;/pre>&lt;p>This is an overload of operator new. It directly returns the value of the pointer. It seems quite useless. So how is it used?&lt;/p>
&lt;pre tabindex="0">&lt;code>void *buf = // Allocate memory for buf here
Class *pc = new (buf) Class();
&lt;/code>&lt;/pre>&lt;p>The above code assigns buf&amp;rsquo;s memory to the pc pointer. It&amp;rsquo;s actually equivalent to:&lt;/p>
&lt;pre tabindex="0">&lt;code>((* Class)buf)-&amp;gt;Class();
Class *pc = buf;
&lt;/code>&lt;/pre></description></item><item><title>Select, Poll, Epoll: A Brief Introduction</title><link>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/select-poll-epoll-%E7%AE%80%E6%98%8E%E4%BB%8B%E7%BB%8D/</link><pubDate>Mon, 12 Mar 2018 13:19:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/select-poll-epoll-%E7%AE%80%E6%98%8E%E4%BB%8B%E7%BB%8D/</guid><description>&lt;p>Source: &lt;a class="link" href="https://cloud.tencent.com/developer/article/1005481" target="_blank" rel="noopener"
>https://cloud.tencent.com/developer/article/1005481&lt;/a>&lt;/p>
&lt;h1 id="function-introduction">Function Introduction
&lt;/h1>&lt;h2 id="select">select
&lt;/h2>&lt;p>Function prototype:&lt;/p>
&lt;pre tabindex="0">&lt;code>int select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
&lt;/code>&lt;/pre>&lt;p>fd_set is a set containing file descriptors, represented as a bitmap with n bits. This means that every call involves copying the file descriptor set to the kernel.&lt;/p>
&lt;h2 id="poll">poll
&lt;/h2>&lt;p>Function prototype:&lt;/p>
&lt;pre tabindex="0">&lt;code>int poll (struct pollfd *fds, unsigned int nfds, int timeout);
&lt;/code>&lt;/pre>&lt;p>Unlike select which uses three bitmaps to represent three fdsets, poll uses an array of pollfd. It takes the length of this array and a timeout value as parameters.&lt;/p>
&lt;h2 id="epoll">epoll
&lt;/h2>&lt;p>epoll was introduced in the 2.6 kernel and is an enhanced version of the previous select and poll.&lt;/p>
&lt;pre tabindex="0">&lt;code>int epoll_create(int size); //creates an epoll handle, size tells the kernel how large this monitoring set will be
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
&lt;/code>&lt;/pre>&lt;p>epoll_create is used to create an epoll handle. The size parameter is a suggestion to the kernel about how many file descriptors to pre-allocate, but it doesn&amp;rsquo;t limit the actual number in operation.
epoll_ctl controls the epoll handle, allowing us to add, delete, or modify file descriptors. The separation of ctl from epoll_wait means we only need to copy the event set once.
epoll_wait is used to get the set of events received from the kernel.&lt;/p>
&lt;h1 id="socket-events">socket events
&lt;/h1>&lt;p>In the Linux 2.6 kernel events, a wakeup callback mechanism is set up. When a socket is waiting for an event to occur, it&amp;rsquo;s managed by the kernel&amp;rsquo;s socket sleep queue. When a socket event occurs, the kernel sequentially traverses each process on the socket sleep queue and notifies it of the event. During notification, it sequentially calls the callback function for that event.&lt;/p>
&lt;h2 id="the-original-select">The original select
&lt;/h2>&lt;p>select was initially just a simple implementation to try to solve the problem of checking multiple file descriptors.&lt;/p>
&lt;h3 id="what-does-select-do">What does select do?
&lt;/h3>&lt;p>After a select call, it does the following:&lt;/p>
&lt;ul>
&lt;li>Copies the file descriptor sets passed as parameters to kernel space&lt;/li>
&lt;li>Iterates through the file descriptors to check if there are any readable events; if there are, it returns&lt;/li>
&lt;li>If there are no readable file descriptors, it begins to sleep, waiting for kernel socket events to occur&lt;/li>
&lt;li>When awakened, it checks again to determine which file descriptor triggered the operation&lt;/li>
&lt;/ul>
&lt;h3 id="what-are-the-problems-with-select">What are the problems with select?
&lt;/h3>&lt;ul>
&lt;li>Each call to socket copies the data to kernel space, which is inefficient&lt;/li>
&lt;li>When any socket is awakened, all sockets need to be traversed, wasting time&lt;/li>
&lt;/ul>
&lt;h3 id="how-can-select-be-improved">How can select be improved?
&lt;/h3>&lt;ul>
&lt;li>The monitored fds set is limited to 1024, which is too small; we want a larger set of fds to monitor&lt;/li>
&lt;li>The fds set needs to be copied from user space to kernel space; we hope to avoid this copying&lt;/li>
&lt;li>When some of the monitored fds have readable data, we want more precise notifications; we want to get a list of fds with readable events from the notification, rather than having to traverse the entire fds set to collect them.&lt;/li>
&lt;/ul>
&lt;h2 id="the-lackluster-poll">The lackluster poll
&lt;/h2>&lt;p>poll only solves the first problem: the 1024 size limitation of fds. It&amp;rsquo;s merely a change in the interface of the parameters being passed.&lt;/p>
&lt;h2 id="the-mature-epoll">The mature epoll
&lt;/h2>&lt;p>epoll solves the second and third problems.&lt;/p>
&lt;ul>
&lt;li>
&lt;p>For the second problem&lt;/p>
&lt;ul>
&lt;li>Breaking down function calls and further subdividing them
For I/O multiplexing, we find that each call to select or poll repeatedly prepares (processes collectively) the entire set of fds that needs to be monitored. However, for frequently called select or poll, the frequency of changes in the fds set is much lower, so there&amp;rsquo;s no need to re-prepare (process collectively) the entire fds set each time.
So, epoll introduces the epoll_ctl system call to separate the high-frequency epoll_wait from the low-frequency epoll_ctl. At the same time, epoll_ctl uses three operations (EPOLL_CTL_ADD, EPOLL_CTL_MOD, EPOLL_CTL_DEL) to distribute modifications to the monitored fds set, ensuring changes only happen when necessary. This turns the high-frequency, large memory copy (collective processing) of select or poll into low-frequency, small memory copy (distributed processing) of epoll_ctl, avoiding a large amount of memory copying.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Using a red-black tree
Additionally, epoll uses epoll_ctl to add, delete, or modify the monitored fds set, which must involve fast fd lookup. Therefore, a data structure with low time complexity for adding, deleting, modifying, and querying is essential to organize the monitored fds set. In Linux kernel versions before 2.6.8, epoll used a hash to organize the fds set, so when creating an epoll fd, epoll needed to initialize the hash size. Hence, epoll_create(int size) had a parameter &amp;lsquo;size&amp;rsquo; to allow the kernel to allocate the hash size based on it. In Linux kernel versions 2.6.8 and later, epoll uses a red-black tree to organize the monitored fds set, so the &amp;lsquo;size&amp;rsquo; parameter in epoll_create(int size) is actually meaningless.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>For the third problem&lt;/p>
&lt;ul>
&lt;li>Using a callback mechanism
From the socket sleep queue wakeup logic above, we know that when a socket wakes up a wait_entry (process) sleeping in its sleep queue, it calls the wait_entry&amp;rsquo;s callback function, and we can do anything in this callback. To achieve traversal of only the ready fds, we need a place to organize those fds that are already ready. For this, epoll introduces an intermediate layer: a doubly linked list (ready_list), a separate sleep queue (single_epoll_wait_list). Unlike select or poll, epoll&amp;rsquo;s process doesn&amp;rsquo;t need to be inserted into all the sleep queues of the socket set for multiplexing. Instead, the process is only inserted into epoll&amp;rsquo;s separate sleep queue; the process sleeps on epoll&amp;rsquo;s separate queue, waiting for events to occur. At the same time, an intermediate wait_entry_sk is introduced, which is closely related to a specific socket sk. wait_entry_sk sleeps on the sk&amp;rsquo;s sleep queue, and its callback function logic is to put the current sk into epoll&amp;rsquo;s ready_list and wake up epoll&amp;rsquo;s single_epoll_wait_list. The callback function of the process sleeping on single_epoll_wait_list becomes clear: traverse all sks on the ready_list, call the poll function of each sk to collect events, and then wake up the process to return from epoll_wait.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="finally-edge-triggering-vs-level-triggering">Finally, Edge Triggering vs. Level Triggering
&lt;/h2>&lt;p>When discussing Epoll, we can&amp;rsquo;t ignore the two modes of Epoll events. Here are the basic concepts of the two modes:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Edge Triggered (ET)
.Read events are triggered when the state of the socket&amp;rsquo;s receive buffer changes, i.e., when the empty receive buffer just receives data
.Write events are triggered when the state of the socket&amp;rsquo;s send buffer changes, i.e., when the full buffer just frees up space
Events are only triggered when the buffer state changes, such as when the data buffer changes from empty to containing data (unreadable to readable)&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Level Triggered (LT)
.As long as the socket&amp;rsquo;s receive buffer is not empty and has data to read, the read event continues to trigger
.As long as the socket&amp;rsquo;s send buffer is not full and can continue to write data, the write event continues to trigger
This conforms to intuitive thinking; the events returned by epoll_wait represent the socket&amp;rsquo;s state&lt;/p>
&lt;/li>
&lt;/ul></description></item><item><title>Cainiao B2B First Interview Experience Record, C++ Backend Development</title><link>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/%E8%8F%9C%E9%B8%9Fb2b%E9%9D%A2%E8%AF%95%E4%B8%80%E9%9D%A2%E7%BB%8F%E5%8E%86%E8%AE%B0%E5%BD%95c++%E5%90%8E%E5%8F%B0%E5%BC%80%E5%8F%91/</link><pubDate>Fri, 09 Mar 2018 02:30:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/%E8%8F%9C%E9%B8%9Fb2b%E9%9D%A2%E8%AF%95%E4%B8%80%E9%9D%A2%E7%BB%8F%E5%8E%86%E8%AE%B0%E5%BD%95c++%E5%90%8E%E5%8F%B0%E5%BC%80%E5%8F%91/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>I&amp;rsquo;m a second-year master&amp;rsquo;s student, and my advisor doesn&amp;rsquo;t allow internships, but I interviewed for spring internships to gain interview experience.
My resume is very simple and modest, so the interviewer&amp;rsquo;s questions were relatively easy. The first interview was scheduled for 7 PM on March 8, 2018. Since I was doing an Alibaba test the night before, the interviewer said the interview would be postponed by one day, which gave me time to prepare. I especially reviewed basic knowledge again, which was very helpful.&lt;/p>
&lt;h2 id="self-introduction">Self-introduction
&lt;/h2>&lt;ul>
&lt;li>Omitted&lt;/li>
&lt;/ul>
&lt;h2 id="project-experience-and-its-impact-on-you">Project experience and its impact on you
&lt;/h2>&lt;ul>
&lt;li>Omitted&lt;/li>
&lt;/ul>
&lt;h2 id="what-are-the-four-layers-of-network-architecture">What are the four layers of network architecture?
&lt;/h2>&lt;ul>
&lt;li>I answered the physical layer which includes fiber optics, switches, and twisted pairs; the network layer with IP; the transport layer with TCP, ICMP, and UDP; and the application layer with HTTP and DNS.&lt;/li>
&lt;li>Standard answer: Actually, the TCP/IP standard does not define functionalities corresponding to the ISO data link layer and physical layer. Instead, it defines protocols like the Address Resolution Protocol (ARP), which provides interfaces between TCP/IP protocol data structures and actual physical hardware. So the first layer should be the link layer, which includes the ARP protocol and device drivers.&lt;/li>
&lt;/ul>
&lt;h2 id="which-layer-are-you-most-familiar-with">Which layer are you most familiar with?
&lt;/h2>&lt;ul>
&lt;li>I answered the transport layer with the TCP protocol.&lt;/li>
&lt;/ul>
&lt;h2 id="what-are-the-differences-between-tcp-and-udp">What are the differences between TCP and UDP?
&lt;/h2>&lt;ul>
&lt;li>Omitted&lt;/li>
&lt;/ul>
&lt;h2 id="three-way-handshake-and-four-way-termination">Three-way handshake and four-way termination?
&lt;/h2>&lt;ul>
&lt;li>Omitted&lt;/li>
&lt;/ul>
&lt;h2 id="why-are-you-familiar-with-this-process">Why are you familiar with this process?
&lt;/h2>&lt;ul>
&lt;li>I answered that I mainly learned it from books. Of course, I also used it in projects and observed the process by capturing packets with tcpdump and Wireshark.&lt;/li>
&lt;/ul>
&lt;h2 id="how-does-io-multiplexing-work">How does I/O multiplexing work?
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>I answered that it&amp;rsquo;s because multithreading is resource-intensive, etc. After listening for a while, the interviewer said that&amp;rsquo;s not what they meant; they wanted to know the implementation principle of I/O multiplexing. I replied that I had forgotten, but I remembered that in the implementation process of select, poll, and epoll, efficient data structures are used to check which file descriptor has I/O operations.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>The standard answer is:&lt;/p>
&lt;blockquote>
&lt;p>The efficiency of epoll lies in the fact that when we call epoll_ctl to insert millions of handles, epoll_wait can still return quickly and effectively give us the handles with events. This is because when we call epoll_create, the kernel not only creates a file node in the epoll file system for us and builds a red-black tree in the kernel cache to store the sockets passed by epoll_ctl later, but also establishes a list to store ready events. When epoll_wait is called, it only needs to observe whether there is data in this list. If there is data, it returns; if there is no data, it sleeps until it returns when the timeout time arrives, even if the list has no data. Therefore, epoll_wait is very efficient.
Another fatal weakness of traditional select/poll is that when you have a large socket collection, due to network latency, only part of the sockets are &amp;ldquo;active&amp;rdquo; at any time, but select/poll will linearly scan the entire collection for each call, causing efficiency to decrease linearly. However, epoll does not have this problem; it only operates on &amp;ldquo;active&amp;rdquo; sockets - this is because in the kernel implementation, epoll is implemented based on the callback function on each fd. Therefore, only &amp;ldquo;active&amp;rdquo; sockets will actively call the callback function, while other idle state sockets will not.&lt;/p>
&lt;/blockquote>
&lt;/li>
&lt;/ul>
&lt;h2 id="explain-polymorphism-in-c">Explain polymorphism in C++
&lt;/h2>&lt;ul>
&lt;li>Omitted&lt;/li>
&lt;/ul>
&lt;h2 id="what-is-a-destructor-and-what-is-its-purpose">What is a destructor and what is its purpose?
&lt;/h2>&lt;ul>
&lt;li>Omitted&lt;/li>
&lt;/ul>
&lt;h2 id="what-is-the-purpose-of-a-virtual-destructor">What is the purpose of a virtual destructor?
&lt;/h2>&lt;ul>
&lt;li>Omitted&lt;/li>
&lt;/ul>
&lt;h2 id="should-the-parent-classs-destructor-be-set-as-a-virtual-function">Should the parent class&amp;rsquo;s destructor be set as a virtual function?
&lt;/h2>&lt;ul>
&lt;li>Definitely yes&lt;/li>
&lt;/ul>
&lt;h2 id="what-is-the-difference-between-shallow-copy-and-deep-copy">What is the difference between shallow copy and deep copy?
&lt;/h2>&lt;ul>
&lt;li>Omitted&lt;/li>
&lt;/ul>
&lt;h2 id="if-a-function-accepts-an-object-as-a-parameter-is-that-a-shallow-copy-or-a-deep-copy">If a function accepts an object as a parameter, is that a shallow copy or a deep copy?
&lt;/h2>&lt;ul>
&lt;li>I answered that it depends on the copy constructor. But I wasn&amp;rsquo;t sure about the specifics.&lt;/li>
&lt;li>The standard answer should be: if references or pointers are used, no copying occurs. If the passed class hasn&amp;rsquo;t implemented a copy constructor, it&amp;rsquo;s a shallow copy. If it has implemented a copy constructor, it depends on whether the implemented copy constructor performs a shallow or deep copy.&lt;/li>
&lt;/ul>
&lt;h2 id="what-is-a-pointer-in-c-language">What is a pointer in C language?
&lt;/h2>&lt;ul>
&lt;li>I answered that it&amp;rsquo;s first a variable that stores the address of the internal data type or object it points to. Dereferencing it retrieves the data or object.&lt;/li>
&lt;li>In fact, a pointer is not necessarily a variable; there are also constant pointers, but generally, &amp;ldquo;pointer&amp;rdquo; is short for &amp;ldquo;pointer variable.&amp;rdquo; So the above expression doesn&amp;rsquo;t have any issues.&lt;/li>
&lt;/ul>
&lt;h2 id="what-is-the-difference-between-formal-parameters-and-actual-parameters">What is the difference between formal parameters and actual parameters?
&lt;/h2>&lt;ul>
&lt;li>Honestly, I wasn&amp;rsquo;t clear about this either. I said I didn&amp;rsquo;t know but explained pointers, references, and regular parameter passing.&lt;/li>
&lt;li>Later I found out that actual parameters (arguments) refer to the parameters passed in when an external function is called, and formal parameters are the parameters used when processing inside the function.&lt;/li>
&lt;/ul>
&lt;h2 id="introduce-the-simulated-annealing-algorithm">Introduce the simulated annealing algorithm
&lt;/h2>&lt;ul>
&lt;li>Because it was mentioned on my resume&lt;/li>
&lt;/ul>
&lt;h2 id="what-is-entropy-and-what-does-an-increase-represent">What is entropy, and what does an increase represent?
&lt;/h2>&lt;ul>
&lt;li>Omitted&lt;/li>
&lt;/ul>
&lt;h2 id="the-department-primarily-uses-java-development-would-you-be-resistant-if-asked-to-do-java-development">The department primarily uses Java development. Would you be resistant if asked to do Java development?
&lt;/h2>&lt;ul>
&lt;li>Omitted&lt;/li>
&lt;/ul>
&lt;h2 id="what-would-you-like-to-know-about-the-company">What would you like to know about the company?
&lt;/h2>&lt;ul>
&lt;li>Omitted&lt;/li>
&lt;/ul>
&lt;h2 id="aftermath">Aftermath
&lt;/h2>&lt;p>Total duration was 27 minutes. I spoke rather quickly and was a bit nervous and excited. That&amp;rsquo;s about it, and I&amp;rsquo;ll add more if I remember anything else.&lt;/p></description></item><item><title>HTTP Long and Short Connections</title><link>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/http%E9%95%BF%E8%BF%9E%E6%8E%A5%E4%B8%8E%E7%9F%AD%E8%BF%9E%E6%8E%A5/</link><pubDate>Wed, 07 Mar 2018 03:15:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/http%E9%95%BF%E8%BF%9E%E6%8E%A5%E4%B8%8E%E7%9F%AD%E8%BF%9E%E6%8E%A5/</guid><description>&lt;h2 id="the-relationship-between-http-protocol-and-tcpip-protocol">The Relationship Between HTTP Protocol and TCP/IP Protocol
&lt;/h2>&lt;p>HTTP long and short connections are essentially TCP long and short connections. HTTP is an application layer protocol that uses TCP protocol at the transport layer and IP protocol at the network layer. The IP protocol mainly solves network routing and addressing issues, while the TCP protocol mainly solves how to reliably deliver data packets over the IP layer, ensuring that all packets sent from one end are received at the other end of the network in the same order as they were sent. TCP has reliable, connection-oriented characteristics.&lt;/p>
&lt;h2 id="understanding-why-http-protocol-is-stateless">Understanding Why HTTP Protocol is Stateless
&lt;/h2>&lt;p>The HTTP protocol is stateless, meaning that the protocol has no memory capability for transaction processing, and the server doesn&amp;rsquo;t know what state the client is in. In other words, there is no connection between opening a webpage on a server and previously opening a webpage on that server. HTTP is a stateless connection-oriented protocol. Being stateless doesn&amp;rsquo;t mean that HTTP cannot maintain a TCP connection, and it certainly doesn&amp;rsquo;t mean that HTTP uses the UDP protocol (connectionless).&lt;/p>
&lt;h2 id="what-are-long-connections-and-short-connections">What are Long Connections and Short Connections?
&lt;/h2>&lt;p>In HTTP/1.0, short connections are used by default. This means that the browser and server establish a connection for each HTTP operation, but the connection is terminated once the task is completed. If a client browser accesses an HTML or other type of Web page that contains other Web resources such as JavaScript files, image files, CSS files, etc., the browser will establish an HTTP session each time it encounters such a Web resource. However, from HTTP/1.1 onwards, long connections are used by default to maintain connection characteristics. HTTP protocols using long connections will add this line of code to the response header: &lt;code>Connection:keep-alive&lt;/code>. In the case of using a long connection, when a webpage is fully opened, the TCP connection used to transmit HTTP data between the client and server will not close. If the client accesses the webpage on this server again, it will continue to use this already established connection. Keep-Alive will not permanently maintain the connection; it has a retention time that can be set in different server software (such as Apache). Both the client and server must support long connections to implement them. The long and short connections of the HTTP protocol are essentially the long and short connections of the TCP protocol.&lt;/p>
&lt;h2 id="tcp-connection">TCP Connection
&lt;/h2>&lt;p>When using TCP protocol for network communication, a connection must be established between the server and client before any actual read/write operations. When the read/write operations are completed and both parties no longer need this connection, they can release it. Establishing a connection requires a three-way handshake, while releasing it requires a four-way handshake, so establishing each connection consumes resources and time.&lt;/p>
&lt;h2 id="tcp-short-connection">TCP Short Connection
&lt;/h2>&lt;p>Let&amp;rsquo;s simulate a short connection scenario. The client initiates a connection to the server, the server accepts the client connection, and both parties establish a connection. After the client and server complete a read/write operation, their connection is actively closed.&lt;/p>
&lt;h2 id="tcp-long-connection">TCP Long Connection
&lt;/h2>&lt;p>Now let&amp;rsquo;s simulate a long connection scenario. The client initiates a connection to the server, the server accepts the client connection, and both parties establish a connection. After the client and server complete a read/write operation, their connection is not actively closed, and subsequent read/write operations will continue to use this connection.&lt;/p>
&lt;p>Let&amp;rsquo;s first talk about the TCP keepalive function mentioned in TCP/IP in Detail. The keepalive function is mainly provided for server applications. Server applications want to know whether the client host has crashed, so they can use resources on behalf of the client. If the client has disappeared, leaving a half-open connection on the server, and the server is waiting for data from the client, the server will wait indefinitely for client data. The keepalive function attempts to detect such half-open connections on the server side.&lt;/p>
&lt;p>If a given connection has no activity for two hours, the server sends a probe segment to the client. The client host must be in one of the following four states:&lt;/p>
&lt;ol>
&lt;li>The client host is still running normally and is reachable from the server. The client&amp;rsquo;s TCP responds normally, and the server knows that the other party is normal. The server resets the keepalive timer after two hours.&lt;/li>
&lt;li>The client host has crashed and is either shut down or restarting. In either case, the client&amp;rsquo;s TCP does not respond. The server will not receive a response to the probe and will time out after 75 seconds. The server sends a total of 10 such probes, each at an interval of 75 seconds. If the server does not receive a response, it assumes that the client host is closed and terminates the connection.&lt;/li>
&lt;li>The client host has crashed and has restarted. The server will receive a response to its keepalive probe, which is a reset, causing the server to terminate the connection.&lt;/li>
&lt;li>The client is running normally, but the server is unreachable. This situation is similar to category 2; TCP can only discover that it has not received a response to the probe.&lt;/li>
&lt;/ol>
&lt;h2 id="how-to-choose-between-long-and-short-connections">How to Choose Between Long and Short Connections
&lt;/h2>&lt;p>Long connections are often used for frequent operations, point-to-point communications, and situations where the number of connections cannot be too many. Each TCP connection requires a three-way handshake, which takes time. If each operation first establishes a connection and then operates, processing speed will be reduced significantly. So, if the connection is not disconnected after each operation, subsequent processing can directly send data packets without establishing a TCP connection. For example, database connections use long connections. Using short connections for frequent communication will cause socket errors, and frequent socket creation is also a waste of resources.&lt;/p>
&lt;p>Web services like HTTP websites generally use short connections because long connections consume certain resources on the server side. For websites with such frequent connections from thousands or even millions of clients, using short connections saves resources. If long connections were used, and there were thousands of users at the same time, each user occupying one connection would be unimaginable. Therefore, short connections are better for situations with high concurrency but where each user does not need frequent operations.&lt;/p></description></item><item><title>MySQL Study Notes</title><link>https://nansenli.com/post/jianshu/%E6%95%B0%E6%8D%AE%E5%BA%93/mysql%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</link><pubDate>Mon, 05 Mar 2018 07:04:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E6%95%B0%E6%8D%AE%E5%BA%93/mysql%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>I&amp;rsquo;m using Docker for learning. I&amp;rsquo;ve configured the docker-cn mirror.&lt;/p>
&lt;h2 id="setting-up-a-mysql-cluster">Setting Up a MySQL Cluster
&lt;/h2>&lt;p>Use the following command to pull the MySQL image:&lt;/p>
&lt;pre>&lt;code>docker pull mysql
&lt;/code>&lt;/pre>
&lt;p>Use the following commands to set up one master and one slave:&lt;/p>
&lt;pre tabindex="0">&lt;code>docker run --name mysql-master -P -d -e MYSQL_ROOT_PASSWORD=ln mysql
docker run --name mysql-slave1 -P -d -e MYSQL_ROOT_PASSWORD=ln mysql
&lt;/code>&lt;/pre>&lt;p>Where -d configures it as a daemon process, -P exposes all ports, and the root password is set to &amp;ldquo;ln&amp;rdquo;.&lt;/p>
&lt;p>Use the following commands to enter the two Docker containers:&lt;/p>
&lt;pre>&lt;code>docker exec -it mysql-master bash
docker exec -it mysql-slave1 bash
&lt;/code>&lt;/pre>
&lt;p>Check the network IP. First, we need to install net-tools, which requires an update, and before updating, we need to change the source:&lt;/p>
&lt;pre tabindex="0">&lt;code>sed -i &amp;#39;s/deb.debian.org/mirrors.ustc.edu.cn/g&amp;#39; /etc/apt/sources.list
apt update
apt install net-tools
&lt;/code>&lt;/pre>&lt;p>Enter ifconfig to check the IP address.
The master is 172.17.0.2, and slave1 is 172.17.0.3.&lt;/p>
&lt;p>Set login permissions
In the MySQL master, follow these steps. First login, then set the root user to be accessible without a password from anywhere.&lt;/p>
&lt;pre tabindex="0">&lt;code>mysql -uroot -p
mysql&amp;gt; grant all privileges on *.* to root@&amp;#39;%&amp;#39; identified by &amp;#39;&amp;#39;;
Query OK, 0 rows affected (0.02 sec)
mysql&amp;gt; flush privileges;
Query OK, 0 rows affected (0.02 sec)
&lt;/code>&lt;/pre>&lt;p>In slave1, use the master&amp;rsquo;s database with an empty password:&lt;/p>
&lt;pre>&lt;code>mysql -uroot -p -h 172.17.0.2
&lt;/code>&lt;/pre>
&lt;p>Create a database in the master:&lt;/p>
&lt;pre>&lt;code>create database test_docker;
&lt;/code>&lt;/pre>
&lt;p>Check if the database exists in the slave:&lt;/p>
&lt;pre>&lt;code>show databases;
&lt;/code>&lt;/pre>
&lt;h2 id="miscellaneous">Miscellaneous
&lt;/h2>&lt;p>SQL is case-insensitive; SELECT and select are the same.
It&amp;rsquo;s better to add a semicolon at the end of SQL statements.
Use &amp;ldquo;use&amp;rdquo; to select a database. Use &amp;ldquo;set&amp;rdquo; to set values.
The method to add users is with &amp;ldquo;create&amp;rdquo;.
Then grant permissions to users for databases and tables.&lt;/p>
&lt;pre tabindex="0">&lt;code>mysql -u root -p
Enter password:**
mysql&amp;gt; use mysql;
mysql&amp;gt; create user &amp;#39;linanwx&amp;#39;@&amp;#39;%&amp;#39; identified by &amp;#39;ln&amp;#39;;
mysql&amp;gt; grant all on *.* to &amp;#39;linanwx&amp;#39;@&amp;#39;%&amp;#39;;
&lt;/code>&lt;/pre>&lt;p>SHOW DATABASES is used to display the current databases.
SHOW TABLES is used to display the current tables.&lt;/p></description></item><item><title>Nowcoder Database Notes</title><link>https://nansenli.com/post/jianshu/%E6%95%B0%E6%8D%AE%E5%BA%93/%E7%89%9B%E5%AE%A2%E6%95%B0%E6%8D%AE%E5%BA%93%E7%AC%94%E8%AE%B0/</link><pubDate>Mon, 05 Mar 2018 07:04:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E6%95%B0%E6%8D%AE%E5%BA%93/%E7%89%9B%E5%AE%A2%E6%95%B0%E6%8D%AE%E5%BA%93%E7%AC%94%E8%AE%B0/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>I&amp;rsquo;m not familiar with SQL but don&amp;rsquo;t want to spend time learning it. So let&amp;rsquo;s learn through practice.&lt;/p>
&lt;h2 id="transactions">Transactions
&lt;/h2>&lt;p>A transaction is the basic unit of concurrency control. A transaction is a sequence of operations that must either all be executed or none at all; it&amp;rsquo;s an indivisible work unit. For example, in bank transfers: debiting one account and crediting another account are two operations that must either both be executed or neither. Therefore, they should be considered as one transaction. Transactions are the unit by which databases maintain data consistency, ensuring data consistency at the end of each transaction.&lt;/p>
&lt;p>In simple terms, a transaction is an atomic operation that cannot be divided.&lt;/p>
&lt;h2 id="views">Views
&lt;/h2>&lt;p>A view is a virtual table that doesn&amp;rsquo;t actually exist. It&amp;rsquo;s just a partial table extracted from the database to make it convenient for certain people to see. Views are defined by select queries—when you create a view, you&amp;rsquo;re actually executing a select statement in the database.&lt;/p>
&lt;h2 id="instead-of-triggers">INSTEAD OF Triggers
&lt;/h2>&lt;ul>
&lt;li>Definition of Triggers
Triggers are actually a special type of stored procedure. The main function of triggers is to monitor user modifications to data. If a trigger is built for a data table, then when the data table is modified (insert, update, or delete), SQL Server automatically executes the trigger to take appropriate measures for the data modification, thereby ensuring data integrity and security.&lt;/li>
&lt;/ul>
&lt;p>INSTEAD OF triggers specify that the trigger is executed instead of the triggering SQL statement, thus replacing the operation of the triggering statement.&lt;/p>
&lt;h2 id="stored-procedures">Stored Procedures
&lt;/h2>&lt;p>A stored procedure is a group of SQL statements in a database designed to accomplish a specific function. It&amp;rsquo;s stored in the database and doesn&amp;rsquo;t need to be recompiled when called again after the first compilation. Users execute it by specifying the name of the stored procedure and providing parameters (if the stored procedure has parameters). Stored procedures are important objects in databases.&lt;/p>
&lt;h2 id="sql-relational-algebra">SQL Relational Algebra
&lt;/h2>&lt;p>&lt;strong>Five Basic Operations:&lt;/strong>&lt;/p>
&lt;pre>&lt;code>Union (∪), Difference (-), Cartesian Product (×), Projection (σ), Selection (π)
&lt;/code>&lt;/pre>
&lt;p>&lt;strong>Four Combined Operations:&lt;/strong>&lt;/p>
&lt;pre>&lt;code>Intersection (∩), Join (equijoin), Natural Join, Division (÷)
&lt;/code>&lt;/pre>
&lt;p>Union includes records that are in both tables. Difference includes records that are in the first table but not in the second. Cartesian product is all one-to-one combinations of records from both tables. Projection is vertical selection, while selection is horizontal selection. Intersection is similar to union, and join connects through common attributes. Division is a complex operation.&lt;/p>
&lt;h2 id="relational-schema">Relational Schema
&lt;/h2>&lt;p>A relational schema is a description of a relation. As the real world changes over time, the relation in a relational schema will also change. However, many existing facts in the real world dictate that all possible relations in a relational schema must satisfy certain integrity constraints. These constraints are either expressed through limitations on the &lt;a class="link" href="https://baike.baidu.com/item/%E5%8F%96%E5%80%BC%E8%8C%83%E5%9B%B4" target="_blank" rel="noopener"
>value range&lt;/a> of attributes, such as employee age less than 65 (mandatory retirement at 65), or through the interconnection between &lt;a class="link" href="https://baike.baidu.com/item/%E5%B1%9E%E6%80%A7%E5%80%BC" target="_blank" rel="noopener"
>attribute values&lt;/a> (mainly reflected in whether values are equal). The relational schema should capture these integrity constraints.&lt;/p>
&lt;h2 id="non-relational-databases">Non-relational Databases
&lt;/h2>&lt;p>NoSQL (NoSQL = Not Only SQL) means &amp;ldquo;not just SQL.&amp;rdquo; It&amp;rsquo;s used for storing ultra-large-scale data.&lt;/p></description></item><item><title>C++ Smart Pointers Learning</title><link>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/c++-%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88%E5%AD%A6%E4%B9%A0/</link><pubDate>Sat, 03 Feb 2018 09:30:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/c++-%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88%E5%AD%A6%E4%B9%A0/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>Smart pointers seem to be used more and more widely. It&amp;rsquo;s necessary to remember them well.&lt;/p>
&lt;h2 id="shared_ptr">shared_ptr
&lt;/h2>&lt;p>shared_ptr allows multiple pointers to point to the same object, while unique_ptr exclusively owns the object it points to.&lt;/p>
&lt;p>Example:&lt;/p>
&lt;pre tabindex="0">&lt;code>#include &amp;lt;memory&amp;gt;
#include &amp;lt;thread&amp;gt;
class A
{
char * str;
public:
A(){
str = new char[20];
printf(&amp;#34;Constructor\n&amp;#34;);
}
~A()
{
printf(&amp;#34;Destructor\n&amp;#34;);
delete [] str;
}
};
std::shared_ptr&amp;lt;A&amp;gt; xixi;
void test()
{
std::shared_ptr&amp;lt;A&amp;gt; shared_a = std::make_shared&amp;lt;A&amp;gt;();
xixi = shared_a;
printf(&amp;#34;%d\n&amp;#34;, xixi.use_count());
}
int main()
{
test();
std::this_thread::sleep_for(std::chrono::seconds(1));
}
&lt;/code>&lt;/pre>&lt;p>In the example above, xixi is a global smart pointer that will only release the object it points to when the program ends. If we remove &lt;code>xixi = shared_a&lt;/code>, we would see the destructor being called before the delay in the main function.&lt;/p>
&lt;h2 id="unique_ptr">unique_ptr
&lt;/h2>&lt;p>unique_ptr exclusively owns an object and can only be moved (std::move) between different unique_ptrs. There is no make_unique statement. You can only construct a unique_ptr by passing a pointer to an object in the constructor.&lt;/p>
&lt;p>Use cases for unique_ptr:&lt;/p>
&lt;ol>
&lt;li>Providing exception safety guarantees for dynamically allocated resources
Let&amp;rsquo;s look at the following code:&lt;/li>
&lt;/ol>
&lt;pre tabindex="0">&lt;code>void Func()
{
int *p = new int(5);
// ...（may throw an exception）
delete p;
}
&lt;/code>&lt;/pre>&lt;p>This is our traditional approach: after we dynamically allocate memory, it&amp;rsquo;s possible that our subsequent code might not execute the delete operation due to an exception being thrown or early exit (if statement).&lt;/p>
&lt;p>The solution is to use unique_ptr to manage dynamic memory. As long as the unique_ptr is successfully created, its destructor will be called, ensuring that dynamic resources are released.&lt;/p>
&lt;pre tabindex="0">&lt;code>void Func()
{
unique_ptr&amp;lt;int&amp;gt; p(new int(5));
// ...（may throw an exception）
}
&lt;/code>&lt;/pre>&lt;h2 id="weak_ptr">weak_ptr
&lt;/h2>&lt;p>weak_ptr is also a powerful pointer, despite its name suggesting weakness. weak_ptr doesn&amp;rsquo;t control the lifecycle of an object and is used when you need to use an object without owning it. weak_ptr can be promoted to a shared_ptr if the object still exists.&lt;/p></description></item><item><title>Cross-compiling libcurl Dynamic Library for ARM</title><link>https://nansenli.com/post/jianshu/%E5%B5%8C%E5%85%A5%E5%BC%8F/libcurl%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%91arm%E7%89%88%E6%9C%AC%E5%8A%A8%E6%80%81%E5%BA%93/</link><pubDate>Tue, 30 Jan 2018 10:53:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E5%B5%8C%E5%85%A5%E5%BC%8F/libcurl%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%91arm%E7%89%88%E6%9C%AC%E5%8A%A8%E6%80%81%E5%BA%93/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>Recently, due to project requirements, I needed to cross-compile libcurl to enable HTTPS communication. Here I&amp;rsquo;ll document the process.&lt;/p>
&lt;p>First, download the latest version of the source code and extract it:&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/e2f1c84ddaa0.png"
loading="lazy"
alt="image.png"
>&lt;/p>
&lt;p>I used the cross-compilation toolchain that comes with Deepin Linux. Just install it with &lt;code>sudo apt install gcc-5-arm-linux-gnueabihf&lt;/code>.&lt;/p>
&lt;p>arm-linux contents:&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/69a2e0029a46.png"
loading="lazy"
alt="image.png"
>&lt;/p>
&lt;p>In the openssl directory, run the following commands:&lt;/p>
&lt;pre tabindex="0">&lt;code>source ../arm-linux
./Configure --prefix=$(pwd)/../output shared no-asm linux-armv4
make -j4
make install
&lt;/code>&lt;/pre>&lt;p>First, import the environment variables, then compile with the output path set to the &amp;ldquo;output&amp;rdquo; directory.&lt;/p>
&lt;p>Then in the libcurl directory, run the following commands:&lt;/p>
&lt;pre tabindex="0">&lt;code>source ../arm-linux
./configure --with-ssl=$(pwd)/../output --prefix=$PWD/../output --build=x86_64-pc-linux-gnu --host=arm-linux-gnueabihf CC=arm-linux-gnueabihf-gcc-5 --without-zlib
&lt;/code>&lt;/pre>&lt;p>Here&amp;rsquo;s a summary of the curl configuration:&lt;/p>
&lt;pre tabindex="0">&lt;code> curl version: 7.58.0
Host setup: x86_64-pc-linux-gnu
Install prefix: /media/linan/72605383-b1ba-4c79-90fd-443514396fbc/curl_workdir/curl-7.58.0/output
Compiler: arm-linux-gnueabihf-gcc-5
SSL support: enabled (OpenSSL)
SSH support: no (--with-libssh2)
zlib support: no (--with-zlib)
brotli support: no (--with-brotli)
GSS-API support: no (--with-gssapi)
TLS-SRP support: enabled
resolver: POSIX threaded
IPv6 support: enabled
Unix sockets support: enabled
IDN support: no (--with-{libidn2,winidn})
Build libcurl: Shared=yes, Static=yes
Built-in manual: enabled
--libcurl option: enabled (--disable-libcurl-option)
Verbose errors: enabled (--disable-verbose)
SSPI support: no (--enable-sspi)
ca cert bundle: no
ca cert path: no
ca fallback: no
LDAP support: no (--enable-ldap / --with-ldap-lib / --with-lber-lib)
LDAPS support: no (--enable-ldaps)
RTSP support: enabled
RTMP support: no (--with-librtmp)
metalink support: no (--with-libmetalink)
PSL support: no (libpsl not found)
HTTP2 support: disabled (--with-nghttp2)
Protocols: DICT FILE FTP FTPS GOPHER HTTP HTTPS IMAP IMAPS POP3 POP3S RTSP SMB SMBS SMTP SMTPS TELNET TFTP
&lt;/code>&lt;/pre>&lt;p>Then run &lt;code>make&lt;/code> and &lt;code>make install&lt;/code>. An &amp;ldquo;output&amp;rdquo; directory will appear in the curl directory.&lt;/p>
&lt;p>Finally, what we need are the dynamic libraries from openssl and curl: libssl.so, libcrypto.so, and libcurl.so.&lt;/p></description></item><item><title>Algorithm Problem — Shooting Game</title><link>https://nansenli.com/post/jianshu/%E7%AE%97%E6%B3%95/%E7%AE%97%E6%B3%95%E9%A2%98%E7%9B%AE%E5%B0%84%E5%87%BB%E6%B8%B8%E6%88%8F/</link><pubDate>Tue, 23 Jan 2018 01:39:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E7%AE%97%E6%B3%95/%E7%AE%97%E6%B3%95%E9%A2%98%E7%9B%AE%E5%B0%84%E5%87%BB%E6%B8%B8%E6%88%8F/</guid><description>&lt;p>Link: &lt;a class="link" href="https://www.nowcoder.com/questionTerminal/d3f26db0325444078717cc802e0056d8" target="_blank" rel="noopener"
>https://www.nowcoder.com/questionTerminal/d3f26db0325444078717cc802e0056d8&lt;/a>
Source: NowCoder&lt;/p>
&lt;p>Xiayi is playing a new shooting game that takes place on a two-dimensional plane. Xiayi is at the origin (0,0), and there are n monsters on the plane, each with coordinates (x[i], y[i]). With one shot, Xiayi can eliminate all monsters that are on the x-axis and y-axis (including the origin) at once.
Xiayi is a VIP player in this game and has two special privilege operations:&lt;/p>
&lt;ol>
&lt;li>Make all monsters on the plane move simultaneously in any same direction for any same distance&lt;/li>
&lt;li>Make all monsters on the plane rotate around Xiayi (0,0) by any same angle simultaneously
Xiayi wants to take a shot. Before shooting, he can use these two privilege operations any number of times.&lt;/li>
&lt;/ol>
&lt;p>Xiayi wants to know the maximum number of monsters he can eliminate at once when he shoots. Please help Xiayi.&lt;/p>
&lt;p>As shown in the example:&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/ee548e34f160.webp"
loading="lazy"
alt="image"
>&lt;/p>
&lt;p>All points can be rotated clockwise or counterclockwise by 45° with respect to the origin (0,0), which places all points on the coordinate axes, so all 5 monsters can be eliminated.&lt;/p>
&lt;h5 id="input-description">&lt;strong>Input Description:&lt;/strong>
&lt;/h5>&lt;p>The input consists of three lines.
The first line contains a positive integer n (1 ≤ n ≤ 50), representing the number of monsters on the plane.
The second line includes n integers x[i] (-1,000,000 ≤ x[i] ≤ 1,000,000), representing the x-coordinate of each monster, separated by spaces.
The third line includes n integers y[i] (-1,000,000 ≤ y[i] ≤ 1,000,000), representing the y-coordinate of each monster, separated by spaces.&lt;/p>
&lt;h5 id="output-description">&lt;strong>Output Description:&lt;/strong>
&lt;/h5>&lt;p>Output an integer representing the maximum number of monsters Xiayi can eliminate.&lt;/p>
&lt;p>Example 1&lt;/p>
&lt;h2 id="input">Input
&lt;/h2>&lt;p>5
0 -1 1 1 -1
0 -1 -1 1 1&lt;/p>
&lt;h2 id="output">Output
&lt;/h2>&lt;p>5&lt;/p>
&lt;h2 id="analysis">Analysis
&lt;/h2>&lt;p>The problem is equivalent to finding a cross shape that covers as many points as possible.
Considering that one line can cover at least two points, and adding a perpendicular line can cover at least 3 points, we can iterate based on this. For any three points, we select two of them to form a line (three possible combinations), and for the third point, we create a perpendicular line to this line. This cross shape already passes through three points. For the remaining points, we check if they are on this cross shape. To determine if a point is on the cross shape, first check if it&amp;rsquo;s on the same line as the first line. Otherwise, determine if the line formed by this point and the third point is perpendicular to the second line.
When there are three or fewer points, we can cover all of them.&lt;/p>
&lt;pre tabindex="0">&lt;code>#include &amp;lt;vector&amp;gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;
struct point{
int x = 0;
int y = 0;
};
bool is_sameline(point p1, point p2, point p3){
return ((p1.x - p2.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p2.y)) == 0;
}
bool is_vertical(point p1, point p2){
return (p1.x * p2.x + p1.y * p2.y) == 0;
}
bool is_vertical(point p1, point p2, point p3, point p4){
point v1, v2;
v1.x = p1.x - p2.x;
v1.y = p1.y - p2.y;
v2.x = p3.x - p4.x;
v2.y = p3.y - p4.y;
return is_vertical(v1, v2);
}
int main()
{
int n, ret = 0;
cin &amp;gt;&amp;gt; n;
point inputs[n];
for (int i = 0; i &amp;lt; n; i++)
cin &amp;gt;&amp;gt; inputs[i].x;
for (int i = 0; i &amp;lt; n; i++)
cin &amp;gt;&amp;gt; inputs[i].y;
if (n &amp;lt; 4)
{
cout &amp;lt;&amp;lt; n &amp;lt;&amp;lt; endl;
return 0;
};
vector&amp;lt;int&amp;gt; select = {1, 1, 1};
for (int i = 0; i &amp;lt; n - 3; i++)
select.push_back(0);
do
{
vector&amp;lt;point&amp;gt; shizi;
for (int i = 0; i &amp;lt; n; i++)
{
if (select[i])
{
shizi.push_back(inputs[i]);
}
}
vector&amp;lt;vector&amp;lt;point&amp;gt;&amp;gt; status;
status.push_back({shizi[0], shizi[1], shizi[2]});
status.push_back({shizi[0], shizi[2], shizi[1]});
status.push_back({shizi[1], shizi[2], shizi[0]});
for (auto points : status)
{
int count = 0;
for (int i = 0; i &amp;lt; n; i++)
{
if (!select[i])
{
if (is_sameline(points[0], points[1], inputs[i]))
count++;
if (is_vertical(points[0], points[1], points[2], inputs[i]))
count++;
}
}
ret = max(ret, count);
}
} while (prev_permutation(select.begin(), select.end()));
cout &amp;lt;&amp;lt; ret + 3 &amp;lt;&amp;lt; endl;
return 0;
}
&lt;/code>&lt;/pre></description></item><item><title>Algorithm Problem — Longest Common Bracket Sequence</title><link>https://nansenli.com/post/jianshu/%E7%AE%97%E6%B3%95/%E7%AE%97%E6%B3%95%E9%A2%98%E7%9B%AE%E6%9C%80%E9%95%BF%E5%85%AC%E5%85%B1%E5%AD%90%E6%8B%AC%E5%8F%B7%E5%BA%8F%E5%88%97/</link><pubDate>Sun, 21 Jan 2018 14:05:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E7%AE%97%E6%B3%95/%E7%AE%97%E6%B3%95%E9%A2%98%E7%9B%AE%E6%9C%80%E9%95%BF%E5%85%AC%E5%85%B1%E5%AD%90%E6%8B%AC%E5%8F%B7%E5%BA%8F%E5%88%97/</guid><description>&lt;p>Link: &lt;a class="link" href="https://www.nowcoder.com/questionTerminal/504ad6420b314e5bb614e1684ad46d4d" target="_blank" rel="noopener"
>https://www.nowcoder.com/questionTerminal/504ad6420b314e5bb614e1684ad46d4d&lt;/a>
Source: NowCoder&lt;/p>
&lt;p>A valid bracket matching sequence is defined as:&lt;/p>
&lt;ol>
&lt;li>An empty string &amp;quot;&amp;quot; is a valid bracket sequence&lt;/li>
&lt;li>If &amp;ldquo;X&amp;rdquo; and &amp;ldquo;Y&amp;rdquo; are valid sequences, then &amp;ldquo;XY&amp;rdquo; is also a valid bracket sequence&lt;/li>
&lt;li>If &amp;ldquo;X&amp;rdquo; is a valid sequence, then &amp;ldquo;(X)&amp;rdquo; is also a valid bracket sequence&lt;/li>
&lt;li>Every valid bracket sequence can be generated using the above rules
For example, &amp;ldquo;&amp;rdquo;, &amp;ldquo;()&amp;rdquo;, &amp;ldquo;()()()&amp;rdquo;, &amp;ldquo;(()())&amp;rdquo;, &amp;ldquo;(((())))&amp;rdquo; are all valid.
A sequence obtained by removing zero or more characters from a string S is called a subsequence of S.
For example, the subsequences of &amp;ldquo;abcde&amp;rdquo; include &amp;ldquo;abe&amp;rdquo;, &amp;ldquo;&amp;rdquo;, &amp;ldquo;abcde&amp;rdquo;, etc.
Define LCS(S,T) as the length of the longest common subsequence between string S and string T, which is the length of the longest sequence W that is both a subsequence of S and a subsequence of T.
Xiaoyi gives you a valid bracket matching sequence s, and he hopes you can find a bracket sequence t with the following characteristics:&lt;/li>
&lt;li>t is different from s but has the same length&lt;/li>
&lt;li>t is also a valid bracket matching sequence&lt;/li>
&lt;li>LCS(s, t) is the largest among all t that satisfy the above two conditions
Since there may be multiple such t, Xiaoyi needs you to calculate how many such t exist.&lt;/li>
&lt;/ol>
&lt;p>As shown in the example: s = &amp;ldquo;(())()&amp;rdquo;, valid bracket matching sequences with the same length as s include:
&amp;ldquo;()(())&amp;rdquo;, &amp;ldquo;((()))&amp;rdquo;, &amp;ldquo;()()()&amp;rdquo;, &amp;ldquo;(()())&amp;rdquo;, where LCS(&amp;quot;(())()&amp;quot;, &amp;ldquo;()(()))&amp;rdquo;) is 4, and the others are all 5, so the output is 3.&lt;/p>
&lt;h5 id="input-description">&lt;strong>Input Description:&lt;/strong>
&lt;/h5>&lt;!-- raw HTML omitted -->
&lt;h5 id="output-description">&lt;strong>Output Description:&lt;/strong>
&lt;/h5>&lt;p>Output a positive integer, the number of t that satisfy the conditions.&lt;/p>
&lt;p>Example 1&lt;/p>
&lt;h2 id="input">Input
&lt;/h2>&lt;p>(())()&lt;/p>
&lt;h2 id="output">Output
&lt;/h2>&lt;p>3&lt;/p>
&lt;h2 id="analysis">Analysis
&lt;/h2>&lt;p>The problem requires iterating through all bracket sequences of the same length and calculating the length of the longest common subsequence between each bracket sequence and the given string. Then, count how many times the maximum length occurs.&lt;/p>
&lt;p>If we were to follow the problem description exactly, it would be impossible to solve. Just generating bracket sequences of up to 50 characters would likely exceed the time limit.&lt;/p>
&lt;p>Since we&amp;rsquo;re searching for how many times the longest subsequence occurs, we can assume that the longest subsequence is the original string length - 1, and then find all bracket sequences that can be formed from this subsequence. If none exist, we try subsequences of length original length - 2, and so on, decreasing the subsequence length until we find a solution.&lt;/p>
&lt;p>In fact, there always exists a subsequence of length original length - 1 that can form another valid bracket sequence different from the original. This is because any bracket sequence can be modified by moving just one bracket to form another different valid bracket sequence (think about why). If this is possible, then the longest common subsequence between the new string and the original string would be n-1, since you only moved one bracket to a new position. The order of all other brackets remains unchanged. So, each time we remove one bracket and insert it in a new position. We count how many valid bracket sequences can be formed this way, subtract the original string (i.e., subtract 1), and that&amp;rsquo;s the answer to the problem.&lt;/p>
&lt;pre tabindex="0">&lt;code>#include &amp;lt;vector&amp;gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;set&amp;gt;
using namespace std;
bool isKuohao(const string &amp;amp; str)
{
int count=0;
for(auto c:str)
{
if(c == &amp;#39;(&amp;#39;)
count ++;
if(c == &amp;#39;)&amp;#39;)
count --;
if(count&amp;lt;0)
return false;
}
return count == 0;
}
int main()
{
int ret=0;
string input;
cin &amp;gt;&amp;gt; input;
set&amp;lt;string&amp;gt; haveChose;
set&amp;lt;string&amp;gt; haveBuild;
/* Select any one bracket */
for (int i = 0; i &amp;lt; input.size(); i++)
{
char q[] = {input[i], &amp;#39;\0&amp;#39;};
/* Delete that bracket */
string delete_char(input);
delete_char.erase(delete_char.begin() + i);
if (haveChose.count(delete_char))
continue;
else
haveChose.insert(delete_char);
/* Insert the original bracket into a new array */
for (int j = 0; j &amp;lt; delete_char.size() + 1; j++)
{
string add_delete_char(delete_char);
add_delete_char.insert(j, q);
if (haveBuild.count(add_delete_char))
continue;
else
haveBuild.insert(add_delete_char);
if(isKuohao(add_delete_char)){
ret ++;
}
}
}
cout &amp;lt;&amp;lt; ret-1 &amp;lt;&amp;lt; endl;
}
&lt;/code>&lt;/pre></description></item><item><title>Algorithm Problem — Touring the Magic Kingdom</title><link>https://nansenli.com/post/jianshu/%E7%AE%97%E6%B3%95/%E7%AE%97%E6%B3%95%E9%A2%98%E7%9B%AE%E6%B8%B8%E5%8E%86%E9%AD%94%E6%B3%95%E7%8E%8B%E5%9B%BD/</link><pubDate>Fri, 19 Jan 2018 10:05:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E7%AE%97%E6%B3%95/%E7%AE%97%E6%B3%95%E9%A2%98%E7%9B%AE%E6%B8%B8%E5%8E%86%E9%AD%94%E6%B3%95%E7%8E%8B%E5%9B%BD/</guid><description>&lt;p>Link: &lt;a class="link" href="https://www.nowcoder.com/questionTerminal/f58859adc39f4edc9cd8e40ba4160339" target="_blank" rel="noopener"
>https://www.nowcoder.com/questionTerminal/f58859adc39f4edc9cd8e40ba4160339&lt;/a>
Source: NowCoder&lt;/p>
&lt;p>The Magic Kingdom has a total of n cities, numbered from 0 to n-1. The roads between the n cities form exactly a tree.
Xiaoyi is currently in city 0. Each move, Xiaoyi will walk from his current city to an adjacent city. Xiaoyi can move at most L times.
If Xiaoyi has reached a city, he is considered to have toured that city. Xiaoyi now wants to make a good travel plan so that he can tour as many cities as possible. Please help him calculate the maximum number of cities he can tour (note that city 0 is already toured, and cities already toured are not counted again).&lt;/p>
&lt;h5 id="input-description">&lt;strong>Input Description:&lt;/strong>
&lt;/h5>&lt;p>The input consists of two lines. The first line includes two positive integers &lt;code>n (2 ≤ n ≤ 50)&lt;/code> and &lt;code>L (1 ≤ L ≤ 100)&lt;/code>, representing the number of cities and the number of moves Xiaoyi can make.
The second line includes &lt;code>n-1&lt;/code> integers &lt;code>parent[i] (0 ≤ parent[i] ≤ i)&lt;/code>. For each valid &lt;code>i (0 ≤ i ≤ n - 2)&lt;/code>, there is a road connecting city &lt;code>(i+1)&lt;/code> and city &lt;code>parent[i]&lt;/code>.&lt;/p>
&lt;h5 id="output-description">&lt;strong>Output Description:&lt;/strong>
&lt;/h5>&lt;p>Output an integer representing the maximum number of cities Xiaoyi can tour.&lt;/p>
&lt;h2 id="analysis">Analysis
&lt;/h2>&lt;p>After abstracting the problem, it means traversing a tree, and after a specified number of steps, finding the path that passes through the maximum number of nodes. It might be easier to understand if we hang this tree from the root node. Although some solutions grow from bottom to top, I rebuilt the tree and used a hanging tree approach.&lt;/p>
&lt;p>Starting from the root node, first determine whether the left subtree or the right subtree has a greater depth, and traverse the deeper subtree first. Continue until all steps are used up.&lt;/p>
&lt;p>The depth of the tree can be easily calculated through post-order traversal, but it turns out this solution only passes 60% of the test cases.&lt;/p>
&lt;pre tabindex="0">&lt;code>45 73
0 0 0 1 0 0 3 5 6 8 7 9 1 10 1 2 15 6 8 11 14 17 8 14 3 21 23 3 21 15 12 5 21 31 11 13 7 17 20 26 28 16 36 26
&lt;/code>&lt;/pre>&lt;p>The error is in this test case. The correct answer is 41, but using a simple greedy algorithm only yields 39 cities.&lt;/p>
&lt;p>Later, looking at the explanation, I still couldn&amp;rsquo;t fully understand it. In any case, I saw that in the correct solution, the final answer is obtained directly after calculating the depth.&lt;/p>
&lt;p>Suppose we have already calculated the maximum depth of each node, represented by deep[i], with the depth of the bottom layer of the tree being 1.&lt;/p>
&lt;p>Obviously, the longest path from the root node to any node = deep[0] - 1.&lt;/p>
&lt;p>Based on this path, we can visit some additional nodes. However, each time after visiting these nodes, we must return to this path. This round trip requires an extra two steps for each node visited, and visiting two nodes requires four extra steps.&lt;/p>
&lt;p>Looking at the diagram makes it easier to understand:&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/95a9d67e20f8.png"
loading="lazy"
alt="image.png"
>&lt;/p>
&lt;p>Reference code:&lt;/p>
&lt;pre tabindex="0">&lt;code>#include &amp;lt;vector&amp;gt;
#include &amp;lt;iostream&amp;gt;
using namespace std;
vector&amp;lt;vector&amp;lt;int&amp;gt; &amp;gt; tree;
vector&amp;lt;int&amp;gt; deep;
void calc_deep(int i)
{
int max_deep = 0;
for(auto j:tree[i])
{
calc_deep(j);
max_deep = max(deep[j], max_deep);
}
deep[i] = max_deep + 1;
}
int main()
{
int n, L;
cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; L;
/* Build the tree */
tree.resize(n);
deep.resize(n);
for(int i=0;i&amp;lt;n-1;i++)
{
int num;
cin &amp;gt;&amp;gt; num;
tree[num].push_back(i+1);
}
/* Calculate depth */
calc_deep(0);
// int validpath = min(deep[0] -1,L);
// cout &amp;lt;&amp;lt; min(n, 1 + validpath + (L - validpath)/2) &amp;lt;&amp;lt; endl;
int long_path = deep[0] - 1;
if(long_path &amp;gt; L) cout &amp;lt;&amp;lt; L + 1;
else cout &amp;lt;&amp;lt; 1 + long_path + (L - long_path)/2;
}
&lt;/code>&lt;/pre></description></item><item><title>Bash Language Study Notes</title><link>https://nansenli.com/post/jianshu/linux%E7%BC%96%E7%A8%8B/bash-%E8%AF%AD%E8%A8%80-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</link><pubDate>Thu, 18 Jan 2018 09:23:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/linux%E7%BC%96%E7%A8%8B/bash-%E8%AF%AD%E8%A8%80-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>Most people have used terminal command lines. Sometimes we need to repeatedly enter the same commands, so we can write our own scripts to automate this work.&lt;/p>
&lt;h2 id="specifying-the-interpreter">Specifying the Interpreter
&lt;/h2>&lt;p>&lt;code>#!/bin/bash&lt;/code>
This line specifies bash as the interpreter for our script. If we&amp;rsquo;re writing in Python syntax, we should change it to Python.&lt;/p>
&lt;h2 id="defining-variables">Defining Variables
&lt;/h2>&lt;p>variable_name=value
For example: name=&amp;ldquo;hahaha&amp;rdquo;
For ordinary characters, adding double quotes or single quotes doesn&amp;rsquo;t matter. name=hahaha also works.&lt;/p>
&lt;h2 id="using-variables">Using Variables
&lt;/h2>&lt;p>Add $ before a variable name to use its value. This means to evaluate the symbol&amp;rsquo;s value.&lt;/p>
&lt;h2 id="strings">Strings
&lt;/h2>&lt;p>Quotes have the following characteristics:&lt;/p>
&lt;ul>
&lt;li>Spaces act as word separators&lt;/li>
&lt;li>Words in single quotes&lt;/li>
&lt;li>Character pattern matching&lt;/li>
&lt;li>Pathname expansion&lt;/li>
&lt;li>Process substitution (redirection)&lt;/li>
&lt;/ul>
&lt;p>Single quotes won&amp;rsquo;t process the content further, while double quotes will evaluate the content. You can see the difference between echo &amp;ldquo;$name&amp;rdquo; and echo &amp;lsquo;$name&amp;rsquo; to understand this.&lt;/p>
&lt;p>String concatenation is just placing two strings together.
${#string} is used to get the string length, ${string:1:4} is used for slicing.&lt;/p>
&lt;h2 id="getting-input">Getting Input
&lt;/h2>&lt;p>read inputstr
read -p &amp;ldquo;please input : &amp;quot; inputstr
The above two lines get input content from standard input.&lt;/p>
&lt;h3 id="getting-script-execution-results">Getting Script Execution Results
&lt;/h3>&lt;p>For example, to get the output of a Python script:
into=$(&amp;lsquo;python3&amp;rsquo; &amp;lsquo;pyscript.py&amp;rsquo; &amp;lsquo;arg1&amp;rsquo; &amp;lsquo;arg2&amp;rsquo;)
arg1 and arg2 are parameters&lt;/p></description></item><item><title>Learning and Thinking About Dynamic Programming Algorithms</title><link>https://nansenli.com/post/jianshu/%E7%AE%97%E6%B3%95/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%97%E6%B3%95%E5%AD%A6%E4%B9%A0%E4%B8%8E%E6%80%9D%E8%80%83/</link><pubDate>Thu, 18 Jan 2018 08:52:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E7%AE%97%E6%B3%95/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%97%E6%B3%95%E5%AD%A6%E4%B9%A0%E4%B8%8E%E6%80%9D%E8%80%83/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>Dynamic programming is a common type of problem in coding interviews. Mastering it is crucial.&lt;/p>
&lt;h2 id="netease-problem">NetEase Problem
&lt;/h2>&lt;p>Little Q and Dr. Niu are singing a song together. This song consists of n notes, each represented by a positive integer.
Each note must be sung by either Little Q or Dr. Niu. The difficulty of singing a series of notes equals the sum of the absolute differences between all adjacent notes. For example, if a sequence of notes is 8, 8, 13, 12, then its difficulty is |8 - 8| + |13 - 8| + |12 - 13| = 6 (where || represents absolute value).
Now we need to distribute these n notes between Little Q and Dr. Niu to minimize the sum of their singing difficulties. Calculate the minimum possible total difficulty.
As shown in the example: Little Q chooses to sing {5, 6} with difficulty 1, Dr. Niu chooses to sing {1, 2, 1} with difficulty 2, the sum of difficulties is 3, which is the minimum possible difficulty.&lt;/p>
&lt;h2 id="greedy-approach-incorrect">Greedy Approach (Incorrect)
&lt;/h2>&lt;p>Sort all the numbers and use the largest difference between two numbers as a dividing point. Assign the lower half to Little Q and the upper half to Dr. Niu, then calculate the result. This approach passes 60% of the test cases.
If time is limited or you don&amp;rsquo;t know how to solve it optimally, you can use this approach.&lt;/p>
&lt;pre tabindex="0">&lt;code>#include &amp;lt;vector&amp;gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;cmath&amp;gt;
using namespace std;
int main(){
int n;
cin &amp;gt;&amp;gt; n;
vector&amp;lt;int&amp;gt; nums;
for(int i=0;i&amp;lt;n;i++)
{
int num;
cin &amp;gt;&amp;gt; num;
nums.push_back(num);
}
vector&amp;lt;int&amp;gt; nums_sort(nums.begin(), nums.end());
sort(nums_sort.begin(), nums_sort.end());
int max_gap = 0;
auto max_gap_it = nums_sort.begin();
for(auto it=nums_sort.begin(); it!=nums_sort.end()-1; it++){
if(*(it+1) - *it &amp;gt; max_gap){
max_gap = *(it+1) - *it;
max_gap_it = it;
}
}
int max_gap_num = *max_gap_it;
int ret = 0;
int last_1 = -1;
int last_2 = -1;
for(auto it = nums.begin(); it!=nums.end(); it++){
if(*it &amp;lt;= max_gap_num){
if(last_1 != -1){
ret += abs(*it - last_1);
}
last_1 = *it;
}
else{
if(last_2 != -1){
ret += abs(*it - last_2);
}
last_2 = *it;
}
}
cout &amp;lt;&amp;lt; ret;
}
&lt;/code>&lt;/pre>&lt;h2 id="thinking-about-dynamic-programming">Thinking About Dynamic Programming
&lt;/h2>&lt;p>Since I don&amp;rsquo;t know much about dynamic programming, let&amp;rsquo;s start with the coin change problem.&lt;/p>
&lt;ul>
&lt;li>Coin Change Problem&lt;/li>
&lt;/ul>
&lt;p>The core of dynamic programming lies in breaking down the problem and recursively combining subproblems to solve the original problem.&lt;/p>
&lt;p>For example, suppose we have coins with denominations of 1, 3, and 5, and we want to make change for 11 using the minimum number of coins.
If you&amp;rsquo;re human, you might say, &amp;ldquo;I can easily see that two 5-coins and one 1-coin, for a total of 3 coins, is optimal.&amp;rdquo; Let&amp;rsquo;s try a different example: what about making change for 237? You might say, &amp;ldquo;I&amp;rsquo;ll first use 5-value coins to make 200, then find the optimal combination for the remaining 37.&amp;rdquo; This method assumes that splitting 237 into two parts and finding the optimal solution for each part will yield the global optimal solution. If that were always true, there would be no problem. But the issue is with splitting into 200 and 37 - there&amp;rsquo;s no proof that this is an effective split. Dynamic programming is built on effective partitioning.&lt;/p>
&lt;p>Both divide-and-conquer algorithms and dynamic programming are based on partitioning. The difference is that dynamic programming involves state transitions, while divide-and-conquer doesn&amp;rsquo;t. Divide-and-conquer can decompose from top to bottom, while dynamic programming usually builds from bottom to top.&lt;/p>
&lt;p>Dynamic programming involves two dimensions. The first dimension is usually related to the scale of the problem, and the second dimension needs to be extracted from the problem. In this coin change problem, the second dimension is the allowed coins: for example, allowing only 1-value coins, allowing 1 and 3-value coins, or allowing 1, 3, and 5-value coins. We denote this with index 1, 2, 3, and use j to represent this variable. If we use i to represent the current problem scale (237), then we want to find c[i][j], which is c[237][3]. For this problem, we can make two assumptions: first, the optimal solution uses at least one 5-value coin; second, the optimal solution uses no 5-value coins. For the second assumption, c[237][3] = c[237][2], because if we don&amp;rsquo;t use any 5-value coins, then the optimal solution should be the same as using only 1 and 3-value coins. For the first assumption, since the solution must contain at least one 5-value coin, after removing this 5-value coin, we have 232 left. Since c[237][3] is the optimal value for making change for 237 using coins of values 1, 3, and 5, and it must contain at least one 5-value coin, the remaining coins that make up 232 must also be in an optimal state (if the subproblem weren&amp;rsquo;t optimal, that is, if there were a better solution for making 232 within the solution for 237, then the solution for 237 wouldn&amp;rsquo;t be optimal, contradicting our assumption). So c[232][3] = c[237][3] - 1.&lt;/p>
&lt;p>This allows us to derive:
c[i][j] = c[i][j-1] (Assumption 1)
c[i][j] = c[i-value of denomination j][j] + 1 (Assumption 2)
When written in the form of min(), this becomes the familiar state transition equation.&lt;/p>
&lt;p>We ultimately want to find c[11][5], and we work from the c[0][0] state towards the bottom right corner of the table to derive the answer.&lt;/p>
&lt;ul>
&lt;li>Two-Person Singing Difficulty Problem
&lt;a class="link" href="https://www.nowcoder.com/questionTerminal/fddf64d5757e41ec93f3ef0c0a10b891" target="_blank" rel="noopener"
>https://www.nowcoder.com/questionTerminal/fddf64d5757e41ec93f3ef0c0a10b891&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>We must remember:&lt;/p>
&lt;blockquote>
&lt;p>The optimal solution to the problem is not necessarily the value in the bottom-right corner of the state matrix&lt;/p>
&lt;/blockquote>
&lt;p>We must always remember that the bottom-right cell of the state matrix is not necessarily the direct optimal solution. Thinking this way often leads to confusion. Instead, we should think that the matrix indices i and j are definitely related to the two dimensions of the problem, but dp[i][j] does not necessarily represent the value of the optimal solution.&lt;/p>
&lt;p>For example, in this problem, we need to find the optimal difficulty coefficient. If dp[i_max][j_max] were the optimal difficulty coefficient, what would i and j index? This is unsolvable. Looking at it differently, what exactly is the optimal difficulty coefficient? Since state transitions exist, the optimal state must be the minimum value obtained from multiple states. What does i represent? If i represents the current note being sung, what does j represent? J can only represent the note sung by the other person. Then, in the optimal state, how many situations are there? We calculate the optimal value from these situations. In the optimal state, one person must be singing the last note, while the other person might not be singing any note, might be singing the first note, might be singing the second note&amp;hellip; might be singing the second-to-last note. We calculate the minimum value of all these situations to find the optimal solution.&lt;/p>
&lt;p>Another question is whether i and j should be assigned to specific people. If i represents Little Q&amp;rsquo;s notes and j represents Dr. Niu&amp;rsquo;s notes, then the matrix would be symmetric, and we&amp;rsquo;d be calculating half of the content redundantly. Actually, we don&amp;rsquo;t care who is singing what, because they are equivalent. We can only think of it as one person currently singing up to position i, and the other person last sang up to position j.&lt;/p>
&lt;p>What states can c[i][j] transition from?
For example, if one person has sung up to the 6th note, and the other person last sang up to the 3rd note, if the current total difficulty is minimal, then:
The 6th, 5th, and 4th notes are all sung by the first person. This is because the second person has only sung up to the 3rd note and cannot sing later notes.
So c[6][3] = c[5][3] + difficulty difference between notes 5 and 6 = c[4][3] + difficulty difference between notes 4 and 5.
But how is c[4][3] transitioned? If the first person has sung up to the 4th note, and the other person has sung up to the 3rd note, this means the first person interrupted the second person&amp;rsquo;s singing. Since we don&amp;rsquo;t know from where the first person jumped to the 4th note, we assume all possible notes. If they jumped from the 2nd note, then it&amp;rsquo;s c[3][2] + difference between notes 3 and 4. If they jumped from the 1st note, then it&amp;rsquo;s c[3][1] + difference between notes 1 and 4. If they didn&amp;rsquo;t jump, then it&amp;rsquo;s c[3][0] + difference between notes 0 and 4. Since the last note sung by the second person is always less than the note being sung by the first person, we don&amp;rsquo;t need to consider the case where j &amp;gt;= i.&lt;/p>
&lt;p>Based on this, here&amp;rsquo;s the code:&lt;/p>
&lt;pre tabindex="0">&lt;code>#include &amp;lt;vector&amp;gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;cmath&amp;gt;
#include &amp;lt;array&amp;gt;
using namespace std;
vector&amp;lt;int&amp;gt; nums;
array&amp;lt;array&amp;lt;int, 2100&amp;gt;, 2100&amp;gt; dp;
/* Calculate the difficulty between two notes. Note that the index of the first note in the input data is 0, so there&amp;#39;s a difference of 1. If the starting note is 0, it means it&amp;#39;s the first time singing, so no additional difficulty */
int diffcult(int s, int e)
{
if(s == 0) return 0;
// printf(&amp;#34;diffcult %d %d to %d %d is %d \n&amp;#34;, s,nums[s-1], e,nums[e-1], abs(nums[e-1] - nums[s-1]));
return abs(nums[e-1] - nums[s-1]);
}
int main()
{
int n;
cin &amp;gt;&amp;gt; n;
for (int i = 0; i &amp;lt; n; i++)
{
int num;
cin &amp;gt;&amp;gt; num;
nums.push_back(num);
}
/* Start from 0 and consider all notes. The worst case for the first person is not singing at all, in which case j also doesn&amp;#39;t sing, and we continue */
for (int i = 0; i &amp;lt;= n; i++)
{
/* Also start from 0 here, because in the worst case, the first person doesn&amp;#39;t sing at all, which is position 0 */
for (int j = 0; j &amp;lt;= n; j++)
{
/* Don&amp;#39;t consider later cases. This could also be incorporated into the range limitation above */
if (j &amp;gt;= i)
continue;
/* In the general case, for example, if the first person is singing the 6th note and the second person is singing the 3rd note, it clearly transitions from dp[5][3] */
if (j + 1 &amp;lt; i)
{
dp[i][j] = dp[i - 1][j] + diffcult(i - 1, i);
}
/* Otherwise, if the second person just finished singing and the first person took over, the first person&amp;#39;s note could be jumped from earlier */
if (j + 1 == i)
{
/* k represents which note i jumped from */
int min_cost = -1;
for (int k = 0; k &amp;lt; j; k++)
{
int cost = dp[j][k] + diffcult(k, j + 1);
/* Only record the minimum jump difficulty */
if(min_cost == -1 || min_cost &amp;gt; cost){
min_cost = cost;
}
}
if(min_cost == -1) min_cost = 0;
dp[i][j] = min_cost;
}
// for(int a=0;a&amp;lt;=n;a++){
// for(int b=0;b&amp;lt;=n;b++){
// if(a==i &amp;amp;&amp;amp; b == j){
// printf(&amp;#34;\t【%d】&amp;#34;, dp[a][b]);
// }
// else printf(&amp;#34;\t%d&amp;#34;, dp[a][b]);
// }
// // printf(&amp;#34;\n&amp;#34;);
// }
// printf(&amp;#34;\n&amp;#34;);
}
}
int min_cost = -1;
for(int j=0;j&amp;lt;n;j++){
if(min_cost == -1 || min_cost &amp;gt; dp[n][j]){
min_cost = dp[n][j];
}
}
printf(&amp;#34;%d&amp;#34;, min_cost);
}
&lt;/code>&lt;/pre>&lt;ul>
&lt;li>Longest Palindromic Substring Problem
&lt;a class="link" href="https://leetcode.com/problems/longest-palindromic-substring/description/" target="_blank" rel="noopener"
>https://leetcode.com/problems/longest-palindromic-substring/description/&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>I now understand why dynamic programming is presented in this way. The key point of two-dimensional dynamic programming problems is:&lt;/p>
&lt;blockquote>
&lt;p>The planar expansion of linear growth problems&lt;/p>
&lt;/blockquote>
&lt;p>For example, in the coin change problem, expanding the second dimension represents making change with the current coin type. In the singing problem, it&amp;rsquo;s the minimum difficulty when singing up to the current position while the other person last sang at a certain position. For the longest palindromic substring, it becomes the starting position.&lt;/p>
&lt;p>In the longest palindromic substring, i represents the starting character, and j represents the ending character of the substring. When i is the starting character and j is the ending character, we have the answer to the problem.&lt;/p>
&lt;p>For the smallest subproblem, which is a single character, its length is 1. For longer strings, if the characters at both ends are different, then it equals the smaller value of removing either the right or left character. If the characters at both ends are the same, then it equals the value of the substring with both ends removed plus 2.&lt;/p>
&lt;pre tabindex="0">&lt;code>#include &amp;lt;vector&amp;gt;
#include &amp;lt;string&amp;gt;
using namespace std;
class Solution {
public:
int longestPalindromeSubseq(string s) {
vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; dp;
dp.resize(s.size() + 1);
for (int i = 0; i &amp;lt; s.size() + 1; i++)
{
dp[i].resize(s.size() + 1);
}
int len = s.size();
for(int t=0;t&amp;lt;len;t++){
for(int j=t; j&amp;lt;len;j++){
int i = j-t;
if(i==j){
dp[i][j] = 1;
}
else if(s[i] == s[j]){
dp[i][j] = dp[i+1][j-1] + 2;
}
else{
if(dp[i+1][j]&amp;gt; dp[i][j-1]){
dp[i][j] = dp[i+1][j];
}
else{
dp[i][j] = dp[i][j-1];
}
}
}
}
return dp[0][len-1];
}
};
&lt;/code>&lt;/pre></description></item><item><title>Red-Black Tree Implementation Principles</title><link>https://nansenli.com/post/jianshu/%E7%AE%97%E6%B3%95/%E7%BA%A2%E9%BB%91%E6%A0%91%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/</link><pubDate>Mon, 15 Jan 2018 10:00:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E7%AE%97%E6%B3%95/%E7%BA%A2%E9%BB%91%E6%A0%91%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>Binary search trees can degrade in performance
Balanced binary trees require too much overhead during insertion and deletion to maintain search performance
This is where red-black trees come in. Unlike balanced binary trees, red-black trees don&amp;rsquo;t require the height difference between two subtrees to be at most 1 at all times
Reference: &lt;a class="link" href="http://www.sohu.com/a/201923614_466939" target="_blank" rel="noopener"
>http://www.sohu.com/a/201923614_466939&lt;/a>&lt;/p>
&lt;h2 id="properties">Properties
&lt;/h2>&lt;ul>
&lt;li>Nodes consist of red and black colors, with the root node being black&lt;/li>
&lt;li>Leaf nodes are made up of black NIL nodes&lt;/li>
&lt;li>A red node&amp;rsquo;s children must be black nodes, and must contain two leaf nodes. This means there cannot be two consecutive red nodes.&lt;/li>
&lt;li>Starting from any node, any path to any NIL node must pass through the same number of black nodes&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/5195eeed6692.png"
loading="lazy"
alt="image.png"
>&lt;/p>
&lt;p>This means that starting from the root node to any leaf node, the difference between the longest path and the shortest path is no more than half of the longest path. In this case, the longest and shortest paths have the same number of black nodes, and the longest path can have at most one additional red node for each black node.&lt;/p>
&lt;h2 id="recoloring-and-rotation">Recoloring and Rotation
&lt;/h2>&lt;p>When a node is inserted (or deleted), the properties of the red-black tree may be violated. Recoloring and rotation are used to restore these properties.&lt;/p>
&lt;h3 id="recoloring">Recoloring
&lt;/h3>&lt;p>If two consecutive nodes are red, recoloring is needed&lt;/p>
&lt;h3 id="rotation">Rotation
&lt;/h3>&lt;p>If a path has too many black nodes, rotation is needed&lt;/p>
&lt;h2 id="conditions-for-recoloring-and-rotation">Conditions for Recoloring and Rotation
&lt;/h2>&lt;p>This will be summarized later&lt;/p></description></item><item><title>Linux: How to Use Terminal Background Tasks and Multiple Windows</title><link>https://nansenli.com/post/jianshu/linux%E7%BC%96%E7%A8%8B/linux-%E7%BB%88%E7%AB%AF%E5%90%8E%E5%8F%B0%E4%BB%BB%E5%8A%A1%E5%A4%9A%E7%AA%97%E5%8F%A3%E6%80%8E%E4%B9%88%E4%BD%BF%E7%94%A8/</link><pubDate>Thu, 11 Jan 2018 15:01:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/linux%E7%BC%96%E7%A8%8B/linux-%E7%BB%88%E7%AB%AF%E5%90%8E%E5%8F%B0%E4%BB%BB%E5%8A%A1%E5%A4%9A%E7%AA%97%E5%8F%A3%E6%80%8E%E4%B9%88%E4%BD%BF%E7%94%A8/</guid><description>&lt;h2 id="byobu">byobu
&lt;/h2>&lt;p>byobu is a tool that replaces screen and tmux. You can install it using &lt;code>apt install byobu&lt;/code>. Running byobu is simple and its commands are straightforward:&lt;/p>
&lt;ul>
&lt;li>f2: Create a new window&lt;/li>
&lt;li>f3: Return to the window on the left&lt;/li>
&lt;li>f4: Return to the window on the right&lt;/li>
&lt;li>f9: Exit byobu, commands running in open windows will not be interrupted&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>Strongly recommended for those who don&amp;rsquo;t want to use screen or tmux! You&amp;rsquo;ll be satisfied.&lt;/p>
&lt;/blockquote></description></item><item><title>C++ Basics — Differences Between Pointers and References</title><link>https://nansenli.com/post/jianshu/c++/c++%E5%9F%BA%E6%9C%AC%E9%97%AE%E9%A2%98%E6%8C%87%E9%92%88%E4%B8%8E%E5%BC%95%E7%94%A8%E7%9A%84%E5%8C%BA%E5%88%AB/</link><pubDate>Wed, 10 Jan 2018 08:44:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/c++/c++%E5%9F%BA%E6%9C%AC%E9%97%AE%E9%A2%98%E6%8C%87%E9%92%88%E4%B8%8E%E5%BC%95%E7%94%A8%E7%9A%84%E5%8C%BA%E5%88%AB/</guid><description>&lt;h3 id="differences-between-pointers-and-references">Differences Between Pointers and References
&lt;/h3>&lt;ul>
&lt;li>Pointers can be null pointers, but references cannot be null references. This leads to the following distinction.&lt;/li>
&lt;li>Pointers can be invalid, while references are always valid. If a pointer is a null pointer, it cannot be legally used. This requires checking the validity of a pointer each time it is used. In contrast, references don&amp;rsquo;t need to be checked.&lt;/li>
&lt;li>Pointers can be modified, references cannot. A pointer is a variable that stores an address pointing to an object, and it can be changed to point to other addresses, no longer pointing to the original object. However, a reference is bound to an object during initialization, and while you can modify the contents of the object, the reference cannot be reassigned to refer to another object.&lt;/li>
&lt;li>Based on the above situations, pointers and references have different applications. Pointers can be used in scenarios where you need to point to different objects at different times or where you might need to point to no object at all. If you always need to point to a single object and will not change what you&amp;rsquo;re pointing to after initialization, you should use a reference.&lt;/li>
&lt;/ul></description></item><item><title>Thoughts on the Phenomenon of Telecom Hijacking Red Packet Ads on CSDN Mobile</title><link>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/%E5%85%B3%E4%BA%8Ecsdn%E7%A7%BB%E5%8A%A8%E7%AB%AF%E8%A2%AB%E7%94%B5%E4%BF%A1%E5%8A%AB%E6%8C%81%E7%BA%A2%E5%8C%85%E5%B9%BF%E5%91%8A%E7%9A%84%E7%8E%B0%E8%B1%A1%E4%B8%8E%E6%84%9F%E6%83%B3/</link><pubDate>Sun, 31 Dec 2017 06:24:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/%E5%85%B3%E4%BA%8Ecsdn%E7%A7%BB%E5%8A%A8%E7%AB%AF%E8%A2%AB%E7%94%B5%E4%BF%A1%E5%8A%AB%E6%8C%81%E7%BA%A2%E5%8C%85%E5%B9%BF%E5%91%8A%E7%9A%84%E7%8E%B0%E8%B1%A1%E4%B8%8E%E6%84%9F%E6%83%B3/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>Recently I suspected being hijacked by China Telecom, so I&amp;rsquo;m recording my analysis here.&lt;/p>
&lt;h2 id="phenomenon">Phenomenon
&lt;/h2>&lt;p>I use a Sichuan Telecom SIM card with my Samsung S8. When visiting the following websites, I am redirected to a full-screen advertisement, which is the Alipay red packet interface. The user experience is terrible. Below is an address that gets hijacked:
&lt;a class="link" href="http://m.blog.csdn.net/skyroben/article/details/70195575" target="_blank" rel="noopener"
>http://m.blog.csdn.net/skyroben/article/details/70195575&lt;/a>&lt;/p>
&lt;h2 id="analysis">Analysis
&lt;/h2>&lt;ul>
&lt;li>When using SS proxy (4G) on my phone to visit the above website, there&amp;rsquo;s no advertisement or hijacking&lt;/li>
&lt;li>With direct connection via Telecom WiFi, hijacking occurs&lt;/li>
&lt;li>Using hotspot and accessing the URL from a computer, no hijacking occurs&lt;/li>
&lt;li>Using hotspot and changing the user agent on the computer to an Android client, hijacking occurs with some probability??&lt;/li>
&lt;li>Without using hotspot, with a wired connection (Telecom) on the computer, changing the user agent to Android client, hijacking occurs with some probability??&lt;/li>
&lt;li>Using Chrome on mobile (S8), connected to Telecom WiFi, adding view-source: before the URL to view the source code, and saving the results for comparison on the computer, no differences were found&lt;/li>
&lt;li>Using Chrome on mobile (S8), connected to China Mobile WiFi, hijacking occurs! (Script cache?)&lt;/li>
&lt;li>Using iOS Chrome, connected to Telecom WiFi, no hijacking&lt;/li>
&lt;/ul>
&lt;h2 id="summary">Summary
&lt;/h2>&lt;p>Since the webpage source code doesn&amp;rsquo;t change after hijacking, it might be a script hijack. Since hijacking also occurs when using China Mobile WiFi, we can&amp;rsquo;t rule out the possibility that CSDN added the advertisements themselves. Using an SS proxy can bypass the hijacking, indicating that the hijacking is regional. The fact that computers and iOS devices aren&amp;rsquo;t hijacked suggests that the hijacking specifically targets Android browsers.&lt;/p>
&lt;h2 id="conclusion">Conclusion
&lt;/h2>&lt;p>Regular users should avoid using Telecom networks and Android phones, and all websites are advised to use HTTPS encryption.&lt;/p></description></item><item><title>How to Implement STL Vector in C++</title><link>https://nansenli.com/post/jianshu/c++/c++%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0-stl-%E4%B8%AD%E7%9A%84vector/</link><pubDate>Sat, 30 Dec 2017 08:49:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/c++/c++%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0-stl-%E4%B8%AD%E7%9A%84vector/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>In interviews, you might be asked how to implement a vector. This requires understanding the underlying implementation of vector.
Before that, you need to learn about dynamic memory management, especially allocators, which are explained in the C++ Primer book.&lt;/p>
&lt;h2 id="basic-contents-to-implement">Basic Contents to Implement
&lt;/h2>&lt;p>On the cplusplus website, common usages are as follows:
Member functions
(Constructor)
Construct vector (public member function)
(Destructor)
Vector destructor (public member function)&lt;/p>
&lt;p>Iterators:
begin
Return iterator to beginning (public member function)
end
Return iterator to end (public member function)&lt;/p>
&lt;p>Capacity:
size
Return size (public member function)&lt;/p>
&lt;p>Element access:
operator[]
Access element (public member function)
at
Access element (public member function)&lt;/p>
&lt;p>Modifiers:
push_back
Add element at the end (public member function)
pop_back
Delete last element (public member function)&lt;/p>
&lt;h2 id="main-structure-of-a-basic-vector">Main Structure of a Basic Vector
&lt;/h2>&lt;pre tabindex="0">&lt;code>#include &amp;lt;cstddef&amp;gt;
#include &amp;lt;stdexcept&amp;gt;
#include &amp;lt;memory&amp;gt;
#include &amp;lt;iterator&amp;gt;
template &amp;lt;typename T&amp;gt;
class vector
{
public:
using value_type = T;
using iterator = value_type *;
using size_type = std::size_t;
public:
vector() = default;
~vector();
iterator begin() const;
iterator end() const;
size_type size() const;
value_type &amp;amp;operator[](size_type i) const;
value_type &amp;amp;at(size_type i) const;
void push_back(const value_type &amp;amp;new_elem);
void pop_back();
private:
iterator startptr = nullptr;
iterator endptr = nullptr;
iterator capptr = nullptr;
std::allocator&amp;lt;value_type&amp;gt; alloc;
private:
void check_cap();
void free();
};
&lt;/code>&lt;/pre>&lt;p>In our class, we&amp;rsquo;ve simply implemented iterators, push_back, pop_back, as well as the [] operator, at function, and size function. To implement memory management, we also need to implement constructors, destructors, and capacity checking functions.&lt;/p>
&lt;h2 id="internal-implementation-of-basic-functions">Internal Implementation of Basic Functions
&lt;/h2>&lt;pre tabindex="0">&lt;code>template &amp;lt;typename T&amp;gt;
typename vector&amp;lt;T&amp;gt;::iterator vector&amp;lt;T&amp;gt;::begin() const
{
return startptr;
}
template &amp;lt;typename T&amp;gt;
typename vector&amp;lt;T&amp;gt;::iterator vector&amp;lt;T&amp;gt;::end() const
{
return endptr;
}
template &amp;lt;typename T&amp;gt;
typename vector&amp;lt;T&amp;gt;::size_type vector&amp;lt;T&amp;gt;::size() const
{
return endptr - startptr;
}
template &amp;lt;typename T&amp;gt;
typename vector&amp;lt;T&amp;gt;::value_type &amp;amp;vector&amp;lt;T&amp;gt;::operator[](size_type i) const
{
return *(startptr + i);
}
template &amp;lt;typename T&amp;gt;
typename vector&amp;lt;T&amp;gt;::value_type &amp;amp;vector&amp;lt;T&amp;gt;::at(size_type i) const
{
if (startptr + i &amp;gt;= endptr)
{
throw std::runtime_error(&amp;#34;out of range!&amp;#34;);
}
return *(startptr + i);
}
template &amp;lt;typename T&amp;gt;
vector&amp;lt;T&amp;gt;::~vector()
{
free();
}
&lt;/code>&lt;/pre>&lt;p>The above are implementations of simple functions. They simply retrieve internal data.&lt;/p>
&lt;pre tabindex="0">&lt;code>template &amp;lt;typename T&amp;gt;
void vector&amp;lt;T&amp;gt;::free()
{
if (startptr)
{
for (auto p = startptr; p != endptr; p++)
{
alloc.destroy(p);
}
alloc.deallocate(startptr, endptr - startptr);
}
}
template &amp;lt;typename T&amp;gt;
void vector&amp;lt;T&amp;gt;::check_cap()
{
if (endptr == capptr)
{
int newsize = size() ? size() &amp;lt;&amp;lt; 1 : 1;
auto newstartptr = alloc.allocate(newsize);
auto newendptr = uninitialized_copy(std::make_move_iterator(startptr), std::make_move_iterator(endptr), newstartptr);
free();
startptr = newstartptr;
endptr = newendptr;
capptr = newstartptr + newsize;
}
}
template &amp;lt;typename T&amp;gt;
void vector&amp;lt;T&amp;gt;::push_back(const value_type &amp;amp;new_elem)
{
check_cap();
alloc.construct(endptr, new_elem);
endptr++;
}
template &amp;lt;typename T&amp;gt;
void vector&amp;lt;T&amp;gt;::pop_back()
{
if(endptr-startptr&amp;gt;0){
alloc.destroy(endptr);
endptr--;
}
}
&lt;/code>&lt;/pre>&lt;p>This part deals with memory and uses allocator to manage memory. The constructor separates the allocation of space and construction, dividing the process into allocating space, reclaiming space, destruction, and construction. The capacity check involves these four scenarios: first allocate new space, then construct new elements in the new position, then destruct old elements, and release old space. The free function is used to destruct old elements and release old space. Here, uninitialized_copy function and make_move_iterator, move iterators, and uninitialized copy functions are used to construct new elements in new positions, aiming to speed up the construction of new elements.&lt;/p>
&lt;h2 id="advanced-functions-and-implementation">Advanced Functions and Implementation
&lt;/h2>&lt;h3 id="erase">erase
&lt;/h3>&lt;p>Next, let&amp;rsquo;s implement some additional functions. Let&amp;rsquo;s start with erase.
Erase deletes all content from the specified position to the specified position. The function prototype is as follows:&lt;/p>
&lt;pre tabindex="0">&lt;code> public:
iterator erase(const_iterator position);
iterator erase(const_iterator first, const_iterator last);
&lt;/code>&lt;/pre>&lt;p>The first function can also be seen as simply calling the second function. We implement the second function as follows:&lt;/p>
&lt;pre tabindex="0">&lt;code>template &amp;lt;typename T&amp;gt;
typename vector&amp;lt;T&amp;gt;::iterator vector&amp;lt;T&amp;gt;::erase(const_iterator first, const_iterator last)
{
if(last &amp;gt;= endptr || first &amp;lt; startptr) throw std::runtime_error(&amp;#34;out of range!&amp;#34;);
iterator newendptr = std::copy(last, static_cast&amp;lt;const_iterator&amp;gt;(endptr), first);
while(newendptr &amp;lt; endptr){
alloc.destroy(--endptr);
}
return endptr;
}
&lt;/code>&lt;/pre>&lt;p>First, perform a validity check. Then, use std::copy to copy the later content to the part to be deleted. Note that the copy will automatically call the assignment function for the part being overwritten, and the assignment function should call the destructor internally. Then, if there is still content that needs to be destructed (this occurs only when the moved content is not as long as the deleted content), destruct that content. Then return the end pointer.&lt;/p>
&lt;p>The other overloaded function is relatively simple to implement:&lt;/p>
&lt;pre tabindex="0">&lt;code>template&amp;lt;typename T&amp;gt;
typename vector&amp;lt;T&amp;gt;::iterator vector&amp;lt;T&amp;gt;::erase(const_iterator position)
{
return erase(position, position+1);
}
&lt;/code>&lt;/pre></description></item><item><title>C++11 Features - Simple Introduction</title><link>https://nansenli.com/post/jianshu/c++/c++11-%E7%89%B9%E6%80%A7-%E7%AE%80%E5%8D%95%E4%BB%8B%E7%BB%8D/</link><pubDate>Thu, 28 Dec 2017 07:31:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/c++/c++11-%E7%89%B9%E6%80%A7-%E7%AE%80%E5%8D%95%E4%BB%8B%E7%BB%8D/</guid><description>&lt;h2 id="summarized-from-c-primer-5th-edition-as-a-quick-review-of-these-features">Summarized from C++ Primer 5th Edition, as a quick review of these features
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>long long type
Defined to be at least 32 bits in length. Generally not very useful.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>List initialization
A list is a set of elements composed of braces and commas, for example {1,2,3}.
We can use braces to initialize variables.
int a{0};
The equal sign in a declaration is not assignment but initialization, so the following form can also be used for initialization:
int a = {0};
This has the same effect as int a = 0;&lt;/p>
&lt;/li>
&lt;li>
&lt;p>nullptr constant
This is because before C++11, assigning a null pointer was done directly with 0. Now using nullptr is better. nullptr is essentially an object that can only have a right value and can only be type-converted to a pointer with a value of address 0, which solves some problems. Related information can be found in how to implement nullptr.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>constexpr variables
Stands for &amp;ldquo;const expressions&amp;rdquo;. The difference from const is that const indicates that the variable name must be a constant when used later, while constexpr not only requires this but also requires that the expression used to initialize the variable must also be a constant.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Type aliases
using uint = unsigned int
typedef uint unsigned int
These two statements are equivalent&lt;/p>
&lt;/li>
&lt;li>
&lt;p>auto type specifier
When declaring and defining a variable, the compiler automatically deduces the type of the declared variable based on the definition type.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>decltype type indicator
When declaring a variable, you can use decltype(expressions) to determine the type of an expression as the type of the declared variable&lt;/p>
&lt;/li>
&lt;li>
&lt;p>In-class initialization&lt;/p>
&lt;/li>
&lt;/ul>
&lt;pre tabindex="0">&lt;code> class A {
public:
int a = 7;
};
&lt;/code>&lt;/pre>&lt;p>This is equivalent to the following in previous versions:&lt;/p>
&lt;pre tabindex="0">&lt;code> class A {
public:
int a;
A() : a(7) {}
};
&lt;/code>&lt;/pre>&lt;ul>
&lt;li>
&lt;p>Using auto or decltype to deduce types
auto len = line.size(); automatically deduces string::size_type which is better&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Range-based for statements
Print characters in a string
for(auto c:str) cout &amp;laquo; c &amp;laquo; endl;
Similar to for in statements in Python&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Nested vectors
vector&amp;lt;vector&lt;!-- raw HTML omitted --> on older compilers, a space is needed, so the two angle brackets aren&amp;rsquo;t adjacent &amp;gt;&lt;/p>
&lt;/li>
&lt;li>
&lt;p>List initialization of vector objects
vector&lt;!-- raw HTML omitted --> strs = {&amp;ldquo;a&amp;rdquo;, &amp;ldquo;bb&amp;rdquo;, &amp;ldquo;ccc&amp;rdquo;};&lt;/p>
&lt;/li>
&lt;li>
&lt;p>cbegin and cend
Return iterators of type const_iterator&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Using auto in two-dimensional arrays&lt;/p>
&lt;/li>
&lt;/ul>
&lt;pre tabindex="0">&lt;code>int mat[3][4];
int main()
{
for (int(*p)[4] = mat; p != mat + 3; p++)
{
/* ... */
}
for (auto p = mat; p != mat + 3; p++)
{
/* ... */
}
}
&lt;/code>&lt;/pre>&lt;ul>
&lt;li>
&lt;p>Division rounding
C++11 specifies that division always rounds toward zero&lt;/p>
&lt;/li>
&lt;li>
&lt;p>List assignment to vector objects
vector&lt;!-- raw HTML omitted --> strs;
strs = {&amp;ldquo;a&amp;rdquo;, &amp;ldquo;bb&amp;rdquo;, &amp;ldquo;ccc&amp;rdquo;};&lt;/p>
&lt;/li>
&lt;li>
&lt;p>139 Using sizeof to get the size of class members
Normally class members can only be accessed through objects. But sizeof can directly determine the size of members.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>168 Range-based for statements
In range-based for statements, you cannot add elements to vector objects because it may cause the end element of the range-based for statement to become invalid. This is similar to issues in Python range loops.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>197 initializer_list
initializer_list is a template type, the object elements are always constants, and the types must be the same. Lists in braces are objects of this type.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>203 List initialization of return values
Returning an initializer_list can initialize temporary quantities for objects with a return type of vector.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>206 Defining trailing return types
Declaring a function that returns an array pointer
int * func(int i) this is not quite right, as it returns a pointer, not an array pointer
int (* func(int i))[10] this would return a pointer to an array of size 10
You can also write: auto func(int i) -&amp;gt; int(*)[10];&lt;/p>
&lt;/li>
&lt;li>
&lt;p>206 Using decltype to simplify return types
decltype can be used to simplify return types, but this need is less common.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>214 constexpr functions
A constexpr function is a function that returns a literal type and can only have one return statement.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>237 default default constructor
You can assign an equal sign to a constructor to indicate using the compiler-provided default constructor.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>246 In-class initialization
The new standard allows for initializing members with values directly within the class&lt;/p>
&lt;/li>
&lt;li>
&lt;p>261 Delegating constructors
When initializing a constructor, use an initializer list to initialize members. In addition, you can use other constructors to help construct.
For example, a constructor is func(int a,int b), and a delegating constructor func():func(0,0){} so it can be constructed with default values 0,0.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>268 constexpr constructors
Used to generate constexpr objects&lt;/p>
&lt;/li>
&lt;li>
&lt;p>284 Using string type to represent file paths
In stream types, previously only C character strings could be used, now strings of string type can be used&lt;/p>
&lt;/li>
&lt;li>
&lt;p>293 array and forward_list
Very fast, comparable to handwritten lists, safer than arrays&lt;/p>
&lt;/li>
&lt;li>
&lt;p>300 Using list initialization for containers
list&lt;!-- raw HTML omitted --> authors = {&amp;ldquo;milton&amp;rdquo;, &amp;ldquo;shakespeare&amp;rdquo;, &amp;ldquo;austen&amp;rdquo;};
vector&amp;lt;const char*&amp;gt; articles = {&amp;ldquo;a&amp;rdquo;, &amp;ldquo;b&amp;rdquo;, &amp;ldquo;cc&amp;rdquo;};&lt;/p>
&lt;/li>
&lt;li>
&lt;p>303 Non-member swap for containers
Using swap can quickly exchange two content containers&lt;/p>
&lt;/li>
&lt;li>
&lt;p>308 Using the return value of insert
The new standard&amp;rsquo;s insert returns an iterator after successful insertion, and the position of the iterator points to the newly inserted element. Using this feature, multiple elements can be repeatedly inserted at that position.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>308 Using emplace functions
The emplace function can achieve construction rather than copying.
This method can pass the parameters needed by the constructor without constructing the object and then making a copy.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>shrink_to_fit
Calling this function on a container will release extra space occupied by a vector or string. Generally not very useful&lt;/p>
&lt;/li>
&lt;li>
&lt;p>String type numeric conversion functions
to_string represents a group of overloaded functions that can convert data of int, double types to string type.
stoi can convert string to int, similar to other functions, including different bases and floating-point numbers&lt;/p>
&lt;/li>
&lt;li>
&lt;p>346 lambda expressions
Simple lambda expression
auto f = []{return 42;};
Here, a lambda expression is assigned to f. Later, f() can be called to get 42
How to pass parameters to a lambda expression?
[](int a, int b){return a&amp;gt;b;};
But sometimes you don&amp;rsquo;t want to pass a parameter, yet still have the lambda expression use external variables?
[&amp;amp;out](int a, int b){return a&amp;lt;out &amp;amp;&amp;amp; out &amp;lt; b;};
But what if I don&amp;rsquo;t want the above expression to return int, but double?
[&amp;amp;out](int a, int b)-&amp;gt;double{return a&amp;lt;out &amp;amp;&amp;amp; out &amp;lt; b;}&lt;/p>
&lt;/li>
&lt;li>
&lt;p>354 bind
The bind function can change a function call to a simple call&lt;/p>
&lt;/li>
&lt;li>
&lt;p>377 List initialization of associative containers
map&amp;lt;string, string&amp;gt; authors={ {&amp;ldquo;joncy&amp;rdquo;, &amp;ldquo;james&amp;rdquo;} };
set&lt;!-- raw HTML omitted --> exclude = {&amp;ldquo;the&amp;rdquo;, &amp;ldquo;but&amp;rdquo; };&lt;/p>
&lt;/li>
&lt;li>
&lt;p>380 List initialization of pair return types
Returning a list represented by {} to a pair type automatically converts.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>384 List initialization of pairs
When inserting pairs into a map, the simplest way is to insert a pair of key-values represented by braces. The inserted content will be initialized as a pair.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>394 Unordered containers
Add unordered in front of map and set to get unordered containers. Implemented using hash underneath. Requires implementation of a hash template for stored elements.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>400 Smart pointers
shared_ptr allows multiple pointers to point to the same object. unique_ptr exclusively owns the object it points to.
Smart pointers are templates, and when declaring them, you need to explicitly provide the type, similar to using vector.
shared_ptr&lt;!-- raw HTML omitted --> p1;&lt;/p>
&lt;/li>
&lt;/ul></description></item><item><title>Epoll TCP Server and Client Simple Example</title><link>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/epoll-tcp%E6%9C%8D%E5%8A%A1%E5%99%A8%E4%B8%8E%E5%AE%A2%E6%88%B7%E7%AB%AF%E7%AE%80%E6%98%8E%E4%BE%8B%E5%AD%90/</link><pubDate>Mon, 25 Dec 2017 02:52:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/epoll-tcp%E6%9C%8D%E5%8A%A1%E5%99%A8%E4%B8%8E%E5%AE%A2%E6%88%B7%E7%AB%AF%E7%AE%80%E6%98%8E%E4%BE%8B%E5%AD%90/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>epoll is a Linux-specific API that provides efficient file descriptor management. It&amp;rsquo;s commonly used to monitor the readability of multiple file descriptors in batch and is one of the effective methods for high-concurrency servers. Below is a simple example of an epoll server and client.&lt;/p>
&lt;h2 id="server-side">Server Side
&lt;/h2>&lt;pre tabindex="0">&lt;code>#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;sys/socket.h&amp;gt;
#include &amp;lt;netinet/in.h&amp;gt;
#include &amp;lt;arpa/inet.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;sys/epoll.h&amp;gt;
using namespace std;
const int MAX_EPOLL_EVENTS = 1000;
const int MAX_MSG_LEN = 1024;
void setFdNonblock(int fd)
{
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
}
void err_exit(const char *s){
printf(&amp;#34;error: %s\n&amp;#34;,s);
exit(0);
}
int create_socket(const char *ip, const int port_number)
{
struct sockaddr_in server_addr = {0};
/* Set IPv4 mode */
server_addr.sin_family = AF_INET; /* ipv4 */
/* Set port number */
server_addr.sin_port = htons(port_number);
/* Set host address */
if(inet_pton(server_addr.sin_family, ip, &amp;amp;server_addr.sin_addr) == -1){
err_exit(&amp;#34;inet_pton&amp;#34;);
}
/* Create socket */
int sockfd = socket(PF_INET, SOCK_STREAM, 0);
if(sockfd == -1){
err_exit(&amp;#34;socket&amp;#34;);
}
/* Set reuse mode */
int reuse = 1;
if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &amp;amp;reuse, sizeof(reuse)) == -1)
{
err_exit(&amp;#34;setsockopt&amp;#34;);
}
/* Bind to port */
if(bind(sockfd, (sockaddr *)&amp;amp;server_addr, sizeof(server_addr)) == -1){
err_exit(&amp;#34;bind&amp;#34;);
}
/* Set passive open */
if(listen(sockfd, 5) == -1){
err_exit(&amp;#34;listen&amp;#34;);
}
return sockfd;
}
int main(int argc, const char *argv[])
{
/* Help message */
if(argc &amp;lt; 3){
printf(&amp;#34;usage:%s ip port\n&amp;#34;, argv[0]);
exit(0);
}
/* Get server parameters */
const char * ip = argv[1];
const int port = atoi(argv[2]);
/* Create socket */
int sockfd = create_socket(ip, port);
printf(&amp;#34;success create sockfd %d\n&amp;#34;, sockfd);
setFdNonblock(sockfd);
/* Create epoll */
int epollfd = epoll_create1(0);
if(epollfd == -1) err_exit(&amp;#34;epoll_create1&amp;#34;);
/* Add sockfd to epollfd interest list */
struct epoll_event ev;
ev.data.fd = sockfd;
ev.events = EPOLLIN ;
if(epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &amp;amp;ev) == -1){
err_exit(&amp;#34;epoll_ctl1&amp;#34;);
}
/* Create a list to store events returned by wait */
struct epoll_event events[MAX_EPOLL_EVENTS] = {0};
/* Start waiting for all events registered on epoll */
while(1){
/* Wait for events */
printf(&amp;#34;begin wait\n&amp;#34;);
int number = epoll_wait(epollfd, events, MAX_EPOLL_EVENTS, -1);
printf(&amp;#34;end wait\n&amp;#34;);
sleep(1);
if(number &amp;gt; 0){
/* Traverse all events */
for (int i = 0; i &amp;lt; number; i++)
{
int eventfd = events[i].data.fd;
/* If the fd triggering the event is sockfd, someone has connected, and we need to accept them */
if(eventfd == sockfd){
printf(&amp;#34;accept new client...\n&amp;#34;);
struct sockaddr_in client_addr;
socklen_t client_addr_len = sizeof(client_addr);
int connfd = accept(sockfd, (struct sockaddr *)&amp;amp;client_addr, &amp;amp;client_addr_len);
setFdNonblock(connfd);
/* After accept, we need to add the file descriptor to the monitoring list */
struct epoll_event ev;
ev.data.fd = connfd;
ev.events = EPOLLIN;
if(epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &amp;amp;ev) == -1){
err_exit(&amp;#34;epoll_ctl2&amp;#34;);
}
printf(&amp;#34;accept new client end.\n&amp;#34;);
}
/* If the triggering fd is not sockfd, it&amp;#39;s the newly added connfd */
else{
/* Read content until encountering a newline, then display the content */
printf(&amp;#34;read start...\n&amp;#34;);
while(1){
char buff = -1;
int ret = read(eventfd, &amp;amp;buff, 1);
if(ret &amp;gt; 0){
printf(&amp;#34;%c&amp;#34;, buff);
}
if(buff == &amp;#39;\n&amp;#39;){
break;
}
else if (ret == 0){
printf(&amp;#34;client close.\n&amp;#34;);
close(eventfd);
epoll_ctl(epollfd, EPOLL_CTL_DEL, eventfd, NULL);
break;
}
else if (ret &amp;lt; 0){
printf(&amp;#34;read error.\n&amp;#34;);
break;
}
}
printf(&amp;#34;read end.\n&amp;#34;);
}
}
}
}
}
&lt;/code>&lt;/pre>&lt;h2 id="client-side">Client Side
&lt;/h2>&lt;pre tabindex="0">&lt;code>#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;arpa/inet.h&amp;gt;
#include &amp;lt;sys/socket.h&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;iostream&amp;gt;
using namespace std;
void err_exit(const char *s){
printf(&amp;#34;error: %s\n&amp;#34;,s);
exit(0);
}
int create_socket(const char *ip, const int port_number)
{
struct sockaddr_in server_addr = {0};
/* Set IPv4 mode */
server_addr.sin_family = AF_INET; /* ipv4 */
/* Set port number */
server_addr.sin_port = htons(port_number);
/* Set host address */
if(inet_pton(PF_INET, ip, &amp;amp;server_addr.sin_addr) == -1){
err_exit(&amp;#34;inet_pton&amp;#34;);
}
/* Create socket */
int sockfd = socket(PF_INET, SOCK_STREAM, 0);
if(sockfd == -1){
err_exit(&amp;#34;socket&amp;#34;);
}
if(connect(sockfd, (struct sockaddr*)&amp;amp;server_addr, sizeof(server_addr)) == -1){
err_exit(&amp;#34;connect&amp;#34;);
}
return sockfd;
}
int main(int argc, const char *argv[]){
if(argc &amp;lt; 3){
printf(&amp;#34;usage:%s ip port\n&amp;#34;, argv[0]);
exit(0);
}
/* Get server parameters */
const char * ip = argv[1];
const int port = atoi(argv[2]);
//Create socket
int sock = create_socket(ip, port);
//Initiate request to server (specific IP and port)
while(1){
string buff;
getline(cin, buff);
if(buff == &amp;#34;exit&amp;#34;) break;
write(sock, buff.c_str(), buff.size());
char end = &amp;#39;\n&amp;#39;;
write(sock, &amp;amp;end, 1);
}
close(sock);
return 0;
}
&lt;/code>&lt;/pre>&lt;h2 id="compilation">Compilation
&lt;/h2>&lt;p>Save the above text as socket_server.cpp and socket_client.cpp, then compile and link the programs.&lt;/p>
&lt;pre tabindex="0">&lt;code>g++ -Wall socket_server.cpp -o server &amp;amp;&amp;amp; g++ -Wall socket_client.cpp -o client
&lt;/code>&lt;/pre>&lt;h2 id="execution">Execution
&lt;/h2>&lt;pre tabindex="0">&lt;code>./server localhost 1234
&lt;/code>&lt;/pre>&lt;pre tabindex="0">&lt;code>./client localhost 1234
&lt;/code>&lt;/pre>&lt;p>Entering text on the client side and pressing Enter will display it on the server side. Press Ctrl+C or type &amp;ldquo;exit&amp;rdquo; to close the client.&lt;/p>
&lt;h2 id="execution-flow">Execution Flow
&lt;/h2>&lt;p>The server first creates a passive open socket file descriptor, then adds this file descriptor to the epoll interest list. It then enters a loop. Whenever the interest list&amp;rsquo;s wait ends, it means the corresponding file descriptor can be operated on. When a client connects to the passive open socket file descriptor, it indicates a client has connected, and the passive open file descriptor can be accepted. The new file descriptor created after accept is the file descriptor for communicating with the client, which is also added to the interest list. When the client sends data, this file descriptor will also generate a readable signal, causing the wait to end. At this point, it enters processing mode, reading and displaying the data sent by the client.&lt;/p></description></item><item><title>SOCKET Learning Notes</title><link>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/socket-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</link><pubDate>Mon, 25 Dec 2017 02:34:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/socket-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>Interview questions mentioning epoll involve socket programming. To understand epoll principles in depth, we first need to understand socket programming. A socket is an inter-process communication (IPC) mechanism, even in networks. This means the main entities communicating in a network are processes, not computers. Socket learning includes how to establish servers and clients, and how to use socket APIs.&lt;/p>
&lt;h4 id="fdsocketdomain-type-protocol">fd=socket(domain, type, protocol);
&lt;/h4>&lt;p>The socket call can be used to create a socket, for example,
domain can specify IPv4, type can specify TCP, and protocol is generally 0.&lt;/p>
&lt;ul>
&lt;li>
&lt;p>domain
domain specifies the communication scope and the type of communication address. There are several classic types:
UNIX IPV4 IPV6, corresponding to parameters AF_UNIX AF_INET AF_INET6.
The domain parameters all start with AF, representing address families. PF stands for protocol families. Originally, address families and protocol families were designed to have many-to-many relationships, but during implementation, each protocol family corresponds to exactly one address family. So basically, specifying the domain determines the protocol family and the address family.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>type
Socket indicates whether it&amp;rsquo;s a stream or a datagram, which essentially means TCP or UDP. For TCP, it&amp;rsquo;s SOCK_STREAM; for UDP, it&amp;rsquo;s SOCK_DGRAM.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h4 id="bindint-sockfd-const-struct-sockaddr-addr-socklen_t-addrlen">bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
&lt;/h4>&lt;p>This call is used to bind a socket to an address. After that, TCP packets can be sent, and in some cases, UDP packets can also be sent through write, but only data from the peer socket can be read on this socket.&lt;/p>
&lt;ul>
&lt;li>sockaddr
This structure has an integer representing the address type, followed by a char array. As we&amp;rsquo;ll see, depending on the use case, other data structures are passed in, but they are generic.&lt;/li>
&lt;/ul>
&lt;h4 id="listenint-sockfd-int-backlog">listen(int sockfd, int backlog)
&lt;/h4>&lt;p>Marks a socket descriptor as passive. It can be connected by active sockets. backlog is used to limit the number of pending connections.&lt;/p>
&lt;h4 id="acceptint-sockfd-struct-sockaddr-addr-socklen_t--addrlen">accept(int sockfd, struct sockaddr *addr, socklen_t * addrlen);
&lt;/h4>&lt;p>The accept call blocks and waits for an incoming request on the file descriptor sockfd. Once the request succeeds, a new socket is created, and this new socket connects with the other party.&lt;/p>
&lt;ul>
&lt;li>addr
Returns the address of the other party&lt;/li>
&lt;li>addrlen
Passes in the length of addr, indicating the maximum length that can be written.&lt;/li>
&lt;/ul>
&lt;h4 id="connectint-sockfd-const-struct-sockaddr--addr-socklen_t-addrlen">connect(int sockfd, const struct sockaddr * addr, socklen_t addrlen);
&lt;/h4>&lt;p>Connects sockfd to the address described by addr.&lt;/p>
&lt;h4 id="closeint-fd">close(int fd)
&lt;/h4>&lt;p>Used to close the connection&lt;/p>
&lt;h4 id="read-write">read write
&lt;/h4>&lt;p>Used for reading from or writing to sockfd&lt;/p>
&lt;h4 id="recvfromint-sockfd-void-buffer-size_t-length-int-flags-struct-sockaddr-src_addr-socklen_t-addrlen">recvfrom(int sockfd, void *buffer, size_t length, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
&lt;/h4>&lt;h4 id="sendtoint-sockfd-const-void-buffer-size_t-length-int-flags-const-struct-sockaddr-dest_addr-socklen_t-addrlen">sendto(int sockfd, const void *buffer, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
&lt;/h4>&lt;p>Used to send and receive UDP packets. The server side cannot use the listen function and accept function, and the client side cannot use the connect function.&lt;/p>
&lt;h4 id="unix-domain">unix domain
&lt;/h4>&lt;p>Using the APIs above, communication through files on the local machine can be implemented.
The sockaddr used by unix domain is sockaddr_un, represented as follows:&lt;/p>
&lt;pre tabindex="0">&lt;code>struct sockaddr_un{
sa_family_t sun_family;
char sun_path[108];
}
&lt;/code>&lt;/pre>&lt;h4 id="network-byte-order">Network Byte Order
&lt;/h4>&lt;p>The network byte order follows big-endian, while x86 is a little-endian structure. The conversion is done using the following functions:&lt;/p>
&lt;ul>
&lt;li>htons&lt;/li>
&lt;li>htonl&lt;/li>
&lt;li>ntohs&lt;/li>
&lt;li>ntohl
h is for host, n is for net, s is for 16-bit, and l is for 32-bit. s and l stand for short and long, although these standards are no longer used that way now.&lt;/li>
&lt;/ul>
&lt;h4 id="internet-socket-address-structure">Internet Socket Address Structure
&lt;/h4>&lt;p>The socket address used in networks is sockaddr_in, defined as follows:&lt;/p>
&lt;pre tabindex="0">&lt;code>struct sockaddr_in{
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
unsigned char __pad[X];
}
&lt;/code>&lt;/pre>&lt;p>As you can see, the difference is that the char array is replaced by a port and an address. sin is the abbreviation of socket Internet, which is as poor an abbreviation as sun.&lt;/p>
&lt;h4 id="internet-socket-address-conversion">Internet Socket Address Conversion
&lt;/h4>&lt;p>APIs for converting string address formats to binary address formats:&lt;/p>
&lt;h4 id="inet_ptonint-domain-const-char-src-void-addrptr">inet_pton(int domain, const char *src, void *addrptr);
&lt;/h4>&lt;p>This function is used to convert the string contained in src to a &lt;strong>network byte order&lt;/strong> binary address, storing it in addrptr. p stands for presentation, meaning a human-readable address.&lt;/p>
&lt;h4 id="const-char--inet_ntopint-domain-const-void-addrptr-char-dst_str-size_t-len">const char * inet_ntop(int domain, const void *addrptr, char *dst_str, size_t len);
&lt;/h4>&lt;p>This function converts a network byte order binary address to a human-readable address, writing it to dst_str. The buffer size is passed in by len.&lt;/p>
&lt;h4 id="getaddrinfoconst-char-host-const-char-service-const-struct-addrinfo-hints-struct-result">getaddrinfo(const char *host, const char *service, const struct addrinfo *hints, struct **result);
&lt;/h4>&lt;p>This function returns a socket address and port number given a host name and service name.
getaddrinfo takes host, service, and hints as inputs, where the host parameter includes a hostname or an IPv4 string. Service is a service name or a port number. After calling this function, freeaddrinfo should be used to free the space.&lt;/p>
&lt;h4 id="getnameinfoconst-struct-sockaddr-addr-socklen_t-addrlen-char-host-size_t-hostlen-char-service-size_t-servlen-int-flags">getnameinfo(const struct sockaddr *addr, socklen_t addrlen, char *host, size_t hostlen, char *service, size_t servlen, int flags);
&lt;/h4>&lt;p>Given a socket address structure, returns a host and service name string.&lt;/p>
&lt;h4 id="setsockoptint-sockfd-int-level-int-optname-const-void-optval-socklen_t-optlen">setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
&lt;/h4>&lt;p>sockfd is the file descriptor pointing to the socket. The level parameter specifies the protocol to which the socket option applies, such as TCP or IP, indicating the socket API layer where the option takes effect. Generally, this option is set to SOL_SOCKET, indicating that it applies to the socket API layer. The optname parameter indicates the option we expect to set, optvalue is used to set the value of that option, which can be an integer or a pointer to a structure pointing to a buffer, and the optlen parameter is the size of the region pointed to by that pointer.&lt;/p>
&lt;p>For example, to set sockfd to the reuseaddr property, you can call:&lt;/p>
&lt;pre tabindex="0">&lt;code> int reuse = 1;
if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &amp;amp;reuse, sizeof(reuse)) == -1)
{
err_exit(&amp;#34;setsockopt&amp;#34;);
}
&lt;/code>&lt;/pre>&lt;h4 id="getsockoptint-sockfd-int-level-int-optname-void-optval-socklen_t-optlen">getsockopt(int sockfd, int level, int optname, void *optval, socklen_t optlen);
&lt;/h4>&lt;p>The usage is the same as above, but it&amp;rsquo;s for getting rather than setting.&lt;/p></description></item><item><title>Other I/O Models Summary</title><link>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/%E5%85%B6%E4%BB%96-i-o-%E6%A8%A1%E5%9E%8B%E6%80%BB%E7%BB%93/</link><pubDate>Thu, 21 Dec 2017 06:18:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/%E5%85%B6%E4%BB%96-i-o-%E6%A8%A1%E5%9E%8B%E6%80%BB%E7%BB%93/</guid><description>&lt;ul>
&lt;li>I/O Multiplexing&lt;/li>
&lt;li>Signal-Driven I/O&lt;/li>
&lt;li>Linux-specific epoll&lt;/li>
&lt;/ul>
&lt;h2 id="io-multiplexing">I/O Multiplexing
&lt;/h2>&lt;p>File descriptors are examined through the select system call or the poll system call.&lt;/p>
&lt;ul>
&lt;li>select function
&lt;code>int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);&lt;/code>
The parameters respectively mean: the range of file descriptors to test (0 to fd-1), file descriptors that meet three different condition requirements, and timeout period.&lt;/li>
&lt;li>poll function
&lt;code>int poll(struct pollfd fds[], nfds_t nfds, int timeout);&lt;/code>
The parameters are: an array containing elements with file descriptors, states to be checked, returned states; the array length; and the timeout period.&lt;/li>
&lt;/ul>
&lt;p>The difference between the two is that the array size passed into poll is not limited by the size defined by fd_set. Poll&amp;rsquo;s events and revents are a bit more versatile. Select is used more widely. In newer versions, select&amp;rsquo;s performance has been optimized to be comparable to poll.&lt;/p>
&lt;ul>
&lt;li>epoll function
The epoll function includes three APIs: epoll_create(), epoll_ctl(), and epoll_wait().
Among the three calls, epoll has the best performance and most effectiveness. However, it can only be used on Linux.&lt;/li>
&lt;/ul>
&lt;p>This is mainly because a server will have multiple socket connections, and if a thread were created for each socket, it would consume considerable resources. Therefore, epoll is needed to optimize performance.&lt;/p></description></item><item><title>C++ Object-Oriented Programming Summary</title><link>https://nansenli.com/post/jianshu/c++/c++-%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1-%E6%80%BB%E7%BB%93/</link><pubDate>Thu, 14 Dec 2017 05:53:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/c++/c++-%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1-%E6%80%BB%E7%BB%93/</guid><description>&lt;h3 id="introduction">Introduction
&lt;/h3>&lt;p>The biggest difference between C++ and C is object-oriented programming. Although I understand some concepts about object-oriented programming and have written some functions, I rarely use them in practice. This article is a quick summary of Chapter 15 of C++ Primer.&lt;/p>
&lt;h3 id="overview">Overview
&lt;/h3>&lt;p>Object-oriented programming, also known as OOP, has three core ideas: data abstraction, inheritance, and dynamic binding. Data abstraction refers to the separation of declaration and implementation, inheritance refers to the subclass obtaining all members of the parent class, and dynamic binding means calling different functions based on the class type when invoking class functions.&lt;/p>
&lt;h3 id="declaring-base-classes-and-derived-classes">Declaring Base Classes and Derived Classes
&lt;/h3>&lt;p>The following code provides a simple example, declaring a base class animal and a derived class cat.&lt;/p>
&lt;pre tabindex="0">&lt;code>#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;iostream&amp;gt;
class animal
{
public:
virtual void eat(){};
};
class cat : public animal
{
public:
void eat() { std::cout &amp;lt;&amp;lt; &amp;#34;eat fish&amp;#34; &amp;lt;&amp;lt; std::endl; }
};
int main()
{
cat c;
c.eat();
}
&lt;/code>&lt;/pre>&lt;h3 id="type-conversion-and-inheritance">Type Conversion and Inheritance
&lt;/h3>&lt;p>Pointers and references of derived classes can be converted to pointers or references of base classes, but pointers or references of base classes cannot be converted to those of derived classes. This means that an object pointed to by a base class pointer could be a base class or a derived class, but definitely not a parent class of the base class.&lt;/p>
&lt;p>A derived class object can be assigned to a base class, but only the base class portion will be copied.&lt;/p>
&lt;h3 id="virtual-functions">Virtual Functions
&lt;/h3>&lt;p>Virtual functions are resolved at runtime. Continuing with our example above, suppose we write a new function called doeat that internally calls the eat method of animal. For animal, eat is &amp;ldquo;eat meat or grass&amp;rdquo;, but the final displayed result is &amp;ldquo;eat fish&amp;rdquo;. This proves that at runtime, when the doeat function calls animal&amp;rsquo;s eat method, the eat method finds the real object cat&amp;rsquo;s eat method and calls it.&lt;/p>
&lt;pre tabindex="0">&lt;code>class animal
{
public:
virtual void eat() { std::cout &amp;lt;&amp;lt; &amp;#34;eat meat or grass&amp;#34; &amp;lt;&amp;lt; std::endl; };
};
class cat : public animal
{
public:
void eat() { std::cout &amp;lt;&amp;lt; &amp;#34;eat fish&amp;#34; &amp;lt;&amp;lt; std::endl; }
};
void doeat(animal &amp;amp;a) { a.eat(); }
int main()
{
cat c;
doeat(c);
}
&lt;/code>&lt;/pre>&lt;p>Additionally, to develop good habits, it&amp;rsquo;s best to use the override keyword in derived classes to explicitly declare overridden virtual functions.&lt;/p>
&lt;h3 id="pure-virtual-functions">Pure Virtual Functions
&lt;/h3>&lt;p>Assigning a value of 0 after defining a virtual function declares a pure virtual function. Pure virtual functions are equivalent to interfaces, and derived classes must implement these interfaces.&lt;/p>
&lt;h2 id="abstract-base-classes">Abstract Base Classes
&lt;/h2>&lt;p>Classes containing pure virtual functions are abstract base classes. Abstract base classes cannot be instantiated.&lt;/p>
&lt;h3 id="example-text-line-query-program">Example: Text Line Query Program
&lt;/h3>&lt;p>Given a text, we need to query its contents. Supporting word queries and logical queries.&lt;/p></description></item><item><title>C++ Virtual Functions Summary</title><link>https://nansenli.com/post/jianshu/c++/c++-%E8%99%9A%E5%87%BD%E6%95%B0-%E6%80%BB%E7%BB%93/</link><pubDate>Thu, 14 Dec 2017 05:53:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/c++/c++-%E8%99%9A%E5%87%BD%E6%95%B0-%E6%80%BB%E7%BB%93/</guid><description>&lt;p>Virtual functions are C++&amp;rsquo;s way of implementing polymorphism.&lt;/p>
&lt;ul>
&lt;li>What is polymorphism?
Polymorphism is often viewed as the third pillar of object-oriented programming after encapsulation and inheritance.
To give a simple example, if we have a class Animal, and classes like Chicken and Dog that inherit from Animal, and they respond differently to the same message derived from the parent class Animal. For instance, the Animal class has an action &amp;ldquo;makeSound()&amp;rdquo;, while the Chicken class would &amp;ldquo;crow()&amp;rdquo; and the Dog class would &amp;ldquo;bark()&amp;rdquo; - this is called polymorphism.
According to Wikipedia, polymorphism can be defined as &amp;ldquo;the ability to associate different specific behaviors with a single generalized notation.&amp;rdquo; It refers to when a computer program runs, the same message may be sent to objects of different classes, and the system can trigger corresponding class methods based on the object&amp;rsquo;s class, resulting in different behaviors. Simply put, polymorphism means that the same message given to different objects triggers different actions.&lt;/li>
&lt;/ul>
&lt;p>Virtual functions enable dynamic polymorphism, which is determined at runtime. Only during program execution does the system decide whether to call the base class or child class function. The system determines which function to call based on the object that the base class pointer points to.&lt;/p>
&lt;ul>
&lt;li>
&lt;p>How to declare a virtual function?
Add the virtual keyword before the declaration to declare a virtual function. The function is declared in the parent class and implemented in the child class.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>What is a pure virtual function?
A pure virtual function is a function that is assigned a value of 0 when declaring a virtual function. After using this declaration method, the inheriting class must implement this virtual function.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>How are virtual functions implemented?
Through a virtual table (vtable). Each object has a virtual table pointer that points to the virtual table. The virtual table is essentially an array, not a linked list. The addresses of virtual functions are stored in the virtual table in the order of declaration.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>How is the virtual function table implemented?
To accomplish the functionality of virtual functions, the compiler (note, this is why C++ is sometimes called compiler-oriented programming) creates a table for each class that declares internal virtual functions, called a vtable. In the vtable, the addresses of virtual functions of specific types are placed in the order of declaration. Each class with virtual functions contains a pointer, called a vpointer or vptr, pointing to this vtable. Whenever a call to a class&amp;rsquo;s virtual function occurs, the compiler redirects to call the function in the vtable pointed to by this vptr, rather than statically calling a specific function.&lt;/p>
&lt;/li>
&lt;/ul></description></item><item><title>Linux: Using popen to Execute Bash Commands and Get Return Strings</title><link>https://nansenli.com/post/jianshu/linux%E7%BC%96%E7%A8%8B/linux-popen%E6%89%A7%E8%A1%8Cbash%E5%91%BD%E4%BB%A4%E5%B9%B6%E8%8E%B7%E5%8F%96%E8%BF%94%E5%9B%9E%E5%AD%97%E7%AC%A6%E4%B8%B2/</link><pubDate>Wed, 08 Nov 2017 07:22:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/linux%E7%BC%96%E7%A8%8B/linux-popen%E6%89%A7%E8%A1%8Cbash%E5%91%BD%E4%BB%A4%E5%B9%B6%E8%8E%B7%E5%8F%96%E8%BF%94%E5%9B%9E%E5%AD%97%E7%AC%A6%E4%B8%B2/</guid><description>&lt;p>The system command returns an integer value, making it difficult to determine specific outcomes. You can use popen to execute bash commands and retrieve return values. Note that popen captures the standard output as the return string. If you need to get error output content, you must redirect stderr to stdout.&lt;/p>
&lt;pre tabindex="0">&lt;code>void system_cmd(const char * command, char * result)
{
FILE *fpRead;
fpRead = popen(command, &amp;#34;r&amp;#34;);
char buf[1024] = {0};
memset(buf,&amp;#39;\0&amp;#39;,sizeof(buf));
while(fgets(buf,1024-1,fpRead)!=NULL)
{
if (buf[strlen(buf) - 1] == &amp;#39;\n&amp;#39;) {
buf[strlen(buf) - 1] = &amp;#39;\0&amp;#39;; //remove newline character
}
strcpy(result, buf);
}
if(fpRead!=NULL)
pclose(fpRead);
}
&lt;/code>&lt;/pre>&lt;p>Usage example:&lt;/p>
&lt;pre tabindex="0">&lt;code> char pid[10]={0};
system_cmd(&amp;#34;pgrep bash&amp;#34;, pid);
&lt;/code>&lt;/pre></description></item><item><title>Key Points for Installing GPU Version of TensorFlow on Deepin</title><link>https://nansenli.com/post/jianshu/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/deepin-%E5%AE%89%E8%A3%85-gpu%E7%89%88-tensorflow-%E8%A6%81%E7%82%B9/</link><pubDate>Wed, 13 Sep 2017 10:44:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/deepin-%E5%AE%89%E8%A3%85-gpu%E7%89%88-tensorflow-%E8%A6%81%E7%82%B9/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>Although there are many tutorials on how to install TensorFlow on Ubuntu, there isn&amp;rsquo;t a single article explaining how to install TensorFlow on Deepin systems. Here I&amp;rsquo;ll explain the key points of the installation process.&lt;/p>
&lt;h2 id="process">Process
&lt;/h2>&lt;ul>
&lt;li>First, download the Nvidia Linux driver from the official website. If you can&amp;rsquo;t run it, you&amp;rsquo;ll need to modify the driver&amp;rsquo;s execution permissions using chmod u+x. For information on installing graphics drivers on Deepin, refer to this article: &lt;a class="link" href="https://wiki.deepin.org/index.php?title=%E6%98%BE%E5%8D%A1" target="_blank" rel="noopener"
>https://wiki.deepin.org/index.php?title=%E6%98%BE%E5%8D%A1&lt;/a>&lt;/li>
&lt;li>Install Docker-ce. You must follow the official installation guide; this is the only method to properly install the latest version of Docker: &lt;a class="link" href="https://wiki.deepin.org/index.php?title=Docker" target="_blank" rel="noopener"
>https://wiki.deepin.org/index.php?title=Docker&lt;/a>&lt;/li>
&lt;li>Download Nvidia-docker. Find the Ubuntu installation package and installation steps on the Nvidia-Docker page on GitHub and follow them.&lt;/li>
&lt;li>Next, run &lt;code>docker run -it -p 8888:8888 tensorflow/tensorflow:latest-gpu&lt;/code>. At this point, you can enter the official TensorFlow GPU version container. Open localhost:8888 to see the Jupyter page.&lt;/li>
&lt;/ul>
&lt;h2 id="pitfalls">Pitfalls
&lt;/h2>&lt;p>When installing the Nvidia graphics driver, according to the installation software&amp;rsquo;s instructions, you need to close the Linux desktop system X server. At this point, you can use tty1&lt;del>6 for terminal operations. However, if Deepin has already installed its own Nvidia driver, when stopping the lightdm service, Deepin closes both the X server and the monitor, making it impossible to display the tty1&lt;/del>6 screens. This issue prevents Nvidia driver installation. Even after manually uninstalling the official Deepin Nvidia driver through complex uninstallation commands, when switching to tty1~6 and closing the graphical interface, the tty screen still shuts down, resulting in a black screen and making it impossible to continue with driver uninstallation and reinstallation of the official Nvidia driver.&lt;/p>
&lt;h2 id="solution">Solution
&lt;/h2>&lt;p>To avoid problems with Nvidia driver installation while maintaining a clean and scientific operation, you must install the official Nvidia Linux driver during the first installation of the Deepin system, before any graphics drivers are installed. First, press the shortcut &amp;ldquo;Ctrl+Alt+F2&amp;rdquo; to enter tty2, then enter &lt;code>sudo systemctl stop lightdm&lt;/code> to stop the lightdm service. At this point, when the computer closes the X server, it won&amp;rsquo;t cause the monitor to shut down. Then run &lt;code>chmod u+x NVIDIA-Linux-x86_64-352.55.run&lt;/code> to grant executable permissions, followed by &lt;code>sudo ./NVIDIA-Linux-x86_64-352.55.run&lt;/code> to install the driver file. After restarting, you can use the official closed-source driver normally.&lt;/p>
&lt;h2 id="follow-up">Follow-up
&lt;/h2>&lt;p>Using a GPU to run TensorFlow programs is very fast. Normal programs can speed up by more than 10 times, and some programs can speed up by 50 to 100 times, so using a GPU for TensorFlow programming is essential.
Additionally, I&amp;rsquo;m not sure if it&amp;rsquo;s an issue with Deepin, Nvidia-Docker, or the driver, but after the computer goes into standby mode, it causes errors in the TensorFlow container. Therefore, don&amp;rsquo;t let the machine enter standby mode during training.&lt;/p></description></item><item><title>TensorFlow: A Simple Introduction Case Study</title><link>https://nansenli.com/post/jianshu/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/tensorflow-%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E7%9A%84%E5%85%A5%E9%97%A8%E7%94%A8%E4%BE%8B/</link><pubDate>Wed, 09 Aug 2017 07:08:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/tensorflow-%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E7%9A%84%E5%85%A5%E9%97%A8%E7%94%A8%E4%BE%8B/</guid><description>&lt;h2 id="case-study">Case Study
&lt;/h2>&lt;p>Suppose I have a series of x-y data points with a linear relationship between x and y. How would we fit a straight line to this data?&lt;/p>
&lt;p>In the program below, train_x is a series of numbers between -1 and 1, and train_y is twice x plus 10, with a random number between 0 and 1 added.
Next, we build the model. X and Y are tensor placeholders waiting to be initialized. During optimization, the XY values in the model will continuously change to data from train_x and train_y, and then the optimizer will optimize by changing the slope w and intercept b in a direction that reduces error. Through iteration, w and b will eventually make the model fit the data.&lt;/p>
&lt;p>After the model is built, we start running it. First, we open a session, and we must remember to initialize all variables. Next, we iterate through all the data 10 times. In each iteration, we input a coordinate, calculate the error, and use gradient descent to correct w and b. Finally, we output the calculated values of w and b.&lt;/p>
&lt;pre tabindex="0">&lt;code>import tensorflow as tf
import numpy as np
train_x = np.linspace(-1, 1, 101)
train_y = 2 * train_x + np.random.rand(train_x.shape[0]) + 10
X = tf.placeholder(&amp;#34;float&amp;#34;)
Y = tf.placeholder(&amp;#34;float&amp;#34;)
w = tf.Variable(0.0, name = &amp;#34;w&amp;#34;)
b = tf.Variable(0.0, name = &amp;#34;b&amp;#34;)
loss = tf.square(Y - tf.multiply(X,w) - b)
train_op = tf.train.GradientDescentOptimizer(0.01).minimize(loss)
with tf.Session() as session:
session.run(tf.global_variables_initializer())
for i in range(10):
for x,y in zip(train_x, train_y):
session.run(train_op, feed_dict={X:x, Y:y})
print(&amp;#34;w: &amp;#34;, session.run(w))
print(&amp;#34;b: &amp;#34;, session.run(b))
&lt;/code>&lt;/pre>&lt;p>Running results:
&lt;img src="http://otwwkzjm5.bkt.clouddn.com/17-8-9/99539186.jpg"
loading="lazy"
alt="Results"
>&lt;/p></description></item><item><title>TensorFlow Hello World Introduction</title><link>https://nansenli.com/post/jianshu/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/tensorflow-hello-world-%E5%85%A5%E9%97%A8/</link><pubDate>Tue, 08 Aug 2017 09:58:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/tensorflow-hello-world-%E5%85%A5%E9%97%A8/</guid><description>&lt;h2 id="quick-installation-guide">Quick Installation Guide
&lt;/h2>&lt;ul>
&lt;li>Step 1: Install Docker&lt;/li>
&lt;/ul>
&lt;p>&lt;a class="link" href="https://www.docker-cn.com/community-edition#/download" target="_blank" rel="noopener"
>https://www.docker-cn.com/community-edition#/download&lt;/a>&lt;/p>
&lt;p>Then configure the official Chinese mirror.&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/5832fec39b1c.jpg"
loading="lazy"
alt="image.png"
>&lt;/p>
&lt;ul>
&lt;li>Step 2: Set up TensorFlow environment&lt;/li>
&lt;/ul>
&lt;p>&lt;code>$ docker run -it -p 8888:8888 tensorflow/tensorflow&lt;/code>
Running this command will automatically download the TensorFlow image, provided that the repository mirror is set to a Chinese mirror; otherwise, the download will be very slow. After running the command, the terminal will display a URL that prompts you to open a web page. When you open this URL, you&amp;rsquo;ll see the TensorFlow Jupyter editing environment, where we&amp;rsquo;ll input all our code.&lt;/p>
&lt;ul>
&lt;li>Mounting Docker file directory
If we need to access local files, we need to mount a local folder to the container directory. Close the container, reopen it, and use &lt;code>-v host_directory:container_directory&lt;/code> for mounting.
&lt;code>docker run -v /Users/hahaha/tensorflow/:/notebooks -it -p 8888:8888 tensorflow/tensorflow&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>Where /Users/hahaha/tensorflow/ is a folder on my Mac, and notebooks is the default Jupyter editing directory in the TensorFlow container.&lt;/p>
&lt;h2 id="running-hello-world-code">Running Hello World Code
&lt;/h2>&lt;p>&lt;img src="https://nansenli.com/img/rescued/5f59450b005f.png"
loading="lazy"
alt="image.png"
>
Create a new Python 2 Jupyter file, enter the following code, and then click the play button. At this point, a &amp;ldquo;Hello, TensorFlow!&amp;rdquo; string should appear below, indicating that the program has run successfully.&lt;/p>
&lt;h2 id="program-explanation">Program Explanation
&lt;/h2>&lt;p>From this simple code, we can see that TensorFlow is very easy to use. It&amp;rsquo;s imported as a standard Python library without requiring additional services to be started. For those new to TensorFlow, you might wonder why we need to use tf.constant() and tf.Session() to output a &amp;ldquo;Hello World&amp;rdquo; string when Python itself could do it. The reason is that TensorFlow defines and runs models and training through Graphs and Sessions, which provides significant benefits for complex models and distributed training.&lt;/p>
&lt;p>First, in TensorFlow, there are two concepts: Graph and Operation. Operation represents what needs to be computed. A Graph contains many Operations. A Session is used to execute Operations in a Graph.&lt;/p>
&lt;h2 id="basic-usage">Basic Usage
&lt;/h2>&lt;p>When using TensorFlow, you must understand that TensorFlow:&lt;/p>
&lt;ul>
&lt;li>Uses a &lt;code>graph&lt;/code> to represent computational tasks&lt;/li>
&lt;li>Executes the graph in a &lt;code>context&lt;/code> called a &lt;code>Session&lt;/code>&lt;/li>
&lt;li>Represents data using &lt;code>tensors&lt;/code>&lt;/li>
&lt;li>Maintains state through &lt;code>Variables&lt;/code>&lt;/li>
&lt;li>Uses &lt;code>feed&lt;/code> and &lt;code>fetch&lt;/code> to assign values to or retrieve data from &lt;code>arbitrary operations&lt;/code>&lt;/li>
&lt;/ul>
&lt;h2 id="overview">Overview
&lt;/h2>&lt;p>TensorFlow is a programming system that uses graphs to represent computational tasks. Nodes in the graph are called &lt;strong>ops&lt;/strong> (short for operations). An op takes 0 or more Tensors, performs computations, and produces 0 or more Tensors. Each Tensor is a typed multi-dimensional array. For example, you can represent a small batch of images as a four-dimensional floating-point array with dimensions [batch, height, width, channels].&lt;/p>
&lt;p>A TensorFlow graph &lt;strong>describes&lt;/strong> the computation process. To perform computation, the graph must be launched in a &lt;code>session&lt;/code>. The &lt;code>session&lt;/code> distributes the graph&amp;rsquo;s ops to devices like CPUs or GPUs and provides methods to execute ops. After execution, these methods return the resulting tensors. In Python, the returned tensors are &lt;a class="link" href="http://www.numpy.org/" target="_blank" rel="noopener"
>numpy&lt;/a> &lt;code>ndarray&lt;/code> objects. In C and C++, the returned tensors are tensorflow::Tensor instances.&lt;/p></description></item><item><title>Using TensorFlow for a Kaggle Task — Titanic: Machine Learning from Disaster</title><link>https://nansenli.com/post/jianshu/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/%E4%BD%BF%E7%94%A8tensorflow%E5%AE%8C%E6%88%90kaggle%E4%BB%BB%E5%8A%A1%E6%B3%B0%E5%9D%A6%E5%B0%BC%E5%85%8B%E5%8F%B7titanic--machine-learning-from-disaster/</link><pubDate>Fri, 04 Aug 2017 10:08:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/%E4%BD%BF%E7%94%A8tensorflow%E5%AE%8C%E6%88%90kaggle%E4%BB%BB%E5%8A%A1%E6%B3%B0%E5%9D%A6%E5%B0%BC%E5%85%8B%E5%8F%B7titanic--machine-learning-from-disaster/</guid><description>&lt;h2 id="import-necessary-libraries">Import Necessary Libraries
&lt;/h2>&lt;pre tabindex="0">&lt;code>import csv
import tensorflow as tf
import numpy as np
import random
import sys
import pandas as pd
from pandas import DataFrame
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
&lt;/code>&lt;/pre>&lt;h2 id="read-source-file-and-display">Read Source File and Display
&lt;/h2>&lt;p>In this section, we&amp;rsquo;ll work with basic CSV operations and display the results.
We&amp;rsquo;ll read the train.csv file downloaded from Kaggle and show its contents.&lt;/p>
&lt;pre tabindex="0">&lt;code>trainFilePath = &amp;#39;./train.csv&amp;#39;
trainSize = 0
def testCSV(filePath):
with open(filePath, &amp;#39;rb&amp;#39;) as trainFile:
global trainSize
csvReader = csv.reader(trainFile)
dataList = [data for data in csvReader]
df = DataFrame(dataList[1:], columns=dataList[0])
trainSize = len(df)
print(df)
print(&amp;#34;trainSize&amp;#34;, trainSize)
testCSV(trainFilePath)
&lt;/code>&lt;/pre>&lt;h2 id="read-source-file-extract-data-and-build-neural-network">Read Source File, Extract Data, and Build Neural Network
&lt;/h2>&lt;p>In this section, we&amp;rsquo;ll extract gender, class, ticket fare, and SibSp from the source file to fit the survival probability.
Then we&amp;rsquo;ll build a 5-layer neural network with 3 hidden layers containing 4-10-20-10-2 neurons respectively.
Finally, we&amp;rsquo;ll execute the reading function.&lt;/p>
&lt;pre tabindex="0">&lt;code>def readTrainDataCSV(filePath):
global trainData, targetData, classifier
with open(filePath, &amp;#39;rb&amp;#39;) as trainFile:
csvReader = csv.reader(trainFile)
dataList = [data for data in csvReader]
dataSize = len(dataList) - 1
trainData = np.ndarray((dataSize, 4), dtype=np.float32)
targetData = np.ndarray((dataSize, 1), dtype=np.int32)
trainDataFrame = DataFrame(dataList[1:], columns=dataList[0])
trainDataFrame_fliter = trainDataFrame.loc[:,[&amp;#39;Pclass&amp;#39;,&amp;#39;Sex&amp;#39;,&amp;#39;SibSp&amp;#39;,&amp;#39;Fare&amp;#39;,&amp;#39;Survived&amp;#39;]]
for i in range(dataSize):
thisData = np.array(trainDataFrame_fliter.iloc[i])
Pclass,Sex,SibSp,Fare,Survived = thisData
Pclass = float(Pclass)
Sex = 0 if Sex == &amp;#39;female&amp;#39; else 1
SibSp = float(SibSp)
Fare = float(Fare)
Survived = int(Survived)
print(Pclass,Sex,SibSp,Fare,Survived)
trainData[i,:] = [Pclass,Sex,SibSp,Fare]
targetData[i,:] = [Survived]
print(thisData)
print(trainData)
print(targetData)
feature_columns = [tf.contrib.layers.real_valued_column(&amp;#34;&amp;#34;, dimension=4)]
classifier = tf.contrib.learn.DNNClassifier(feature_columns=feature_columns,
hidden_units=[10, 20, 10],
n_classes=2)
# model_dir=&amp;#34;/tmp/titanic_model&amp;#34;)
readTrainDataCSV(trainFilePath)
&lt;/code>&lt;/pre>&lt;h2 id="create-input-data">Create Input Data
&lt;/h2>&lt;p>We&amp;rsquo;ll wrap the training data and labels into a tuple and return it.&lt;/p>
&lt;pre tabindex="0">&lt;code>def get_train_inputs():
x = tf.constant(trainData)
y = tf.constant(targetData)
print(x)
print(y)
return x, y
get_train_inputs()
&lt;/code>&lt;/pre>&lt;h2 id="train-the-model">Train the Model
&lt;/h2>&lt;p>Now we start training the neural network.&lt;/p>
&lt;pre tabindex="0">&lt;code>def train():
classifier.fit(input_fn=get_train_inputs, steps=2000)
train()
&lt;/code>&lt;/pre>&lt;h2 id="check-accuracy">Check Accuracy
&lt;/h2>&lt;p>We use the entire dataset to check accuracy. Note that we should use a validation set for this task, but since this is just for demonstration purposes, we&amp;rsquo;ll skip that step.&lt;/p>
&lt;pre tabindex="0">&lt;code>accuracy_score = classifier.evaluate(input_fn=get_train_inputs,
steps=1)[&amp;#34;accuracy&amp;#34;]
print(&amp;#34;accuracy:&amp;#34;,accuracy_score)
&lt;/code>&lt;/pre>&lt;h2 id="read-test-set-and-output-results">Read Test Set and Output Results
&lt;/h2>&lt;p>In this section, we&amp;rsquo;ll read the test data from Kaggle and output the results to a file, which will ultimately be submitted to the official website.&lt;/p>
&lt;pre tabindex="0">&lt;code>testFilePath = &amp;#39;./test.csv&amp;#39;
def readTestDataCSV(filePath):
global testData, PassengerIdStart
with open(filePath, &amp;#39;rb&amp;#39;) as testFile:
csvReader = csv.reader(testFile)
dataList = [data for data in csvReader]
dataSize = len(dataList)-1
trainDataFrame = DataFrame(dataList[1:], columns=dataList[0])
trainDataFrame_fliter = trainDataFrame.loc[:,[&amp;#39;Pclass&amp;#39;,&amp;#39;Sex&amp;#39;,&amp;#39;SibSp&amp;#39;,&amp;#39;Fare&amp;#39;]]
testData = np.ndarray((dataSize, 4), dtype=np.float32)
PassengerIdStart = trainDataFrame[&amp;#39;PassengerId&amp;#39;][0]
PassengerIdStart = int(PassengerIdStart)
print(&amp;#39;PassengerId&amp;#39;,PassengerIdStart)
for i in range(dataSize):
thisData = np.array(trainDataFrame_fliter.iloc[i])
Pclass,Sex,SibSp,Fare = thisData
Pclass = float(Pclass)
Sex = 0 if Sex == &amp;#39;female&amp;#39; else 1
SibSp = float(SibSp)
Fare = 0 if Fare==&amp;#39;&amp;#39; else float(Fare)
print(Pclass,Sex,SibSp,Fare)
testData[i,:] = [Pclass,Sex,SibSp,Fare]
print(thisData)
print(testData)
def testData_samples():
return testData
readTestDataCSV(testFilePath)
predictions = list(classifier.predict(input_fn=testData_samples))
print(predictions)
with open(&amp;#39;predictions.csv&amp;#39;, &amp;#39;wb&amp;#39;) as csvfile:
writer = csv.writer(csvfile, dialect=&amp;#39;excel&amp;#39;)
writer.writerow([&amp;#39;PassengerId&amp;#39;,&amp;#39;Survived&amp;#39;])
PassengerId = PassengerIdStart
for i in predictions:
writer.writerow([PassengerId, i])
PassengerId += 1
&lt;/code>&lt;/pre>&lt;p>Finally, using only 4 features, we achieved an accuracy of 75%. The next goal is to utilize the other available data.&lt;/p></description></item><item><title>Building a Personal Blog with Flask</title><link>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/%E5%9F%BA%E4%BA%8Eflask%E7%9A%84%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2%E6%90%AD%E5%BB%BA/</link><pubDate>Sun, 30 Jul 2017 17:38:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E7%BD%91%E7%BB%9C/%E5%9F%BA%E4%BA%8Eflask%E7%9A%84%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2%E6%90%AD%E5%BB%BA/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>The source code can be run directly, supports markdown syntax, supports email registration, and any issues can be referenced in &lt;a class="link" href="https://book.douban.com/subject/26274202/" target="_blank" rel="noopener"
>《Flask Web Development: Web Application Development in Action with Python》&lt;/a>.&lt;/p>
&lt;p>Blog website: &lt;a class="link" href="http://www.unrealblue.cc" target="_blank" rel="noopener"
>http://www.unrealblue.cc&lt;/a>
Project repository: &lt;a class="link" href="https://github.com/linanwx/unrealblue-blog" target="_blank" rel="noopener"
>https://github.com/linanwx/unrealblue-blog&lt;/a>&lt;/p>
&lt;h2 id="preview">Preview
&lt;/h2>&lt;p>&lt;img src="https://nansenli.com/img/rescued/a0027251f6b8.jpg"
loading="lazy"
alt="image.png"
>&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/ee8b6a7c6286.jpg"
loading="lazy"
alt="markdown syntax"
>&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/2482752767fe.jpg"
loading="lazy"
alt="login"
>&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/a827700f36d3.jpg"
loading="lazy"
alt="homepage"
>&lt;/p>
&lt;h2 id="deployment-process">Deployment Process
&lt;/h2>&lt;p>First, to verify the program functionality, you need to deploy it on your local machine. After that, you can use nginx as a reverse proxy tool to expose the port, so that others can access your blog on the public network. Then follow the same steps on the server. The specific process is as follows:&lt;/p>
&lt;ol>
&lt;li>Install the virtualenv Python virtual environment with &lt;code>pip install virtualenv&lt;/code> or &lt;code>pip3 install virtualenv&lt;/code>. Then use virtualenv to create a venv environment in a suitable directory, for example under this project with &lt;code>virtualenv venv&lt;/code>.&lt;/li>
&lt;li>Activate the virtualenv environment by running the activate script in the venv directory: &lt;code>. venv/bin/activate&lt;/code>. Note the position of the dot. After this, you will see the (venv) marker at the beginning of the command line.&lt;/li>
&lt;li>Install all modules listed in requirements.txt within the virtual environment: &lt;code>pip3 install -r requirements.txt&lt;/code>. If installation is too slow, you may need to configure a domestic pip source. See the pip official page for how to change the pip source.&lt;/li>
&lt;li>Import environment variables by creating an &lt;code>env&lt;/code> file in the project directory with the following fields:&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>&lt;code>MAIL_USERNAME=email@example.com&lt;/code> (Email used by the server to send verification codes, an email account with smtp service enabled. The program uses QQ email by default, modify the config file to use other types of email)&lt;/li>
&lt;li>&lt;code>MAIL_PASSWORD=password&lt;/code> (Password for the above email, note that QQ email uses a special 16-digit password)&lt;/li>
&lt;li>&lt;code>FLASK_ADMIN=admin@example.com&lt;/code> (After the server is running, an account created with this email will be the administrator)&lt;/li>
&lt;li>&lt;code>MAIL_SERVER=smtp.qq.com&lt;/code> (Email server address)&lt;/li>
&lt;li>&lt;code>FLASKY_MAIL_SENDER=example@foxmail.com&lt;/code> (Sender&amp;rsquo;s email)&lt;/li>
&lt;/ul>
&lt;ol start="5">
&lt;li>Set up database migration by entering the following commands:&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>&lt;code>python manager.py db init&lt;/code> (The init command creates a migration repository, which will add a migrations folder)&lt;/li>
&lt;li>&lt;code>python manager.py db migrate -m &amp;quot;initial migration&amp;quot;&lt;/code> (The migrate command is used to automatically create migration scripts)&lt;/li>
&lt;li>&lt;code>python manager.py db upgrade&lt;/code> (Update the database. The first time you use this command, it will create a new database called data-dev.sqlite)&lt;/li>
&lt;/ul>
&lt;ol start="6">
&lt;li>Deploy the program: &lt;code>python manager.py deploy&lt;/code>&lt;/li>
&lt;li>Run the program locally: &lt;code>python manager.py runserver&lt;/code>. Open http://127.0.0.1:5000 to view it, press Ctrl+C to exit the program.&lt;/li>
&lt;li>If running on a server and you want to preserve data, you can copy the migrations folder and the data-dev.sqlite database to the server, then use &lt;code>./venv/bin/gunicorn -w 4 -b 127.0.0.1:8080 manager:app&lt;/code>. At this point, you should be able to see the webpage on port 8080, and this port is exposed to the external network. Enter the server address in your local browser, and you will be able to see the blog.&lt;/li>
&lt;/ol></description></item><item><title>Deep Learning Beginner's Study Notes One (Udacity)</title><link>https://nansenli.com/post/jianshu/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E4%B8%80%E4%BC%98%E8%BE%BE%E5%AD%A6%E5%9F%8E/</link><pubDate>Wed, 19 Jul 2017 07:42:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E4%B8%80%E4%BC%98%E8%BE%BE%E5%AD%A6%E5%9F%8E/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>On a whim, I decided to learn machine learning. These are notes from my learning process.&lt;/p>
&lt;h2 id="preparation">Preparation
&lt;/h2>&lt;p>I made these preparations:&lt;/p>
&lt;ul>
&lt;li>A MacBook with Python environment set up, and numpy and matplotlib installed&lt;/li>
&lt;li>Registered for Udacity&amp;rsquo;s free &amp;ldquo;Deep Learning&amp;rdquo; course (in collaboration with Google)&lt;/li>
&lt;li>Studied Liao Xuefeng&amp;rsquo;s Python introductory tutorial&lt;/li>
&lt;li>Spent two days roughly browsing through &amp;ldquo;Machine Learning in Action&amp;rdquo;&lt;/li>
&lt;/ul>
&lt;p>Learning these fundamentals should be sufficient for the upcoming Udacity course.&lt;/p>
&lt;h2 id="course-one-from-machine-learning-to-deep-learning">Course One: From Machine Learning to Deep Learning
&lt;/h2>&lt;p>&lt;img src="https://nansenli.com/img/rescued/71c43f961173.png"
loading="lazy"
alt="Introduction"
>&lt;/p>
&lt;p>Sections 1-8 mainly introduce the current state of deep learning and related knowledge.&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/ff643a3a1ec4.png"
loading="lazy"
alt="image.png"
>&lt;/p>
&lt;p>Sections 9-12 introduce the softmax model.&lt;/p>
&lt;p>After a rough review of &amp;ldquo;Machine Learning in Action,&amp;rdquo; I learned that machine learning consists of several classification and clustering algorithms. On the surface, machine learning appears to be a collection of classification and clustering algorithms. Among these algorithms, one called logistic regression classification was introduced.&lt;/p>
&lt;p>In sections 9-12, the focus is on the classifier model—logistic regression, using the softmax function as the classification function.&lt;/p>
&lt;ul>
&lt;li>What is the softmax function?&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/cb4b58f90872.jpg"
loading="lazy"
alt="softmax"
>&lt;/p>
&lt;p>This image illustrates what a softmax function is. For each number z in the original sequence, we calculate exp(z), and the proportion of each new number&amp;rsquo;s magnitude becomes the softmax probability for that number.&lt;/p>
&lt;ul>
&lt;li>Properties&lt;/li>
&lt;/ul>
&lt;p>If the inputs are scaled up proportionally, the classifier&amp;rsquo;s results become more polarized and confident. If the inputs are scaled down proportionally, the classifier&amp;rsquo;s results tend toward the average and lack confidence.&lt;/p>
&lt;ul>
&lt;li>Algorithm&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">import&lt;/span> numpy &lt;span style="color:#66d9ef">as&lt;/span> np
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">def&lt;/span> &lt;span style="color:#a6e22e">softmax&lt;/span>(x):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;&amp;#34;&amp;#34;Compute softmax values for each sets of scores in x.&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> expList &lt;span style="color:#f92672">=&lt;/span> [np&lt;span style="color:#f92672">.&lt;/span>exp(i) &lt;span style="color:#66d9ef">for&lt;/span> i &lt;span style="color:#f92672">in&lt;/span> x]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> expSum &lt;span style="color:#f92672">=&lt;/span> sum(expList)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> x &lt;span style="color:#f92672">=&lt;/span> [i&lt;span style="color:#f92672">/&lt;/span>expSum &lt;span style="color:#66d9ef">for&lt;/span> i &lt;span style="color:#f92672">in&lt;/span> expList]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> np&lt;span style="color:#f92672">.&lt;/span>array(x)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://nansenli.com/img/rescued/8ba75ea46168.png"
loading="lazy"
alt="image.png"
>&lt;/p>
&lt;p>Sections 13-14 mainly discuss One-Hot encoding. After the softmax function provides a sequence of probability values, how do we determine the classification? For example, a sequence where the highest probability is 1 and others are 0 is called One-Hot encoding. This type of encoding has already determined the classification.&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/d0050afa0519.png"
loading="lazy"
alt="Cross-entropy"
>&lt;/p>
&lt;p>Sections 15-16 cover cross-entropy. Softmax can calculate a probability sequence, and OneHot is a determined classification. So how do we calculate the distance from a probability sequence to a specific classification? We use cross-entropy to measure this distance.&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/7a800c91612a.png"
loading="lazy"
alt="image.png"
>&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/a1e862b4bd40.png"
loading="lazy"
alt="image.png"
>&lt;/p>
&lt;p>Sections 17-20 explain how to use this classifier. Section 18 specifically discusses why special initial data is needed.&lt;/p>
&lt;pre tabindex="0">&lt;code>sum = 1000000000
for i in range(1000000):
sum += 0.000001
sum -= 1000000000
print(sum)
&lt;/code>&lt;/pre>&lt;p>The result of running this code is not 1. If we change sum to a very small number, like 1, instead of 1000000000, we find that the error becomes smaller. Based on this reason, we want our initial data to always have a mean of 0 and consistent variance in all directions. For example, for a grayscale image with pixel values from 0-255, we need to subtract 128 and then divide by 128, so that each number is between -1 and 1. Such initial data is more suitable for training.&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/34f543ea3c1e.png"
loading="lazy"
alt="image.png"
>&lt;/p>
&lt;p>This way, we can proceed with training. Reviewing the video content: xi is the training data matrix, w is a random weight matrix. For performance reasons, random values are taken from a normal distribution with an axis of 0 and very small variance. Then we calculate the probability sequence and the distance to the target. Then we compute the average distance to all targets. Our goal is to make this distance smaller, so we optimize the weight matrix along the direction of gradient descent while optimizing the intercept b. We repeat this process continuously until we reach a local optimum.&lt;/p>
&lt;ul>
&lt;li>Installing Docker&lt;/li>
&lt;/ul>
&lt;p>&lt;a class="link" href="https://www.docker-cn.com/community-edition#/download" target="_blank" rel="noopener"
>https://www.docker-cn.com/community-edition#/download&lt;/a>&lt;/p>
&lt;p>Configure the official Chinese mirror.&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/1a9fbd212a79.png"
loading="lazy"
alt="image.png"
>&lt;/p>
&lt;ul>
&lt;li>Installing Jupyter Notebook&lt;/li>
&lt;/ul>
&lt;p>&lt;code>$ pip3 install jupyter&lt;/code>
&lt;code>$ jupyter notebook&lt;/code>
You can now use the jupyter notebook command to open a Jupyter editor.&lt;/p>
&lt;ul>
&lt;li>Setting up TensorFlow environment&lt;/li>
&lt;/ul>
&lt;p>&lt;code>$ docker run -it -p 8888:8888 tensorflow/tensorflow&lt;/code>
Running this command will automatically download the TensorFlow image, provided that the repository mirror is set to a Chinese mirror; otherwise, the download will be very slow. After running the command, you&amp;rsquo;ll be prompted to open a webpage. When you open this URL, you&amp;rsquo;ll see the TensorFlow Jupyter editing environment, assuming Jupyter Notebook is installed correctly.&lt;/p>
&lt;ul>
&lt;li>Mounting Docker&amp;rsquo;s file directory
We need to import the official assignments. Close the container, reopen it, and use &lt;code>-v host_directory:container_directory&lt;/code> for mounting.
&lt;code>docker run -v /Users/hahaha/tensorflow/:/notebooks -it -p 8888:8888 tensorflow/tensorflow&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>Where /Users/hahaha/tensorflow/ is a folder on my Mac, and notebooks is the default Jupyter editing directory in TensorFlow.&lt;/p>
&lt;p>Paste the first assignment file, 1_notmnist.ipynb, into the mounted directory on the host. This file can be found here: &lt;a class="link" href="https://github.com/tensorflow/tensorflow/blob/master/tensorflow/examples/udacity/1_notmnist.ipynb" target="_blank" rel="noopener"
>1_notmnist.ipynb&lt;/a>&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/7faf184ae282.png"
loading="lazy"
alt="Assignment One Content"
>&lt;/p>
&lt;h2 id="assignment-code-segment-one">Assignment Code Segment One
&lt;/h2>&lt;p>First, run the import statements in the first code segment. There should be no errors. If you see red error output, it means these imports were not successful.&lt;/p>
&lt;pre tabindex="0">&lt;code># These are all the modules we&amp;#39;ll be using later. Make sure you can import them
# before proceeding further.
from __future__ import print_function
# print function
import matplotlib.pyplot as plt
# plotting tool
import numpy as np
# matrix calculations
import os
# file paths
import sys
# file output
import tarfile
# decompression
from IPython.display import display, Image
# display images
from scipy import ndimage
# image processing
from sklearn.linear_model import LogisticRegression
# logistic regression module for linear models
from six.moves.urllib.request import urlretrieve
# url handling
from six.moves import cPickle as pickle
# data processing
# Config the matplotlib backend as plotting inline in IPython
%matplotlib inline
# matplotlib is the most famous Python chart plotting extension library,
# it supports outputting various formats of graphical images, and can use various GUI interface libraries to display charts interactively.
# Using the %matplotlib command can embed matplotlib charts directly into the Notebook,
# or display charts using a specified interface library, it has a parameter specifying how matplotlib charts are displayed.
# inline indicates embedding charts in the Notebook.
&lt;/code>&lt;/pre>&lt;h2 id="assignment-code-segment-two">Assignment Code Segment Two
&lt;/h2>&lt;p>Next is the second code segment, which will download letter sets for training and testing, approximately 300MB in size. After successful download, you can see these two files in the mounted directory.&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/66a1b044606a.png"
loading="lazy"
alt="Assignment"
>&lt;/p>
&lt;pre tabindex="0">&lt;code>url = &amp;#39;https://commondatastorage.googleapis.com/books1000/&amp;#39;
last_percent_reported = None
data_root = &amp;#39;.&amp;#39; # Change me to store data elsewhere
def download_progress_hook(count, blockSize, totalSize):
&amp;#34;&amp;#34;&amp;#34;A hook to report the progress of a download. This is mostly intended for users with
slow internet connections. Reports every 5% change in download progress.
&amp;#34;&amp;#34;&amp;#34;
# Hook function to display download progress in real-time
global last_percent_reported
percent = int(count * blockSize * 100 / totalSize)
if last_percent_reported != percent:
if percent % 5 == 0:
sys.stdout.write(&amp;#34;%s%%&amp;#34; % percent)
sys.stdout.flush()
else:
sys.stdout.write(&amp;#34;.&amp;#34;)
sys.stdout.flush()
last_percent_reported = percent
def maybe_download(filename, expected_bytes, force=False):
&amp;#34;&amp;#34;&amp;#34;Download a file if not present, and make sure it&amp;#39;s the right size.&amp;#34;&amp;#34;&amp;#34;
dest_filename = os.path.join(data_root, filename)
# data_root is the current directory, add the filename to it, set as the location to save the file
if force or not os.path.exists(dest_filename):
# force is to force download, ignoring already downloaded files
print(&amp;#39;Attempting to download:&amp;#39;, filename)
filename, _ = urlretrieve(url + filename, dest_filename, reporthook=download_progress_hook)
# Use urlretrieve to download the file, with the hook attached
print(&amp;#39;\nDownload Complete!&amp;#39;)
statinfo = os.stat(dest_filename)
# Get information about the downloaded file
if statinfo.st_size == expected_bytes:
# Correct size
print(&amp;#39;Found and verified&amp;#39;, dest_filename)
else:
# Wrong size, prompt user to use a browser to download
raise Exception(
&amp;#39;Failed to verify &amp;#39; + dest_filename + &amp;#39;. Can you get to it with a browser?&amp;#39;)
return dest_filename
train_filename = maybe_download(&amp;#39;notMNIST_large.tar.gz&amp;#39;, 247336696)
test_filename = maybe_download(&amp;#39;notMNIST_small.tar.gz&amp;#39;, 8458043)
&lt;/code>&lt;/pre>&lt;h2 id="assignment-code-segment-three">Assignment Code Segment Three
&lt;/h2>&lt;p>Extracting use cases&lt;/p>
&lt;pre tabindex="0">&lt;code>num_classes = 10
# Total number of digits
np.random.seed(133)
# Initialize random seed
def maybe_extract(filename, force=False):
# Assuming already extracted
root = os.path.splitext(os.path.splitext(filename)[0])[0] # remove .tar.gz
# splitext(filename)[0] removes one suffix, used twice to remove both suffixes, i.e., remove the .tar.gz suffix
if os.path.isdir(root) and not force:
# You may override by setting force=True.
# If already extracted, don&amp;#39;t extract again
print(&amp;#39;%s already present - Skipping extraction of %s.&amp;#39; % (root, filename))
else:
print(&amp;#39;Extracting data for %s. This may take a while. Please wait.&amp;#39; % root)
tar = tarfile.open(filename)
sys.stdout.flush()
tar.extractall(data_root)
tar.close()
# Extract to the current directory
data_folders = [
os.path.join(root, d) for d in sorted(os.listdir(root))
if os.path.isdir(os.path.join(root, d))]
if len(data_folders) != num_classes:
raise Exception(
&amp;#39;Expected %d folders, one per class. Found %d instead.&amp;#39; % (
num_classes, len(data_folders)))
print(data_folders)
# Check if the number of extracted directories matches expectations, and print the extracted directories
return data_folders
train_folders = maybe_extract(train_filename)
test_folders = maybe_extract(test_filename)
&lt;/code>&lt;/pre>&lt;h2 id="question-one">Question One
&lt;/h2>&lt;p>Write code to display information about the extracted file contents&lt;/p>
&lt;ul>
&lt;li>Reference answer&lt;/li>
&lt;/ul>
&lt;pre tabindex="0">&lt;code>import random
import matplotlib.image as mpimg
def plot_samples(data_folders, sample_size, title=None):
fig = plt.figure()
# Create empty figure
if title: fig.suptitle(title, fontsize=16, fontweight=&amp;#39;bold&amp;#39;)
# Add title
for folder in data_folders:
# Loop through each letter
image_files = os.listdir(folder)
image_sample = random.sample(image_files, sample_size)
# Randomly select a certain number of images from that letter
for image in image_sample:
image_file = os.path.join(folder, image)
ax = fig.add_subplot(len(data_folders), sample_size, sample_size * data_folders.index(folder) +
image_sample.index(image) + 1)
# Create a subplot
image = mpimg.imread(image_file)
# Load subplot image
ax.imshow(image)
# Display subplot image
ax.set_axis_off()
# Turn off subplot coordinate lines
fig.set_size_inches(18.5, 10.5)
# Set the display size of the image
plt.show()
plot_samples(train_folders, 20, &amp;#39;Train&amp;#39;)
plot_samples(test_folders, 20, &amp;#39;Test&amp;#39;)
&lt;/code>&lt;/pre>&lt;p>Running results:&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/94887f5b6eab.png"
loading="lazy"
alt="Training.png"
>&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/3930a2cfe6b0.png"
loading="lazy"
alt="Testing.png"
>&lt;/p>
&lt;p>As we can see, some of the training data has issues.&lt;/p>
&lt;pre tabindex="0">&lt;code>
## Assignment Code Segment Four
After this, we need to normalize the data, which means transforming each image pixel from 0~255 to -1.0~1.0, and persisting it to a file.
&lt;/code>&lt;/pre>&lt;p>image_size = 28 # Pixel width and height.
pixel_depth = 255.0 # Number of levels per pixel.&lt;/p>
&lt;h1 id="image-width-height-and-pixel-depth">Image width, height and pixel depth
&lt;/h1>&lt;p>def load_letter(folder, min_num_images):
&amp;ldquo;&amp;ldquo;&amp;ldquo;Load the data for a single letter label.&amp;rdquo;&amp;rdquo;&amp;rdquo;&lt;/p>
&lt;h1 id="process-files-in-a-folder-belonging-to-one-letter">Process files in a folder belonging to one letter
&lt;/h1>&lt;p>image_files = os.listdir(folder)&lt;/p>
&lt;h1 id="list-all-files-in-that-directory">List all files in that directory
&lt;/h1>&lt;p>dataset = np.ndarray(shape=(len(image_files), image_size, image_size),
dtype=np.float32)&lt;/p>
&lt;h1 id="create-a-dataset-with-length-equal-to-number-of-files-width-and-height-of-28">Create a dataset with length equal to number of files, width and height of 28
&lt;/h1>&lt;p>print(folder)&lt;/p>
&lt;h1 id="print-directory">Print directory
&lt;/h1>&lt;p>num_images = 0&lt;/p>
&lt;h1 id="initialize-num_images">Initialize num_images
&lt;/h1>&lt;p>for image in image_files:&lt;/p>
&lt;h1 id="process-each-file">Process each file
&lt;/h1>&lt;pre>&lt;code>image_file = os.path.join(folder, image)
&lt;/code>&lt;/pre>
&lt;h1 id="get-complete-file-path">Get complete file path
&lt;/h1>&lt;pre>&lt;code>try:
image_data = (ndimage.imread(image_file).astype(float) -
pixel_depth / 2) / pixel_depth
&lt;/code>&lt;/pre>
&lt;h1 id="read-in-the-image-and-normalize-it">Read in the image and normalize it
&lt;/h1>&lt;pre>&lt;code> if image_data.shape != (image_size, image_size):
&lt;/code>&lt;/pre>
&lt;h1 id="check-image-width-and-height">Check image width and height
&lt;/h1>&lt;pre>&lt;code> raise Exception('Unexpected image shape: %s' % str(image_data.shape))
dataset[num_images, :, :] = image_data
&lt;/code>&lt;/pre>
&lt;h1 id="read-into-the-dataset">Read into the dataset
&lt;/h1>&lt;pre>&lt;code> num_images = num_images + 1
&lt;/code>&lt;/pre>
&lt;h1 id="increment-image-number">Increment image number
&lt;/h1>&lt;pre>&lt;code>except IOError as e:
&lt;/code>&lt;/pre>
&lt;h1 id="if-file-cant-be-read-skip-it">If file can&amp;rsquo;t be read, skip it
&lt;/h1>&lt;pre>&lt;code> print('Could not read:', image_file, ':', e, '- it\'s ok, skipping.')
&lt;/code>&lt;/pre>
&lt;p>dataset = dataset[0:num_images, :, :]&lt;/p>
&lt;h1 id="if-fewer-files-were-read-than-the-minimum-required">If fewer files were read than the minimum required
&lt;/h1>&lt;p>if num_images &amp;lt; min_num_images:
raise Exception(&amp;lsquo;Many fewer images than expected: %d &amp;lt; %d&amp;rsquo; %
(num_images, min_num_images))&lt;/p>
&lt;h1 id="display-number-of-missing-files">Display number of missing files
&lt;/h1>&lt;p>print(&amp;lsquo;Full dataset tensor:&amp;rsquo;, dataset.shape)&lt;/p>
&lt;h1 id="display-file-count-image-width-and-height">Display file count, image width and height
&lt;/h1>&lt;p>print(&amp;lsquo;Mean:&amp;rsquo;, np.mean(dataset))&lt;/p>
&lt;h1 id="mean-value">Mean value
&lt;/h1>&lt;p>print(&amp;lsquo;Standard deviation:&amp;rsquo;, np.std(dataset))&lt;/p>
&lt;h1 id="standard-deviation">Standard deviation
&lt;/h1>&lt;p>return dataset&lt;/p>
&lt;p>def maybe_pickle(data_folders, min_num_images_per_class, force=False):
dataset_names = []
for folder in data_folders:&lt;/p>
&lt;h1 id="process-each-letter-folder">Process each letter folder
&lt;/h1>&lt;pre>&lt;code>set_filename = folder + '.pickle'
&lt;/code>&lt;/pre>
&lt;h1 id="set-output-file">Set output file
&lt;/h1>&lt;pre>&lt;code>dataset_names.append(set_filename)
&lt;/code>&lt;/pre>
&lt;h1 id="set-processed-folders">Set processed folders
&lt;/h1>&lt;pre>&lt;code>if os.path.exists(set_filename) and not force:
# You may override by setting force=True.
&lt;/code>&lt;/pre>
&lt;h1 id="check-if-processed-file-already-exists">Check if processed file already exists
&lt;/h1>&lt;pre>&lt;code> print('%s already present - Skipping pickling.' % set_filename)
else:
print('Pickling %s.' % set_filename)
dataset = load_letter(folder, min_num_images_per_class)
&lt;/code>&lt;/pre>
&lt;h1 id="normalize-all-images-in-this-folder">Normalize all images in this folder
&lt;/h1>&lt;pre>&lt;code> try:
with open(set_filename, 'wb') as f:
pickle.dump(dataset, f, pickle.HIGHEST_PROTOCOL)
&lt;/code>&lt;/pre>
&lt;h1 id="persist-data-save-to-disk-instead-of-keeping-in-memory">Persist data, save to disk instead of keeping in memory
&lt;/h1>&lt;pre>&lt;code> except Exception as e:
print('Unable to save data to', set_filename, ':', e)
&lt;/code>&lt;/pre>
&lt;p>return dataset_names&lt;/p>
&lt;p>train_datasets = maybe_pickle(train_folders, 45000)
test_datasets = maybe_pickle(test_folders, 1800)&lt;/p>
&lt;pre tabindex="0">&lt;code>
## Question Two
Display processed images
- Reference answer
&lt;/code>&lt;/pre>&lt;p>def plot_samples_2(data_folders, sample_size, title=None):
fig = plt.figure()&lt;/p>
&lt;h1 id="create-empty-figure">Create empty figure
&lt;/h1>&lt;pre>&lt;code>if title: fig.suptitle(title, fontsize=16, fontweight='bold')
&lt;/code>&lt;/pre>
&lt;h1 id="add-title">Add title
&lt;/h1>&lt;pre>&lt;code>for folder in data_folders:
&lt;/code>&lt;/pre>
&lt;h1 id="loop-through-each-letter">Loop through each letter
&lt;/h1>&lt;pre>&lt;code> with open(folder, 'rb') as pk_f:
data = pickle.load(pk_f)
for index, image in enumerate(data):
if index &amp;lt; sample_size :
&lt;/code>&lt;/pre>
&lt;h1 id="randomly-select-a-certain-number-of-images-from-that-letter">Randomly select a certain number of images from that letter
&lt;/h1>&lt;pre>&lt;code> ax = fig.add_subplot(len(data_folders), sample_size, sample_size * data_folders.index(folder) +
index + 1)
&lt;/code>&lt;/pre>
&lt;h1 id="load-subplot-image">Load subplot image
&lt;/h1>&lt;pre>&lt;code> ax.imshow(image)
&lt;/code>&lt;/pre>
&lt;h1 id="display-subplot-image">Display subplot image
&lt;/h1>&lt;pre>&lt;code> ax.set_axis_off()
&lt;/code>&lt;/pre>
&lt;h1 id="turn-off-subplot-coordinate-lines">Turn off subplot coordinate lines
&lt;/h1>&lt;pre>&lt;code>fig.set_size_inches(18.5, 10.5)
&lt;/code>&lt;/pre>
&lt;h1 id="set-the-display-size-of-the-image">Set the display size of the image
&lt;/h1>&lt;pre>&lt;code>plt.show()
&lt;/code>&lt;/pre>
&lt;p>plot_samples_2(train_datasets, 20, &amp;lsquo;Train&amp;rsquo;)
plot_samples_2(test_datasets, 20, &amp;lsquo;Test&amp;rsquo;)&lt;/p>
&lt;pre tabindex="0">&lt;code>
![image.png](/img/rescued/c09ea5ab69e9.png)
![image.png](/img/rescued/d4363496653b.png)
## Question Three
Check if the number of files under each letter is similar.
- Reference answer
&lt;/code>&lt;/pre>&lt;p>file_path = &amp;rsquo;notMNIST_large/{0}.pickle&amp;rsquo;
for ele in &amp;lsquo;ABCDEFJHIJ&amp;rsquo;:
with open(file_path.format(ele), &amp;lsquo;rb&amp;rsquo;) as pk_f:&lt;/p>
&lt;h1 id="loop-through-each-directory">Loop through each directory
&lt;/h1>&lt;pre>&lt;code> dat = pickle.load(pk_f)
&lt;/code>&lt;/pre>
&lt;h1 id="load-the-persisted-file-in-this-directory">Load the persisted file in this directory
&lt;/h1>&lt;pre>&lt;code>print('number of pictures in {}.pickle = '.format(ele), dat.shape[0])
&lt;/code>&lt;/pre>
&lt;h1 id="print-relevant-information">Print relevant information
&lt;/h1>&lt;pre tabindex="0">&lt;code>Results show that the numbers are basically consistent.
![Question 3 Result](/img/rescued/31bcb36f8c25.png)
## Code Segment—Data Splitting
Data cannot be loaded all at once into memory. This code segment splits the data.
&lt;/code>&lt;/pre>&lt;p>def make_arrays(nb_rows, img_size):
if nb_rows:
dataset = np.ndarray((nb_rows, img_size, img_size), dtype=np.float32)&lt;/p>
&lt;h1 id="create-an-empty-set-data-type-is-a-matrix-with-rows-length-img_size-width-img_size-height-data-type-is-32-bit-float">Create an empty set, data type is a matrix with rows length, img_size width, img_size height, data type is 32-bit float
&lt;/h1>&lt;pre>&lt;code>labels = np.ndarray(nb_rows, dtype=np.int32)
&lt;/code>&lt;/pre>
&lt;h1 id="create-a-label-data-type-is-32-bit-integer-length-is-rows">Create a label, data type is 32-bit integer, length is rows
&lt;/h1>&lt;p>else:
dataset, labels = None, None
return dataset, labels&lt;/p>
&lt;h1 id="return-created-data-type">Return created data type
&lt;/h1>&lt;p>def merge_datasets(pickle_files, train_size, valid_size=0):
num_classes = len(pickle_files)&lt;/p>
&lt;h1 id="number-of-categories-to-process">Number of categories to process
&lt;/h1>&lt;p>valid_dataset, valid_labels = make_arrays(valid_size, image_size)&lt;/p>
&lt;h1 id="build-validation-dataset-length-is-validation-length">Build validation dataset, length is validation length
&lt;/h1>&lt;p>train_dataset, train_labels = make_arrays(train_size, image_size)&lt;/p>
&lt;h1 id="build-training-dataset-length-is-training-length">Build training dataset, length is training length
&lt;/h1>&lt;p>vsize_per_class = valid_size // num_classes
tsize_per_class = train_size // num_classes&lt;/p>
&lt;h1 id="calculate-average-length-for-each-category-with-given-training-and-validation-lengths">Calculate average length for each category with given training and validation lengths
&lt;/h1>&lt;p>start_v, start_t = 0, 0&lt;/p>
&lt;h1 id="initialize-indices-start_v-is-the-start-of-validation-data-start_t-is-the-start-of-training-data">Initialize indices, start_v is the start of validation data, start_t is the start of training data
&lt;/h1>&lt;p>end_v, end_t = vsize_per_class, tsize_per_class&lt;/p>
&lt;h1 id="initialize-indices-end_v-is-the-end-of-validation-data-end_t-is-the-end-of-training-data">Initialize indices, end_v is the end of validation data, end_t is the end of training data
&lt;/h1>&lt;p>end_l = vsize_per_class + tsize_per_class&lt;/p>
&lt;h1 id="initialize-indices-end_l-is-the-end-of-the-letter-set-equal-to-length-of-validation-data-for-each-category--length-of-training-data">Initialize indices, end_l is the end of the letter set, equal to length of validation data for each category + length of training data
&lt;/h1>&lt;p>for label, pickle_file in enumerate(pickle_files):&lt;/p>
&lt;h1 id="loop-through-each-pickle_file">Loop through each pickle_file
&lt;/h1>&lt;pre>&lt;code>try:
with open(pickle_file, 'rb') as f:
&lt;/code>&lt;/pre>
&lt;h1 id="open-this-persistence-file">Open this persistence file
&lt;/h1>&lt;pre>&lt;code> letter_set = pickle.load(f)
&lt;/code>&lt;/pre>
&lt;h1 id="load-dataset">Load dataset
&lt;/h1>&lt;pre>&lt;code> # let's shuffle the letters to have random validation and training set
np.random.shuffle(letter_set)
&lt;/code>&lt;/pre>
&lt;h1 id="shuffle-the-dataset">Shuffle the dataset
&lt;/h1>&lt;pre>&lt;code> if valid_dataset is not None:
&lt;/code>&lt;/pre>
&lt;h1 id="if-not-a-test-set-update-the-test-set-otherwise-valid_dataset-is-not-updated">If not a test set, update the test set, otherwise valid_dataset is not updated
&lt;/h1>&lt;pre>&lt;code> valid_letter = letter_set[:vsize_per_class, :, :]
&lt;/code>&lt;/pre>
&lt;h1 id="numpy-slicing-----httpbrieflyxme2015python-modulenumpy-array-split">numpy slicing &lt;a class="link" href="http://brieflyx.me/2015/python-module/numpy-array-split/" target="_blank" rel="noopener"
>http://brieflyx.me/2015/python-module/numpy-array-split/&lt;/a>
&lt;/h1>&lt;h1 id="select-data-of-valid-data-per-class-count-from-shuffled-data-for-processing-put-into-valid_letter">Select data of &amp;lsquo;valid data per class&amp;rsquo; count from shuffled data for processing, put into valid_letter
&lt;/h1>&lt;pre>&lt;code> valid_dataset[start_v:end_v, :, :] = valid_letter
&lt;/code>&lt;/pre>
&lt;h1 id="put-this-data-into-valid_dataset">Put this data into valid_dataset
&lt;/h1>&lt;pre>&lt;code> valid_labels[start_v:end_v] = label
&lt;/code>&lt;/pre>
&lt;h1 id="mark-label-should-be-one-of-09">Mark label should be one of 0~9
&lt;/h1>&lt;pre>&lt;code> start_v += vsize_per_class
end_v += vsize_per_class
&lt;/code>&lt;/pre>
&lt;h1 id="update-indices">Update indices
&lt;/h1>&lt;h1 id="at-the-end-of-the-loop-valid_dataset-should-be-data-with-total-length-valid_size-valid_labels-is-the-label-at-the-corresponding-position">At the end of the loop, valid_dataset should be data with total length valid_size, valid_labels is the label at the corresponding position
&lt;/h1>&lt;pre>&lt;code> train_letter = letter_set[vsize_per_class:end_l, :, :]
&lt;/code>&lt;/pre>
&lt;h1 id="other-random-elements-except-valid-part-length-is-end_l---vsize_per_class--tsize_per_class">Other random elements except valid part, length is end_l - vsize_per_class = tsize_per_class
&lt;/h1>&lt;pre>&lt;code> train_dataset[start_t:end_t, :, :] = train_letter
&lt;/code>&lt;/pre>
&lt;h1 id="at-the-end-of-the-loop-train_dataset-should-be-data-with-total-length-train_size">At the end of the loop, train_dataset should be data with total length train_size
&lt;/h1>&lt;h1 id="heading">
&lt;/h1>&lt;pre>&lt;code> train_labels[start_t:end_t] = label
start_t += tsize_per_class
end_t += tsize_per_class
&lt;/code>&lt;/pre>
&lt;h1 id="update-indices-1">Update indices
&lt;/h1>&lt;pre>&lt;code>except Exception as e:
print('Unable to process data from', pickle_file, ':', e)
raise
&lt;/code>&lt;/pre>
&lt;p>return valid_dataset, valid_labels, train_dataset, train_labels&lt;/p>
&lt;p>train_size = 200000
valid_size = 10000
test_size = 10000&lt;/p>
&lt;p>valid_dataset, valid_labels, train_dataset, train_labels = merge_datasets(
train_datasets, train_size, valid_size)
_, _, test_dataset, test_labels = merge_datasets(test_datasets, test_size)&lt;/p>
&lt;p>print(&amp;lsquo;Training:&amp;rsquo;, train_dataset.shape, train_labels.shape)
print(&amp;lsquo;Validation:&amp;rsquo;, valid_dataset.shape, valid_labels.shape)
print(&amp;lsquo;Testing:&amp;rsquo;, test_dataset.shape, test_labels.shape)&lt;/p>
&lt;pre tabindex="0">&lt;code>
## Code Segment—Shuffling Data
Introduction to the permutation function: http://www.jianshu.com/p/f0eb10acaa2d
&lt;/code>&lt;/pre>&lt;p>def randomize(dataset, labels):&lt;/p>
&lt;h1 id="labelsshape0-is-the-length-of-labels">labels.shape[0] is the length of labels
&lt;/h1>&lt;p>permutation = np.random.permutation(labels.shape[0])&lt;/p>
&lt;h1 id="randomly-select-a-shuffled-set-of-this-many-numbers">Randomly select a shuffled set of this many numbers
&lt;/h1>&lt;p>print(labels.shape[0])
shuffled_dataset = dataset[permutation,:,:]&lt;/p>
&lt;h1 id="shuffle-data">Shuffle data
&lt;/h1>&lt;p>shuffled_labels = labels[permutation]&lt;/p>
&lt;h1 id="shuffle-labels">Shuffle labels
&lt;/h1>&lt;p>return shuffled_dataset, shuffled_labels
train_dataset, train_labels = randomize(train_dataset, train_labels)
test_dataset, test_labels = randomize(test_dataset, test_labels)
valid_dataset, valid_labels = randomize(valid_dataset, valid_labels)&lt;/p>
&lt;pre tabindex="0">&lt;code>
## Question Four
Verify if the shuffled data is correct
- Reference answer
&lt;/code>&lt;/pre>&lt;p>import random
def plot_sample_3(dataset, labels, title):
fig = plt.figure()
plt.suptitle(title, fontsize=16, fontweight=&amp;lsquo;bold&amp;rsquo;)&lt;/p>
&lt;h1 id="set-title-style">Set title style
&lt;/h1>&lt;pre>&lt;code>items = random.sample(range(len(labels)), 200)
&lt;/code>&lt;/pre>
&lt;h1 id="shuffle-the-sequential-sequence-of-labels-length">Shuffle the sequential sequence of labels length
&lt;/h1>&lt;pre>&lt;code>for i, item in enumerate(items):
&lt;/code>&lt;/pre>
&lt;h1 id="randomly-pick-one">Randomly pick one
&lt;/h1>&lt;pre>&lt;code> plt.subplot(10, 20, i + 1)
&lt;/code>&lt;/pre>
&lt;h1 id="draw-subplot">Draw subplot
&lt;/h1>&lt;pre>&lt;code> plt.axis('off')
&lt;/code>&lt;/pre>
&lt;h1 id="turn-off-coordinate-axes">Turn off coordinate axes
&lt;/h1>&lt;pre>&lt;code> plt.title(chr(ord('A') + labels[item]))
&lt;/code>&lt;/pre>
&lt;h1 id="add-title-1">Add title
&lt;/h1>&lt;pre>&lt;code> plt.imshow(dataset[item])
&lt;/code>&lt;/pre>
&lt;h1 id="display-subplot-at-corresponding-position">Display subplot at corresponding position
&lt;/h1>&lt;pre>&lt;code>fig.set_size_inches(18.5, 10.5)
plt.show()
&lt;/code>&lt;/pre>
&lt;h1 id="display-image">Display image
&lt;/h1>&lt;p>plot_sample_3(train_dataset, train_labels, &amp;rsquo;train dataset suffled&amp;rsquo;)
plot_sample_3(valid_dataset, valid_labels, &amp;lsquo;valid dataset suffled&amp;rsquo;)
plot_sample_3(test_dataset, test_labels, &amp;rsquo;test dataset suffled&amp;rsquo;)&lt;/p>
&lt;pre tabindex="0">&lt;code>
![Question 4](/img/rescued/2041fe44668c.png)
Similar two figures omitted
## Code Segment—Saving Data
&lt;/code>&lt;/pre>&lt;p>pickle_file = os.path.join(data_root, &amp;rsquo;notMNIST.pickle&amp;rsquo;)&lt;/p>
&lt;h1 id="output-file-path">Output file path
&lt;/h1>&lt;p>try:
f = open(pickle_file, &amp;lsquo;wb&amp;rsquo;)&lt;/p>
&lt;h1 id="open-this-file">Open this file
&lt;/h1>&lt;p>save = {
&amp;rsquo;train_dataset&amp;rsquo;: train_dataset,
&amp;rsquo;train_labels&amp;rsquo;: train_labels,
&amp;lsquo;valid_dataset&amp;rsquo;: valid_dataset,
&amp;lsquo;valid_labels&amp;rsquo;: valid_labels,
&amp;rsquo;test_dataset&amp;rsquo;: test_dataset,
&amp;rsquo;test_labels&amp;rsquo;: test_labels,
}&lt;/p>
&lt;h1 id="write-a-dictionary-string-ndarray">Write a dictionary string-ndarray
&lt;/h1>&lt;p>pickle.dump(save, f, pickle.HIGHEST_PROTOCOL)
f.close()
except Exception as e:
print(&amp;lsquo;Unable to save data to&amp;rsquo;, pickle_file, &amp;lsquo;:&amp;rsquo;, e)
raise&lt;/p>
&lt;pre tabindex="0">&lt;code>
## Code Segment—Displaying Saved Data Size
&lt;/code>&lt;/pre>&lt;p>statinfo = os.stat(pickle_file)
print(&amp;lsquo;Compressed pickle size:&amp;rsquo;, statinfo.st_size)&lt;/p>
&lt;pre tabindex="0">&lt;code>
## Question Five
Google translation of the question:
By construction, this dataset may contain a lot of overlapping samples, including in the validation and test sets. Overlap between training and test can skew the results if you expect to use your model in an environment where there is never an overlap, but in practice this doesn&amp;#39;t usually matter. Measure how much overlap there is between training, validation, and test samples.
Optional question:
What about the duplicates between datasets? (For instance, the same letter images)
Create a sanitized validation and test set, and compare your accuracy on those versus your accuracy on the original sets.
The basic idea is that training data should not overlap with testing data, otherwise it leads to inaccurate accuracy.
Reference code:
- Just check the number of duplicate images
&lt;/code>&lt;/pre>&lt;p>import hashlib&lt;/p>
&lt;p>pickle_file = os.path.join(&amp;rsquo;.&amp;rsquo;, &amp;rsquo;notMNIST.pickle&amp;rsquo;)
try:
with open(pickle_file, &amp;lsquo;rb&amp;rsquo;) as f:
data = pickle.load(f)
except Exception as e:
print(&amp;lsquo;Unable to open data from&amp;rsquo;, pickle_file, &amp;lsquo;:&amp;rsquo;, e)
raise&lt;/p>
&lt;h1 id="after-saving-the-data-if-the-kernel-crashed-you-can-read-directly-from-local-without-rerunning-previous-code">After saving the data, if the kernel crashed, you can read directly from local without rerunning previous code
&lt;/h1>&lt;h1 id="if-theres-an-error-you-can-search-for-the-exception-online">If there&amp;rsquo;s an error, you can search for the exception online
&lt;/h1>&lt;p>def calcOverlap(sourceSet, targetSet, description):
sourceSetMd5 = np.array([hashlib.md5(img).hexdigest() for img in sourceSet])&lt;/p>
&lt;h1 id="build-an-md5-table">Build an md5 table
&lt;/h1>&lt;pre>&lt;code>targetSetMd5 = np.array([hashlib.md5(img).hexdigest() for img in targetSet])
&lt;/code>&lt;/pre>
&lt;h1 id="build-an-md5-table-1">Build an md5 table
&lt;/h1>&lt;pre>&lt;code>overlap = np.intersect1d(sourceSetMd5, targetSetMd5, assume_unique=False)
&lt;/code>&lt;/pre>
&lt;h1 id="deduplicate">Deduplicate
&lt;/h1>&lt;pre>&lt;code>print(description)
print(&amp;quot;overlap&amp;quot;,overlap.shape[0], &amp;quot;from&amp;quot;,sourceSetMd5.shape[0],&amp;quot;to&amp;quot;, targetSetMd5.shape[0])
print(&amp;quot;rate&amp;quot;,overlap.shape[0]*100.0/sourceSetMd5.shape[0],&amp;quot;% and&amp;quot;, overlap.shape[0]*100.0/targetSetMd5.shape[0],&amp;quot;%&amp;quot;)
&lt;/code>&lt;/pre>
&lt;h1 id="print-overlap-count">Print overlap count
&lt;/h1>&lt;p>calcOverlap(data[&amp;rsquo;train_dataset&amp;rsquo;], data[&amp;lsquo;valid_dataset&amp;rsquo;], &amp;ldquo;train_dataset &amp;amp; valid_dataset&amp;rdquo;)
calcOverlap(data[&amp;rsquo;train_dataset&amp;rsquo;], data[&amp;rsquo;test_dataset&amp;rsquo;], &amp;ldquo;train_dataset &amp;amp; test_dataset&amp;rdquo;)
calcOverlap(data[&amp;rsquo;test_dataset&amp;rsquo;], data[&amp;lsquo;valid_dataset&amp;rsquo;], &amp;ldquo;test_dataset &amp;amp; valid_dataset&amp;rdquo;)&lt;/p>
&lt;pre tabindex="0">&lt;code>
![Running result](/img/rescued/fc3c3db78e52.png)
- Remove duplicate image resources
To be updated
## Question Six
Use logistic regression to train the model and test it
- Reference code
&lt;/code>&lt;/pre>&lt;p>import random
def disp_sample_dataset(dataset, labels,trueLabels, title=None):&lt;/p>
&lt;h1 id="display-training-results">Display training results
&lt;/h1>&lt;pre>&lt;code>fig = plt.figure()
if title: fig.suptitle(title, fontsize=16, fontweight='bold')
&lt;/code>&lt;/pre>
&lt;h1 id="set-title-style-1">Set title style
&lt;/h1>&lt;pre>&lt;code>items = random.sample(range(len(labels)), 200)
&lt;/code>&lt;/pre>
&lt;h1 id="randomly-select-a-series-of-images">Randomly select a series of images
&lt;/h1>&lt;pre>&lt;code>for i, item in enumerate(items):
plt.subplot(10, 20, i + 1)
&lt;/code>&lt;/pre>
&lt;h1 id="set-a-subplot">Set a subplot
&lt;/h1>&lt;pre>&lt;code> plt.axis('off')
&lt;/code>&lt;/pre>
&lt;h1 id="turn-off-coordinate-lines">Turn off coordinate lines
&lt;/h1>&lt;pre>&lt;code> lab = str(chr(ord('A') + labels[item]))
trueLab = str(chr(ord('A') + trueLabels[item]))
if lab == trueLab:
plt.title( lab )
else:
plt.title(lab + &amp;quot; but &amp;quot; + trueLab)
&lt;/code>&lt;/pre>
&lt;h1 id="add-title-2">Add title
&lt;/h1>&lt;pre>&lt;code> plt.imshow(dataset[item])
&lt;/code>&lt;/pre>
&lt;h1 id="display-this-image">Display this image
&lt;/h1>&lt;pre>&lt;code>fig.set_size_inches(18.5, 10.5)
plt.show()
&lt;/code>&lt;/pre>
&lt;p>def train_and_predict(train_dataset, train_labels, test_dataset, test_labels ,sample_size):
regr = LogisticRegression()&lt;/p>
&lt;h1 id="generate-trainer">Generate trainer
&lt;/h1>&lt;pre>&lt;code>X_train = train_dataset[:sample_size].reshape(sample_size, 784)
&lt;/code>&lt;/pre>
&lt;h1 id="choose-amount-of-data-to-train-based-on-sample_size">Choose amount of data to train based on sample_size
&lt;/h1>&lt;h1 id="compress-2d-vector-to-1d-vector">Compress 2D vector to 1D vector
&lt;/h1>&lt;pre>&lt;code>y_train = train_labels[:sample_size]
&lt;/code>&lt;/pre>
&lt;h1 id="extract-training-data">Extract training data
&lt;/h1>&lt;pre>&lt;code>regr.fit(X_train, y_train)
&lt;/code>&lt;/pre>
&lt;h1 id="train-data">Train data
&lt;/h1>&lt;pre>&lt;code>X_test = test_dataset.reshape(test_dataset.shape[0], 28 * 28)
&lt;/code>&lt;/pre>
&lt;h1 id="compress-test-data-to-1d-vector">Compress test data to 1D vector
&lt;/h1>&lt;pre>&lt;code>y_test = test_labels
&lt;/code>&lt;/pre>
&lt;h1 id="true-labels-corresponding-to-test-data">True labels corresponding to test data
&lt;/h1>&lt;pre>&lt;code>pred_labels = regr.predict(X_test)
&lt;/code>&lt;/pre>
&lt;h1 id="generate-prediction-data">Generate prediction data
&lt;/h1>&lt;pre>&lt;code>print('Accuracy:', regr.score(X_test, y_test), 'when sample_size=', sample_size)
disp_sample_dataset(test_dataset, pred_labels, test_labels, 'sample_size=' + str(sample_size))
&lt;/code>&lt;/pre>
&lt;p>train_and_predict(data[&amp;rsquo;train_dataset&amp;rsquo;],data[&amp;rsquo;train_labels&amp;rsquo;],data[&amp;rsquo;test_dataset&amp;rsquo;],data[&amp;rsquo;test_labels&amp;rsquo;], 1000)&lt;/p>
&lt;pre tabindex="0">&lt;code>
![image.png](/img/rescued/abdb5d761390.png)
## Model Performance
Sections 22~27 discuss model performance-related knowledge. We usually hope that the model&amp;#39;s performance can reach 100%, which is obviously impossible. Also, in order to improve the accuracy of the training set, the model may overfit. At this point, we should follow two points:
- Don&amp;#39;t use all training data at once, but use it in blocks, train a portion each time
- When model parameter changes cause 30 or more cases to change from error to correct, then this parameter change is effective.
![Model Performance](/img/rescued/a8e154206ab8.png)
## Stochastic Gradient Descent
Sections 29~31 explain what stochastic gradient descent is.
During training, to make the model move in the optimal direction, we need to calculate the derivative at that point. 1. The calculation of derivatives is quite large, so we need to randomly select a subset of samples to calculate derivatives, to substitute for the real derivative. This is stochastic gradient descent. 2. To reduce the randomness of random selection, we use momentum inertia to reduce randomness. 3. To make the model stable in later stages, we reduce the learning step size.
End of Course One
&amp;gt; Reference for assignment code
&amp;gt; http://www.hankcs.com/ml/notmnist.html
&lt;/code>&lt;/pre></description></item><item><title>Learning Cocos Creator from Zero - Part One</title><link>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/cocos-creator-%E4%BB%8E%E9%9B%B6%E5%AD%A6%E4%B9%A0-%E4%B8%80/</link><pubDate>Tue, 27 Jun 2017 09:31:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/cocos-creator-%E4%BB%8E%E9%9B%B6%E5%AD%A6%E4%B9%A0-%E4%B8%80/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>Despite the prejudice against Cocos2dx on Zhihu, it&amp;rsquo;s better to try different game engines rather than hesitating about which one to use. Moreover, this software was developed by Chinese developers, so it shouldn&amp;rsquo;t be too difficult to learn.&lt;/p>
&lt;p>Although I haven&amp;rsquo;t learned JavaScript before, it seems possible to look up references while writing. JavaScript tutorial: &lt;a class="link" href="http://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000" target="_blank" rel="noopener"
>http://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000&lt;/a>&lt;/p>
&lt;p>The current version of Cocos Creator is 1.5.1, and there&amp;rsquo;s a detailed manual on the official website at &lt;a class="link" href="http://www.cocos.com/docs/creator/" target="_blank" rel="noopener"
>http://www.cocos.com/docs/creator/&lt;/a>. This article is a simplified version of the &amp;ldquo;Quick Start&amp;rdquo; chapter, with improved gameplay elements.&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/62b797900564.png"
loading="lazy"
alt="Zhihu user expression.png"
>&lt;/p>
&lt;h2 id="installation">Installation
&lt;/h2>&lt;p>Installation on Windows is straightforward. For Mac installation, there are a few things to note.
After downloading the installation package from the official website and dragging it to Applications, when you open it, you&amp;rsquo;ll see a warning that the application is from an untrusted developer. In this case, open System Preferences &amp;gt; Security &amp;amp; Privacy &amp;gt; General, where you&amp;rsquo;ll see an &amp;ldquo;Open&amp;rdquo; button. After opening it this way, you won&amp;rsquo;t be reminded again.&lt;/p>
&lt;h2 id="quick-start">Quick Start
&lt;/h2>&lt;p>&lt;img src="https://nansenli.com/img/rescued/9e8dbbc03ce7.png"
loading="lazy"
alt="dashboard"
>&lt;/p>
&lt;p>From the dashboard, you can create a new project or open an existing one. According to the tutorial&amp;rsquo;s quick start section, we can download an initial project from this address: &lt;a class="link" href="https://github.com/cocos-creator/tutorial-first-game/releases/download/v1.2/start_project.zip" target="_blank" rel="noopener"
>Download Initial Project&lt;/a>.
Or click here to download: &lt;a class="link" href="https://github.com/cocos-creator/tutorial-first-game/releases/download/v1.2/start_project.zip" target="_blank" rel="noopener"
>https://github.com/cocos-creator/tutorial-first-game/releases/download/v1.2/start_project.zip&lt;/a>&lt;/p>
&lt;p>The final game effect of this quick start is like this: &lt;a class="link" href="http://fbdemos.avosapps.com/star-catcher/" target="_blank" rel="noopener"
>http://fbdemos.avosapps.com/star-catcher/&lt;/a>.
This game is somewhat similar to coin-catching games, but more difficult. The game has a very steep learning curve. Due to the monster&amp;rsquo;s horizontal acceleration being difficult to control and its jumping up and down, it&amp;rsquo;s almost impossible to catch the stars. There&amp;rsquo;s also no storyline. We should make it more fun.&lt;/p>
&lt;p>After opening the project, let&amp;rsquo;s learn about the interface. The resource panel is in the bottom left. In the initial project, a project directory has already been established with the most basic resources and directory organization. &amp;ldquo;Assets&amp;rdquo; means assets, and under this main directory, there are three subdirectories containing fonts, audio, and image resources.&lt;/p>
&lt;h2 id="creating-a-scene">Creating a Scene
&lt;/h2>&lt;p>Scenes are crucial because they contain game scripts, and scenes are automatically loaded when the game starts. Click the plus sign under assets in the resource panel to create a scene, rename it to &amp;ldquo;game&amp;rdquo;, and double-click to open it.&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/a862f7b07015.png"
loading="lazy"
alt="Screenshot 2017-06-20 11.01.53.png"
>&lt;/p>
&lt;p>The hierarchy manager in the upper left shows the relationships between nodes in the current scene.
In this game scene, there is only one root node, canvas. Currently, this node is empty, meaning nothing will be loaded.&lt;/p>
&lt;p>Click the root node in the right data panel to set the resolution of the root node. The height of the resolution is set to adaptive, always 960x640, consistent with the aspect ratio of the first-generation iPhone. I guess this resolution should be dp rather than pixel.&lt;/p>
&lt;h2 id="scene-graphics">Scene Graphics
&lt;/h2>&lt;p>Drag the background resource onto the canvas, making the background a child node of the canvas. Be careful not to make the background another root node.
Use Cmd+S to save the changes.
Select the background image in the scene, then find the fourth button of the transform tools in the upper left, which are translation, rotation, scaling, and rectangular transformation. Select rectangular transformation and transform the background into a size that can cover the scene, as shown below.&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/6b7b8f0da17f.png"
loading="lazy"
alt="Scene graphics"
>&lt;/p>
&lt;p>The above steps can also be done by directly setting the properties of the background, setting the position to 0, 0, and the width and height to 1600 and 800.&lt;/p>
&lt;p>Use the same method to add the ground.
Use the same method to insert the little monster and rename it to &amp;ldquo;player&amp;rdquo;. The default anchor point of the image is the center position. Here, set the y value of the anchor to 0.&lt;/p>
&lt;h2 id="creating-scripts">Creating Scripts
&lt;/h2>&lt;p>Then comes an amazing moment: the tutorial actually says it doesn&amp;rsquo;t matter if you don&amp;rsquo;t know programming, &lt;strong>you can let your programmer friend solve it&lt;/strong>. Indeed, all I&amp;rsquo;m missing now is a programmer.&lt;/p>
&lt;p>Create a JavaScript script file at the location of the player resource in the image below, and open it.
&lt;img src="https://nansenli.com/img/rescued/f7c5d8668523.png"
loading="lazy"
alt="Script"
>&lt;/p>
&lt;p>Insert the following content in properties, which are the physical properties of the little monster:&lt;/p>
&lt;pre tabindex="0">&lt;code> jumpHeight:0,
jumpDuration:0,
maxMoveSpeed:0,
accel:0,
&lt;/code>&lt;/pre>&lt;p>Select the little monster image, find &amp;ldquo;Add Component&amp;rdquo; in the property box, add the player script to the player image, and set the relevant parameters.&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/83e4e58a11e8.png"
loading="lazy"
alt="Parameter setting"
>&lt;/p>
&lt;h2 id="improving-the-script">Improving the Script
&lt;/h2>&lt;p>Let&amp;rsquo;s continue to add other functions to the script. First is the jumping action.&lt;/p>
&lt;p>Add this section below properties:&lt;/p>
&lt;pre tabindex="0">&lt;code> setJumpAction: function(){
var jumpUp = cc.moveBy(this.jumpDuration, cc.p(0,this.jumpHeight)).easing(cc.easeQuadraticActionOut());
var jumpDown = cc.moveBy(this.jumpDuration, cc.p(0,-this.jumpHeight)).easing(cc.easeQuadraticActionIn());
return cc.repeatForever(cc.sequence(jumpUp, jumpDown));
},
&lt;/code>&lt;/pre>&lt;p>moveBy is an official API. Looking up its usage in the official API, the first parameter is the movement duration, and the second parameter is the movement position. Obviously, it moves up a certain distance. The easing that follows generates a gradual motion, producing easeQuadraticActionIn and Out, which are quadratic curves. The original tutorial used cubic curves, which differ too much from physical movement in the real world.&lt;/p>
&lt;p>Add the following code to the onLoad method to start the animation:&lt;/p>
&lt;pre tabindex="0">&lt;code> onLoad: function () {
this.jumpAction = this.setJumpAction();
this.node.runAction(this.jumpAction);
},
&lt;/code>&lt;/pre>&lt;p>Now click the play button to view the initial effect.&lt;/p></description></item><item><title>Weekly LeetCode — 501. Find Mode in Binary Search Tree</title><link>https://nansenli.com/post/jianshu/%E7%AE%97%E6%B3%95/%E6%AF%8F%E5%91%A8%E4%B8%80%E9%81%93leetcode-501--find-mode-in-binary-search-tree/</link><pubDate>Mon, 26 Jun 2017 10:54:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E7%AE%97%E6%B3%95/%E6%AF%8F%E5%91%A8%E4%B8%80%E9%81%93leetcode-501--find-mode-in-binary-search-tree/</guid><description>&lt;h2 id="problem">Problem
&lt;/h2>&lt;p>Given a Binary Search Tree (BST), find the mode (most frequently occurring value).
Requirement: Apart from the recursion stack space, the space complexity should be O(1).&lt;/p>
&lt;h2 id="analysis">Analysis
&lt;/h2>&lt;p>Clearly, an in-order traversal of a binary search tree produces a sorted array.
Finding the mode in a sorted array means looking for the longest sequence of consecutive repeated numbers. Note that the problem requires returning all modes if there are multiple values with the same highest frequency. Additionally, the space complexity must be O(1). Writing code that meets these requirements is not difficult.&lt;/p>
&lt;p>Runtime: 16ms, beats 62% of submissions&lt;/p>
&lt;pre tabindex="0">&lt;code>/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
private:
vector&amp;lt;int&amp;gt; mode;
int last;
int lastCount;
int modeCount;
public:
vector&amp;lt;int&amp;gt; findMode(TreeNode* root) {
last = 0;
lastCount = 0;
modeCount = 0;
mode.clear();
runMode(root);
return mode;
}
void runMode(TreeNode* root){
if(root != NULL){
runMode(root -&amp;gt; left);
int nowValue = root-&amp;gt;val;
int nowCount = nowValue != last? 1 : lastCount + 1;
if(modeCount == nowCount){
mode.push_back(nowValue);
}
if(modeCount &amp;lt; nowCount){
modeCount = nowCount;
mode.clear();
mode.push_back(nowValue);
}
last = nowValue;
lastCount = nowCount;
runMode(root -&amp;gt; right);
}
}
};
&lt;/code>&lt;/pre></description></item><item><title>Weekly LeetCode — 3. Longest Substring Without Repeating Characters</title><link>https://nansenli.com/post/jianshu/%E7%AE%97%E6%B3%95/%E6%AF%8F%E5%91%A8%E4%B8%80%E9%81%93leetcode-3--longest-substring-without-repeating-characters/</link><pubDate>Sat, 24 Jun 2017 08:21:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E7%AE%97%E6%B3%95/%E6%AF%8F%E5%91%A8%E4%B8%80%E9%81%93leetcode-3--longest-substring-without-repeating-characters/</guid><description>&lt;h2 id="problem">Problem
&lt;/h2>&lt;p>Find the longest substring in a given string that doesn&amp;rsquo;t contain any repeating characters, and return the length of this substring.&lt;/p>
&lt;h2 id="approach">Approach
&lt;/h2>&lt;p>It&amp;rsquo;s easy to come up with an algorithm with &lt;code>O(n^2)&lt;/code> time complexity. Iterate through each character, and for each character as a starting point, determine the length of the longest substring without repeating characters. Then output the maximum length among all substrings.&lt;/p>
&lt;p>Runtime: 600ms, beats 0% of submissions&lt;/p>
&lt;pre tabindex="0">&lt;code>class Solution {
public:
int lengthOfLongestSubstring(string s) {
int ret = 0;
for(int i=0; i&amp;lt;s.size(); i++){
unordered_set&amp;lt;char&amp;gt; buff;
for(int j=i; j&amp;lt;s.size(); j++){
if(buff.count(s[j]) == 0) buff.insert(s[j]);
else break;
}
if(buff.size() &amp;gt; ret) ret = buff.size();
}
return ret;
}
};
&lt;/code>&lt;/pre>&lt;h2 id="improvement">Improvement
&lt;/h2>&lt;p>As we can see, the algorithm above is quite inefficient and is one of the slowest algorithms in the world. When searching for substrings, we need to set a starting point. This starting point is the position where, up to the current iteration, we have the longest substring without repeating characters. By subtracting this position from the current iteration position, we can get the length of the longest substring.&lt;/p>
&lt;p>Runtime: 15ms, beats 96% of submissions&lt;/p>
&lt;pre tabindex="0">&lt;code>class Solution {
public:
int lengthOfLongestSubstring(string s) {
vector&amp;lt;int&amp;gt; lastPos(260, -1);
int length = s.size();
int ret = 0;
int start = 0;
for(int i=0; i&amp;lt;length; i++)
{
char c = s[i];
if(lastPos[c]+1 &amp;gt; start) start = lastPos[c] + 1;
if(i-start+1 &amp;gt; ret) ret = i-start+1;
lastPos[c] = i;
}
return ret;
}
};
&lt;/code>&lt;/pre></description></item><item><title>Weekly LeetCode — 260. Single Number III</title><link>https://nansenli.com/post/jianshu/%E7%AE%97%E6%B3%95/%E6%AF%8F%E5%91%A8%E4%B8%80%E9%81%93leetcode-260--single-number-iii/</link><pubDate>Thu, 22 Jun 2017 03:49:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E7%AE%97%E6%B3%95/%E6%AF%8F%E5%91%A8%E4%B8%80%E9%81%93leetcode-260--single-number-iii/</guid><description>&lt;h2 id="problem">Problem
&lt;/h2>&lt;p>Given an array of numbers, where all numbers appear twice except for two numbers that appear only once. Find these two numbers. Try to achieve O(1) space complexity.&lt;/p>
&lt;h2 id="analysis">Analysis
&lt;/h2>&lt;p>Although the solution requires O(1) space complexity, this approach is quite complex and difficult to conceive, and it&amp;rsquo;s challenging to write an algorithm with a small constant factor.
&lt;a class="link" href="https://www.lijinma.com/blog/2014/05/29/amazing-xor/" target="_blank" rel="noopener"
>Feel the magic of XOR&lt;/a>
The link above provides the standard approach for this problem. In this method, first XOR all the numbers, and the result will be the XOR of the two numbers that appear only once. Each bit 1 in this result indicates which bits differ between these two numbers. Pick any one of these bits, and divide the array into two groups based on this bit: one group where this bit is 1, and another where this bit is 0. Then, XORing each group separately will give the two numbers we&amp;rsquo;re looking for.&lt;/p>
&lt;p>This leverages the properties of XOR:&lt;/p>
&lt;blockquote>
&lt;p>a ^ b ^ c = a ^ c ^ b
a ^ a = 0
0 ^ a = a
a ^ b = c =&amp;gt; a ^ c = b&lt;/p>
&lt;/blockquote>
&lt;p>Runtime: 13ms, beats 68% of submissions.&lt;/p>
&lt;pre tabindex="0">&lt;code>class Solution {
public:
vector&amp;lt;int&amp;gt; singleNumber(vector&amp;lt;int&amp;gt;&amp;amp; nums) {
int axorb = 0, last = 0;
vector&amp;lt;int&amp;gt; ret(2, 0);
for(auto it = nums.begin(); it!=nums.end() ; it++)
{
axorb ^= *it;
}
last = axorb &amp;amp; (~(axorb - 1));
for(auto it = nums.begin(); it!=nums.end() ; it++)
{
if ((last &amp;amp; *it) != 0)
ret[0] ^= *it;
}
ret[1] = axorb ^ ret[0];
return ret;
}
};
&lt;/code>&lt;/pre>&lt;h2 id="alternative-method">Alternative Method
&lt;/h2>&lt;p>If we&amp;rsquo;re not restricted to constant space complexity, we can also use a hash table. This approach has good extensibility and is less obscure.&lt;/p>
&lt;p>Runtime: 16ms, beats 34% of submissions.&lt;/p>
&lt;pre tabindex="0">&lt;code>class Solution {
public:
vector&amp;lt;int&amp;gt; singleNumber(vector&amp;lt;int&amp;gt;&amp;amp; nums) {
unordered_set&amp;lt;int&amp;gt; buff(nums.size());
for(auto i = nums.begin(); i!=nums.end() ; i++)
{
auto it = buff.find(*i);
if(it == buff.end()){
buff.insert(*i);
}
else{
buff.erase(it);
}
}
vector&amp;lt;int&amp;gt; ret;
for(const int &amp;amp; i : buff){
ret.push_back(i);
}
return ret;
}
};
&lt;/code>&lt;/pre></description></item><item><title>Weekly LeetCode — 43. Multiply Strings</title><link>https://nansenli.com/post/jianshu/%E7%AE%97%E6%B3%95/%E6%AF%8F%E5%91%A8%E4%B8%80%E9%81%93leetcode-43--multiply-strings-/</link><pubDate>Mon, 19 Jun 2017 06:39:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E7%AE%97%E6%B3%95/%E6%AF%8F%E5%91%A8%E4%B8%80%E9%81%93leetcode-43--multiply-strings-/</guid><description>&lt;h2 id="problem">Problem:
&lt;/h2>&lt;p>Given two decimal numbers as strings, return their product as a string. Requirements:&lt;/p>
&lt;ol>
&lt;li>Do not use built-in big number arithmetic.&lt;/li>
&lt;li>String length ≤ 110&lt;/li>
&lt;li>Inputs have no leading zeros&lt;/li>
&lt;li>Strings contain only digits&lt;/li>
&lt;/ol>
&lt;h2 id="thoughts">Thoughts
&lt;/h2>&lt;p>Initially, I didn&amp;rsquo;t notice the prohibition on using built-in functions, so I directly used Python&amp;rsquo;s &lt;code>str&lt;/code> and &lt;code>int&lt;/code> functions, which ranked in the top 10%&amp;hellip; Later, I realized the problem doesn&amp;rsquo;t allow built-in functions. I&amp;rsquo;m not sure if this Python feature counts as built-in big number arithmetic. This is a big number multiplication problem, and there are many established algorithms for this.&lt;/p>
&lt;p>My initial Python submission, which beat 88% of submissions:&lt;/p>
&lt;pre tabindex="0">&lt;code>class Solution(object):
def multiply(self, num1, num2):
return str(int(num1)*int(num2))
&lt;/code>&lt;/pre>&lt;p>The most basic algorithm would be to simulate manual multiplication. First, you need to implement &lt;strong>string addition&lt;/strong> and &lt;strong>multi-digit multiplication by a single digit&lt;/strong>. With these, you can calculate multi-digit by multi-digit multiplication. For example, to calculate &lt;code>12345*67890&lt;/code>, you compute &lt;code>12345*6 + 12345*7 + 12345*8 + 12345*9 + 12345*0&lt;/code>, then add trailing zeros to each result and sum them. However, this approach would be relatively slow.&lt;/p>
&lt;p>Moreover, CPUs can already handle additions that don&amp;rsquo;t overflow, so we should leverage this. We can improve the above algorithm by calculating &lt;code>12345*67890&lt;/code> as &lt;code>(12300 + 45)*(67800 + 90)&lt;/code>. This breaks down into 4 multiplication operations. For numbers with trailing zeros, we can remove them and add them back to the result later. If none of these four multiplications overflow, there&amp;rsquo;s no problem. Otherwise, we can continue decomposing.&lt;/p>
&lt;p>The Karatsuba algorithm can further improve this approach.
We notice that in the addition, there&amp;rsquo;s &lt;code>12300*90+45*67800&lt;/code>. We can use previously calculated results, namely &lt;code>12300*67800&lt;/code> and &lt;code>45*90&lt;/code>, and then calculate &lt;code>(12300+45)*(67800+90) - 12300*67800 - 45*90&lt;/code> to get &lt;code>12300*90 + 45*67800&lt;/code>. This reduces the number of multiplication operations by one.&lt;/p>
&lt;p>Implementing the Karatsuba algorithm would be a bit more complex, so I&amp;rsquo;ll first submit an O(n²) algorithm (the basic version) to see its performance, and then improve it. (Results show that the manual calculation method is already quite fast.)&lt;/p>
&lt;h2 id="first-version">First Version
&lt;/h2>&lt;p>First, we need to write a string addition algorithm. Looking at the input and output data types, they&amp;rsquo;re strings. So we can add them digit by digit. We can use two integer arrays to store each digit, then add them to create a third array. Some digits in this third array will exceed 10, so we carry over from the lower digits to the higher ones. Finally, we convert this array back to a string.&lt;/p>
&lt;p>Although local testing was fine, the submission was very slow, ranking only at the 10th percentile.&lt;/p>
&lt;h2 id="improvement">Improvement
&lt;/h2>&lt;p>Theoretically, this algorithm shouldn&amp;rsquo;t be slow, but in practice it is. The issue might be with unnecessary conversions between integers and strings. In the above algorithm, when calculating multiplication, we converted strings to numbers and then back to strings, which might be causing the extra time. So, we should directly add the results to the final result.&lt;/p>
&lt;p>After submission, the runtime was 9ms, beating 50% of submissions.&lt;/p>
&lt;h2 id="further-improvement">Further Improvement
&lt;/h2>&lt;p>Repeatedly converting the same strings might be consuming time.
We can cache string conversions - convert once and store the result, then retrieve directly without additional calculations when needed.&lt;/p>
&lt;p>The final code, ready for compilation and execution. Runtime: 6ms, beating 76% of submissions. It seems string conversion was indeed the performance bottleneck.&lt;/p>
&lt;pre tabindex="0">&lt;code>#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
using namespace std;
const int BINARY = 10;
class Solution
{
private:
vector&amp;lt;int&amp;gt; result;
string _num1, _num2;
long num1buff[120];
long num2buff[120];
public:
string multiply(string num1, string num2)
{
result.clear();
result.resize(num1.length() + num2.length() + 1);
memset(num1buff, -1 , sizeof(long)*120);
memset(num2buff, -1 , sizeof(long)*120);
_num1 = num1;
_num2 = num2;
for(auto &amp;amp;c : _num1){
c-=&amp;#39;0&amp;#39;;
}
for(auto &amp;amp;c : _num2){
c-=&amp;#39;0&amp;#39;;
}
// This is a recursive process. Let&amp;#39;s see when it terminates.
// It terminates when two numbers multiplied don&amp;#39;t overflow.
// Assuming int is 30 bits in binary (for multiplication, we need 30 bits),
// the original two numbers must be 15 bits, which is about 32768.
// So two 4-digit numbers multiplied shouldn&amp;#39;t overflow.
// Using recursion to calculate the product
addMultiply(0,num1.length(), 0, num2.length());
string ret;
int i = result.size() -1;
for(; i&amp;gt;0; i--)
{
if(result[i] != 0) break;
}
for(; i&amp;gt;=0; i--)
{
ret.push_back(result[i] + &amp;#39;0&amp;#39;);
}
return ret;
}
void addMultiply(int a1, int a2, int b1, int b2 )
{;
// Check if direct calculation is possible
if (a1 == a2 || b1 == b2)
return;
if (a2 - a1 &amp;lt; 10 &amp;amp;&amp;amp; b2 - b1 &amp;lt; 10)
{
long int_num1 = getLong1(a1, a2);
long int_num2 = getLong2(b1, b2);
long output = int_num1 * int_num2;
int pos = _num1.length() + _num2.length() - a2 - b2;
while (output != 0 || result[pos] &amp;gt;= BINARY)
{
long a = output % BINARY;
result[pos] += a;
result[pos + 1] += result[pos] / BINARY;
result[pos] %= BINARY;
output /= BINARY;
pos++;
}
return;
}
// Otherwise, split the longer number
if(a2 - a1 &amp;gt;= 10){
addMultiply(a1, (a2 + a1)/2, b1, b2);
addMultiply((a2 + a1)/2, a2, b1, b2);
}
else {
addMultiply(a1, a2, (b1+b2)/2, b2);
addMultiply(a1, a2, b1, (b1+b2)/2);
}
}
long getLong1(int a, int b){
long ret = 0;
if(num1buff[a] != -1) return num1buff[a];
for(int i=a; i!=b;i++){
ret *= BINARY;
ret += _num1[i] ;
}
num1buff[a] = ret;
return ret;
}
long getLong2(int a, int b){
long ret = 0;
if(num2buff[a] != -1) return num2buff[a];
for(int i=a; i!=b;i++){
ret *= BINARY;
ret += _num2[i] ;
}
num2buff[a] = ret;
return ret;
}
};
int main(void)
{
Solution s;
for(int i=0;i&amp;lt;10000;i++){
cout &amp;lt;&amp;lt; s.multiply(&amp;#34;12345678901&amp;#34;, &amp;#34;100&amp;#34;) &amp;lt;&amp;lt; endl;
cout &amp;lt;&amp;lt; s.multiply(&amp;#34;100&amp;#34;, &amp;#34;100&amp;#34;) &amp;lt;&amp;lt; endl;
}
return 0;
}
&lt;/code>&lt;/pre>&lt;p>Final ranking: beats 70% of submissions&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/6f6d2f53b3ee.png"
loading="lazy"
alt="Ranking"
>&lt;/p></description></item><item><title>How to Set Up Xiaohe Shuangpin in Microsoft Pinyin for Windows 10</title><link>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/%E5%A6%82%E4%BD%95%E5%9C%A8windows10%E7%9A%84%E5%BE%AE%E8%BD%AF%E6%8B%BC%E9%9F%B3%E4%B8%AD%E8%AE%BE%E7%BD%AE%E5%B0%8F%E9%B9%A4%E5%8F%8C%E6%8B%BC/</link><pubDate>Mon, 05 Jun 2017 06:16:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E9%9A%8F%E7%AC%94/%E5%A6%82%E4%BD%95%E5%9C%A8windows10%E7%9A%84%E5%BE%AE%E8%BD%AF%E6%8B%BC%E9%9F%B3%E4%B8%AD%E8%AE%BE%E7%BD%AE%E5%B0%8F%E9%B9%A4%E5%8F%8C%E6%8B%BC/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>The latest version of Windows 10 system&amp;rsquo;s built-in Pinyin input method already supports Xiaohe Shuangpin. Mom no longer has to worry about me installing bloatware!&lt;/p>
&lt;p>After 4 years, here&amp;rsquo;s an updated advanced setup method. Mom no longer has to worry about me configuring keys one by one:&lt;/p>
&lt;ol>
&lt;li>Press Win + R, type regedit, and open Registry Editor&lt;/li>
&lt;li>Find the &lt;code>Computer\HKEY_CURRENT_USER\Software\Microsoft\InputMethod\Settings\CHS&lt;/code> entry&lt;/li>
&lt;li>Create a new string value named UserDefinedDoublePinyinScheme0 with the value &lt;code>小鹤双拼*2*^*iuvdjhcwfg^xmlnpbksqszxkrltvyovt&lt;/code>&lt;/li>
&lt;li>Open Microsoft Pinyin input method settings and set &lt;code>Xiaohe Shuangpin&lt;/code> as the default scheme for Shuangpin.&lt;/li>
&lt;/ol>
&lt;p>If you&amp;rsquo;re concerned about registry modifications causing system issues, here&amp;rsquo;s a manual configuration method:&lt;/p>
&lt;h2 id="step-1-open-settings">Step 1: Open Settings
&lt;/h2>&lt;p>Although the system&amp;rsquo;s Pinyin input method doesn&amp;rsquo;t have Xiaohe Shuangpin by default, we can customize it.&lt;/p>
&lt;p>First, find the Shuangpin settings location. If you can&amp;rsquo;t find it, you can refer to the animated GIF:
&lt;img src="https://nansenli.com/img/rescued/98a28b5a7a49.gif"
loading="lazy"
alt="Find settings"
>&lt;/p>
&lt;h2 id="step-2-edit-custom-shuangpin">Step 2: Edit Custom Shuangpin
&lt;/h2>&lt;p>The Shuangpin settings are shown in the figure below&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/068b77b04598.png"
loading="lazy"
alt="Xiaohe scheme"
>&lt;/p>
&lt;p>Except for the template section which doesn&amp;rsquo;t need to be set, the rest should be configured according to the image. One is the zero initial mode, and the other is custom key mapping. Custom key mapping should be set according to the preview in the image. Note that there is no &amp;ldquo;er&amp;rdquo; syllable in Xiaohe Shuangpin, so you can set it to either the &amp;ldquo;e&amp;rdquo; or &amp;ldquo;r&amp;rdquo; key. Also, both the &amp;ldquo;ue&amp;rdquo; and &amp;ldquo;ve&amp;rdquo; syllables are set to the &amp;ldquo;T&amp;rdquo; key. Here&amp;rsquo;s a tip: when setting keys, you can directly press the corresponding key instead of finding the key to set.&lt;/p>
&lt;h2 id="step-3-save-the-scheme">Step 3: Save the Scheme
&lt;/h2>&lt;p>If the add button becomes available, it means the scheme is set correctly. After adding the scheme, set it as the default scheme, and you can now use Xiaohe Shuangpin!&lt;/p>
&lt;h2 id="testing">Testing
&lt;/h2>&lt;p>Perform a typing test to check if the settings are correct&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/582a066041c2.gif"
loading="lazy"
alt="Testing"
>&lt;/p>
&lt;h2 id="step-4-click-like">Step 4: Click &amp;ldquo;Like&amp;rdquo;
&lt;/h2>&lt;p>Click &amp;ldquo;Like&amp;rdquo; to improve page ranking and help more people use Xiaohe Shuangpin on Windows 10&lt;/p></description></item><item><title>Using STM32 HAL Library to Drive UART in RT-Thread</title><link>https://nansenli.com/post/jianshu/%E5%B5%8C%E5%85%A5%E5%BC%8F/%E5%9C%A8rt-thread%E4%B8%AD%E4%BD%BF%E7%94%A8stm32%E7%9A%84hal%E5%BA%93%E9%A9%B1%E5%8A%A8%E4%B8%B2%E5%8F%A3uart/</link><pubDate>Thu, 06 Apr 2017 10:22:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E5%B5%8C%E5%85%A5%E5%BC%8F/%E5%9C%A8rt-thread%E4%B8%AD%E4%BD%BF%E7%94%A8stm32%E7%9A%84hal%E5%BA%93%E9%A9%B1%E5%8A%A8%E4%B8%B2%E5%8F%A3uart/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>Previously, I had been trying to develop STM32 with RT-Thread under Linux. While this approach is feasible, it comes with various inconveniences. Using VSCode + Scons + openocd for editing, compiling/linking, and flashing is possible for development, but there&amp;rsquo;s no complete solution for debugging in this environment, requiring Eclipse for debugging. Although converting RT-Thread to an Eclipse project is possible, at that point it might be better to directly use Keil 5 for development under Windows.&lt;/p>
&lt;p>Since the STM32F103&amp;rsquo;s onboard flash is too small and its stack is very limited, I switched to STM32F411. The board is from Waveshare&amp;rsquo;s XNUCLEO, which is compatible with STM&amp;rsquo;s official NUCLEO boards.&lt;/p>
&lt;h2 id="installing-keil-523">Installing Keil 5.23
&lt;/h2>&lt;p>I first installed Keil 5.11 and cracked it, then uninstalled it and installed Keil 5.23, which automatically showed as successfully cracked. The cracking tool is the registration code generator that has been popular for many years. I&amp;rsquo;m using Keil 5.23 because RT-Thread projects can only be opened with this version.&lt;/p>
&lt;h2 id="installing-libraries-with-pack-installer">Installing Libraries with Pack Installer
&lt;/h2>&lt;p>You need to install Keil::STM32F4xx_DFP to open the nucleo project in RT-Thread&amp;rsquo;s bps branch.&lt;/p>
&lt;h2 id="installing-stm32cubemx">Installing STM32CubeMX
&lt;/h2>&lt;p>With STM32CubeMX, we can easily generate initialization code by clicking with the mouse. However, we still need to write function calls ourselves, which is not as convenient as NPX&amp;rsquo;s PE.&lt;/p>
&lt;h2 id="installing-st-link-driver">Installing ST-Link Driver
&lt;/h2>&lt;p>Waveshare&amp;rsquo;s 100 yuan board includes an ST-Link debugger, so we&amp;rsquo;ll use ST-Link for debugging.&lt;/p>
&lt;h2 id="downloading-the-nucleo-project">Downloading the Nucleo Project
&lt;/h2>&lt;p>Open the Keil 5 project in &lt;code>rt-thread-master\bsp\stm32f411-nucleo&lt;/code>, try to compile it, and after it passes, set up the project&amp;rsquo;s debug configuration to use ST-Link for debugging, then download the program to the board. Although the program is downloaded, there&amp;rsquo;s no visible effect because the main function is empty.&lt;/p>
&lt;p>Let&amp;rsquo;s add the following code to the main function:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-C" data-lang="C">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>(&lt;span style="color:#66d9ef">void&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">/* user app entry */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">MX_GPIO_Init&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">while&lt;/span>(&lt;span style="color:#ae81ff">1&lt;/span>){
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">rt_thread_delay&lt;/span>(&lt;span style="color:#ae81ff">5&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">HAL_GPIO_WritePin&lt;/span> (GPIOC, GPIO_PIN_5, GPIO_PIN_SET);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">rt_thread_delay&lt;/span>(&lt;span style="color:#ae81ff">5&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">HAL_GPIO_WritePin&lt;/span> (GPIOC, GPIO_PIN_5, GPIO_PIN_RESET);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And add the LED initialization function:&lt;/p>
&lt;pre tabindex="0">&lt;code>static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_5, GPIO_PIN_RESET);
/*Configure GPIO pin : PC5 */
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &amp;amp;GPIO_InitStruct);
}
&lt;/code>&lt;/pre>&lt;p>Now we can see the onboard LED4 light up and blink.&lt;/p>
&lt;p>Where do these initialization codes come from? The code in the main function is found online, a simple function to operate GPIO. The MX_GPIO_Init code segment comes from the code automatically generated by STM32CubeMX. Below I&amp;rsquo;ll explain how to generate UART initialization code using STM32CubeMX.&lt;/p>
&lt;h2 id="generating-uart-initialization-code-with-stm32cubemx">Generating UART Initialization Code with STM32CubeMX
&lt;/h2>&lt;ul>
&lt;li>First, create a project and set the chip model. Here&amp;rsquo;s a demonstration:&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/905af84ac774.gif"
loading="lazy"
alt="2017-04-06_16-41-06.gif"
>&lt;/p>
&lt;ul>
&lt;li>Then set up the serial port pins.
Since RT-Thread has already initialized UART1, we can&amp;rsquo;t use this one. We can use UART6.&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/4985758a4b21.gif"
loading="lazy"
alt="2017-04-06_16-57-29.gif"
>&lt;/p>
&lt;ul>
&lt;li>Open the project and extract the code. The blue part is the code we need to take:&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/f06cc889e552.gif"
loading="lazy"
alt="2017-04-06_17-01-11.gif"
>&lt;/p>
&lt;ul>
&lt;li>Port the code
In the void HAL_UART_MspInit(UART_HandleTypeDef *huart) function, add the code for USART6:&lt;/li>
&lt;/ul>
&lt;pre tabindex="0">&lt;code>void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
GPIO_InitTypeDef GPIO_InitStruct;
if (huart-&amp;gt;Instance == USART2)
{
/*##-1- Enable peripherals and GPIO Clocks #################################*/
/* Enable GPIO TX/RX clock */
USARTx_TX_GPIO_CLK_ENABLE();
USARTx_RX_GPIO_CLK_ENABLE();
/* Enable USARTx clock */
USARTx_CLK_ENABLE();
/*##-2- Configure peripheral GPIO ##########################################*/
/* UART TX GPIO pin configuration */
GPIO_InitStruct.Pin = USARTx_TX_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
GPIO_InitStruct.Alternate = USARTx_TX_AF;
HAL_GPIO_Init(USARTx_TX_GPIO_PORT, &amp;amp;GPIO_InitStruct);
/* UART RX GPIO pin configuration */
GPIO_InitStruct.Pin = USARTx_RX_PIN;
GPIO_InitStruct.Alternate = USARTx_RX_AF;
HAL_GPIO_Init(USARTx_RX_GPIO_PORT, &amp;amp;GPIO_InitStruct);
HAL_NVIC_SetPriority(USART2_IRQn, 0, 1);
HAL_NVIC_EnableIRQ(USART2_IRQn);
}
if (huart-&amp;gt;Instance == USART6)
{
/* USER CODE BEGIN USART6_MspInit 0 */
/* USER CODE END USART6_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_USART6_CLK_ENABLE();
/**USART6 GPIO Configuration
PC6 ------&amp;gt; USART6_TX
PC7 ------&amp;gt; USART6_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF8_USART6;
HAL_GPIO_Init(GPIOC, &amp;amp;GPIO_InitStruct);
/* Peripheral interrupt init */
HAL_NVIC_SetPriority(USART6_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART6_IRQn);
/* USER CODE BEGIN USART6_MspInit 1 */
/* USER CODE END USART6_MspInit 1 */
}
}
&lt;/code>&lt;/pre>&lt;p>In the main.c file, add the &lt;code>MX_USART6_UART_Init();&lt;/code> function content, and call &lt;code>MX_USART6_UART_Init();&lt;/code> in the main function to initialize.&lt;/p>
&lt;p>Then, test it in the main() function:&lt;/p>
&lt;pre tabindex="0">&lt;code> uint8_t TxData[10]= &amp;#34;01234abcde&amp;#34;;
HAL_UART_Transmit(&amp;amp;huart6,TxData,10,0xf);
&lt;/code>&lt;/pre>&lt;p>We&amp;rsquo;ll place the Waveshare board&amp;rsquo;s JP4 jumper on the right side so that the USB TO UART can use PC6 and PC7 as the serial interface. Then, connect the USB interface to the PC, and we can view the character string received by the PC in a serial terminal like XSHELL.&lt;/p></description></item><item><title>Using SPI and UART Devices in RT-Thread</title><link>https://nansenli.com/post/jianshu/%E5%B5%8C%E5%85%A5%E5%BC%8F/%E5%9C%A8rt-thread%E4%B8%AD%E4%BD%BF%E7%94%A8spi%E5%92%8Cuart%E8%AE%BE%E5%A4%87/</link><pubDate>Tue, 07 Mar 2017 09:41:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E5%B5%8C%E5%85%A5%E5%BC%8F/%E5%9C%A8rt-thread%E4%B8%AD%E4%BD%BF%E7%94%A8spi%E5%92%8Cuart%E8%AE%BE%E5%A4%87/</guid><description>&lt;p>RT-Thread uses function calls similar to Linux. Here&amp;rsquo;s how to use devices in a simple way.&lt;/p>
&lt;ul>
&lt;li>Hardware Environment: STM32F103RBT6&lt;/li>
&lt;/ul>
&lt;h2 id="user-program-location">User Program Location
&lt;/h2>&lt;p>User programs are written and called in applications.c in the applications folder under the bsp folder. If you have many programs, create another c file to separate them.&lt;/p>
&lt;h2 id="spi-driver-files">SPI Driver Files
&lt;/h2>&lt;p>The stm32f10x drivers folder doesn&amp;rsquo;t have SPI drivers. We need to port them from stm32f107. Copy rt_stm32f10x_spi.c, rt_stm32f10x_spi.h, and platform.c files, then add the call to rt_platform_init() in the rt_init_thread_entry function, and add the headers to the corresponding files. Then modify SConscript to include platform.c and rt_stm32f10x_spi.c for compilation, and add the RT_USING_SPI and RT_USING_SPI1 macro definitions in the config.&lt;/p>
&lt;p>Add the following code to the SPI initialization in platform.c to configure the frequency:&lt;/p>
&lt;pre tabindex="0">&lt;code> /* config spi */
{
struct rt_spi_configuration cfg;
cfg.data_width = 8;
cfg.mode = RT_SPI_MODE_3 | RT_SPI_MSB; /* SPI Compatible Modes 3 and SPI_FirstBit_MSB in lis302dl datasheet */
//APB2=168M/2=84M, SPI1 = 84/2,4,8,16,32 = 42M, 21M, 10.5M, 5.25M, 2.625M ...
cfg.max_hz = 2625000; /* SPI_BaudRatePrescaler_16=84000000/16=5.25MHz. The max_hz of lis302dl is 10MHz in datasheet */
rt_spi_configure(&amp;amp;spi_device, &amp;amp;cfg);
} /* config spi */
&lt;/code>&lt;/pre>&lt;h2 id="hardware-configuration">Hardware Configuration
&lt;/h2>&lt;p>The hardware pins are configured in the platform.c file. We won&amp;rsquo;t modify the default configuration, which means PA4567 are the default SPI input/output pins.&lt;/p>
&lt;pre tabindex="0">&lt;code>/*
* SPI1_MOSI: PA7
* SPI1_MISO: PA6
* SPI1_SCK : PA5
*
* CS0: PA4 SD card. You can change this to your own SPI device&amp;#39;s CS pin
*/
&lt;/code>&lt;/pre>&lt;h2 id="spi-calls">SPI Calls
&lt;/h2>&lt;p>SPI calls don&amp;rsquo;t require the open function, just execute:&lt;/p>
&lt;pre tabindex="0">&lt;code> rt_spi_transfer(spi_device, &amp;amp;ReadAddr, &amp;amp;pBuffer, 1);
&lt;/code>&lt;/pre>&lt;p>or rt_spi_send_then_recv can also achieve different effects.&lt;/p>
&lt;h2 id="uart-calls">UART Calls
&lt;/h2>&lt;p>UART calls require the open function. Here&amp;rsquo;s a simple example of using a serial device:&lt;/p>
&lt;pre tabindex="0">&lt;code>void device_thread_entry(void* parameter)
{
rt_device_t device;
device = rt_device_find(&amp;#34;uart2&amp;#34;);
rt_device_open(device, RT_DEVICE_OFLAG_RDWR|RT_DEVICE_FLAG_INT_RX);
char test[10] = &amp;#34;abc&amp;#34;;
while(1){
rt_size_t reclen = rt_device_read(device, 0, test, 10);
if(reclen &amp;gt; 0) rt_device_write(device, 0, test, reclen);
rt_thread_delay(500);
}
}
&lt;/code>&lt;/pre>&lt;p>You can start this thread, and when we input up to 10 characters in uart2, they will be echoed back after 5 seconds. If more than 10 characters are entered, they should be displayed in the next 5-second cycle.
The official markdown documentation uses a message mechanism to handle UART communication, which can also be referenced.&lt;/p></description></item><item><title>Deploying RT-thread System on STM32 in Ubuntu Environment</title><link>https://nansenli.com/post/jianshu/%E5%B5%8C%E5%85%A5%E5%BC%8F/ubuntu%E7%8E%AF%E5%A2%83%E4%B8%8B%E5%9C%A8stm32%E4%B8%8A%E9%83%A8%E7%BD%B2rt-thread%E7%B3%BB%E7%BB%9F/</link><pubDate>Thu, 02 Mar 2017 07:59:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E5%B5%8C%E5%85%A5%E5%BC%8F/ubuntu%E7%8E%AF%E5%A2%83%E4%B8%8B%E5%9C%A8stm32%E4%B8%8A%E9%83%A8%E7%BD%B2rt-thread%E7%B3%BB%E7%BB%9F/</guid><description>&lt;p>RT-Thread is an open-source embedded operating system from China, with the current version being 2.2. It supports many microcontrollers, such as the STM32 series, and can even run on Bluetooth BLE chips. Despite resource constraints in microcontrollers, deploying an easy-to-use micro operating system is beneficial for project development and code management.&lt;/p>
&lt;h2 id="required-tools">Required Tools
&lt;/h2>&lt;ul>
&lt;li>An STM32 development board with USB download capability&lt;/li>
&lt;li>Ubuntu system&lt;/li>
&lt;li>USB to serial cable&lt;/li>
&lt;/ul>
&lt;p>I&amp;rsquo;m using the classic &lt;code>ALIENTEK STM32 MINI V2.0&lt;/code> development board with an &lt;code>STM32F103RBT6&lt;/code> chip, and Ubuntu version &lt;code>16.04&lt;/code>&lt;/p>
&lt;h2 id="1-download-the-cross-compilation-toolchain">1. Download the Cross-compilation Toolchain
&lt;/h2>&lt;ul>
&lt;li>Download the &lt;code>gcc-arm-none-eabi&lt;/code> cross-compilation toolchain. For 16.04 systems, simply use &lt;code>apt install gcc-arm-none-eabi&lt;/code> to install&lt;/li>
&lt;/ul>
&lt;h2 id="2-download-rt-thread-compilation-tools">2. Download RT-Thread Compilation Tools
&lt;/h2>&lt;ul>
&lt;li>This system uses scons for compilation. For 16.04 systems, use &lt;code>apt install scons&lt;/code> to install&lt;/li>
&lt;/ul>
&lt;h2 id="3-download-the-source-code">3. Download the Source Code
&lt;/h2>&lt;ul>
&lt;li>Download the RT-Thread system from: &lt;code>https://github.com/RT-Thread&lt;/code>&lt;/li>
&lt;li>Extract the directory&lt;/li>
&lt;/ul>
&lt;h2 id="4-modify-the-compilation-project">4. Modify the Compilation Project
&lt;/h2>&lt;ul>
&lt;li>Enter the &lt;code>bsp/stm32f10x&lt;/code> directory&lt;/li>
&lt;li>Edit the &lt;code>rtconfig.py&lt;/code> file&lt;/li>
&lt;li>Change CROSS_TOOL to gcc&lt;/li>
&lt;/ul>
&lt;h2 id="5-configure-for-your-board">5. Configure for Your Board
&lt;/h2>&lt;ul>
&lt;li>Configure the crystal oscillator in &lt;code>stm32f10x.h&lt;/code> via &lt;code>HSE_VALUE&lt;/code>, for example 800000&lt;/li>
&lt;li>Configure RAM in &lt;code>board.h&lt;/code> via &lt;code>STM32_SRAM_SIZE&lt;/code>, for example 20&lt;/li>
&lt;li>Configure the LED in &lt;code>led.c&lt;/code>&lt;/li>
&lt;/ul>
&lt;h2 id="6-compile-the-source-code">6. Compile the Source Code
&lt;/h2>&lt;ul>
&lt;li>Run &lt;code>scons&lt;/code> in the &lt;code>bsp/stm32f10x&lt;/code> directory to generate the rtthread.bin file&lt;/li>
&lt;/ul>
&lt;h2 id="7-flash-the-code">7. Flash the Code
&lt;/h2>&lt;ul>
&lt;li>You can use Jlink for downloading. Download the Linux executable file from the Jlink official website. Since I&amp;rsquo;m using a Debian system, I can directly open the JLink_Linux_V614a_x86_64.deb file to install. Then, in the system&amp;rsquo;s lib/ directory, execute the &lt;code>JlinkExe&lt;/code> file to download and flash the program through Jlink.&lt;/li>
&lt;li>Here I used a more convenient serial port flashing method. First set pin B0 to high level, B1 to low level, then use the STM32flash-5.0 software for flashing.
&lt;ul>
&lt;li>Download from &lt;code>https://sourceforge.net/projects/stm32flash/&lt;/code>&lt;/li>
&lt;li>Extract the directory, run make in that directory to get stm32flash&lt;/li>
&lt;li>Copy &lt;code>rtthread.bin&lt;/code> to that directory, connect the USB to serial cable, then enter &lt;code>sudo ./stm32flash -w rtthread.bin -v -g 0x0 /dev/ttyUSB0&lt;/code>, where &lt;code>ttyUSB0&lt;/code> is the serial device&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="8-verify-success">8. Verify Success
&lt;/h2>&lt;ul>
&lt;li>If you can see the LED blinking, the deployment was successful&lt;/li>
&lt;/ul></description></item><item><title>Adding SPI Driver Support for Cubieboard2</title><link>https://nansenli.com/post/jianshu/%E5%B5%8C%E5%85%A5%E5%BC%8F/cubieboard2-%E6%B7%BB%E5%8A%A0spi-%E9%A9%B1%E5%8A%A8%E6%94%AF%E6%8C%81/</link><pubDate>Tue, 28 Feb 2017 12:14:00 +0000</pubDate><guid>https://nansenli.com/post/jianshu/%E5%B5%8C%E5%85%A5%E5%BC%8F/cubieboard2-%E6%B7%BB%E5%8A%A0spi-%E9%A9%B1%E5%8A%A8%E6%94%AF%E6%8C%81/</guid><description>&lt;h2 id="0-introduction">0. Introduction
&lt;/h2>&lt;p>Since the official stable kernel linux-3.4 for Cubieboard2 doesn&amp;rsquo;t include native SPI driver support, we need to modify the kernel source code, compile and put it into NAND to replace the original uImage, and add the new kernel module files. Additionally, we need to modify system files to fully support full-duplex communication.&lt;/p>
&lt;h2 id="1-flash-debian-system-to-nand-windows">1. Flash Debian System to NAND (Windows)
&lt;/h2>&lt;p>First, download the customized Debian system for Cubieboard2 from: &lt;a class="link" href="http://dl.cubieboard.org/software/a20-cubieboard/debian/nand/debian-nand.img.gz" target="_blank" rel="noopener"
>http://dl.cubieboard.org/software/a20-cubieboard/debian/nand/debian-nand.img.gz&lt;/a>&lt;/p>
&lt;p>Download Phoenixsuit and open the downloaded image through it. With the Cubieboard2 unpowered, hold down the FEL button while connecting it to the computer via an OTG cable. In Device Manager, install the driver for this unrecognized device, specifying the Phoenixsuit installation directory as the driver location, and confirm installation.&lt;/p>
&lt;p>Then format and flash the image.&lt;/p>
&lt;h2 id="2-log-in-to-the-system">2. Log in to the System
&lt;/h2>&lt;p>Connect the Cubieboard2 to an Ethernet cable. The Debian system is set to a static IP address 192.168.1.124 by default, which you can access via SSH. You can also log in through a TTL line from UART, change to DHCP mode, then check the IP address from the router before logging in. The router username and password are root/cubieboard.&lt;/p>
&lt;h2 id="3-modify-the-kernel-ubuntu">3. Modify the Kernel (Ubuntu)
&lt;/h2>&lt;p>The current Linux kernel doesn&amp;rsquo;t support SPI drivers for Cubieboard2. First, download the linux-3.4 branch kernel source: &lt;a class="link" href="https://github.com/linux-sunxi/linux-sunxi" target="_blank" rel="noopener"
>https://github.com/linux-sunxi/linux-sunxi&lt;/a>. Then add SPI support to the kernel (not recommended); for details, see &lt;a class="link" href="http://blog.csdn.net/u010352603/article/details/51657265#" target="_blank" rel="noopener"
>http://blog.csdn.net/u010352603/article/details/51657265#&lt;/a>. Instead, directly download a pre-modified version with SPI support from &lt;a class="link" href="https://github.com/linanwx/cubieboard2-spi-support" target="_blank" rel="noopener"
>https://github.com/linanwx/cubieboard2-spi-support&lt;/a>. After downloading, delete the drivers/spi directory in the kernel directory and place the downloaded spi folder in its place.&lt;/p>
&lt;h2 id="4-compile-the-kernel-ubuntu">4. Compile the Kernel (Ubuntu)
&lt;/h2>&lt;p>Prepare an arm-linux-gnueabihf-gcc compiler, version not higher than 5.0.&lt;/p>
&lt;p>In the kernel directory, using the terminal, enter:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>make ARCH&lt;span style="color:#f92672">=&lt;/span>arm CROSS_COMPILE&lt;span style="color:#f92672">=&lt;/span>arm-linux-gnueabihf- sun7i_defconfig
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Enter:&lt;/p>
&lt;pre tabindex="0">&lt;code>gedit .config
&lt;/code>&lt;/pre>&lt;p>Find the following options and change them to:&lt;/p>
&lt;pre tabindex="0">&lt;code>CONFIG_SPI=y
CONFIG_SUNXI_NAND_PARTITION=y
CONFIG_SUNXI_NAND=y
&lt;/code>&lt;/pre>&lt;p>Enter the following command to start compiling the kernel:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>make -j4ARCH&lt;span style="color:#f92672">=&lt;/span>arm CROSS_COMPILE&lt;span style="color:#f92672">=&lt;/span>arm-linux-gnueabihf- uImage modules
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Since CONFIG_SPI and CONFIG_SUNXI_NAND support are enabled, a series of settings are required. When you encounter spi support, CONFIG_SPI_SUN7I, CONFIG_SUN7I_SPI_NDMA, enter y; in other cases, press enter.&lt;/p>
&lt;p>Check if the uImage file exists in the path arch/arm/boot&lt;/p>
&lt;p>Enter the following command to generate kernel modules:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>make ARCH&lt;span style="color:#f92672">=&lt;/span>arm CROSS_COMPILE&lt;span style="color:#f92672">=&lt;/span>arm-linux-gnueabihf- INSTALL_MOD_PATH&lt;span style="color:#f92672">=&lt;/span>output modules_install
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Check if the &lt;code>output/lib/modules/3.4.104&lt;/code> directory has been created.&lt;/p>
&lt;h2 id="5-replace-the-kernel-cubieboard2">5. Replace the Kernel (Cubieboard2)
&lt;/h2>&lt;p>On Cubieboard2, open the terminal and enter:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>mkdir /media/nanda
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>mount /dev/nanda /media/nanda
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Copy the uImage to the &lt;code>/media/nanda&lt;/code> directory of the Cubieboard2 Debian system, overwriting the original kernel.&lt;/p>
&lt;p>Use the tar command to pack the &lt;code>output/lib/modules/3.4.104&lt;/code> directory, transfer this file to Cubieboard2&amp;rsquo;s &lt;code>/lib/modules&lt;/code> directory, then extract it.&lt;/p>
&lt;h2 id="6-check-compilation-success">6. Check Compilation Success
&lt;/h2>&lt;p>Restart. If the system enters normally, it was successful.&lt;/p>
&lt;h2 id="7-modify-system-files-to-enable-spi-and-support-full-duplex-spi-cubieboard2">7. Modify System Files to Enable SPI and Support Full-Duplex SPI (Cubieboard2)
&lt;/h2>&lt;p>Remount &lt;code>/dev/nanda&lt;/code> to the &lt;code>/media/nanda/&lt;/code> directory and enter it.&lt;/p>
&lt;p>Enter the following commands:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>bin2fex script.bin script.fex
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>nano script.fex
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Find [spi0_para], change the line with &amp;ldquo;used&amp;rdquo; to 1, and add the following code:&lt;/p>
&lt;pre tabindex="0">&lt;code>[spi_devices]
spi_dev_num =1
[spi_board0]
modalias =&amp;#34;spidev&amp;#34;
max_speed_hz =100000
bus_num =0
chip_select =0
mode =0
full_duplex =1
manual_cs =0
&lt;/code>&lt;/pre>&lt;p>Enter the following commands:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>fex2bin script.fex script.bin
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>nano /usr/include/linux/spi/spidev.h
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Add the following line above &lt;code>__u16 delay_usecs;&lt;/code>:&lt;/p>
&lt;pre tabindex="0">&lt;code>__u16 interbyte_usecs;
&lt;/code>&lt;/pre>&lt;h2 id="8-test-spi">8. Test SPI
&lt;/h2>&lt;p>Restart Cubieboard2. Check if the spidev device exists in the &lt;code>/dev&lt;/code> path. Short-circuit pins 46 and 48; pin diagram available at &lt;code>http://docs.cubieboard.org/products/a10_cubieboard/expansion_ports&lt;/code>.
Create a new text file with the following code:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-c" data-lang="c">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/*
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"> * SPI testing utility (using spidev driver)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"> * Copyright (c) 2007 MontaVista Software, Inc.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"> * Copyright (c) 2007 Anton Vorontsov &amp;lt;avorontsov@ru.mvista.com&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"> * This program is free software; you can redistribute it and/or modify
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"> * it under the terms of the GNU General Public License as published by
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"> * the Free Software Foundation; either version 2 of the License.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"> * Cross-compile with cross-gcc -I/path/to/cross-kernel/include
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;stdint.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;unistd.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;stdio.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;stdlib.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;getopt.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;fcntl.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;sys/ioctl.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;linux/types.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;linux/spi/spidev.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">pabort&lt;/span>(&lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">char&lt;/span> &lt;span style="color:#f92672">*&lt;/span>s)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">perror&lt;/span>(s);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">abort&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">char&lt;/span> &lt;span style="color:#f92672">*&lt;/span>device &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;/dev/spidev0.0&amp;#34;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">uint8_t&lt;/span> mode;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">uint8_t&lt;/span> bits &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">8&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">uint32_t&lt;/span> speed &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">500000&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">uint16_t&lt;/span> delay;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">transfer&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> fd)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">int&lt;/span> ret;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">uint8_t&lt;/span> tx[] &lt;span style="color:#f92672">=&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ae81ff">0xFF&lt;/span>, &lt;span style="color:#ae81ff">0xFF&lt;/span>, &lt;span style="color:#ae81ff">0xFF&lt;/span>, &lt;span style="color:#ae81ff">0xFF&lt;/span>, &lt;span style="color:#ae81ff">0xFF&lt;/span>, &lt;span style="color:#ae81ff">0xFF&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ae81ff">0x40&lt;/span>, &lt;span style="color:#ae81ff">0x00&lt;/span>, &lt;span style="color:#ae81ff">0x00&lt;/span>, &lt;span style="color:#ae81ff">0x00&lt;/span>, &lt;span style="color:#ae81ff">0x00&lt;/span>, &lt;span style="color:#ae81ff">0x95&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ae81ff">0xFF&lt;/span>, &lt;span style="color:#ae81ff">0xFF&lt;/span>, &lt;span style="color:#ae81ff">0xFF&lt;/span>, &lt;span style="color:#ae81ff">0xFF&lt;/span>, &lt;span style="color:#ae81ff">0xFF&lt;/span>, &lt;span style="color:#ae81ff">0xFF&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ae81ff">0xFF&lt;/span>, &lt;span style="color:#ae81ff">0xFF&lt;/span>, &lt;span style="color:#ae81ff">0xFF&lt;/span>, &lt;span style="color:#ae81ff">0xFF&lt;/span>, &lt;span style="color:#ae81ff">0xFF&lt;/span>, &lt;span style="color:#ae81ff">0xFF&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ae81ff">0xFF&lt;/span>, &lt;span style="color:#ae81ff">0xFF&lt;/span>, &lt;span style="color:#ae81ff">0xFF&lt;/span>, &lt;span style="color:#ae81ff">0xFF&lt;/span>, &lt;span style="color:#ae81ff">0xFF&lt;/span>, &lt;span style="color:#ae81ff">0xFF&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ae81ff">0xDE&lt;/span>, &lt;span style="color:#ae81ff">0xAD&lt;/span>, &lt;span style="color:#ae81ff">0xBE&lt;/span>, &lt;span style="color:#ae81ff">0xEF&lt;/span>, &lt;span style="color:#ae81ff">0xBA&lt;/span>, &lt;span style="color:#ae81ff">0xAD&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ae81ff">0xF0&lt;/span>, &lt;span style="color:#ae81ff">0x0D&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> };
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">uint8_t&lt;/span> rx[&lt;span style="color:#a6e22e">ARRAY_SIZE&lt;/span>(tx)] &lt;span style="color:#f92672">=&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ae81ff">0&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> };
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">struct&lt;/span> spi_ioc_transfer tr &lt;span style="color:#f92672">=&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .tx_buf &lt;span style="color:#f92672">=&lt;/span> (&lt;span style="color:#66d9ef">unsigned&lt;/span> &lt;span style="color:#66d9ef">long&lt;/span>)tx,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .rx_buf &lt;span style="color:#f92672">=&lt;/span> (&lt;span style="color:#66d9ef">unsigned&lt;/span> &lt;span style="color:#66d9ef">long&lt;/span>)rx,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .len &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#a6e22e">ARRAY_SIZE&lt;/span>(tx),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .delay_usecs &lt;span style="color:#f92672">=&lt;/span> delay,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .speed_hz &lt;span style="color:#f92672">=&lt;/span> speed,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .bits_per_word &lt;span style="color:#f92672">=&lt;/span> bits,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> };
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ret &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#a6e22e">ioctl&lt;/span>(fd, &lt;span style="color:#a6e22e">SPI_IOC_MESSAGE&lt;/span>(&lt;span style="color:#ae81ff">1&lt;/span>), &lt;span style="color:#f92672">&amp;amp;&lt;/span>tr);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (ret &lt;span style="color:#f92672">==&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">pabort&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;can&amp;#39;t send spi message&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span> (ret &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>; ret &lt;span style="color:#f92672">&amp;lt;&lt;/span> &lt;span style="color:#a6e22e">ARRAY_SIZE&lt;/span>(tx); ret&lt;span style="color:#f92672">++&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#f92672">!&lt;/span>(ret &lt;span style="color:#f92672">%&lt;/span> &lt;span style="color:#ae81ff">6&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">puts&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">printf&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;%.2X &amp;#34;&lt;/span>, rx[ret]);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">puts&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">print_usage&lt;/span>(&lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">char&lt;/span> &lt;span style="color:#f92672">*&lt;/span>prog)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">printf&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Usage: %s [-DsbdlHOLC3]&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>, prog);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">puts&lt;/span>(&lt;span style="color:#e6db74">&amp;#34; -D --device device to use (default /dev/spidev0.0)&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34; -s --speed max speed (Hz)&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34; -d --delay delay (usec)&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34; -b --bpw bits per word &lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34; -l --loop loopback&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34; -H --cpha clock phase&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34; -O --cpol clock polarity&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34; -L --lsb least significant bit first&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34; -C --cs-high chip select active high&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34; -3 --3wire SI/SO signals shared&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">exit&lt;/span>(&lt;span style="color:#ae81ff">1&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">parse_opts&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> argc, &lt;span style="color:#66d9ef">char&lt;/span> &lt;span style="color:#f92672">*&lt;/span>argv[])
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">while&lt;/span> (&lt;span style="color:#ae81ff">1&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">struct&lt;/span> option lopts[] &lt;span style="color:#f92672">=&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {&lt;span style="color:#e6db74">&amp;#34;device&amp;#34;&lt;/span>, &lt;span style="color:#ae81ff">1&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;D&amp;#39;&lt;/span>},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {&lt;span style="color:#e6db74">&amp;#34;speed&amp;#34;&lt;/span>, &lt;span style="color:#ae81ff">1&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;s&amp;#39;&lt;/span>},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {&lt;span style="color:#e6db74">&amp;#34;delay&amp;#34;&lt;/span>, &lt;span style="color:#ae81ff">1&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;d&amp;#39;&lt;/span>},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {&lt;span style="color:#e6db74">&amp;#34;bpw&amp;#34;&lt;/span>, &lt;span style="color:#ae81ff">1&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;b&amp;#39;&lt;/span>},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {&lt;span style="color:#e6db74">&amp;#34;loop&amp;#34;&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;l&amp;#39;&lt;/span>},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {&lt;span style="color:#e6db74">&amp;#34;cpha&amp;#34;&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;H&amp;#39;&lt;/span>},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {&lt;span style="color:#e6db74">&amp;#34;cpol&amp;#34;&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;O&amp;#39;&lt;/span>},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {&lt;span style="color:#e6db74">&amp;#34;lsb&amp;#34;&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;L&amp;#39;&lt;/span>},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {&lt;span style="color:#e6db74">&amp;#34;cs-high&amp;#34;&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;C&amp;#39;&lt;/span>},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {&lt;span style="color:#e6db74">&amp;#34;3wire&amp;#34;&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;3&amp;#39;&lt;/span>},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {&lt;span style="color:#e6db74">&amp;#34;no-cs&amp;#34;&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;N&amp;#39;&lt;/span>},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {&lt;span style="color:#e6db74">&amp;#34;ready&amp;#34;&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;R&amp;#39;&lt;/span>},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {NULL, &lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> };
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">int&lt;/span> c;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> c &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#a6e22e">getopt_long&lt;/span>(argc, argv, &lt;span style="color:#e6db74">&amp;#34;D:s:d:b:lHOLC3NR&amp;#34;&lt;/span>, lopts, NULL);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (c &lt;span style="color:#f92672">==&lt;/span> &lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">switch&lt;/span> (c)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">case&lt;/span> &lt;span style="color:#e6db74">&amp;#39;D&amp;#39;&lt;/span>&lt;span style="color:#f92672">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> device &lt;span style="color:#f92672">=&lt;/span> optarg;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">case&lt;/span> &lt;span style="color:#e6db74">&amp;#39;s&amp;#39;&lt;/span>&lt;span style="color:#f92672">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> speed &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#a6e22e">atoi&lt;/span>(optarg);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">case&lt;/span> &lt;span style="color:#e6db74">&amp;#39;d&amp;#39;&lt;/span>&lt;span style="color:#f92672">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> delay &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#a6e22e">atoi&lt;/span>(optarg);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">case&lt;/span> &lt;span style="color:#e6db74">&amp;#39;b&amp;#39;&lt;/span>&lt;span style="color:#f92672">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bits &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#a6e22e">atoi&lt;/span>(optarg);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">case&lt;/span> &lt;span style="color:#e6db74">&amp;#39;l&amp;#39;&lt;/span>&lt;span style="color:#f92672">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> mode &lt;span style="color:#f92672">|=&lt;/span> SPI_LOOP;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">case&lt;/span> &lt;span style="color:#e6db74">&amp;#39;H&amp;#39;&lt;/span>&lt;span style="color:#f92672">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> mode &lt;span style="color:#f92672">|=&lt;/span> SPI_CPHA;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">case&lt;/span> &lt;span style="color:#e6db74">&amp;#39;O&amp;#39;&lt;/span>&lt;span style="color:#f92672">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> mode &lt;span style="color:#f92672">|=&lt;/span> SPI_CPOL;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">case&lt;/span> &lt;span style="color:#e6db74">&amp;#39;L&amp;#39;&lt;/span>&lt;span style="color:#f92672">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> mode &lt;span style="color:#f92672">|=&lt;/span> SPI_LSB_FIRST;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">case&lt;/span> &lt;span style="color:#e6db74">&amp;#39;C&amp;#39;&lt;/span>&lt;span style="color:#f92672">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> mode &lt;span style="color:#f92672">|=&lt;/span> SPI_CS_HIGH;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">case&lt;/span> &lt;span style="color:#e6db74">&amp;#39;3&amp;#39;&lt;/span>&lt;span style="color:#f92672">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> mode &lt;span style="color:#f92672">|=&lt;/span> SPI_3WIRE;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">case&lt;/span> &lt;span style="color:#e6db74">&amp;#39;N&amp;#39;&lt;/span>&lt;span style="color:#f92672">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> mode &lt;span style="color:#f92672">|=&lt;/span> SPI_NO_CS;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">case&lt;/span> &lt;span style="color:#e6db74">&amp;#39;R&amp;#39;&lt;/span>&lt;span style="color:#f92672">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> mode &lt;span style="color:#f92672">|=&lt;/span> SPI_READY;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">default&lt;/span>&lt;span style="color:#f92672">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">print_usage&lt;/span>(argv[&lt;span style="color:#ae81ff">0&lt;/span>]);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> argc, &lt;span style="color:#66d9ef">char&lt;/span> &lt;span style="color:#f92672">*&lt;/span>argv[])
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">int&lt;/span> ret &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">int&lt;/span> fd;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">parse_opts&lt;/span>(argc, argv);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> fd &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#a6e22e">open&lt;/span>(device, O_RDWR);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (fd &lt;span style="color:#f92672">&amp;lt;&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">pabort&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;can&amp;#39;t open device&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">/*
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"> * spi mode
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ret &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#a6e22e">ioctl&lt;/span>(fd, SPI_IOC_WR_MODE, &lt;span style="color:#f92672">&amp;amp;&lt;/span>mode);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (ret &lt;span style="color:#f92672">==&lt;/span> &lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">pabort&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;can&amp;#39;t set spi mode&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ret &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#a6e22e">ioctl&lt;/span>(fd, SPI_IOC_RD_MODE, &lt;span style="color:#f92672">&amp;amp;&lt;/span>mode);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (ret &lt;span style="color:#f92672">==&lt;/span> &lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">pabort&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;can&amp;#39;t get spi mode&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">/*
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"> * bits per word
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ret &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#a6e22e">ioctl&lt;/span>(fd, SPI_IOC_WR_BITS_PER_WORD, &lt;span style="color:#f92672">&amp;amp;&lt;/span>bits);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (ret &lt;span style="color:#f92672">==&lt;/span> &lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">pabort&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;can&amp;#39;t set bits per word&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ret &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#a6e22e">ioctl&lt;/span>(fd, SPI_IOC_RD_BITS_PER_WORD, &lt;span style="color:#f92672">&amp;amp;&lt;/span>bits);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (ret &lt;span style="color:#f92672">==&lt;/span> &lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">pabort&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;can&amp;#39;t get bits per word&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">/*
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"> * max speed hz
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ret &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#a6e22e">ioctl&lt;/span>(fd, SPI_IOC_WR_MAX_SPEED_HZ, &lt;span style="color:#f92672">&amp;amp;&lt;/span>speed);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (ret &lt;span style="color:#f92672">==&lt;/span> &lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">pabort&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;can&amp;#39;t set max speed hz&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ret &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#a6e22e">ioctl&lt;/span>(fd, SPI_IOC_RD_MAX_SPEED_HZ, &lt;span style="color:#f92672">&amp;amp;&lt;/span>speed);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (ret &lt;span style="color:#f92672">==&lt;/span> &lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">pabort&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;can&amp;#39;t get max speed hz&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">printf&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;spi mode: %d&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>, mode);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">printf&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;bits per word: %d&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>, bits);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">printf&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;max speed: %d Hz (%d KHz)&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>, speed, speed &lt;span style="color:#f92672">/&lt;/span> &lt;span style="color:#ae81ff">1000&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">transfer&lt;/span>(fd);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">close&lt;/span>(fd);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> ret;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Compile this file and run it. If the output looks like the following, the modification was successful:&lt;/p>
&lt;pre tabindex="0">&lt;code>spi mode: 0
bits per word: 8
max speed: 500000 Hz (500 KHz)
FF FF FF FF FF FF
40 00 00 00 00 95
FF FF FF FF FF FF
FF FF FF FF FF FF
FF FF FF FF FF FF
DE AD BE EF BA AD
F0 0D
&lt;/code>&lt;/pre></description></item><item><title>ARM GIC-400 Registers</title><link>https://nansenli.com/post/kokeyuan/arm-gic-400-registers/</link><pubDate>Thu, 30 Jun 2016 20:00:00 +0000</pubDate><guid>https://nansenli.com/post/kokeyuan/arm-gic-400-registers/</guid><description>&lt;h2 id="1-introduction">1. Introduction
&lt;/h2>&lt;p>GIC-400 is used for interrupt control. The A20 chip on the Cubieboard2 uses this controller.&lt;/p>
&lt;p>The GIC controller address on Cubieboard2 is 0x01C80000&amp;mdash;0x01C87FFF, with a length equal to the GIC-400 register length.&lt;/p>
&lt;p>The specific details are shown below.&lt;/p>
&lt;p>With 0x01C80000 as the starting address, the following offsets are provided:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Offset Address&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>0x0000-0x0FFF&lt;/td>
&lt;td>Reserved, unused&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x1000-0x1FFF&lt;/td>
&lt;td>Distributor, useful&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x2000-0x3FFF&lt;/td>
&lt;td>CPU interfaces, useful&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x4000-0x4FFF&lt;/td>
&lt;td>Virtual interface control block, for the processor that is performing the access&lt;!-- raw HTML omitted -->Virtualization-related, unused&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x5000-0x5FFF&lt;/td>
&lt;td>Virtual interface control block, for the processor selected by address bits [11:9]&lt;!-- raw HTML omitted -->Virtualization-related, unused&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x5000-0x51FF&lt;!-- raw HTML omitted -->0x5200-0x53FF&lt;!-- raw HTML omitted -->&amp;hellip;&lt;!-- raw HTML omitted -->0x5E00-0x5FFF&lt;/td>
&lt;td>Alias for Processor 0&lt;!-- raw HTML omitted -->Alias for Processor 1&lt;!-- raw HTML omitted -->&amp;hellip;&lt;!-- raw HTML omitted -->Alias for Processor 7&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x6000-0x7FFF&lt;/td>
&lt;td>Virtual CPU interfaces&lt;!-- raw HTML omitted -->Virtualization-related, unused&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The commonly used parts are the second and third blocks in the diagram, the 0x1000~0x3fff configuration.&lt;/p>
&lt;h2 id="2-distributor">2. Distributor
&lt;/h2>&lt;p>The address of this distributor register block = GIC controller address + 0x1000. Remember this offset when using it. Starting from here, the following registers are available:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Offset&lt;/th>
&lt;th>Name&lt;/th>
&lt;th>Access&lt;/th>
&lt;th>Reset Value&lt;/th>
&lt;th>Function&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>0x000&lt;/td>
&lt;td>GICD_CTLR&lt;/td>
&lt;td>RW&lt;/td>
&lt;td>0x00000000[c]&lt;/td>
&lt;td>Distributor Control Register&lt;!-- raw HTML omitted -->Write 1 to enable the controller, required&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x004&lt;/td>
&lt;td>GICD_TYPER&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>Configuration-dependent[d]&lt;/td>
&lt;td>Interrupt Controller Type Register&lt;!-- raw HTML omitted -->Used to check the total number of interrupt lines&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x008&lt;/td>
&lt;td>GICD_IIDR&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>0x0200143B&lt;/td>
&lt;td>Distributor Implementer Identification Register, GICD_IIDR&lt;!-- raw HTML omitted -->Contains version information, not particularly useful&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x080-0x0BC&lt;/td>
&lt;td>GICD_IGROUPRn&lt;/td>
&lt;td>RW&lt;/td>
&lt;td>0x00000000&lt;/td>
&lt;td>Interrupt Group Registers[e]&lt;!-- raw HTML omitted -->A bitmap that controls whether interrupts belong to Group A or B&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x100&lt;/td>
&lt;td>GICD_ISENABLERn&lt;/td>
&lt;td>RW[f]&lt;/td>
&lt;td>SGIs and PPIs:0x0000FFFF[g]&lt;/td>
&lt;td>Interrupt Set-Enable Registers&lt;!-- raw HTML omitted -->A bitmap for enabling individual interrupts, write 1 to enable. Useful&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x104-0x13C&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>SPIs: 0x00000000&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x180&lt;/td>
&lt;td>GICD_ICENABLERn&lt;/td>
&lt;td>RW[f]&lt;/td>
&lt;td>0x0000FFFF[g]&lt;/td>
&lt;td>Interrupt Clear-Enable Registers&lt;!-- raw HTML omitted -->Similar to the previous register, but opposite function, write 1 to disable&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x184-0x1BC&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>0x00000000&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x200-0x23C&lt;/td>
&lt;td>GICD_ISPENDRn&lt;/td>
&lt;td>RW&lt;/td>
&lt;td>0x00000000&lt;/td>
&lt;td>Interrupt Set-Pending Registers&lt;!-- raw HTML omitted -->Pending bitmap, write 1 to enter pending state&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x280-0x2BC&lt;/td>
&lt;td>GICD_ICPENDRn&lt;/td>
&lt;td>RW&lt;/td>
&lt;td>0x00000000&lt;/td>
&lt;td>Interrupt Clear-Pending Registers&lt;!-- raw HTML omitted -->Similar to above, write 1 for opposite effect, prevents pending state&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x300-0x33C&lt;/td>
&lt;td>GICD_ISACTIVERn&lt;/td>
&lt;td>RW&lt;/td>
&lt;td>0x00000000&lt;/td>
&lt;td>Interrupt Set-Active Registers&lt;!-- raw HTML omitted -->Bitmap, write 1 to activate an interrupt&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x380-0x3BC&lt;/td>
&lt;td>GICD_ICACTIVERn&lt;/td>
&lt;td>RW&lt;/td>
&lt;td>0x00000000&lt;/td>
&lt;td>Interrupt Clear-Active Registers&lt;!-- raw HTML omitted -->Write 1 to deactivate an interrupt&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x400-0x5FC&lt;/td>
&lt;td>GICD_IPRIORITYRn&lt;/td>
&lt;td>RW&lt;/td>
&lt;td>0x00000000&lt;/td>
&lt;td>Interrupt Priority Registers&lt;!-- raw HTML omitted -->Stores priority levels for each interrupt, 8 bits per interrupt&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x800-0x81C&lt;/td>
&lt;td>GICD_ITARGETSRn&lt;/td>
&lt;td>RO[h]&lt;/td>
&lt;td>-&lt;/td>
&lt;td>Interrupt Processor Targets Registers[i]&lt;!-- raw HTML omitted -->Determines which processor an interrupt should be sent to for handling&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x820-0x9FC&lt;/td>
&lt;td>&lt;/td>
&lt;td>RW&lt;/td>
&lt;td>0x00000000&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0xC00&lt;/td>
&lt;td>GICD_ICFGRn&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>SGIs: 0xAAAAAAAA&lt;/td>
&lt;td>Interrupt Configuration Registers, GICD_ICFGRn&lt;!-- raw HTML omitted -->Configures whether interrupts are level-triggered or falling-edge triggered&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0xC04&lt;/td>
&lt;td>&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>PPIs: 0x55540000&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0xC08-0xC7C&lt;/td>
&lt;td>&lt;/td>
&lt;td>RW[j]&lt;/td>
&lt;td>SPIs: 0x55555555&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0xD00&lt;/td>
&lt;td>GICD_PPISR&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>0x00000000&lt;/td>
&lt;td>Private Peripheral Interrupt Status Register, GICD_PPISR&lt;!-- raw HTML omitted -->Generally not used&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0xD04-0xD3C&lt;/td>
&lt;td>GICD_SPISRn&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>0x00000000&lt;/td>
&lt;td>Shared Peripheral Interrupt Status Registers, GICD_SPISRn&lt;!-- raw HTML omitted -->Not used&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0xF00&lt;/td>
&lt;td>GICD_SGIR&lt;/td>
&lt;td>WO&lt;/td>
&lt;td>-&lt;/td>
&lt;td>Software Generated Interrupt Register&lt;!-- raw HTML omitted -->Controls software interrupts&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0xF10-0xF1C&lt;/td>
&lt;td>GICD_CPENDSGIRn&lt;/td>
&lt;td>RW&lt;/td>
&lt;td>0x00000000&lt;/td>
&lt;td>SGI Clear-Pending Registers&lt;!-- raw HTML omitted -->Pending bits for software interrupts&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0xF20-0xF2C&lt;/td>
&lt;td>GICD_SPENDSGIRn&lt;/td>
&lt;td>RW&lt;/td>
&lt;td>0x00000000&lt;/td>
&lt;td>SGI Set-Pending Registers&lt;!-- raw HTML omitted -->Similar to above, but writing 1 stops pending&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0xFD0&lt;/td>
&lt;td>GICD_PIDR4&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>0x00000004&lt;/td>
&lt;td>Peripheral ID 4 Register&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0xFD4&lt;/td>
&lt;td>GICD_PIDR5&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>0x00000000&lt;/td>
&lt;td>Peripheral ID 5 Register&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0xFD8&lt;/td>
&lt;td>GICD_PIDR6&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>0x00000000&lt;/td>
&lt;td>Peripheral ID 6 Register&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0xFDC&lt;/td>
&lt;td>GICD_PIDR7&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>0x00000000&lt;/td>
&lt;td>Peripheral ID 7 Register&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0xFE0&lt;/td>
&lt;td>GICD_PIDR0&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>0x00000090&lt;/td>
&lt;td>Peripheral ID 0 Register&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0xFE4&lt;/td>
&lt;td>GICD_PIDR1&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>0x000000B4&lt;/td>
&lt;td>Peripheral ID 1 Register&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0xFE8&lt;/td>
&lt;td>GICD_PIDR2&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>0x0000002B&lt;/td>
&lt;td>Peripheral ID 2 Register&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0xFEC&lt;/td>
&lt;td>GICD_PIDR3&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>0x00000000&lt;/td>
&lt;td>Peripheral ID 3 Register&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0xFF0&lt;/td>
&lt;td>GICD_CIDR0&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>0x0000000D&lt;/td>
&lt;td>Component ID 0 Register&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0xFF4&lt;/td>
&lt;td>GICD_CIDR1&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>0x000000F0&lt;/td>
&lt;td>Component ID 1 Register&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0xFF8&lt;/td>
&lt;td>GICD_CIDR2&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>0x00000005&lt;/td>
&lt;td>Component ID 2 Register&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0xFFC&lt;/td>
&lt;td>GICD_CIDR3&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>0x000000B1&lt;/td>
&lt;td>Component ID 3 Register&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="3-cpu-interface">3. CPU Interface
&lt;/h2>&lt;p>Starting address offset is 0x2000&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Offset&lt;/th>
&lt;th>Name&lt;/th>
&lt;th>Type&lt;/th>
&lt;th>Reset&lt;/th>
&lt;th>Description[a]&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>0x0000&lt;/td>
&lt;td>GICC_CTLR&lt;/td>
&lt;td>RW&lt;/td>
&lt;td>0x00000000&lt;/td>
&lt;td>CPU Interface Control Register&lt;!-- raw HTML omitted -->Enable bit. Write 1 to enable&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x0004&lt;/td>
&lt;td>GICC_PMR&lt;/td>
&lt;td>RW&lt;/td>
&lt;td>0x00000000&lt;/td>
&lt;td>Interrupt Priority Mask Register&lt;!-- raw HTML omitted -->Limits minimum interrupt priority, interrupts with values above this cannot trigger. Best to set this high&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x0008&lt;/td>
&lt;td>GICC_BPR&lt;/td>
&lt;td>RW&lt;/td>
&lt;td>0x00000002[b]&lt;/td>
&lt;td>Binary Point Register&lt;!-- raw HTML omitted -->The minimum value of the Binary Point Register depends on which security-banked copy is considered:&lt;!-- raw HTML omitted -->0x2 Secure copy&lt;!-- raw HTML omitted -->0x3 Non-secure copy&lt;!-- raw HTML omitted -->Priority grouping&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x000C&lt;/td>
&lt;td>GICC_IAR&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>0x000003FF&lt;/td>
&lt;td>Interrupt Acknowledge Register&lt;!-- raw HTML omitted -->Read-only, interrupt ID&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x0010&lt;/td>
&lt;td>GICC_EOIR&lt;/td>
&lt;td>WO&lt;/td>
&lt;td>-&lt;/td>
&lt;td>End of Interrupt Register&lt;!-- raw HTML omitted -->Write to inform the CPU that interrupt processing is complete&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x0014&lt;/td>
&lt;td>GICC_RPR&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>0x000000FF&lt;/td>
&lt;td>Running Priority Register&lt;!-- raw HTML omitted -->Current interrupt priority&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x0018&lt;/td>
&lt;td>GICC_HPPIR&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>0x000003FF&lt;/td>
&lt;td>Highest Priority Pending Interrupt Register [c]&lt;!-- raw HTML omitted -->Highest priority interrupt number and its pending value&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x001C&lt;/td>
&lt;td>GICC_ABPR&lt;/td>
&lt;td>RW&lt;/td>
&lt;td>0x00000003&lt;/td>
&lt;td>Aliased Binary Point Register[d]&lt;!-- raw HTML omitted -->The minimum value of the Aliased Binary Point Register is 0x3.&lt;!-- raw HTML omitted -->Alias register&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x0020&lt;/td>
&lt;td>GICC_AIAR&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>0x000003FF&lt;/td>
&lt;td>Aliased Interrupt Acknowledge Register[d]&lt;!-- raw HTML omitted -->Alias register&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x0024&lt;/td>
&lt;td>GICC_AEOIR&lt;/td>
&lt;td>WO&lt;/td>
&lt;td>-&lt;/td>
&lt;td>Aliased End of Interrupt Register[d]&lt;!-- raw HTML omitted -->Alias register&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x0028&lt;/td>
&lt;td>GICC_AHPPIR&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>0x000003FF&lt;/td>
&lt;td>Aliased Highest Priority Pending Interrupt Register[c][d]&lt;!-- raw HTML omitted -->Alias register&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x00D0&lt;/td>
&lt;td>GICC_APR0&lt;/td>
&lt;td>RW&lt;/td>
&lt;td>0x00000000&lt;/td>
&lt;td>Active Priority Register&lt;!-- raw HTML omitted -->Used for saving and restoring&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x00E0&lt;/td>
&lt;td>GICC_NSAPR0&lt;/td>
&lt;td>RW&lt;/td>
&lt;td>0x00000000&lt;/td>
&lt;td>Non-Secure Active Priority Register[d]&lt;!-- raw HTML omitted -->Used for saving and restoring&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x00FC&lt;/td>
&lt;td>GICC_IIDR&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>0x0202143B&lt;/td>
&lt;td>CPU Interface Identification Register, GICC_IIDR&lt;!-- raw HTML omitted -->Contains version information&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0x1000&lt;/td>
&lt;td>GICC_DIR&lt;/td>
&lt;td>WO&lt;/td>
&lt;td>-&lt;/td>
&lt;td>Deactivate Interrupt Register&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table></description></item><item><title>NRF24L01 + STC15F204EA Wireless Communication Source Code</title><link>https://nansenli.com/post/kokeyuan/nrf24l01-stc15f204ea-wireless-communication/</link><pubDate>Sun, 01 Sep 2013 00:55:00 +0000</pubDate><guid>https://nansenli.com/post/kokeyuan/nrf24l01-stc15f204ea-wireless-communication/</guid><description>&lt;p>This code is suitable for small wireless transceiver modules composed of NRF24L01 and STC15F204EA (STC15L204EA), and can also be applied to general 51 microcontrollers communicating with NRF24L01.&lt;/p>
&lt;p>A few days ago, I bought a small wireless transceiver module online. The source code provided by the seller was very simple and not particularly useful, so I ported the STM3224l01 program from ATOM, which is more complete and practical.&lt;/p>
&lt;p>Since there are almost no pins available to display transmission and reception content, serial communication is used to display the content.&lt;/p>
&lt;p>Applicable module image:&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/6297a49400b0.png"
loading="lazy"
alt="Module image"
>&lt;/p>
&lt;p>Main wiring diagram:&lt;/p>
&lt;p>&lt;img src="https://nansenli.com/img/rescued/e6ede2277ff3.png"
loading="lazy"
alt="Wiring diagram"
>&lt;/p>
&lt;p>First, for serial display, I collected the following code. The reason it&amp;rsquo;s so complex is that this chip doesn&amp;rsquo;t have a serial port function.&lt;/p>
&lt;p>First is the H file:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-c" data-lang="c">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#ifndef _UART_H
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#define _UART_H
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#define MCU_FREQ 11059200 &lt;/span>&lt;span style="color:#75715e">// Set crystal frequency
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define UART_BUAD 38400
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#define ON 1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#define OFF 0
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#define UART_TX_PIN P31
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#define UART_TX_SET(n) UART_TX_PIN = n
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#define UART_TX_HIGH() UART_TX_SET(1)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#define UART_TX_LOW() UART_TX_SET(0)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#define UART_TX_FLIP() UART_TX_PIN = !UART_TX_PIN
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#define UART_RX_PIN P30
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#define UART_RX_SET(n) UART_RX_PIN = n
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#define UART_RX_HIGH() UART_RX_SET(1)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#define UART_RX_LOW() UART_RX_SET(0)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#define UART_RX_FLIP() UART_RX_PIN = !UART_RX_PIN
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">uartInit&lt;/span>(&lt;span style="color:#66d9ef">void&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">uartSendString&lt;/span>(&lt;span style="color:#66d9ef">char&lt;/span> &lt;span style="color:#f92672">*&lt;/span>pS);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">uartSendNum&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> num);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#endif
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In the file, you need to set the crystal frequency and baud rate correctly, which are MCU_FREQ and UART_BUAD respectively. Once set, they enable correct transmission and reception.&lt;/p>
&lt;p>Next is the C file:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-c" data-lang="c">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;#34;stdio.h&amp;#34;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;#34;uart.h&amp;#34;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;#34;15f204ea.h&amp;#34; // Header file provided by STC official website&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">typedef&lt;/span> &lt;span style="color:#66d9ef">unsigned&lt;/span> &lt;span style="color:#66d9ef">char&lt;/span> u8;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">typedef&lt;/span> &lt;span style="color:#66d9ef">unsigned&lt;/span> &lt;span style="color:#66d9ef">short&lt;/span> u16;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">typedef&lt;/span> &lt;span style="color:#66d9ef">unsigned&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> u32;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">typedef&lt;/span> &lt;span style="color:#66d9ef">unsigned&lt;/span> &lt;span style="color:#66d9ef">char&lt;/span> uchar;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">typedef&lt;/span> &lt;span style="color:#66d9ef">unsigned&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> uint;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">typedef&lt;/span> &lt;span style="color:#66d9ef">unsigned&lt;/span> &lt;span style="color:#66d9ef">char&lt;/span> BYTE;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">static&lt;/span> bit bUartFlag;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/******************************************************************************/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// Function name: uartInit
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// Input parameters: none
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// Output parameters: none
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// Function: Set up timer0 operating mode
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">/******************************************************************************/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">uartInit&lt;/span>(&lt;span style="color:#66d9ef">void&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">/*
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"> * Set timer0 as 16-bit auto-reload timer
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> AUXR &lt;span style="color:#f92672">|=&lt;/span> &lt;span style="color:#ae81ff">0x80&lt;/span>; &lt;span style="color:#75715e">// Timer0 in 1T mode
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> TMOD &lt;span style="color:#f92672">&amp;amp;=&lt;/span> &lt;span style="color:#ae81ff">0xF0&lt;/span>; &lt;span style="color:#75715e">// Set timer to mode 0 (16-bit auto-reload)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> TL0 &lt;span style="color:#f92672">=&lt;/span> (&lt;span style="color:#ae81ff">0xFFFF&lt;/span> &lt;span style="color:#f92672">-&lt;/span> MCU_FREQ &lt;span style="color:#f92672">/&lt;/span> UART_BUAD) &lt;span style="color:#f92672">&amp;amp;&lt;/span> &lt;span style="color:#ae81ff">0xFF&lt;/span>; &lt;span style="color:#75715e">// Set timer initial value
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> TH0 &lt;span style="color:#f92672">=&lt;/span> ((&lt;span style="color:#ae81ff">0xFFFF&lt;/span> &lt;span style="color:#f92672">-&lt;/span> MCU_FREQ &lt;span style="color:#f92672">/&lt;/span> UART_BUAD) &lt;span style="color:#f92672">&amp;gt;&amp;gt;&lt;/span> &lt;span style="color:#ae81ff">8&lt;/span>) &lt;span style="color:#f92672">&amp;amp;&lt;/span> &lt;span style="color:#ae81ff">0xFF&lt;/span>; &lt;span style="color:#75715e">// Set timer initial value
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> TR0 &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>; &lt;span style="color:#75715e">// Timer0 starts counting
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> ET0 &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>; &lt;span style="color:#75715e">// Enable timer0 interrupt
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> EA &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/******************************************************************************/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// Function name: uartSendData
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// Input parameters: ucData: byte to send
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// Output parameters: none
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// Function: Use serial port to send one byte of data
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">/******************************************************************************/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">uartSendData&lt;/span>(u8 ucData)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> u8 ucCnt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">UART_TX_LOW&lt;/span>(); &lt;span style="color:#75715e">// Serial start bit begins
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> TR0 &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>; &lt;span style="color:#75715e">// Timer0 starts counting
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> ET0 &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>; &lt;span style="color:#75715e">// Enable timer0 interrupt
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> bUartFlag &lt;span style="color:#f92672">=&lt;/span> ON;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">while&lt;/span>(bUartFlag &lt;span style="color:#f92672">==&lt;/span> ON);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">/*
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"> * Starting from the lowest bit, output data through serial port
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span> (ucCnt &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>; ucCnt &lt;span style="color:#f92672">&amp;lt;&lt;/span> &lt;span style="color:#ae81ff">8&lt;/span>; ucCnt&lt;span style="color:#f92672">++&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">UART_TX_SET&lt;/span>(ucData &lt;span style="color:#f92672">&amp;amp;&lt;/span> &lt;span style="color:#ae81ff">0x01&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ucData &lt;span style="color:#f92672">&amp;gt;&amp;gt;=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bUartFlag &lt;span style="color:#f92672">=&lt;/span> ON;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">while&lt;/span>(bUartFlag &lt;span style="color:#f92672">==&lt;/span> ON);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">UART_TX_HIGH&lt;/span>(); &lt;span style="color:#75715e">// Send serial stop bit
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> bUartFlag &lt;span style="color:#f92672">=&lt;/span> ON;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">while&lt;/span>(bUartFlag &lt;span style="color:#f92672">==&lt;/span> ON);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> TR0 &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>; &lt;span style="color:#75715e">// Timer0 stops counting
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> ET0 &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>; &lt;span style="color:#75715e">// Disable timer0 interrupt
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/******************************************************************************/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// Function name: uartSendString
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// Input parameters: pS: string&amp;#39;s address
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// Output parameters: none
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// Function: Send string through serial output
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">/******************************************************************************/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">uartSendString&lt;/span>(&lt;span style="color:#66d9ef">char&lt;/span> &lt;span style="color:#f92672">*&lt;/span>pS)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">while&lt;/span> (&lt;span style="color:#f92672">*&lt;/span>pS) &lt;span style="color:#75715e">// Check for string end marker
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">uartSendData&lt;/span>(&lt;span style="color:#f92672">*&lt;/span>pS&lt;span style="color:#f92672">++&lt;/span>); &lt;span style="color:#75715e">// Send current character
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">uartSendData&lt;/span>(&lt;span style="color:#e6db74">&amp;#39;\r&amp;#39;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">uartSendData&lt;/span>(&lt;span style="color:#e6db74">&amp;#39;\n&amp;#39;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">uartSendNum&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> num){ &lt;span style="color:#75715e">// Use sprintf function to print integer (can also print decimal)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">char&lt;/span> temp[&lt;span style="color:#ae81ff">14&lt;/span>];
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">sprintf&lt;/span>(temp,&lt;span style="color:#e6db74">&amp;#34;%d&amp;#34;&lt;/span>,num);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">uartSendString&lt;/span>(temp);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/******************************************************************************/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// Function name: time0ISR
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// Input parameters: none
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// Output parameters: none
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// Function: Serial port 0 service function
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">/******************************************************************************/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">time0ISR&lt;/span>(&lt;span style="color:#66d9ef">void&lt;/span>) interrupt &lt;span style="color:#ae81ff">1&lt;/span> using &lt;span style="color:#ae81ff">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> EA &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bUartFlag &lt;span style="color:#f92672">=&lt;/span> OFF;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> EA &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This code calls 15f204ea.h, which is a header file I downloaded from the STC website. For ease of reference, I won&amp;rsquo;t list the entire file here as it&amp;rsquo;s too long.&lt;/p>
&lt;p>Now that we&amp;rsquo;ve handled the serial display, let&amp;rsquo;s move on to the 24l01 program code.&lt;/p>
&lt;p>First is the H file:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-c" data-lang="c">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#ifndef __24L01_H
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#define __24L01_H
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;#34;15f204ea.h&amp;#34;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#define u8 unsigned char
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#define u16 unsigned int
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">typedef&lt;/span> &lt;span style="color:#66d9ef">unsigned&lt;/span> &lt;span style="color:#66d9ef">char&lt;/span> uchar;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">typedef&lt;/span> &lt;span style="color:#66d9ef">unsigned&lt;/span> &lt;span style="color:#66d9ef">char&lt;/span> uint;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/*nRF24L01 pin definitions*/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sbit CE &lt;span style="color:#f92672">=&lt;/span> P1&lt;span style="color:#f92672">^&lt;/span>&lt;span style="color:#ae81ff">4&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sbit CSN &lt;span style="color:#f92672">=&lt;/span> P1&lt;span style="color:#f92672">^&lt;/span>&lt;span style="color:#ae81ff">5&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sbit SCK &lt;span style="color:#f92672">=&lt;/span> P1&lt;span style="color:#f92672">^&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sbit MOSI &lt;span style="color:#f92672">=&lt;/span> P1&lt;span style="color:#f92672">^&lt;/span>&lt;span style="color:#ae81ff">3&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sbit MISO &lt;span style="color:#f92672">=&lt;/span> P1&lt;span style="color:#f92672">^&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sbit IRQ &lt;span style="color:#f92672">=&lt;/span> P1&lt;span style="color:#f92672">^&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">//NRF24L01 register operation commands
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define READ_NRF_REG 0x00 &lt;/span>&lt;span style="color:#75715e">//Read configuration register, lower 5 bits are register address
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define WRITE_NRF_REG 0x20 &lt;/span>&lt;span style="color:#75715e">//Write configuration register, lower 5 bits are register address
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define RD_RX_PLOAD 0x61 &lt;/span>&lt;span style="color:#75715e">//Read RX valid data, 1~32 bytes
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define WR_TX_PLOAD 0xA0 &lt;/span>&lt;span style="color:#75715e">//Write TX valid data, 1~32 bytes
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define FLUSH_TX 0xE1 &lt;/span>&lt;span style="color:#75715e">//Clear TX FIFO register. Used in transmit mode
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define FLUSH_RX 0xE2 &lt;/span>&lt;span style="color:#75715e">//Clear RX FIFO register. Used in receive mode
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define REUSE_TX_PL 0xE3 &lt;/span>&lt;span style="color:#75715e">//Reuse last sent packet, CE is high, data packet is continuously sent
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define NOP 0xFF &lt;/span>&lt;span style="color:#75715e">//No operation, can be used to read status register
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">//SPI(NRF24L01) register addresses
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define CONFIG 0x00 &lt;/span>&lt;span style="color:#75715e">//Configuration register address; bit0:1 receive mode, 0 transmit mode; bit1:power select; bit2:CRC mode; bit3:CRC enable;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">//bit4:interrupt MAX_RT (max retransmit interruption) enable; bit5:interrupt TX_DS enable; bit6:interrupt RX_DR enable
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define EN_AA 0x01 &lt;/span>&lt;span style="color:#75715e">//Enable auto-acknowledge function bit0~5, corresponding to channels 0~5
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define EN_RXADDR 0x02 &lt;/span>&lt;span style="color:#75715e">//Receive address allow, bit0~5, corresponding to channels 0~5
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define SETUP_AW 0x03 &lt;/span>&lt;span style="color:#75715e">//Set address width (all data channels): bit1,0:00,3 bytes; 01,4 bytes; 02,5 bytes;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define SETUP_RETR 0x04 &lt;/span>&lt;span style="color:#75715e">//Set auto retransmit; bit3:0, auto retransmit counter; bit7:4, auto retransmit delay 250*x+86us
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define RF_CH 0x05 &lt;/span>&lt;span style="color:#75715e">//RF channel, bit6:0, working channel frequency;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define RF_SETUP 0x06 &lt;/span>&lt;span style="color:#75715e">//RF register; bit3:transmission rate(0:1Mbps,1:2Mbps); bit2:1, transmission power; bit0:low noise amplifier gain
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define STATUS 0x07 &lt;/span>&lt;span style="color:#75715e">//Status register; bit0:TX FIFO full flag; bit3:1, receive data channel number (max:6); bit4, reached max retransmit
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">//bit5:data send complete interrupt; bit6:receive data interrupt
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define MAX_TX 0x10 &lt;/span>&lt;span style="color:#75715e">//Reached maximum send times interrupt
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define TX_OK 0x20 &lt;/span>&lt;span style="color:#75715e">//TX send complete interrupt
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define RX_OK 0x40 &lt;/span>&lt;span style="color:#75715e">//Received data interrupt
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define OBSERVE_TX 0x08 &lt;/span>&lt;span style="color:#75715e">//Send detection register, bit7:4, data packet loss counter; bit3:0, retransmit counter
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define CD 0x09 &lt;/span>&lt;span style="color:#75715e">//Carrier detection register, bit0, carrier detection;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define RX_ADDR_P0 0x0A &lt;/span>&lt;span style="color:#75715e">//Data channel 0 receive address, max length 5 bytes, low byte first
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define RX_ADDR_P1 0x0B &lt;/span>&lt;span style="color:#75715e">//Data channel 1 receive address, max length 5 bytes, low byte first
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define RX_ADDR_P2 0x0C &lt;/span>&lt;span style="color:#75715e">//Data channel 2 receive address, lowest byte can be set, high bytes must be equal to RX_ADDR_P1[39:8];
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define RX_ADDR_P3 0x0D &lt;/span>&lt;span style="color:#75715e">//Data channel 3 receive address, lowest byte can be set, high bytes must be equal to RX_ADDR_P1[39:8];
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define RX_ADDR_P4 0x0E &lt;/span>&lt;span style="color:#75715e">//Data channel 4 receive address, lowest byte can be set, high bytes must be equal to RX_ADDR_P1[39:8];
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define RX_ADDR_P5 0x0F &lt;/span>&lt;span style="color:#75715e">//Data channel 5 receive address, lowest byte can be set, high bytes must be equal to RX_ADDR_P1[39:8];
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#define TX_ADDR 0x10 &lt;/span>&lt;span style="color:#75715e">//Send address (low byte first), under ShockBurstTM mode, RX_ADDR_P0 equals this address
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define RX_PW_P0 0x11 &lt;/span>&lt;span style="color:#75715e">//Receive data channel 0 valid data width (1~32 bytes), setting to 0 is invalid
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define RX_PW_P1 0x12 &lt;/span>&lt;span style="color:#75715e">//Receive data channel 1 valid data width (1~32 bytes), setting to 0 is invalid
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define RX_PW_P2 0x13 &lt;/span>&lt;span style="color:#75715e">//Receive data channel 2 valid data width (1~32 bytes), setting to 0 is invalid
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define RX_PW_P3 0x14 &lt;/span>&lt;span style="color:#75715e">//Receive data channel 3 valid data width (1~32 bytes), setting to 0 is invalid
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define RX_PW_P4 0x15 &lt;/span>&lt;span style="color:#75715e">//Receive data channel 4 valid data width (1~32 bytes), setting to 0 is invalid
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define RX_PW_P5 0x16 &lt;/span>&lt;span style="color:#75715e">//Receive data channel 5 valid data width (1~32 bytes), setting to 0 is invalid
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define FIFO_STATUS 0x17 &lt;/span>&lt;span style="color:#75715e">//FIFO status register; bit0, RX FIFO register empty flag; bit1, RX FIFO full flag; bit2,3, reserved
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">//bit4, TX FIFO empty flag; bit5, TX FIFO full flag; bit6,1, cycle send previous data packet. 0, don&amp;#39;t cycle;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">//////////////////////////////////////////////////////////////////////////////
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">//24L01 operation lines
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define NRF24L01_CE CE &lt;/span>&lt;span style="color:#75715e">//24L01 chip select signal
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define NRF24L01_CSN CSN &lt;/span>&lt;span style="color:#75715e">//SPI chip select signal
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define NRF24L01_IRQ IRQ &lt;/span>&lt;span style="color:#75715e">//IRQ host data input
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">//24L01 send and receive data width definitions
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define TX_ADR_WIDTH 5 &lt;/span>&lt;span style="color:#75715e">//5 bytes address width
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define RX_ADR_WIDTH 5 &lt;/span>&lt;span style="color:#75715e">//5 bytes address width
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define TX_PLOAD_WIDTH 32 &lt;/span>&lt;span style="color:#75715e">//20 bytes user data width
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#define RX_PLOAD_WIDTH 32 &lt;/span>&lt;span style="color:#75715e">//20 bytes user data width
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">NRF24L01_Init&lt;/span>(&lt;span style="color:#66d9ef">void&lt;/span>);&lt;span style="color:#75715e">//Initialization
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">RX_Mode&lt;/span>(&lt;span style="color:#66d9ef">void&lt;/span>);&lt;span style="color:#75715e">//Configure as receive mode
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">TX_Mode&lt;/span>(&lt;span style="color:#66d9ef">void&lt;/span>);&lt;span style="color:#75715e">//Configure as transmit mode
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>u8 &lt;span style="color:#a6e22e">NRF24L01_Check&lt;/span>(&lt;span style="color:#66d9ef">void&lt;/span>);&lt;span style="color:#75715e">//Check if 24L01 exists
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>u8 &lt;span style="color:#a6e22e">NRF24L01_TxPacket&lt;/span>(u8 &lt;span style="color:#f92672">*&lt;/span>txbuf);&lt;span style="color:#75715e">//Send a packet of data
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>u8 &lt;span style="color:#a6e22e">NRF24L01_RxPacket&lt;/span>(u8 &lt;span style="color:#f92672">*&lt;/span>rxbuf);&lt;span style="color:#75715e">//Receive a packet of data
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#endif
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Next is the C file:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-c" data-lang="c">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;#34;15f204ea.h&amp;#34;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;#34;24l01.h&amp;#34;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;#34;intrins.h&amp;#34;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">const&lt;/span> u8 TX_ADDRESS[TX_ADR_WIDTH]&lt;span style="color:#f92672">=&lt;/span>{&lt;span style="color:#ae81ff">0x34&lt;/span>,&lt;span style="color:#ae81ff">0x43&lt;/span>,&lt;span style="color:#ae81ff">0x10&lt;/span>,&lt;span style="color:#ae81ff">0x10&lt;/span>,&lt;span style="color:#ae81ff">0x03&lt;/span>}; &lt;span style="color:#75715e">//Transmit address
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">const&lt;/span> u8 RX_ADDRESS[RX_ADR_WIDTH]&lt;span style="color:#f92672">=&lt;/span>{&lt;span style="color:#ae81ff">0x34&lt;/span>,&lt;span style="color:#ae81ff">0x43&lt;/span>,&lt;span style="color:#ae81ff">0x10&lt;/span>,&lt;span style="color:#ae81ff">0x10&lt;/span>,&lt;span style="color:#ae81ff">0x03&lt;/span>}; &lt;span style="color:#75715e">//Receive address
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/******************************************************************************************
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/*Delay function
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/******************************************************************************************/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">inerDelay_us&lt;/span>(&lt;span style="color:#66d9ef">unsigned&lt;/span> &lt;span style="color:#66d9ef">char&lt;/span> n)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span>(;n&lt;span style="color:#f92672">&amp;gt;&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>;n&lt;span style="color:#f92672">--&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">_nop_&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">//****************************************************************************************
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">/*NRF24L01 initialization
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">//***************************************************************************************/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">NRF24L01_Init&lt;/span>(&lt;span style="color:#66d9ef">void&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">inerDelay_us&lt;/span>(&lt;span style="color:#ae81ff">100&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CE&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>; &lt;span style="color:#75715e">// chip enable
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> CSN&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>; &lt;span style="color:#75715e">// Spi disable
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> SCK&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>; &lt;span style="color:#75715e">//
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/****************************************************************************************************
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/*Function: uint SPI_RW(uint uchar)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/*Function: NRF24L01 SPI write sequence
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/****************************************************************************************************/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>uint &lt;span style="color:#a6e22e">SPI_RW&lt;/span>(uint uchar)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> uint bit_ctr;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span>(bit_ctr&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>;bit_ctr&lt;span style="color:#f92672">&amp;lt;&lt;/span>&lt;span style="color:#ae81ff">8&lt;/span>;bit_ctr&lt;span style="color:#f92672">++&lt;/span>) &lt;span style="color:#75715e">// output 8-bit
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MOSI &lt;span style="color:#f92672">=&lt;/span> (uchar &lt;span style="color:#f92672">&amp;amp;&lt;/span> &lt;span style="color:#ae81ff">0x80&lt;/span>); &lt;span style="color:#75715e">// output &amp;#39;uchar&amp;#39;, MSB to MOSI
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> uchar &lt;span style="color:#f92672">=&lt;/span> (uchar &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>); &lt;span style="color:#75715e">// shift next bit into MSB..
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> SCK &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>; &lt;span style="color:#75715e">// Set SCK high..
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> uchar &lt;span style="color:#f92672">|=&lt;/span> MISO; &lt;span style="color:#75715e">// capture current MISO bit
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> SCK &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>; &lt;span style="color:#75715e">// ..then set SCK low again
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span>(uchar); &lt;span style="color:#75715e">// return read uchar
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/****************************************************************************************************
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/*Function: uchar SPI_Read(uchar reg)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/*Function: NRF24L01 SPI sequence
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/****************************************************************************************************/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>uchar &lt;span style="color:#a6e22e">NRF24L01_Read_Reg&lt;/span>(uchar reg)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> uchar reg_val;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CSN &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>; &lt;span style="color:#75715e">// CSN low, initialize SPI communication...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#a6e22e">SPI_RW&lt;/span>(reg); &lt;span style="color:#75715e">// Select register to read from..
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> reg_val &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#a6e22e">SPI_RW&lt;/span>(&lt;span style="color:#ae81ff">0&lt;/span>); &lt;span style="color:#75715e">// ..then read registervalue
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> CSN &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>; &lt;span style="color:#75715e">// CSN high, terminate SPI communication
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span>(reg_val); &lt;span style="color:#75715e">// return register value
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/****************************************************************************************************/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/*Function: NRF24L01 read and write register function
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/****************************************************************************************************/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>uint &lt;span style="color:#a6e22e">NRF24L01_Write_Reg&lt;/span>(uchar reg, uchar value)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> uint status;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CSN &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>; &lt;span style="color:#75715e">// CSN low, init SPI transaction
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> status &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#a6e22e">SPI_RW&lt;/span>(reg); &lt;span style="color:#75715e">// select register
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#a6e22e">SPI_RW&lt;/span>(value); &lt;span style="color:#75715e">// ..and write value to it..
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> CSN &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>; &lt;span style="color:#75715e">// CSN high again
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span>(status); &lt;span style="color:#75715e">// return nRF24L01 status uchar
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/****************************************************************************************************/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/*Function: uint SPI_Read_Buf(uchar reg, uchar *pBuf, uchar uchars)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/*Function: Used to read data, reg: register address, pBuf: destination data address, uchars: number of bytes to read
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/****************************************************************************************************/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>uint &lt;span style="color:#a6e22e">NRF24L01_Read_Buf&lt;/span>(uchar reg, uchar &lt;span style="color:#f92672">*&lt;/span>pBuf, uchar uchars)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> uint status,uchar_ctr;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CSN &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>; &lt;span style="color:#75715e">// Set CSN low, init SPI tranaction
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> status &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#a6e22e">SPI_RW&lt;/span>(reg); &lt;span style="color:#75715e">// Select register to write to and read status uchar
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span>(uchar_ctr&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>;uchar_ctr&lt;span style="color:#f92672">&amp;lt;&lt;/span>uchars;uchar_ctr&lt;span style="color:#f92672">++&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> pBuf[uchar_ctr] &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#a6e22e">SPI_RW&lt;/span>(&lt;span style="color:#ae81ff">0&lt;/span>); &lt;span style="color:#75715e">//
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CSN &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span>(status); &lt;span style="color:#75715e">// return nRF24L01 status uchar
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/*********************************************************************************************************
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/*Function: uint SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/*Function: Used to write data: register address, pBuf: data to write, uchars: number of bytes to write
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">/*********************************************************************************************************/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>uint &lt;span style="color:#a6e22e">NRF24L01_Write_Buf&lt;/span>(uchar reg, uchar &lt;span style="color:#f92672">*&lt;/span>pBuf, uchar uchars)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> uint status,uchar_ctr;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CSN &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>; &lt;span style="color:#75715e">//SPI enable
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> status &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#a6e22e">SPI_RW&lt;/span>(reg);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span>(uchar_ctr&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>; uchar_ctr&lt;span style="color:#f92672">&amp;lt;&lt;/span>uchars; uchar_ctr&lt;span style="color:#f92672">++&lt;/span>) &lt;span style="color:#75715e">//
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#a6e22e">SPI_RW&lt;/span>(&lt;span style="color:#f92672">*&lt;/span>pBuf&lt;span style="color:#f92672">++&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CSN &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>; &lt;span style="color:#75715e">//Close SPI
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">return&lt;/span>(status); &lt;span style="color:#75715e">//
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">//Check if 24L01 exists
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">//Return value: 0, success; 1, failure
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>u8 &lt;span style="color:#a6e22e">NRF24L01_Check&lt;/span>(&lt;span style="color:#66d9ef">void&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> u8 buf[&lt;span style="color:#ae81ff">5&lt;/span>]&lt;span style="color:#f92672">=&lt;/span>{&lt;span style="color:#ae81ff">0&lt;/span>XA5,&lt;span style="color:#ae81ff">0&lt;/span>XA5,&lt;span style="color:#ae81ff">0&lt;/span>XA5,&lt;span style="color:#ae81ff">0&lt;/span>XA5,&lt;span style="color:#ae81ff">0&lt;/span>XA5};
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> u8 i;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">NRF24L01_Write_Buf&lt;/span>(WRITE_NRF_REG&lt;span style="color:#f92672">+&lt;/span>TX_ADDR,buf,&lt;span style="color:#ae81ff">5&lt;/span>);&lt;span style="color:#75715e">//Write 5 bytes address
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#a6e22e">NRF24L01_Read_Buf&lt;/span>(TX_ADDR,buf,&lt;span style="color:#ae81ff">5&lt;/span>); &lt;span style="color:#75715e">//Read the written address
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">for&lt;/span>(i&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>;i&lt;span style="color:#f92672">&amp;lt;&lt;/span>&lt;span style="color:#ae81ff">5&lt;/span>;i&lt;span style="color:#f92672">++&lt;/span>) &lt;span style="color:#66d9ef">if&lt;/span>(buf[i]&lt;span style="color:#f92672">!=&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>XA5)&lt;span style="color:#66d9ef">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span>(i&lt;span style="color:#f92672">!=&lt;/span>&lt;span style="color:#ae81ff">5&lt;/span>)&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>;&lt;span style="color:#75715e">//24L01 detection error
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>; &lt;span style="color:#75715e">//24L01 detected
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>u8 &lt;span style="color:#a6e22e">NRF24L01_TxPacket&lt;/span>(u8 &lt;span style="color:#f92672">*&lt;/span>txbuf)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> u8 sta;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> NRF24L01_CE&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">NRF24L01_Write_Buf&lt;/span>(WR_TX_PLOAD,txbuf,TX_PLOAD_WIDTH);&lt;span style="color:#75715e">//Write data to TX BUF, 32 bytes
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> NRF24L01_CE&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>;&lt;span style="color:#75715e">//Start transmission
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">while&lt;/span>(NRF24L01_IRQ&lt;span style="color:#f92672">!=&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>);&lt;span style="color:#75715e">//Wait for transmission to complete
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> sta&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#a6e22e">NRF24L01_Read_Reg&lt;/span>(STATUS); &lt;span style="color:#75715e">//Read status register value
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#a6e22e">NRF24L01_Write_Reg&lt;/span>(WRITE_NRF_REG&lt;span style="color:#f92672">+&lt;/span>STATUS,sta); &lt;span style="color:#75715e">//Clear TX_DS or MAX_RT interrupt flag
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">if&lt;/span>(sta&lt;span style="color:#f92672">&amp;amp;&lt;/span>MAX_TX)&lt;span style="color:#75715e">//Maximum retransmission reached
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">NRF24L01_Write_Reg&lt;/span>(FLUSH_TX,&lt;span style="color:#ae81ff">0xff&lt;/span>);&lt;span style="color:#75715e">//Clear TX FIFO register
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">return&lt;/span> MAX_TX;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span>(sta&lt;span style="color:#f92672">&amp;amp;&lt;/span>TX_OK)&lt;span style="color:#75715e">//Transmission completed
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> TX_OK;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#ae81ff">0xff&lt;/span>;&lt;span style="color:#75715e">//Other reasons for transmission failure
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">//Start NRF24L01 to send data once
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">//txbuf: data source address to be sent
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">//Return value: 0, reception complete; others, error code
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>u8 &lt;span style="color:#a6e22e">NRF24L01_RxPacket&lt;/span>(u8 &lt;span style="color:#f92672">*&lt;/span>rxbuf)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> u8 sta;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sta&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#a6e22e">NRF24L01_Read_Reg&lt;/span>(STATUS); &lt;span style="color:#75715e">//Read status register value
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#a6e22e">NRF24L01_Write_Reg&lt;/span>(WRITE_NRF_REG&lt;span style="color:#f92672">+&lt;/span>STATUS,sta); &lt;span style="color:#75715e">//Clear TX_DS or MAX_RT interrupt flag
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">if&lt;/span>(sta&lt;span style="color:#f92672">&amp;amp;&lt;/span>RX_OK)&lt;span style="color:#75715e">//Data received
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">NRF24L01_Read_Buf&lt;/span>(RD_RX_PLOAD,rxbuf,RX_PLOAD_WIDTH);&lt;span style="color:#75715e">//Read data
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#a6e22e">NRF24L01_Write_Reg&lt;/span>(FLUSH_RX,&lt;span style="color:#ae81ff">0xff&lt;/span>);&lt;span style="color:#75715e">//Clear RX FIFO register
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>;&lt;span style="color:#75715e">//No data received
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">//This function initializes NRF24L01 to RX mode
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">//Set RX address, write RX data width, select RF channel, baud rate and LNA HCURR
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">//When CE becomes high, it enters RX mode and can receive data
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">RX_Mode&lt;/span>(&lt;span style="color:#66d9ef">void&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> NRF24L01_CE&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">NRF24L01_Write_Buf&lt;/span>(WRITE_NRF_REG&lt;span style="color:#f92672">+&lt;/span>RX_ADDR_P0,(u8&lt;span style="color:#f92672">*&lt;/span>)RX_ADDRESS,RX_ADR_WIDTH);&lt;span style="color:#75715e">//Write RX node address
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#a6e22e">NRF24L01_Write_Reg&lt;/span>(WRITE_NRF_REG&lt;span style="color:#f92672">+&lt;/span>EN_AA,&lt;span style="color:#ae81ff">0x01&lt;/span>); &lt;span style="color:#75715e">//Enable channel 0 auto-acknowledge
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#a6e22e">NRF24L01_Write_Reg&lt;/span>(WRITE_NRF_REG&lt;span style="color:#f92672">+&lt;/span>EN_RXADDR,&lt;span style="color:#ae81ff">0x01&lt;/span>);&lt;span style="color:#75715e">//Enable channel 0 receive address
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#a6e22e">NRF24L01_Write_Reg&lt;/span>(WRITE_NRF_REG&lt;span style="color:#f92672">+&lt;/span>RF_CH,&lt;span style="color:#ae81ff">40&lt;/span>); &lt;span style="color:#75715e">//Set RF communication frequency
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#a6e22e">NRF24L01_Write_Reg&lt;/span>(WRITE_NRF_REG&lt;span style="color:#f92672">+&lt;/span>RX_PW_P0,RX_PLOAD_WIDTH);&lt;span style="color:#75715e">//Select channel 0 valid data width
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#a6e22e">NRF24L01_Write_Reg&lt;/span>(WRITE_NRF_REG&lt;span style="color:#f92672">+&lt;/span>RF_SETUP,&lt;span style="color:#ae81ff">0x0f&lt;/span>);&lt;span style="color:#75715e">//Set TX transmission parameters, 0db gain, 2Mbps, low noise gain enabled
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#a6e22e">NRF24L01_Write_Reg&lt;/span>(WRITE_NRF_REG&lt;span style="color:#f92672">+&lt;/span>CONFIG, &lt;span style="color:#ae81ff">0x0f&lt;/span>);&lt;span style="color:#75715e">//Configure basic working mode parameters: PWR_UP, EN_CRC, 16BIT_CRC, receive mode
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> NRF24L01_CE &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>; &lt;span style="color:#75715e">//CE is high, enter receive mode
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">//This function initializes NRF24L01 to TX mode
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">//Set TX address, write TX data width, set RX auto-acknowledge address, fill TX send data, select RF channel, baud rate and LNA HCURR
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">//PWR_UP, CRC enable
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">//When CE becomes high, it enters RX mode and can receive data
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">//CE high for more than 10us will start transmission
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">TX_Mode&lt;/span>(&lt;span style="color:#66d9ef">void&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> NRF24L01_CE&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">NRF24L01_Write_Buf&lt;/span>(WRITE_NRF_REG&lt;span style="color:#f92672">+&lt;/span>TX_ADDR,(u8&lt;span style="color:#f92672">*&lt;/span>)TX_ADDRESS,TX_ADR_WIDTH);&lt;span style="color:#75715e">//Write TX node address
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#a6e22e">NRF24L01_Write_Buf&lt;/span>(WRITE_NRF_REG&lt;span style="color:#f92672">+&lt;/span>RX_ADDR_P0,(u8&lt;span style="color:#f92672">*&lt;/span>)RX_ADDRESS,RX_ADR_WIDTH); &lt;span style="color:#75715e">//Set TX node address, mainly to enable ACK
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#a6e22e">NRF24L01_Write_Reg&lt;/span>(WRITE_NRF_REG&lt;span style="color:#f92672">+&lt;/span>EN_AA,&lt;span style="color:#ae81ff">0x01&lt;/span>); &lt;span style="color:#75715e">//Enable channel 0 auto-acknowledge
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#a6e22e">NRF24L01_Write_Reg&lt;/span>(WRITE_NRF_REG&lt;span style="color:#f92672">+&lt;/span>EN_RXADDR,&lt;span style="color:#ae81ff">0x01&lt;/span>); &lt;span style="color:#75715e">//Enable channel 0 receive address
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#a6e22e">NRF24L01_Write_Reg&lt;/span>(WRITE_NRF_REG&lt;span style="color:#f92672">+&lt;/span>SETUP_RETR,&lt;span style="color:#ae81ff">0x1a&lt;/span>);&lt;span style="color:#75715e">//Set auto retransmit interval time: 500us + 86us; maximum auto retransmit times: 10
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#a6e22e">NRF24L01_Write_Reg&lt;/span>(WRITE_NRF_REG&lt;span style="color:#f92672">+&lt;/span>RF_CH,&lt;span style="color:#ae81ff">40&lt;/span>); &lt;span style="color:#75715e">//Set RF channel to 40
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#a6e22e">NRF24L01_Write_Reg&lt;/span>(WRITE_NRF_REG&lt;span style="color:#f92672">+&lt;/span>RF_SETUP,&lt;span style="color:#ae81ff">0x0f&lt;/span>); &lt;span style="color:#75715e">//Set TX transmission parameters, 0db gain, 2Mbps, low noise gain enabled
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#a6e22e">NRF24L01_Write_Reg&lt;/span>(WRITE_NRF_REG&lt;span style="color:#f92672">+&lt;/span>CONFIG,&lt;span style="color:#ae81ff">0x0e&lt;/span>); &lt;span style="color:#75715e">//Configure basic working mode parameters: PWR_UP, EN_CRC, 16BIT_CRC, receive mode, enable all interrupts
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> NRF24L01_CE&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>;&lt;span style="color:#75715e">//CE is high, start transmission after 10us
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#a6e22e">inerDelay_us&lt;/span>(&lt;span style="color:#ae81ff">20&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Next is the main program debugging section:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-c" data-lang="c">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;#34;15f204ea.h&amp;#34;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;#34;24l01.h&amp;#34;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;#34;uart.h&amp;#34;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;#34;intrins.h&amp;#34;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">delay500ms&lt;/span>(&lt;span style="color:#66d9ef">void&lt;/span>) &lt;span style="color:#75715e">//Error -0.000000000063us
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">unsigned&lt;/span> &lt;span style="color:#66d9ef">char&lt;/span> a,b,c;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span>(c&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">212&lt;/span>;c&lt;span style="color:#f92672">&amp;gt;&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>;c&lt;span style="color:#f92672">--&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span>(b&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">160&lt;/span>;b&lt;span style="color:#f92672">&amp;gt;&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>;b&lt;span style="color:#f92672">--&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span>(a&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">80&lt;/span>;a&lt;span style="color:#f92672">&amp;gt;&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>;a&lt;span style="color:#f92672">--&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">_nop_&lt;/span>(); &lt;span style="color:#75715e">//if Keil, require use intrins.h
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">delay100us&lt;/span>(&lt;span style="color:#66d9ef">void&lt;/span>) &lt;span style="color:#75715e">//Error -0.083188657407us
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">unsigned&lt;/span> &lt;span style="color:#66d9ef">char&lt;/span> a,b;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span>(b&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">58&lt;/span>;b&lt;span style="color:#f92672">&amp;gt;&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>;b&lt;span style="color:#f92672">--&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span>(a&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">8&lt;/span>;a&lt;span style="color:#f92672">&amp;gt;&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>;a&lt;span style="color:#f92672">--&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>(){
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> u8 tmp_buf[&lt;span style="color:#ae81ff">33&lt;/span>];
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> u8 key,mode;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> u16 t&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">delay500ms&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">uartInit&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">uartSendString&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Test&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">uartSendNum&lt;/span>(&lt;span style="color:#ae81ff">1234&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">NRF24L01_Init&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">while&lt;/span>(&lt;span style="color:#a6e22e">NRF24L01_Check&lt;/span>())&lt;span style="color:#75715e">//Cannot detect 24L01
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">uartSendString&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Initialization failed&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">delay500ms&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">uartSendString&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Please check&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">delay500ms&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">uartSendString&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Starting&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span>(&lt;span style="color:#ae81ff">0&lt;/span>){
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">RX_Mode&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">uartSendString&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Receive mode&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">while&lt;/span>(&lt;span style="color:#ae81ff">1&lt;/span>){
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span>(&lt;span style="color:#a6e22e">NRF24L01_RxPacket&lt;/span>(tmp_buf)&lt;span style="color:#f92672">==&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>){
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> tmp_buf[&lt;span style="color:#ae81ff">32&lt;/span>]&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>;&lt;span style="color:#75715e">//Add string terminator
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#a6e22e">uartSendString&lt;/span>(tmp_buf);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">else&lt;/span> &lt;span style="color:#a6e22e">delay100us&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">else&lt;/span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">TX_Mode&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">uartSendString&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Transmit mode&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> mode&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">&amp;#39; &amp;#39;&lt;/span>;&lt;span style="color:#75715e">//Start from space character
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">while&lt;/span>(&lt;span style="color:#ae81ff">1&lt;/span>){
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span>(&lt;span style="color:#a6e22e">NRF24L01_TxPacket&lt;/span>(tmp_buf)&lt;span style="color:#f92672">==&lt;/span>TX_OK)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">uartSendString&lt;/span>(tmp_buf);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> key&lt;span style="color:#f92672">=&lt;/span>mode;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span>(t&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>;t&lt;span style="color:#f92672">&amp;lt;&lt;/span>&lt;span style="color:#ae81ff">32&lt;/span>;t&lt;span style="color:#f92672">++&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> key&lt;span style="color:#f92672">++&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span>(key&lt;span style="color:#f92672">&amp;gt;&lt;/span>(&lt;span style="color:#e6db74">&amp;#39;~&amp;#39;&lt;/span>))key&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">&amp;#39; &amp;#39;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> tmp_buf[t]&lt;span style="color:#f92672">=&lt;/span>key;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> mode&lt;span style="color:#f92672">++&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span>(mode&lt;span style="color:#f92672">&amp;gt;&lt;/span>&lt;span style="color:#e6db74">&amp;#39;~&amp;#39;&lt;/span>)mode&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">&amp;#39; &amp;#39;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> tmp_buf[&lt;span style="color:#ae81ff">32&lt;/span>]&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>;&lt;span style="color:#75715e">//Add terminator
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">else&lt;/span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">uartSendString&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Send failed&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> };
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">delay500ms&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Set the crystal to 11.0594MHZ, then write the program.&lt;/p>
&lt;p>First, connect the serial port, then power on. After half a second, the program will send serial data &amp;ldquo;Test&amp;rdquo; and the number &amp;ldquo;1234&amp;rdquo; to the computer. Note that the serial working frequency is 38400.&lt;/p>
&lt;p>If you can receive serial data correctly, then everything is fine; otherwise, there&amp;rsquo;s an issue with the frequency settings or the data cable.&lt;/p>
&lt;p>The program will then perform a check. Upon success, it returns &amp;ldquo;Starting&amp;rdquo;; upon failure, it displays &amp;ldquo;Initialization failed&amp;rdquo; and &amp;ldquo;Please check&amp;rdquo;. Failure is either because the nrf24l01 is damaged or the pins are connected incorrectly.&lt;/p>
&lt;p>Depending on the value in the if statement&amp;rsquo;s parentheses (0 or 1), the program will enter either receive mode or transmit mode.&lt;/p>
&lt;p>In receive mode, the program will send &amp;ldquo;Receive mode&amp;rdquo; through the serial port. If it receives data, it will return the data content.&lt;/p>
&lt;p>In transmit mode, the program will send a code (a sequence of changing characters) to the specified address. If transmission is successful, it returns the length of the data sent and the data itself. When sending fails, it will display &amp;ldquo;Send failed&amp;rdquo;.&lt;/p>
&lt;p>If sending fails, it might be because the receiver doesn&amp;rsquo;t exist. In this case, NRF24L01_TxPacket returns 10 (maximum retransmission attempts) instead of 32 (data bit count), so an error is reported.&lt;/p>
&lt;p>It could also be due to other reasons, in which case NRF24L01 returns 0xff, indicating other causes of failure. In this situation, the problem is unclear and needs careful investigation.&lt;/p>
&lt;p>By writing the transmit program to one chip and the receive program to another, you can see the effect ^_^&lt;/p>
&lt;p>Thus, NRF24L01 and STC15F204EA (STC15L204EA) communication is achieved.&lt;/p></description></item></channel></rss>