diff options
Diffstat (limited to 'search.xml')
| -rw-r--r-- | search.xml | 155 |
1 files changed, 107 insertions, 48 deletions
@@ -919,20 +919,6 @@ await prisma.$transaction(async tx => { </tags> </entry> <entry> - <title>.NET AOT 下的 F# 命令行参数解析库选择</title> - <url>/2024/06/06/NET-AOT-%E4%B8%8B%E7%9A%84-F-%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%8F%82%E6%95%B0%E8%A7%A3%E6%9E%90%E5%BA%93%E9%80%89%E6%8B%A9/</url> - <content><![CDATA[<p>Argu 不支持 AOT,不过用 F# 的话可以看整个 .NET 的生态,我看了一下 C# 的 CommandLineParser:<br><a href="https://github.com/commandlineparser">https://github.com/commandlineparser</a></p> -<p>在这个 PR 中支持了 Native AOT<br><a href="https://github.com/commandlineparser/commandline/pull/913">https://github.com/commandlineparser/commandline/pull/913</a></p> -<p>除此之外,还有一个更加精巧的 F# 库可以用,只有两百多行:<br><a href="https://github.com/B2R2-org/FsOptParse/">https://github.com/B2R2-org/FsOptParse/</a></p> -<p>AOT 后的大小很可观,并且支持 full trim.</p> -<p>用例:</p> -<figure class="highlight fsharp"><table><tr><td class="code"><pre><span class="line"><span class="comment">(** defines a state to pass to the option parser *)</span></span><br><span class="line"><span class="keyword">type</span> <span class="title class_">opts</span> <span class="operator">=</span></span><br><span class="line"> {</span><br><span class="line"> optX <span class="operator">:</span> <span class="type">int</span>;</span><br><span class="line"> optY <span class="operator">:</span> <span class="type">bool</span>;</span><br><span class="line"> optZ <span class="operator">:</span> <span class="type">string</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"><span class="comment">(** default option state *)</span></span><br><span class="line"><span class="keyword">let</span> defaultOpts <span class="operator">=</span></span><br><span class="line"> {</span><br><span class="line"> optX <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"> optY <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line"> optZ <span class="operator">=</span> <span class="string">""</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"><span class="comment">(*</span></span><br><span class="line"><span class="comment"> An example command line specification, which is a list of Options.</span></span><br><span class="line"><span class="comment"> Each Option describes a command line option (switch) that is specified with</span></span><br><span class="line"><span class="comment"> either a short (a single-dash option) or long option (a double-dash option).</span></span><br><span class="line"><span class="comment">*)</span></span><br><span class="line"><span class="keyword">let</span> spec <span class="operator">=</span></span><br><span class="line"> [</span><br><span class="line"> <span class="comment">(* This option can be specified with -x <NUM>. There is an extra argument to</span></span><br><span class="line"><span class="comment"> specify a value in integer. *)</span></span><br><span class="line"> Option (<span class="comment">(* description of the option *)</span></span><br><span class="line"> descr<span class="operator">=</span><span class="string">"this is a testing param X"</span>,</span><br><span class="line"> <span class="comment">(* how many extra argument must be provided by a user? *)</span></span><br><span class="line"> extra<span class="operator">=</span><span class="number">1</span>,</span><br><span class="line"> <span class="comment">(* callback sets up the option and returns it *)</span></span><br><span class="line"> callback<span class="operator">=</span>(<span class="keyword">fun</span> opts arg <span class="operator">-></span> {opts <span class="keyword">with</span> optX<span class="operator">=</span>(int) arg.[<span class="number">0</span>]}),</span><br><span class="line"> <span class="comment">(* use a short option style -x *)</span></span><br><span class="line"> short<span class="operator">=</span><span class="string">"-x"</span></span><br><span class="line"> );</span><br><span class="line"></span><br><span class="line"> <span class="comment">(* This option can be specified with -y. There is no extra argument. This</span></span><br><span class="line"><span class="comment"> option just sets a flag, optY. *)</span></span><br><span class="line"> Option (<span class="comment">(* description of the option *)</span></span><br><span class="line"> descr<span class="operator">=</span><span class="string">"this is a testing param Y"</span>,</span><br><span class="line"> <span class="comment">(* set the option to be true *)</span></span><br><span class="line"> callback<span class="operator">=</span>(<span class="keyword">fun</span> opts _ <span class="operator">-></span> {opts <span class="keyword">with</span> optY<span class="operator">=</span><span class="literal">true</span>}),</span><br><span class="line"> <span class="comment">(* use a short option style (-y) *)</span></span><br><span class="line"> short<span class="operator">=</span><span class="string">"-y"</span>,</span><br><span class="line"> <span class="comment">(* also use a long option style (--yoohoo) *)</span></span><br><span class="line"> long<span class="operator">=</span><span class="string">"--yoohoo"</span></span><br><span class="line"> );</span><br><span class="line"></span><br><span class="line"> <span class="comment">(* A dummy option to pretty-print the usage *)</span></span><br><span class="line"> Option (<span class="comment">(* description of the option *)</span></span><br><span class="line"> descr<span class="operator">=</span><span class="string">""</span>,</span><br><span class="line"> dummy<span class="operator">=</span><span class="literal">true</span></span><br><span class="line"> );</span><br><span class="line"> Option (<span class="comment">(* description of the option *)</span></span><br><span class="line"> descr<span class="operator">=</span><span class="string">"[Required Options]"</span>,</span><br><span class="line"> descrColor<span class="operator">=</span>System.ConsoleColor.DarkCyan,</span><br><span class="line"> dummy<span class="operator">=</span><span class="literal">true</span></span><br><span class="line"> );</span><br><span class="line"></span><br><span class="line"> <span class="comment">(* The third option is a required option. In other words, option parsing</span></span><br><span class="line"><span class="comment"> will raise an exception if this option is not given by a user. This</span></span><br><span class="line"><span class="comment"> option takes in an additional integer argument, and set it to the global</span></span><br><span class="line"><span class="comment"> variable z. *)</span></span><br><span class="line"> Option (<span class="comment">(* description of the option *)</span></span><br><span class="line"> descr<span class="operator">=</span><span class="string">"required parameter <STRING> with an integer option"</span>,</span><br><span class="line"> <span class="comment">(* callback to set the optZ value *)</span></span><br><span class="line"> callback<span class="operator">=</span>(<span class="keyword">fun</span> opts arg <span class="operator">-></span> {opts <span class="keyword">with</span> optZ<span class="operator">=</span>arg.[<span class="number">0</span>]}),</span><br><span class="line"> <span class="comment">(* specifying this is a required option *)</span></span><br><span class="line"> required<span class="operator">=</span><span class="literal">true</span>,</span><br><span class="line"> <span class="comment">(* one additional argument to specify an integer value *)</span></span><br><span class="line"> extra<span class="operator">=</span><span class="number">1</span>,</span><br><span class="line"> <span class="comment">(* use only a long option style *)</span></span><br><span class="line"> long<span class="operator">=</span><span class="string">"--req"</span></span><br><span class="line"> );</span><br><span class="line"> ]</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> _ <span class="operator">=</span></span><br><span class="line"> <span class="keyword">let</span> prog <span class="operator">=</span> <span class="string">"opttest.fsx"</span></span><br><span class="line"> <span class="keyword">let</span> args <span class="operator">=</span> System.Environment.GetCommandLineArgs ()</span><br><span class="line"> <span class="keyword">let</span> usageGetter () <span class="operator">=</span> <span class="string">"[Usage]\n %p %o"</span></span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> <span class="keyword">let</span> left, opts <span class="operator">=</span> optParse spec usageGetter prog args defaultOpts</span><br><span class="line"> <span class="built_in">printfn</span> <span class="string">"Rest args: %A, x: %d, y: %b, z: %s"</span></span><br><span class="line"> left opts.optX opts.optY opts.optZ</span><br><span class="line"> <span class="number">0</span></span><br><span class="line"> <span class="keyword">with</span></span><br><span class="line"> <span class="operator">|</span> SpecErr msg <span class="operator">-></span></span><br><span class="line"> <span class="built_in">eprintfn</span> <span class="string">"Invalid spec: %s"</span> msg</span><br><span class="line"> exit <span class="number">1</span></span><br><span class="line"> <span class="operator">|</span> RuntimeErr msg <span class="operator">-></span></span><br><span class="line"> <span class="built_in">eprintfn</span> <span class="string">"Invalid args given by user: %s"</span> msg</span><br><span class="line"> usagePrint spec prog usageGetter (<span class="keyword">fun</span> () <span class="operator">-></span> exit <span class="number">1</span>)</span><br></pre></td></tr></table></figure> -]]></content> - <tags> - <tag>Technique</tag> - </tags> - </entry> - <entry> <title>N+1 selects problem 与 Prisma ORM</title> <url>/2025/04/02/N-1-selects-problem-%E4%B8%8E-Prisma-ORM/</url> <content><![CDATA[<p>N+1 查询问题是指在通过 ORM 查询数据时,执行了一次初始查询来获取父对象列表(这 1 次查询),然后为列表中的每一个父对象都单独执行了一次额外的查询来获取其关联的子对象(这 N 次查询)。最终导致总共执行了 1 + N 次数据库查询,其中 N 是初始查询返回的父对象的数量。</p> @@ -972,6 +958,20 @@ await prisma.$transaction(async tx => { </tags> </entry> <entry> + <title>.NET AOT 下的 F# 命令行参数解析库选择</title> + <url>/2024/06/06/NET-AOT-%E4%B8%8B%E7%9A%84-F-%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%8F%82%E6%95%B0%E8%A7%A3%E6%9E%90%E5%BA%93%E9%80%89%E6%8B%A9/</url> + <content><![CDATA[<p>Argu 不支持 AOT,不过用 F# 的话可以看整个 .NET 的生态,我看了一下 C# 的 CommandLineParser:<br><a href="https://github.com/commandlineparser">https://github.com/commandlineparser</a></p> +<p>在这个 PR 中支持了 Native AOT<br><a href="https://github.com/commandlineparser/commandline/pull/913">https://github.com/commandlineparser/commandline/pull/913</a></p> +<p>除此之外,还有一个更加精巧的 F# 库可以用,只有两百多行:<br><a href="https://github.com/B2R2-org/FsOptParse/">https://github.com/B2R2-org/FsOptParse/</a></p> +<p>AOT 后的大小很可观,并且支持 full trim.</p> +<p>用例:</p> +<figure class="highlight fsharp"><table><tr><td class="code"><pre><span class="line"><span class="comment">(** defines a state to pass to the option parser *)</span></span><br><span class="line"><span class="keyword">type</span> <span class="title class_">opts</span> <span class="operator">=</span></span><br><span class="line"> {</span><br><span class="line"> optX <span class="operator">:</span> <span class="type">int</span>;</span><br><span class="line"> optY <span class="operator">:</span> <span class="type">bool</span>;</span><br><span class="line"> optZ <span class="operator">:</span> <span class="type">string</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"><span class="comment">(** default option state *)</span></span><br><span class="line"><span class="keyword">let</span> defaultOpts <span class="operator">=</span></span><br><span class="line"> {</span><br><span class="line"> optX <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"> optY <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line"> optZ <span class="operator">=</span> <span class="string">""</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"><span class="comment">(*</span></span><br><span class="line"><span class="comment"> An example command line specification, which is a list of Options.</span></span><br><span class="line"><span class="comment"> Each Option describes a command line option (switch) that is specified with</span></span><br><span class="line"><span class="comment"> either a short (a single-dash option) or long option (a double-dash option).</span></span><br><span class="line"><span class="comment">*)</span></span><br><span class="line"><span class="keyword">let</span> spec <span class="operator">=</span></span><br><span class="line"> [</span><br><span class="line"> <span class="comment">(* This option can be specified with -x <NUM>. There is an extra argument to</span></span><br><span class="line"><span class="comment"> specify a value in integer. *)</span></span><br><span class="line"> Option (<span class="comment">(* description of the option *)</span></span><br><span class="line"> descr<span class="operator">=</span><span class="string">"this is a testing param X"</span>,</span><br><span class="line"> <span class="comment">(* how many extra argument must be provided by a user? *)</span></span><br><span class="line"> extra<span class="operator">=</span><span class="number">1</span>,</span><br><span class="line"> <span class="comment">(* callback sets up the option and returns it *)</span></span><br><span class="line"> callback<span class="operator">=</span>(<span class="keyword">fun</span> opts arg <span class="operator">-></span> {opts <span class="keyword">with</span> optX<span class="operator">=</span>(int) arg.[<span class="number">0</span>]}),</span><br><span class="line"> <span class="comment">(* use a short option style -x *)</span></span><br><span class="line"> short<span class="operator">=</span><span class="string">"-x"</span></span><br><span class="line"> );</span><br><span class="line"></span><br><span class="line"> <span class="comment">(* This option can be specified with -y. There is no extra argument. This</span></span><br><span class="line"><span class="comment"> option just sets a flag, optY. *)</span></span><br><span class="line"> Option (<span class="comment">(* description of the option *)</span></span><br><span class="line"> descr<span class="operator">=</span><span class="string">"this is a testing param Y"</span>,</span><br><span class="line"> <span class="comment">(* set the option to be true *)</span></span><br><span class="line"> callback<span class="operator">=</span>(<span class="keyword">fun</span> opts _ <span class="operator">-></span> {opts <span class="keyword">with</span> optY<span class="operator">=</span><span class="literal">true</span>}),</span><br><span class="line"> <span class="comment">(* use a short option style (-y) *)</span></span><br><span class="line"> short<span class="operator">=</span><span class="string">"-y"</span>,</span><br><span class="line"> <span class="comment">(* also use a long option style (--yoohoo) *)</span></span><br><span class="line"> long<span class="operator">=</span><span class="string">"--yoohoo"</span></span><br><span class="line"> );</span><br><span class="line"></span><br><span class="line"> <span class="comment">(* A dummy option to pretty-print the usage *)</span></span><br><span class="line"> Option (<span class="comment">(* description of the option *)</span></span><br><span class="line"> descr<span class="operator">=</span><span class="string">""</span>,</span><br><span class="line"> dummy<span class="operator">=</span><span class="literal">true</span></span><br><span class="line"> );</span><br><span class="line"> Option (<span class="comment">(* description of the option *)</span></span><br><span class="line"> descr<span class="operator">=</span><span class="string">"[Required Options]"</span>,</span><br><span class="line"> descrColor<span class="operator">=</span>System.ConsoleColor.DarkCyan,</span><br><span class="line"> dummy<span class="operator">=</span><span class="literal">true</span></span><br><span class="line"> );</span><br><span class="line"></span><br><span class="line"> <span class="comment">(* The third option is a required option. In other words, option parsing</span></span><br><span class="line"><span class="comment"> will raise an exception if this option is not given by a user. This</span></span><br><span class="line"><span class="comment"> option takes in an additional integer argument, and set it to the global</span></span><br><span class="line"><span class="comment"> variable z. *)</span></span><br><span class="line"> Option (<span class="comment">(* description of the option *)</span></span><br><span class="line"> descr<span class="operator">=</span><span class="string">"required parameter <STRING> with an integer option"</span>,</span><br><span class="line"> <span class="comment">(* callback to set the optZ value *)</span></span><br><span class="line"> callback<span class="operator">=</span>(<span class="keyword">fun</span> opts arg <span class="operator">-></span> {opts <span class="keyword">with</span> optZ<span class="operator">=</span>arg.[<span class="number">0</span>]}),</span><br><span class="line"> <span class="comment">(* specifying this is a required option *)</span></span><br><span class="line"> required<span class="operator">=</span><span class="literal">true</span>,</span><br><span class="line"> <span class="comment">(* one additional argument to specify an integer value *)</span></span><br><span class="line"> extra<span class="operator">=</span><span class="number">1</span>,</span><br><span class="line"> <span class="comment">(* use only a long option style *)</span></span><br><span class="line"> long<span class="operator">=</span><span class="string">"--req"</span></span><br><span class="line"> );</span><br><span class="line"> ]</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> _ <span class="operator">=</span></span><br><span class="line"> <span class="keyword">let</span> prog <span class="operator">=</span> <span class="string">"opttest.fsx"</span></span><br><span class="line"> <span class="keyword">let</span> args <span class="operator">=</span> System.Environment.GetCommandLineArgs ()</span><br><span class="line"> <span class="keyword">let</span> usageGetter () <span class="operator">=</span> <span class="string">"[Usage]\n %p %o"</span></span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> <span class="keyword">let</span> left, opts <span class="operator">=</span> optParse spec usageGetter prog args defaultOpts</span><br><span class="line"> <span class="built_in">printfn</span> <span class="string">"Rest args: %A, x: %d, y: %b, z: %s"</span></span><br><span class="line"> left opts.optX opts.optY opts.optZ</span><br><span class="line"> <span class="number">0</span></span><br><span class="line"> <span class="keyword">with</span></span><br><span class="line"> <span class="operator">|</span> SpecErr msg <span class="operator">-></span></span><br><span class="line"> <span class="built_in">eprintfn</span> <span class="string">"Invalid spec: %s"</span> msg</span><br><span class="line"> exit <span class="number">1</span></span><br><span class="line"> <span class="operator">|</span> RuntimeErr msg <span class="operator">-></span></span><br><span class="line"> <span class="built_in">eprintfn</span> <span class="string">"Invalid args given by user: %s"</span> msg</span><br><span class="line"> usagePrint spec prog usageGetter (<span class="keyword">fun</span> () <span class="operator">-></span> exit <span class="number">1</span>)</span><br></pre></td></tr></table></figure> +]]></content> + <tags> + <tag>Technique</tag> + </tags> + </entry> + <entry> <title>OCaml Core.Int.pow 的实现</title> <url>/2023/10/21/OCaml-Core-Int-pow-%E7%9A%84%E5%AE%9E%E7%8E%B0/</url> <content><![CDATA[<p>Core内部直接引用的Base的实现, Base的实现在 <a href="https://github.com/janestreet/base/blob/494a0876168d24cda695cbb9a3d86ad8d1eb97d8/src/int_math.ml#L11-L18">src/int_math.ml</a> 中:</p> @@ -2856,6 +2856,65 @@ await prisma.$transaction(async tx => { </tags> </entry> <entry> + <title>使用 Runed intersection observer 与 tanstack query 实现通用的惰性加载组件</title> + <url>/2025/07/31/svelte-lazyquery/</url> + <content><![CDATA[<p>在前端,一个常见的性能瓶颈是初始加载时请求了用户当前 viewpoint 之外的非必要数据。惰性加载(Lazy Loading)是一种关键策略,它将数据获取推迟到组件进入 viewpoint 时才执行。本文将阐述如何结合使用 Svelte 5、<code>@tanstack/svelte-query</code> 和 <code>runed.dev</code> 的 <code>useIntersectionObserver</code>,构建一个通用的、可复用的惰性加载组件。</p> +<p><strong>核心依赖与环境</strong></p> +<ul> +<li><p><strong>Svelte 5:</strong> 本实现依赖于 Svelte 5 的符文(Runes)特性,它提供了更精细、更直观的状态管理能力。</p> +</li> +<li><p><strong>@tanstack/svelte-query:</strong> TanStack Query 的 Svelte 适配版,用户获取数据。</p> +</li> +<li><p><strong>runed.dev:</strong> 一个提供多种 Svelte 5 实用工具的库,本文主要使用其 <code>useIntersectionObserver</code>。</p> +</li> +</ul> +<h4 id="设计哲学:分离关注点"><a href="#设计哲学:分离关注点" class="headerlink" title="设计哲学:分离关注点"></a><strong>设计哲学:分离关注点</strong></h4><p>该方案的核心思想是将“何时加载”与“如何加载”这两个关注点进行解耦。</p> +<ol> +<li><p><strong>何时加载 (When to Load):</strong> 组件的可见性决定了数据加载的时机。我们利用 <code>Intersection Observer API</code> 来精确、高效地监听一个元素是否进入 viewpoint。<code>runed.dev</code> 库为此提供了名为 <code>useIntersectionObserver</code> 的便捷封装。</p> +</li> +<li><p><strong>如何加载 (How to Load):</strong> 数据获取、缓存、同步和状态管理的复杂性由 <code>@tanstack/svelte-query</code> (TanStack Query) 处理。它提供了一套强大的工具集来管理异步数据。</p> +</li> +</ol> +<p>通过将这两者结合,可以创建一个名为 <code>LazyQuery</code> 的抽象组件。该组件内部处理可见性检测,并根据检测结果动态控制 TanStack Query 的执行,而将具体的查询逻辑(<code>queryFn</code>)和键(<code>queryKey</code>)完全交由使用者定义。</p> +<h4 id="LazyQuery-组件的实现"><a href="#LazyQuery-组件的实现" class="headerlink" title="LazyQuery 组件的实现"></a><code>LazyQuery</code> 组件的实现</h4><blockquote> +<p>目标:封装惰性加载逻辑,并向外暴露一个标准的 TanStack Query 接口。</p> +</blockquote> +<p>一、对组件的接口类型做如下定义:</p> +<figure class="highlight typescript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="keyword">type</span> { <span class="title class_">CreateQueryOptions</span>, <span class="title class_">QueryKey</span> } <span class="keyword">from</span> <span class="string">'@tanstack/svelte-query'</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="keyword">type</span> { <span class="title class_">Snippet</span> } <span class="keyword">from</span> <span class="string">'svelte'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> <span class="title class_">Props</span><</span><br><span class="line"> <span class="title class_">TQueryFnData</span> = <span class="built_in">unknown</span>,</span><br><span class="line"> <span class="title class_">TError</span> = <span class="title class_">Error</span>,</span><br><span class="line"> <span class="title class_">TData</span> = <span class="title class_">TQueryFnData</span>,</span><br><span class="line"> <span class="title class_">TQueryKey</span> <span class="keyword">extends</span> <span class="title class_">QueryKey</span> = <span class="title class_">QueryKey</span></span><br><span class="line">> = {</span><br><span class="line"> <span class="comment">/// 一个标准的 TanStack Query 配置对象</span></span><br><span class="line"> <span class="attr">queryOptions</span>: <span class="title class_">CreateQueryOptions</span><<span class="title class_">TQueryFnData</span>, <span class="title class_">TError</span>, <span class="title class_">TData</span>, <span class="title class_">TQueryKey</span>>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/// Svelte 5 的 `Snippet` 类型,它允许父组件向子组件传递一段可执行的 UI 模板。</span></span><br><span class="line"> <span class="comment">/// 这个 `Snippet` 会接收到 `createQuery` 返回的完整查询对象 `query`,</span></span><br><span class="line"> <span class="comment">/// 从而可以访问 `data`, `isLoading`, `error` 等所有状态。</span></span><br><span class="line"> <span class="attr">children</span>: <span class="title class_">Snippet</span><{</span><br><span class="line"> <span class="attr">query</span>: <span class="title class_">ReturnType</span><<span class="keyword">typeof</span> createQuery<<span class="title class_">TQueryFnData</span>, <span class="title class_">TError</span>, <span class="title class_">TData</span>, <span class="title class_">TQueryKey</span>>>;</span><br><span class="line"> }>;</span><br><span class="line">};</span><br></pre></td></tr></table></figure> + +<p>二、组件逻辑:</p> +<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">script</span> <span class="attr">lang</span>=<span class="string">"ts"</span>></span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">import</span> { createQuery, type <span class="title class_">CreateQueryOptions</span>, type <span class="title class_">QueryKey</span> } <span class="keyword">from</span> <span class="string">'@tanstack/svelte-query'</span>;</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">import</span> { useIntersectionObserver } <span class="keyword">from</span> <span class="string">'runed'</span>;</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">import</span> type { <span class="title class_">Snippet</span> } <span class="keyword">from</span> <span class="string">'svelte'</span>;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="comment">// [上述的类型定义]</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">let</span> { queryOptions, children }: <span class="title class_">Props</span> = $props();</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">let</span> <span class="attr">el</span>: <span class="title class_">Element</span>;</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">const</span> { isIntersecting } = <span class="title function_">useIntersectionObserver</span>(</span></span><br><span class="line"><span class="language-javascript"> <span class="function">() =></span> el,</span></span><br><span class="line"><span class="language-javascript"> {</span></span><br><span class="line"><span class="language-javascript"> <span class="attr">rootMargin</span>: <span class="string">'200px'</span>,</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> );</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">const</span> query = <span class="title function_">createQuery</span>({</span></span><br><span class="line"><span class="language-javascript"> ...queryOptions,</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">get</span> <span class="title function_">enabled</span>() {</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">return</span> ($state.<span class="title function_">is</span>(isIntersecting) && (queryOptions.<span class="property">enabled</span> ?? <span class="literal">true</span>));</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> });</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">bind:this</span>=<span class="string">{el}</span>></span></span><br><span class="line"> {#snippet children({ query })}</span><br><span class="line"> {/snippet}</span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure> + +<ul> +<li>我们创建一个 <code>div</code> 元素作为哨兵(sentinel),并用 <code>bind:this={el}</code> 将其 DOM 引用绑定到变量 <code>el</code>。</li> +<li><code>useIntersectionObserver</code> 接收一个返回目标元素的函数 <code>() => el</code>。<ul> +<li>它返回一个响应式的状态对象,其中 <code>isIntersecting</code> 是一个布尔值的符文(rune),当 <code>div</code> 元素进入 viewpoint 时为 <code>true</code>,否则为 <code>false</code>。</li> +</ul> +</li> +<li><code>rootMargin: '200px'</code> 是一个优化选项,它会在元素距离 viewpoint 还有 200px 时就触发加载,从而提升用户体验。</li> +</ul> +<h4 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h4><p>使用 <code>LazyQuery</code> 组件非常直观。开发者只需关注数据获取的业务逻辑,而无需关心惰性加载的实现细节,假设有一个获取图表数据的场景:</p> +<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">script</span> <span class="attr">lang</span>=<span class="string">"ts"</span>></span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">import</span> { <span class="title class_">QueryClientProvider</span>, <span class="title class_">QueryClient</span> } <span class="keyword">from</span> <span class="string">'@tanstack/svelte-query'</span>;</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">import</span> <span class="title class_">LazyQuery</span> <span class="keyword">from</span> <span class="string">'./LazyQuery.svelte'</span>;</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">import</span> <span class="title class_">CopdGoldGradingChart</span> <span class="keyword">from</span> <span class="string">'./CopdGoldGradingChart.svelte'</span>;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">const</span> queryClient = <span class="keyword">new</span> <span class="title class_">QueryClient</span>();</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="comment">// Define the query configuration object, just like with a standard `createQuery`.</span></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">const</span> queryOptions = {</span></span><br><span class="line"><span class="language-javascript"> <span class="attr">queryKey</span>: [<span class="string">'goldGradingData'</span>],</span></span><br><span class="line"><span class="language-javascript"> <span class="attr">queryFn</span>: <span class="title function_">async</span> () => {</span></span><br><span class="line"><span class="language-javascript"> <span class="comment">// Simulate a network request</span></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">await</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function"><span class="params">resolve</span> =></span> <span class="built_in">setTimeout</span>(resolve, <span class="number">1500</span>));</span></span><br><span class="line"><span class="language-javascript"> <span class="comment">// In a real application, this would be an API call</span></span></span><br><span class="line"><span class="language-javascript"> <span class="comment">// const response = await fetch('/api/gold-grading');</span></span></span><br><span class="line"><span class="language-javascript"> <span class="comment">// return await response.json();</span></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">return</span> { <span class="attr">data</span>: { <span class="attr">totalPatients</span>: <span class="number">1234</span>, <span class="attr">chartPoints</span>: [<span class="comment">/* ... */</span>] } };</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> };</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">QueryClientProvider</span> <span class="attr">client</span>=<span class="string">{queryClient}</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"height: 200vh;"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>Scroll down to see the chart...<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">LazyQuery</span> {<span class="attr">queryOptions</span>}></span></span><br><span class="line"> {#snippet children({ query })}</span><br><span class="line"> {#if query.isLoading}</span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>Loading chart data...<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> {:else if query.error}</span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>Error: {query.error.message}<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> {:else if query.data}</span><br><span class="line"> <span class="tag"><<span class="name">CopdGoldGradingChart</span></span></span><br><span class="line"><span class="tag"> <span class="attr">isLoading</span>=<span class="string">{query.isFetching}</span></span></span><br><span class="line"><span class="tag"> <span class="attr">error</span>=<span class="string">{query.error}</span></span></span><br><span class="line"><span class="tag"> <span class="attr">refetch</span>=<span class="string">{()</span> =></span> query.refetch()}</span><br><span class="line"> data={query.data.data}</span><br><span class="line"> totalPatients={query.data.data.totalPatients}</span><br><span class="line"> /></span><br><span class="line"> {/if}</span><br><span class="line"> {/snippet}</span><br><span class="line"> <span class="tag"></<span class="name">LazyQuery</span>></span></span><br><span class="line"><span class="tag"></<span class="name">QueryClientProvider</span>></span></span><br></pre></td></tr></table></figure> + +<ul> +<li>页面初始加载时,<code>LazyQuery</code> 组件被渲染,但由于其 <code>div</code> 在 viewpoint 之外,<code>isIntersecting</code> 为 <code>false</code>。</li> +<li><code>createQuery</code> 被调用,但因为 <code>enabled</code> 条件为 <code>false</code>,查询处于禁用状态,不会发起任何网络请求。</li> +<li><code>children</code> 片段被渲染,此时 <code>query.isLoading</code> 为 <code>true</code>(这是 TanStack Query 禁用查询时的初始状态),显示 “Loading chart data…”。</li> +<li>当用户向下滚动,<code>div</code> 元素进入 viewpoint(或进入 <code>200px</code> 的预加载区域)。</li> +<li><code>useIntersectionObserver</code> 将 <code>isIntersecting</code> 的值更新为 <code>true</code>。</li> +<li>这个变化被 <code>createQuery</code> 的 <code>enabled</code> 访问器捕获,查询被自动激活,<code>queryFn</code> 开始执行。</li> +<li>TanStack Query 负责管理后续的状态变化(<code>isFetching</code>, <code>data</code>, <code>error</code>),并驱动 <code>children</code> 片段内的 UI 自动更新。</li> +</ul> +<h4 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h4><ul> +<li><a href="https://runed.dev/docs/utilities/use-intersection-observer">https://runed.dev/docs/utilities/use-intersection-observer</a></li> +<li><a href="https://tanstack.com/table/latest/docs/introduction">https://tanstack.com/table/latest/docs/introduction</a></li> +</ul> +]]></content> + <tags> + <tag>Technique</tag> + </tags> + </entry> + <entry> <title>tick thread在Multicore OCaml中的作用</title> <url>/2023/08/15/tick-thread%E5%9C%A8Multicore-OCaml%E4%B8%AD%E7%9A%84%E4%BD%9C%E7%94%A8/</url> <content><![CDATA[<p>Multicore OCaml的程序在启动时会运行一个 tick thread,其实现如下:</p> @@ -2894,6 +2953,40 @@ await prisma.$transaction(async tx => { </tags> </entry> <entry> + <title>v8中的Number.toString()</title> + <url>/2023/10/05/v8%E4%B8%AD%E7%9A%84Number-toString/</url> + <content><![CDATA[<p>这里讲一下JavaScript中<code>Number.toString()</code>的实现, 以V8为例。</p> +<ul> +<li><a href="https://github.com/v8/v8/">The official mirror of the V8 Git repository</a></li> +</ul> +<p>在很多地方都能看到:</p> +<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line">*isolate-><span class="built_in">factory</span>()-><span class="built_in">NumberToString</span>(value);</span><br></pre></td></tr></table></figure> +<p>例如 <a href="https://github.com/v8/v8/blob/df99ca37a9d02a3318d3e5487c7803f6230b8f5f/src/builtins/builtins-number.cc#L140">/src/builtins/builtins-number.cc</a> 中。</p> +<p>下面看看<code>NumberToString</code>的定义, 应该是在 <a href="https://github.com/v8/v8/blob/829e8d18d73e21807c78223b0cc107c76c80da27/src/heap/factory-base.cc#L963-L978">src/heap/factory-base.cc</a> 中:</p> +<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="keyword">template</span> <<span class="keyword">typename</span> Impl></span><br><span class="line">Handle<String> FactoryBase<Impl>::<span class="built_in">NumberToString</span>(Handle<Object> number,</span><br><span class="line"> NumberCacheMode mode) {</span><br><span class="line"> <span class="built_in">SLOW_DCHECK</span>(<span class="built_in">IsNumber</span>(*number));</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">IsSmi</span>(*number)) <span class="keyword">return</span> <span class="built_in">SmiToString</span>(Smi::<span class="built_in">cast</span>(*number), mode);</span><br><span class="line"></span><br><span class="line"> <span class="type">double</span> double_value = Handle<HeapNumber>::<span class="built_in">cast</span>(number)-><span class="built_in">value</span>();</span><br><span class="line"> <span class="comment">// Try to canonicalize doubles.</span></span><br><span class="line"> <span class="type">int</span> smi_value;</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">DoubleToSmiInteger</span>(double_value, &smi_value)) {</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">SmiToString</span>(Smi::<span class="built_in">FromInt</span>(smi_value), mode);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">HeapNumberToString</span>(Handle<HeapNumber>::<span class="built_in">cast</span>(number), double_value,</span><br><span class="line"> mode);</span><br><span class="line">}</span><br></pre></td></tr></table></figure> + +<p>可以看到这里调用了 <code>SmiToString</code>, 这里不往下翻这个函数的定义, 只需要知道Smi是什么即可。 Smi 是一种特殊的整数类型,它被用于表示较小的整数值,通常在 32 位系统中是 31 位有符号整数。Smi 类型的值存储在指针的低位,而指针的高位用于标记该值是一个 Smi 类型。<code>IsSmi</code> 函数会检查给定的值是否为 Smi 类型,如果是,则返回 true,否则返回 false。这个函数通常用于 V8 引擎内部的优化和性能优化。</p> +<p>所以<code>NumberToString</code>会判断number是否是一个smi, 如果是的话就调用SmiToString, 否则会尝试将其转换为double再去调用<code>DoubleToSmiInteger</code>, 将<code>DoubleToSmiInteger</code>的调用结果存在<code>smi_value</code>里面, 再通过调用<code>SmiToString</code>将<code>smi_value</code>转换为字符串。</p> +<p>如果这两条路都行不通的话,就直接调用<code>HeapNumberToString</code>了。</p> +<p><code>HeapNumberToString</code>的定义如下:</p> +<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="keyword">template</span> <<span class="keyword">typename</span> Impl></span><br><span class="line">Handle<String> FactoryBase<Impl>::<span class="built_in">HeapNumberToString</span>(Handle<HeapNumber> number,</span><br><span class="line"> <span class="type">double</span> value,</span><br><span class="line"> NumberCacheMode mode) {</span><br><span class="line"> <span class="type">int</span> hash = mode == NumberCacheMode::kIgnore</span><br><span class="line"> ? <span class="number">0</span></span><br><span class="line"> : <span class="built_in">impl</span>()-><span class="built_in">NumberToStringCacheHash</span>(value);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (mode == NumberCacheMode::kBoth) {</span><br><span class="line"> Handle<Object> cached = <span class="built_in">impl</span>()-><span class="built_in">NumberToStringCacheGet</span>(*number, hash);</span><br><span class="line"> <span class="keyword">if</span> (!<span class="built_in">IsUndefined</span>(*cached, <span class="built_in">isolate</span>())) <span class="keyword">return</span> Handle<String>::<span class="built_in">cast</span>(cached);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> Handle<String> result;</span><br><span class="line"> <span class="keyword">if</span> (value == <span class="number">0</span>) {</span><br><span class="line"> result = <span class="built_in">zero_string</span>();</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (std::<span class="built_in">isnan</span>(value)) {</span><br><span class="line"> result = <span class="built_in">NaN_string</span>();</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="type">char</span> arr[kNumberToStringBufferSize];</span><br><span class="line"> <span class="function">base::Vector<<span class="type">char</span>> <span class="title">buffer</span><span class="params">(arr, arraysize(arr))</span></span>;</span><br><span class="line"> <span class="type">const</span> <span class="type">char</span>* string = <span class="built_in">DoubleToCString</span>(value, buffer);</span><br><span class="line"> result = <span class="built_in">CharToString</span>(<span class="keyword">this</span>, string, mode);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (mode != NumberCacheMode::kIgnore) {</span><br><span class="line"> <span class="built_in">impl</span>()-><span class="built_in">NumberToStringCacheSet</span>(number, hash, result);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line">}</span><br></pre></td></tr></table></figure> + +<p>就是熟知的NaN, Undefined处理,重点在:</p> +<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="type">char</span> arr[kNumberToStringBufferSize];</span><br><span class="line"><span class="function">base::Vector<<span class="type">char</span>> <span class="title">buffer</span><span class="params">(arr, arraysize(arr))</span></span>;</span><br><span class="line"><span class="type">const</span> <span class="type">char</span>* string = <span class="built_in">DoubleToCString</span>(value, buffer);</span><br><span class="line">result = <span class="built_in">CharToString</span>(<span class="keyword">this</span>, string, mode);</span><br></pre></td></tr></table></figure> + +<p>这里调用了<code>DoubleToCString</code>, 其定义在 <a href="https://github.com/v8/v8/blob/829e8d18d73e21807c78223b0cc107c76c80da27/src/numbers/conversions.cc#L1064-L1126">/src/numbers/conversions.cc</a> 中:</p> +<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="type">const</span> <span class="type">char</span>* <span class="title">DoubleToCString</span><span class="params">(<span class="type">double</span> v, base::Vector<<span class="type">char</span>> buffer)</span> </span>{</span><br><span class="line"> <span class="keyword">switch</span> (FPCLASSIFY_NAMESPACE::<span class="built_in">fpclassify</span>(v)) {</span><br><span class="line"> <span class="keyword">case</span> FP_NAN:</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"NaN"</span>;</span><br><span class="line"> <span class="keyword">case</span> FP_INFINITE:</span><br><span class="line"> <span class="keyword">return</span> (v < <span class="number">0.0</span> ? <span class="string">"-Infinity"</span> : <span class="string">"Infinity"</span>);</span><br><span class="line"> <span class="keyword">case</span> FP_ZERO:</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"0"</span>;</span><br><span class="line"> <span class="keyword">default</span>: {</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">IsInt32Double</span>(v)) {</span><br><span class="line"> <span class="comment">// This will trigger if v is -0 and -0.0 is stringified to "0".</span></span><br><span class="line"> <span class="comment">// (see ES section 7.1.12.1 #sec-tostring-applied-to-the-number-type)</span></span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">IntToCString</span>(<span class="built_in">FastD2I</span>(v), buffer);</span><br><span class="line"> }</span><br><span class="line"> <span class="function">SimpleStringBuilder <span class="title">builder</span><span class="params">(buffer.begin(), buffer.length())</span></span>;</span><br><span class="line"> <span class="type">int</span> decimal_point;</span><br><span class="line"> <span class="type">int</span> sign;</span><br><span class="line"> <span class="type">const</span> <span class="type">int</span> kV8DtoaBufferCapacity = base::kBase10MaximalLength + <span class="number">1</span>;</span><br><span class="line"> <span class="type">char</span> decimal_rep[kV8DtoaBufferCapacity];</span><br><span class="line"> <span class="type">int</span> length;</span><br><span class="line"></span><br><span class="line"> base::<span class="built_in">DoubleToAscii</span>(</span><br><span class="line"> v, base::DTOA_SHORTEST, <span class="number">0</span>,</span><br><span class="line"> base::<span class="built_in">Vector</span><<span class="type">char</span>>(decimal_rep, kV8DtoaBufferCapacity), &sign,</span><br><span class="line"> &length, &decimal_point);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (sign) builder.<span class="built_in">AddCharacter</span>(<span class="string">'-'</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (length <= decimal_point && decimal_point <= <span class="number">21</span>) {</span><br><span class="line"> <span class="comment">// ECMA-262 section 9.8.1 step 6.</span></span><br><span class="line"> builder.<span class="built_in">AddString</span>(decimal_rep);</span><br><span class="line"> builder.<span class="built_in">AddPadding</span>(<span class="string">'0'</span>, decimal_point - length);</span><br><span class="line"></span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (<span class="number">0</span> < decimal_point && decimal_point <= <span class="number">21</span>) {</span><br><span class="line"> <span class="comment">// ECMA-262 section 9.8.1 step 7.</span></span><br><span class="line"> builder.<span class="built_in">AddSubstring</span>(decimal_rep, decimal_point);</span><br><span class="line"> builder.<span class="built_in">AddCharacter</span>(<span class="string">'.'</span>);</span><br><span class="line"> builder.<span class="built_in">AddString</span>(decimal_rep + decimal_point);</span><br><span class="line"></span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (decimal_point <= <span class="number">0</span> && decimal_point > <span class="number">-6</span>) {</span><br><span class="line"> <span class="comment">// ECMA-262 section 9.8.1 step 8.</span></span><br><span class="line"> builder.<span class="built_in">AddString</span>(<span class="string">"0."</span>);</span><br><span class="line"> builder.<span class="built_in">AddPadding</span>(<span class="string">'0'</span>, -decimal_point);</span><br><span class="line"> builder.<span class="built_in">AddString</span>(decimal_rep);</span><br><span class="line"></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// ECMA-262 section 9.8.1 step 9 and 10 combined.</span></span><br><span class="line"> builder.<span class="built_in">AddCharacter</span>(decimal_rep[<span class="number">0</span>]);</span><br><span class="line"> <span class="keyword">if</span> (length != <span class="number">1</span>) {</span><br><span class="line"> builder.<span class="built_in">AddCharacter</span>(<span class="string">'.'</span>);</span><br><span class="line"> builder.<span class="built_in">AddString</span>(decimal_rep + <span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> builder.<span class="built_in">AddCharacter</span>(<span class="string">'e'</span>);</span><br><span class="line"> builder.<span class="built_in">AddCharacter</span>((decimal_point >= <span class="number">0</span>) ? <span class="string">'+'</span> : <span class="string">'-'</span>);</span><br><span class="line"> <span class="type">int</span> exponent = decimal_point - <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">if</span> (exponent < <span class="number">0</span>) exponent = -exponent;</span><br><span class="line"> builder.<span class="built_in">AddDecimalInteger</span>(exponent);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> builder.<span class="built_in">Finalize</span>();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure> + +<p>不用过多解释, 已经很清晰了, <code>FastD2I</code> 就是 Fast Double to Integer的意思, 定义如下, 注释也很详尽:</p> +<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="comment">// The fast double-to-(unsigned-)int conversion routine does not guarantee</span></span><br><span class="line"><span class="comment">// rounding towards zero.</span></span><br><span class="line"><span class="comment">// The result is undefined if x is infinite or NaN, or if the rounded</span></span><br><span class="line"><span class="comment">// integer value is outside the range of type int.</span></span><br><span class="line"><span class="function"><span class="keyword">inline</span> <span class="type">int</span> <span class="title">FastD2I</span><span class="params">(<span class="type">double</span> x)</span> </span>{</span><br><span class="line"> <span class="built_in">DCHECK</span>(x <= INT_MAX);</span><br><span class="line"> <span class="built_in">DCHECK</span>(x >= INT_MIN);</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">static_cast</span><<span class="type">int32_t</span>>(x);</span><br><span class="line">}</span><br></pre></td></tr></table></figure> + +<p>以上</p> +]]></content> + <tags> + <tag>Technique</tag> + </tags> + </entry> + <entry> <title>Vertical slicing 在开发中的实践探索</title> <url>/2025/05/27/vertical-slicing-practice/</url> <content><![CDATA[<p>垂直切片 (Vertical Slicing) 是一种在敏捷软件开发中将产品需求(通常是用户故事)拆分为可独立交付的、具有端到端功能的小块的方法。这意味着每个“切片”都包含了从用户界面 (UI) 到底层数据库,以及中间所有业务逻辑层所需的工作。</p> @@ -2940,40 +3033,6 @@ await prisma.$transaction(async tx => { </tags> </entry> <entry> - <title>v8中的Number.toString()</title> - <url>/2023/10/05/v8%E4%B8%AD%E7%9A%84Number-toString/</url> - <content><![CDATA[<p>这里讲一下JavaScript中<code>Number.toString()</code>的实现, 以V8为例。</p> -<ul> -<li><a href="https://github.com/v8/v8/">The official mirror of the V8 Git repository</a></li> -</ul> -<p>在很多地方都能看到:</p> -<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line">*isolate-><span class="built_in">factory</span>()-><span class="built_in">NumberToString</span>(value);</span><br></pre></td></tr></table></figure> -<p>例如 <a href="https://github.com/v8/v8/blob/df99ca37a9d02a3318d3e5487c7803f6230b8f5f/src/builtins/builtins-number.cc#L140">/src/builtins/builtins-number.cc</a> 中。</p> -<p>下面看看<code>NumberToString</code>的定义, 应该是在 <a href="https://github.com/v8/v8/blob/829e8d18d73e21807c78223b0cc107c76c80da27/src/heap/factory-base.cc#L963-L978">src/heap/factory-base.cc</a> 中:</p> -<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="keyword">template</span> <<span class="keyword">typename</span> Impl></span><br><span class="line">Handle<String> FactoryBase<Impl>::<span class="built_in">NumberToString</span>(Handle<Object> number,</span><br><span class="line"> NumberCacheMode mode) {</span><br><span class="line"> <span class="built_in">SLOW_DCHECK</span>(<span class="built_in">IsNumber</span>(*number));</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">IsSmi</span>(*number)) <span class="keyword">return</span> <span class="built_in">SmiToString</span>(Smi::<span class="built_in">cast</span>(*number), mode);</span><br><span class="line"></span><br><span class="line"> <span class="type">double</span> double_value = Handle<HeapNumber>::<span class="built_in">cast</span>(number)-><span class="built_in">value</span>();</span><br><span class="line"> <span class="comment">// Try to canonicalize doubles.</span></span><br><span class="line"> <span class="type">int</span> smi_value;</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">DoubleToSmiInteger</span>(double_value, &smi_value)) {</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">SmiToString</span>(Smi::<span class="built_in">FromInt</span>(smi_value), mode);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">HeapNumberToString</span>(Handle<HeapNumber>::<span class="built_in">cast</span>(number), double_value,</span><br><span class="line"> mode);</span><br><span class="line">}</span><br></pre></td></tr></table></figure> - -<p>可以看到这里调用了 <code>SmiToString</code>, 这里不往下翻这个函数的定义, 只需要知道Smi是什么即可。 Smi 是一种特殊的整数类型,它被用于表示较小的整数值,通常在 32 位系统中是 31 位有符号整数。Smi 类型的值存储在指针的低位,而指针的高位用于标记该值是一个 Smi 类型。<code>IsSmi</code> 函数会检查给定的值是否为 Smi 类型,如果是,则返回 true,否则返回 false。这个函数通常用于 V8 引擎内部的优化和性能优化。</p> -<p>所以<code>NumberToString</code>会判断number是否是一个smi, 如果是的话就调用SmiToString, 否则会尝试将其转换为double再去调用<code>DoubleToSmiInteger</code>, 将<code>DoubleToSmiInteger</code>的调用结果存在<code>smi_value</code>里面, 再通过调用<code>SmiToString</code>将<code>smi_value</code>转换为字符串。</p> -<p>如果这两条路都行不通的话,就直接调用<code>HeapNumberToString</code>了。</p> -<p><code>HeapNumberToString</code>的定义如下:</p> -<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="keyword">template</span> <<span class="keyword">typename</span> Impl></span><br><span class="line">Handle<String> FactoryBase<Impl>::<span class="built_in">HeapNumberToString</span>(Handle<HeapNumber> number,</span><br><span class="line"> <span class="type">double</span> value,</span><br><span class="line"> NumberCacheMode mode) {</span><br><span class="line"> <span class="type">int</span> hash = mode == NumberCacheMode::kIgnore</span><br><span class="line"> ? <span class="number">0</span></span><br><span class="line"> : <span class="built_in">impl</span>()-><span class="built_in">NumberToStringCacheHash</span>(value);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (mode == NumberCacheMode::kBoth) {</span><br><span class="line"> Handle<Object> cached = <span class="built_in">impl</span>()-><span class="built_in">NumberToStringCacheGet</span>(*number, hash);</span><br><span class="line"> <span class="keyword">if</span> (!<span class="built_in">IsUndefined</span>(*cached, <span class="built_in">isolate</span>())) <span class="keyword">return</span> Handle<String>::<span class="built_in">cast</span>(cached);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> Handle<String> result;</span><br><span class="line"> <span class="keyword">if</span> (value == <span class="number">0</span>) {</span><br><span class="line"> result = <span class="built_in">zero_string</span>();</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (std::<span class="built_in">isnan</span>(value)) {</span><br><span class="line"> result = <span class="built_in">NaN_string</span>();</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="type">char</span> arr[kNumberToStringBufferSize];</span><br><span class="line"> <span class="function">base::Vector<<span class="type">char</span>> <span class="title">buffer</span><span class="params">(arr, arraysize(arr))</span></span>;</span><br><span class="line"> <span class="type">const</span> <span class="type">char</span>* string = <span class="built_in">DoubleToCString</span>(value, buffer);</span><br><span class="line"> result = <span class="built_in">CharToString</span>(<span class="keyword">this</span>, string, mode);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (mode != NumberCacheMode::kIgnore) {</span><br><span class="line"> <span class="built_in">impl</span>()-><span class="built_in">NumberToStringCacheSet</span>(number, hash, result);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line">}</span><br></pre></td></tr></table></figure> - -<p>就是熟知的NaN, Undefined处理,重点在:</p> -<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="type">char</span> arr[kNumberToStringBufferSize];</span><br><span class="line"><span class="function">base::Vector<<span class="type">char</span>> <span class="title">buffer</span><span class="params">(arr, arraysize(arr))</span></span>;</span><br><span class="line"><span class="type">const</span> <span class="type">char</span>* string = <span class="built_in">DoubleToCString</span>(value, buffer);</span><br><span class="line">result = <span class="built_in">CharToString</span>(<span class="keyword">this</span>, string, mode);</span><br></pre></td></tr></table></figure> - -<p>这里调用了<code>DoubleToCString</code>, 其定义在 <a href="https://github.com/v8/v8/blob/829e8d18d73e21807c78223b0cc107c76c80da27/src/numbers/conversions.cc#L1064-L1126">/src/numbers/conversions.cc</a> 中:</p> -<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="type">const</span> <span class="type">char</span>* <span class="title">DoubleToCString</span><span class="params">(<span class="type">double</span> v, base::Vector<<span class="type">char</span>> buffer)</span> </span>{</span><br><span class="line"> <span class="keyword">switch</span> (FPCLASSIFY_NAMESPACE::<span class="built_in">fpclassify</span>(v)) {</span><br><span class="line"> <span class="keyword">case</span> FP_NAN:</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"NaN"</span>;</span><br><span class="line"> <span class="keyword">case</span> FP_INFINITE:</span><br><span class="line"> <span class="keyword">return</span> (v < <span class="number">0.0</span> ? <span class="string">"-Infinity"</span> : <span class="string">"Infinity"</span>);</span><br><span class="line"> <span class="keyword">case</span> FP_ZERO:</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"0"</span>;</span><br><span class="line"> <span class="keyword">default</span>: {</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">IsInt32Double</span>(v)) {</span><br><span class="line"> <span class="comment">// This will trigger if v is -0 and -0.0 is stringified to "0".</span></span><br><span class="line"> <span class="comment">// (see ES section 7.1.12.1 #sec-tostring-applied-to-the-number-type)</span></span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">IntToCString</span>(<span class="built_in">FastD2I</span>(v), buffer);</span><br><span class="line"> }</span><br><span class="line"> <span class="function">SimpleStringBuilder <span class="title">builder</span><span class="params">(buffer.begin(), buffer.length())</span></span>;</span><br><span class="line"> <span class="type">int</span> decimal_point;</span><br><span class="line"> <span class="type">int</span> sign;</span><br><span class="line"> <span class="type">const</span> <span class="type">int</span> kV8DtoaBufferCapacity = base::kBase10MaximalLength + <span class="number">1</span>;</span><br><span class="line"> <span class="type">char</span> decimal_rep[kV8DtoaBufferCapacity];</span><br><span class="line"> <span class="type">int</span> length;</span><br><span class="line"></span><br><span class="line"> base::<span class="built_in">DoubleToAscii</span>(</span><br><span class="line"> v, base::DTOA_SHORTEST, <span class="number">0</span>,</span><br><span class="line"> base::<span class="built_in">Vector</span><<span class="type">char</span>>(decimal_rep, kV8DtoaBufferCapacity), &sign,</span><br><span class="line"> &length, &decimal_point);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (sign) builder.<span class="built_in">AddCharacter</span>(<span class="string">'-'</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (length <= decimal_point && decimal_point <= <span class="number">21</span>) {</span><br><span class="line"> <span class="comment">// ECMA-262 section 9.8.1 step 6.</span></span><br><span class="line"> builder.<span class="built_in">AddString</span>(decimal_rep);</span><br><span class="line"> builder.<span class="built_in">AddPadding</span>(<span class="string">'0'</span>, decimal_point - length);</span><br><span class="line"></span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (<span class="number">0</span> < decimal_point && decimal_point <= <span class="number">21</span>) {</span><br><span class="line"> <span class="comment">// ECMA-262 section 9.8.1 step 7.</span></span><br><span class="line"> builder.<span class="built_in">AddSubstring</span>(decimal_rep, decimal_point);</span><br><span class="line"> builder.<span class="built_in">AddCharacter</span>(<span class="string">'.'</span>);</span><br><span class="line"> builder.<span class="built_in">AddString</span>(decimal_rep + decimal_point);</span><br><span class="line"></span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (decimal_point <= <span class="number">0</span> && decimal_point > <span class="number">-6</span>) {</span><br><span class="line"> <span class="comment">// ECMA-262 section 9.8.1 step 8.</span></span><br><span class="line"> builder.<span class="built_in">AddString</span>(<span class="string">"0."</span>);</span><br><span class="line"> builder.<span class="built_in">AddPadding</span>(<span class="string">'0'</span>, -decimal_point);</span><br><span class="line"> builder.<span class="built_in">AddString</span>(decimal_rep);</span><br><span class="line"></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// ECMA-262 section 9.8.1 step 9 and 10 combined.</span></span><br><span class="line"> builder.<span class="built_in">AddCharacter</span>(decimal_rep[<span class="number">0</span>]);</span><br><span class="line"> <span class="keyword">if</span> (length != <span class="number">1</span>) {</span><br><span class="line"> builder.<span class="built_in">AddCharacter</span>(<span class="string">'.'</span>);</span><br><span class="line"> builder.<span class="built_in">AddString</span>(decimal_rep + <span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> builder.<span class="built_in">AddCharacter</span>(<span class="string">'e'</span>);</span><br><span class="line"> builder.<span class="built_in">AddCharacter</span>((decimal_point >= <span class="number">0</span>) ? <span class="string">'+'</span> : <span class="string">'-'</span>);</span><br><span class="line"> <span class="type">int</span> exponent = decimal_point - <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">if</span> (exponent < <span class="number">0</span>) exponent = -exponent;</span><br><span class="line"> builder.<span class="built_in">AddDecimalInteger</span>(exponent);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> builder.<span class="built_in">Finalize</span>();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure> - -<p>不用过多解释, 已经很清晰了, <code>FastD2I</code> 就是 Fast Double to Integer的意思, 定义如下, 注释也很详尽:</p> -<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="comment">// The fast double-to-(unsigned-)int conversion routine does not guarantee</span></span><br><span class="line"><span class="comment">// rounding towards zero.</span></span><br><span class="line"><span class="comment">// The result is undefined if x is infinite or NaN, or if the rounded</span></span><br><span class="line"><span class="comment">// integer value is outside the range of type int.</span></span><br><span class="line"><span class="function"><span class="keyword">inline</span> <span class="type">int</span> <span class="title">FastD2I</span><span class="params">(<span class="type">double</span> x)</span> </span>{</span><br><span class="line"> <span class="built_in">DCHECK</span>(x <= INT_MAX);</span><br><span class="line"> <span class="built_in">DCHECK</span>(x >= INT_MIN);</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">static_cast</span><<span class="type">int32_t</span>>(x);</span><br><span class="line">}</span><br></pre></td></tr></table></figure> - -<p>以上</p> -]]></content> - <tags> - <tag>Technique</tag> - </tags> - </entry> - <entry> <title>中老年人沉迷手机的问题</title> <url>/2024/09/08/%E4%B8%AD%E8%80%81%E5%B9%B4%E4%BA%BA%E6%B2%89%E8%BF%B7%E6%89%8B%E6%9C%BA%E7%9A%84%E9%97%AE%E9%A2%98/</url> <content><