From 6ba051fa1ba4e23ee47adef5553263ef654e6008 Mon Sep 17 00:00:00 2001 From: muqiuhan Date: Thu, 31 Jul 2025 10:50:58 +0000 Subject: deploy: ef7c3e415ce43d2b5307bcfdd5c98109faa25721 --- search.xml | 155 ++++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 107 insertions(+), 48 deletions(-) (limited to 'search.xml') diff --git a/search.xml b/search.xml index 73ad7b08..4adf0221 100644 --- a/search.xml +++ b/search.xml @@ -913,20 +913,6 @@ await prisma.$transaction(async tx => {

[^1]: Decipad 博客 “Collaborative and Offline Editing Using CRDTs”
https://www.decipad.com/blog/decipads-innovative-method-collaborative-and-offline-editing-using-crdts

[^2]: Hacker News 讨论(CRDT 相关线程)
https://news.ycombinator.com/item?id=38289327

-]]> - - Technique - - - - .NET AOT 下的 F# 命令行参数解析库选择 - /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/ - Argu 不支持 AOT,不过用 F# 的话可以看整个 .NET 的生态,我看了一下 C# 的 CommandLineParser:
https://github.com/commandlineparser

-

在这个 PR 中支持了 Native AOT
https://github.com/commandlineparser/commandline/pull/913

-

除此之外,还有一个更加精巧的 F# 库可以用,只有两百多行:
https://github.com/B2R2-org/FsOptParse/

-

AOT 后的大小很可观,并且支持 full trim.

-

用例:

-
(** defines a state to pass to the option parser *)
type opts =
{
optX : int;
optY : bool;
optZ : string;
}

(** default option state *)
let defaultOpts =
{
optX = 0;
optY = false;
optZ = "";
}

(*
An example command line specification, which is a list of Options.
Each Option describes a command line option (switch) that is specified with
either a short (a single-dash option) or long option (a double-dash option).
*)
let spec =
[
(* This option can be specified with -x <NUM>. There is an extra argument to
specify a value in integer. *)
Option ((* description of the option *)
descr="this is a testing param X",
(* how many extra argument must be provided by a user? *)
extra=1,
(* callback sets up the option and returns it *)
callback=(fun opts arg -> {opts with optX=(int) arg.[0]}),
(* use a short option style -x *)
short="-x"
);

(* This option can be specified with -y. There is no extra argument. This
option just sets a flag, optY. *)
Option ((* description of the option *)
descr="this is a testing param Y",
(* set the option to be true *)
callback=(fun opts _ -> {opts with optY=true}),
(* use a short option style (-y) *)
short="-y",
(* also use a long option style (--yoohoo) *)
long="--yoohoo"
);

(* A dummy option to pretty-print the usage *)
Option ((* description of the option *)
descr="",
dummy=true
);
Option ((* description of the option *)
descr="[Required Options]",
descrColor=System.ConsoleColor.DarkCyan,
dummy=true
);

(* The third option is a required option. In other words, option parsing
will raise an exception if this option is not given by a user. This
option takes in an additional integer argument, and set it to the global
variable z. *)
Option ((* description of the option *)
descr="required parameter <STRING> with an integer option",
(* callback to set the optZ value *)
callback=(fun opts arg -> {opts with optZ=arg.[0]}),
(* specifying this is a required option *)
required=true,
(* one additional argument to specify an integer value *)
extra=1,
(* use only a long option style *)
long="--req"
);
]

let _ =
let prog = "opttest.fsx"
let args = System.Environment.GetCommandLineArgs ()
let usageGetter () = "[Usage]\n %p %o"
try
let left, opts = optParse spec usageGetter prog args defaultOpts
printfn "Rest args: %A, x: %d, y: %b, z: %s"
left opts.optX opts.optY opts.optZ
0
with
| SpecErr msg ->
eprintfn "Invalid spec: %s" msg
exit 1
| RuntimeErr msg ->
eprintfn "Invalid args given by user: %s" msg
usagePrint spec prog usageGetter (fun () -> exit 1)
]]>
Technique @@ -966,6 +952,20 @@ await prisma.$transaction(async tx => {
  • Stack Overflow: What is the N+1 selects problem in ORM?
  • Prisma Docs: Solving the N+1 problem
  • +]]> + + Technique + +
    + + .NET AOT 下的 F# 命令行参数解析库选择 + /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/ + Argu 不支持 AOT,不过用 F# 的话可以看整个 .NET 的生态,我看了一下 C# 的 CommandLineParser:
    https://github.com/commandlineparser

    +

    在这个 PR 中支持了 Native AOT
    https://github.com/commandlineparser/commandline/pull/913

    +

    除此之外,还有一个更加精巧的 F# 库可以用,只有两百多行:
    https://github.com/B2R2-org/FsOptParse/

    +

    AOT 后的大小很可观,并且支持 full trim.

    +

    用例:

    +
    (** defines a state to pass to the option parser *)
    type opts =
    {
    optX : int;
    optY : bool;
    optZ : string;
    }

    (** default option state *)
    let defaultOpts =
    {
    optX = 0;
    optY = false;
    optZ = "";
    }

    (*
    An example command line specification, which is a list of Options.
    Each Option describes a command line option (switch) that is specified with
    either a short (a single-dash option) or long option (a double-dash option).
    *)
    let spec =
    [
    (* This option can be specified with -x <NUM>. There is an extra argument to
    specify a value in integer. *)
    Option ((* description of the option *)
    descr="this is a testing param X",
    (* how many extra argument must be provided by a user? *)
    extra=1,
    (* callback sets up the option and returns it *)
    callback=(fun opts arg -> {opts with optX=(int) arg.[0]}),
    (* use a short option style -x *)
    short="-x"
    );

    (* This option can be specified with -y. There is no extra argument. This
    option just sets a flag, optY. *)
    Option ((* description of the option *)
    descr="this is a testing param Y",
    (* set the option to be true *)
    callback=(fun opts _ -> {opts with optY=true}),
    (* use a short option style (-y) *)
    short="-y",
    (* also use a long option style (--yoohoo) *)
    long="--yoohoo"
    );

    (* A dummy option to pretty-print the usage *)
    Option ((* description of the option *)
    descr="",
    dummy=true
    );
    Option ((* description of the option *)
    descr="[Required Options]",
    descrColor=System.ConsoleColor.DarkCyan,
    dummy=true
    );

    (* The third option is a required option. In other words, option parsing
    will raise an exception if this option is not given by a user. This
    option takes in an additional integer argument, and set it to the global
    variable z. *)
    Option ((* description of the option *)
    descr="required parameter <STRING> with an integer option",
    (* callback to set the optZ value *)
    callback=(fun opts arg -> {opts with optZ=arg.[0]}),
    (* specifying this is a required option *)
    required=true,
    (* one additional argument to specify an integer value *)
    extra=1,
    (* use only a long option style *)
    long="--req"
    );
    ]

    let _ =
    let prog = "opttest.fsx"
    let args = System.Environment.GetCommandLineArgs ()
    let usageGetter () = "[Usage]\n %p %o"
    try
    let left, opts = optParse spec usageGetter prog args defaultOpts
    printfn "Rest args: %A, x: %d, y: %b, z: %s"
    left opts.optX opts.optY opts.optZ
    0
    with
    | SpecErr msg ->
    eprintfn "Invalid spec: %s" msg
    exit 1
    | RuntimeErr msg ->
    eprintfn "Invalid args given by user: %s" msg
    usagePrint spec prog usageGetter (fun () -> exit 1)
    ]]>
    Technique @@ -2855,6 +2855,65 @@ await prisma.$transaction(async tx => { Technique
    + + 使用 Runed intersection observer 与 tanstack query 实现通用的惰性加载组件 + /2025/07/31/svelte-lazyquery/ + 在前端,一个常见的性能瓶颈是初始加载时请求了用户当前 viewpoint 之外的非必要数据。惰性加载(Lazy Loading)是一种关键策略,它将数据获取推迟到组件进入 viewpoint 时才执行。本文将阐述如何结合使用 Svelte 5、@tanstack/svelte-query 和 runed.dev 的 useIntersectionObserver,构建一个通用的、可复用的惰性加载组件。

    +

    核心依赖与环境

    +
      +
    • Svelte 5: 本实现依赖于 Svelte 5 的符文(Runes)特性,它提供了更精细、更直观的状态管理能力。

      +
    • +
    • @tanstack/svelte-query: TanStack Query 的 Svelte 适配版,用户获取数据。

      +
    • +
    • runed.dev: 一个提供多种 Svelte 5 实用工具的库,本文主要使用其 useIntersectionObserver。

      +
    • +
    +

    设计哲学:分离关注点

    该方案的核心思想是将“何时加载”与“如何加载”这两个关注点进行解耦。

    +
      +
    1. 何时加载 (When to Load): 组件的可见性决定了数据加载的时机。我们利用 Intersection Observer API 来精确、高效地监听一个元素是否进入 viewpoint。runed.dev 库为此提供了名为 useIntersectionObserver 的便捷封装。

      +
    2. +
    3. 如何加载 (How to Load): 数据获取、缓存、同步和状态管理的复杂性由 @tanstack/svelte-query (TanStack Query) 处理。它提供了一套强大的工具集来管理异步数据。

      +
    4. +
    +

    通过将这两者结合,可以创建一个名为 LazyQuery 的抽象组件。该组件内部处理可见性检测,并根据检测结果动态控制 TanStack Query 的执行,而将具体的查询逻辑(queryFn)和键(queryKey)完全交由使用者定义。

    +

    LazyQuery 组件的实现

    +

    目标:封装惰性加载逻辑,并向外暴露一个标准的 TanStack Query 接口。

    +
    +

    一、对组件的接口类型做如下定义:

    +
    import type { CreateQueryOptions, QueryKey } from '@tanstack/svelte-query';
    import type { Snippet } from 'svelte';

    type Props<
    TQueryFnData = unknown,
    TError = Error,
    TData = TQueryFnData,
    TQueryKey extends QueryKey = QueryKey
    > = {
    /// 一个标准的 TanStack Query 配置对象
    queryOptions: CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>;

    /// Svelte 5 的 `Snippet` 类型,它允许父组件向子组件传递一段可执行的 UI 模板。
    /// 这个 `Snippet` 会接收到 `createQuery` 返回的完整查询对象 `query`,
    /// 从而可以访问 `data`, `isLoading`, `error` 等所有状态。
    children: Snippet<{
    query: ReturnType<typeof createQuery<TQueryFnData, TError, TData, TQueryKey>>;
    }>;
    };
    + +

    二、组件逻辑:

    +
    <script lang="ts">
    import { createQuery, type CreateQueryOptions, type QueryKey } from '@tanstack/svelte-query';
    import { useIntersectionObserver } from 'runed';
    import type { Snippet } from 'svelte';

    // [上述的类型定义]

    let { queryOptions, children }: Props = $props();

    let el: Element;
    const { isIntersecting } = useIntersectionObserver(
    () => el,
    {
    rootMargin: '200px',
    }
    );

    const query = createQuery({
    ...queryOptions,
    get enabled() {
    return ($state.is(isIntersecting) && (queryOptions.enabled ?? true));
    }
    });
    </script>

    <div bind:this={el}>
    {#snippet children({ query })}
    {/snippet}
    </div>
    + +
      +
    • 我们创建一个 div 元素作为哨兵(sentinel),并用 bind:this={el} 将其 DOM 引用绑定到变量 el。
    • +
    • useIntersectionObserver 接收一个返回目标元素的函数 () => el。
        +
      • 它返回一个响应式的状态对象,其中 isIntersecting 是一个布尔值的符文(rune),当 div 元素进入 viewpoint 时为 true,否则为 false。
      • +
      +
    • +
    • rootMargin: '200px' 是一个优化选项,它会在元素距离 viewpoint 还有 200px 时就触发加载,从而提升用户体验。
    • +
    +

    使用

    使用 LazyQuery 组件非常直观。开发者只需关注数据获取的业务逻辑,而无需关心惰性加载的实现细节,假设有一个获取图表数据的场景:

    +
    <script lang="ts">
    import { QueryClientProvider, QueryClient } from '@tanstack/svelte-query';
    import LazyQuery from './LazyQuery.svelte';
    import CopdGoldGradingChart from './CopdGoldGradingChart.svelte';

    const queryClient = new QueryClient();

    // Define the query configuration object, just like with a standard `createQuery`.
    const queryOptions = {
    queryKey: ['goldGradingData'],
    queryFn: async () => {
    // Simulate a network request
    await new Promise(resolve => setTimeout(resolve, 1500));
    // In a real application, this would be an API call
    // const response = await fetch('/api/gold-grading');
    // return await response.json();
    return { data: { totalPatients: 1234, chartPoints: [/* ... */] } };
    }
    };
    </script>

    <QueryClientProvider client={queryClient}>
    <div style="height: 200vh;">
    <p>Scroll down to see the chart...</p>
    </div>

    <LazyQuery {queryOptions}>
    {#snippet children({ query })}
    {#if query.isLoading}
    <p>Loading chart data...</p>
    {:else if query.error}
    <p>Error: {query.error.message}</p>
    {:else if query.data}
    <CopdGoldGradingChart
    isLoading={query.isFetching}
    error={query.error}
    refetch={() => query.refetch()}
    data={query.data.data}
    totalPatients={query.data.data.totalPatients}
    />
    {/if}
    {/snippet}
    </LazyQuery>
    </QueryClientProvider>
    + +
      +
    • 页面初始加载时,LazyQuery 组件被渲染,但由于其 div 在 viewpoint 之外,isIntersecting 为 false。
    • +
    • createQuery 被调用,但因为 enabled 条件为 false,查询处于禁用状态,不会发起任何网络请求。
    • +
    • children 片段被渲染,此时 query.isLoading 为 true(这是 TanStack Query 禁用查询时的初始状态),显示 “Loading chart data…”。
    • +
    • 当用户向下滚动,div 元素进入 viewpoint(或进入 200px 的预加载区域)。
    • +
    • useIntersectionObserver 将 isIntersecting 的值更新为 true。
    • +
    • 这个变化被 createQuery 的 enabled 访问器捕获,查询被自动激活,queryFn 开始执行。
    • +
    • TanStack Query 负责管理后续的状态变化(isFetching, data, error),并驱动 children 片段内的 UI 自动更新。
    • +
    +

    参考

    +]]>
    + + Technique + +
    tick thread在Multicore OCaml中的作用 /2023/08/15/tick-thread%E5%9C%A8Multicore-OCaml%E4%B8%AD%E7%9A%84%E4%BD%9C%E7%94%A8/ @@ -2888,6 +2947,40 @@ await prisma.$transaction(async tx => {

    Rimon Tawadrous 在其 GitHub repo 中的测试,对比 100 万条逐条插入实验,UUID v7 相较 UUID v4 在单线程插入上速度快约 3.24%,多线程下更可观【1】。


    参考链接
    [1] “为什么 UUID 7 比 UUID 4 更适合作为 RDBMS 的聚集索引?” dbaplus.cn
    https://dbaplus.cn/news-160-6313-1.html
    [2] “PostgreSQL and UUID as primary key” maciejwalkowiak
    https://maciejwalkowiak.com/blog/postgres-uuid-primary-key/
    [3] “Optimised UUIDs in mysql” stitcher
    https://stitcher.io/blog/optimised-uuids-in-mysql
    [3] “Storing UUID Values in MySQL” percona
    https://www.percona.com/blog/store-uuid-optimized-way/

    +]]> + + Technique + +
    + + v8中的Number.toString() + /2023/10/05/v8%E4%B8%AD%E7%9A%84Number-toString/ + 这里讲一下JavaScript中Number.toString()的实现, 以V8为例。

    + +

    在很多地方都能看到:

    +
    *isolate->factory()->NumberToString(value);
    +

    例如 /src/builtins/builtins-number.cc 中。

    +

    下面看看NumberToString的定义, 应该是在 src/heap/factory-base.cc 中:

    +
    template <typename Impl>
    Handle<String> FactoryBase<Impl>::NumberToString(Handle<Object> number,
    NumberCacheMode mode) {
    SLOW_DCHECK(IsNumber(*number));
    if (IsSmi(*number)) return SmiToString(Smi::cast(*number), mode);

    double double_value = Handle<HeapNumber>::cast(number)->value();
    // Try to canonicalize doubles.
    int smi_value;
    if (DoubleToSmiInteger(double_value, &smi_value)) {
    return SmiToString(Smi::FromInt(smi_value), mode);
    }
    return HeapNumberToString(Handle<HeapNumber>::cast(number), double_value,
    mode);
    }
    + +

    可以看到这里调用了 SmiToString, 这里不往下翻这个函数的定义, 只需要知道Smi是什么即可。 Smi 是一种特殊的整数类型,它被用于表示较小的整数值,通常在 32 位系统中是 31 位有符号整数。Smi 类型的值存储在指针的低位,而指针的高位用于标记该值是一个 Smi 类型。IsSmi 函数会检查给定的值是否为 Smi 类型,如果是,则返回 true,否则返回 false。这个函数通常用于 V8 引擎内部的优化和性能优化。

    +

    所以NumberToString会判断number是否是一个smi, 如果是的话就调用SmiToString, 否则会尝试将其转换为double再去调用DoubleToSmiInteger, 将DoubleToSmiInteger的调用结果存在smi_value里面, 再通过调用SmiToString将smi_value转换为字符串。

    +

    如果这两条路都行不通的话,就直接调用HeapNumberToString了。

    +

    HeapNumberToString的定义如下:

    +
    template <typename Impl>
    Handle<String> FactoryBase<Impl>::HeapNumberToString(Handle<HeapNumber> number,
    double value,
    NumberCacheMode mode) {
    int hash = mode == NumberCacheMode::kIgnore
    ? 0
    : impl()->NumberToStringCacheHash(value);

    if (mode == NumberCacheMode::kBoth) {
    Handle<Object> cached = impl()->NumberToStringCacheGet(*number, hash);
    if (!IsUndefined(*cached, isolate())) return Handle<String>::cast(cached);
    }

    Handle<String> result;
    if (value == 0) {
    result = zero_string();
    } else if (std::isnan(value)) {
    result = NaN_string();
    } else {
    char arr[kNumberToStringBufferSize];
    base::Vector<char> buffer(arr, arraysize(arr));
    const char* string = DoubleToCString(value, buffer);
    result = CharToString(this, string, mode);
    }
    if (mode != NumberCacheMode::kIgnore) {
    impl()->NumberToStringCacheSet(number, hash, result);
    }
    return result;
    }
    + +

    就是熟知的NaN, Undefined处理,重点在:

    +
    char arr[kNumberToStringBufferSize];
    base::Vector<char> buffer(arr, arraysize(arr));
    const char* string = DoubleToCString(value, buffer);
    result = CharToString(this, string, mode);
    + +

    这里调用了DoubleToCString, 其定义在 /src/numbers/conversions.cc 中:

    +
    const char* DoubleToCString(double v, base::Vector<char> buffer) {
    switch (FPCLASSIFY_NAMESPACE::fpclassify(v)) {
    case FP_NAN:
    return "NaN";
    case FP_INFINITE:
    return (v < 0.0 ? "-Infinity" : "Infinity");
    case FP_ZERO:
    return "0";
    default: {
    if (IsInt32Double(v)) {
    // This will trigger if v is -0 and -0.0 is stringified to "0".
    // (see ES section 7.1.12.1 #sec-tostring-applied-to-the-number-type)
    return IntToCString(FastD2I(v), buffer);
    }
    SimpleStringBuilder builder(buffer.begin(), buffer.length());
    int decimal_point;
    int sign;
    const int kV8DtoaBufferCapacity = base::kBase10MaximalLength + 1;
    char decimal_rep[kV8DtoaBufferCapacity];
    int length;

    base::DoubleToAscii(
    v, base::DTOA_SHORTEST, 0,
    base::Vector<char>(decimal_rep, kV8DtoaBufferCapacity), &sign,
    &length, &decimal_point);

    if (sign) builder.AddCharacter('-');

    if (length <= decimal_point && decimal_point <= 21) {
    // ECMA-262 section 9.8.1 step 6.
    builder.AddString(decimal_rep);
    builder.AddPadding('0', decimal_point - length);

    } else if (0 < decimal_point && decimal_point <= 21) {
    // ECMA-262 section 9.8.1 step 7.
    builder.AddSubstring(decimal_rep, decimal_point);
    builder.AddCharacter('.');
    builder.AddString(decimal_rep + decimal_point);

    } else if (decimal_point <= 0 && decimal_point > -6) {
    // ECMA-262 section 9.8.1 step 8.
    builder.AddString("0.");
    builder.AddPadding('0', -decimal_point);
    builder.AddString(decimal_rep);

    } else {
    // ECMA-262 section 9.8.1 step 9 and 10 combined.
    builder.AddCharacter(decimal_rep[0]);
    if (length != 1) {
    builder.AddCharacter('.');
    builder.AddString(decimal_rep + 1);
    }
    builder.AddCharacter('e');
    builder.AddCharacter((decimal_point >= 0) ? '+' : '-');
    int exponent = decimal_point - 1;
    if (exponent < 0) exponent = -exponent;
    builder.AddDecimalInteger(exponent);
    }
    return builder.Finalize();
    }
    }
    }
    + +

    不用过多解释, 已经很清晰了, FastD2I 就是 Fast Double to Integer的意思, 定义如下, 注释也很详尽:

    +
    // The fast double-to-(unsigned-)int conversion routine does not guarantee
    // rounding towards zero.
    // The result is undefined if x is infinite or NaN, or if the rounded
    // integer value is outside the range of type int.
    inline int FastD2I(double x) {
    DCHECK(x <= INT_MAX);
    DCHECK(x >= INT_MIN);
    return static_cast<int32_t>(x);
    }
    + +

    以上

    ]]>
    Technique @@ -2934,40 +3027,6 @@ await prisma.$transaction(async tx => {
  • 按用户角色或权限拆分:先实现某个核心角色的功能,再实现其他角色的特定功能。
  • 简化错误处理或用户体验:先实现基本功能,再完善错误处理和用户体验细节。
  • -]]> - - Technique - -
    - - v8中的Number.toString() - /2023/10/05/v8%E4%B8%AD%E7%9A%84Number-toString/ - 这里讲一下JavaScript中Number.toString()的实现, 以V8为例。

    - -

    在很多地方都能看到:

    -
    *isolate->factory()->NumberToString(value);
    -

    例如 /src/builtins/builtins-number.cc 中。

    -

    下面看看NumberToString的定义, 应该是在 src/heap/factory-base.cc 中:

    -
    template <typename Impl>
    Handle<String> FactoryBase<Impl>::NumberToString(Handle<Object> number,
    NumberCacheMode mode) {
    SLOW_DCHECK(IsNumber(*number));
    if (IsSmi(*number)) return SmiToString(Smi::cast(*number), mode);

    double double_value = Handle<HeapNumber>::cast(number)->value();
    // Try to canonicalize doubles.
    int smi_value;
    if (DoubleToSmiInteger(double_value, &smi_value)) {
    return SmiToString(Smi::FromInt(smi_value), mode);
    }
    return HeapNumberToString(Handle<HeapNumber>::cast(number), double_value,
    mode);
    }
    - -

    可以看到这里调用了 SmiToString, 这里不往下翻这个函数的定义, 只需要知道Smi是什么即可。 Smi 是一种特殊的整数类型,它被用于表示较小的整数值,通常在 32 位系统中是 31 位有符号整数。Smi 类型的值存储在指针的低位,而指针的高位用于标记该值是一个 Smi 类型。IsSmi 函数会检查给定的值是否为 Smi 类型,如果是,则返回 true,否则返回 false。这个函数通常用于 V8 引擎内部的优化和性能优化。

    -

    所以NumberToString会判断number是否是一个smi, 如果是的话就调用SmiToString, 否则会尝试将其转换为double再去调用DoubleToSmiInteger, 将DoubleToSmiInteger的调用结果存在smi_value里面, 再通过调用SmiToString将smi_value转换为字符串。

    -

    如果这两条路都行不通的话,就直接调用HeapNumberToString了。

    -

    HeapNumberToString的定义如下:

    -
    template <typename Impl>
    Handle<String> FactoryBase<Impl>::HeapNumberToString(Handle<HeapNumber> number,
    double value,
    NumberCacheMode mode) {
    int hash = mode == NumberCacheMode::kIgnore
    ? 0
    : impl()->NumberToStringCacheHash(value);

    if (mode == NumberCacheMode::kBoth) {
    Handle<Object> cached = impl()->NumberToStringCacheGet(*number, hash);
    if (!IsUndefined(*cached, isolate())) return Handle<String>::cast(cached);
    }

    Handle<String> result;
    if (value == 0) {
    result = zero_string();
    } else if (std::isnan(value)) {
    result = NaN_string();
    } else {
    char arr[kNumberToStringBufferSize];
    base::Vector<char> buffer(arr, arraysize(arr));
    const char* string = DoubleToCString(value, buffer);
    result = CharToString(this, string, mode);
    }
    if (mode != NumberCacheMode::kIgnore) {
    impl()->NumberToStringCacheSet(number, hash, result);
    }
    return result;
    }
    - -

    就是熟知的NaN, Undefined处理,重点在:

    -
    char arr[kNumberToStringBufferSize];
    base::Vector<char> buffer(arr, arraysize(arr));
    const char* string = DoubleToCString(value, buffer);
    result = CharToString(this, string, mode);
    - -

    这里调用了DoubleToCString, 其定义在 /src/numbers/conversions.cc 中:

    -
    const char* DoubleToCString(double v, base::Vector<char> buffer) {
    switch (FPCLASSIFY_NAMESPACE::fpclassify(v)) {
    case FP_NAN:
    return "NaN";
    case FP_INFINITE:
    return (v < 0.0 ? "-Infinity" : "Infinity");
    case FP_ZERO:
    return "0";
    default: {
    if (IsInt32Double(v)) {
    // This will trigger if v is -0 and -0.0 is stringified to "0".
    // (see ES section 7.1.12.1 #sec-tostring-applied-to-the-number-type)
    return IntToCString(FastD2I(v), buffer);
    }
    SimpleStringBuilder builder(buffer.begin(), buffer.length());
    int decimal_point;
    int sign;
    const int kV8DtoaBufferCapacity = base::kBase10MaximalLength + 1;
    char decimal_rep[kV8DtoaBufferCapacity];
    int length;

    base::DoubleToAscii(
    v, base::DTOA_SHORTEST, 0,
    base::Vector<char>(decimal_rep, kV8DtoaBufferCapacity), &sign,
    &length, &decimal_point);

    if (sign) builder.AddCharacter('-');

    if (length <= decimal_point && decimal_point <= 21) {
    // ECMA-262 section 9.8.1 step 6.
    builder.AddString(decimal_rep);
    builder.AddPadding('0', decimal_point - length);

    } else if (0 < decimal_point && decimal_point <= 21) {
    // ECMA-262 section 9.8.1 step 7.
    builder.AddSubstring(decimal_rep, decimal_point);
    builder.AddCharacter('.');
    builder.AddString(decimal_rep + decimal_point);

    } else if (decimal_point <= 0 && decimal_point > -6) {
    // ECMA-262 section 9.8.1 step 8.
    builder.AddString("0.");
    builder.AddPadding('0', -decimal_point);
    builder.AddString(decimal_rep);

    } else {
    // ECMA-262 section 9.8.1 step 9 and 10 combined.
    builder.AddCharacter(decimal_rep[0]);
    if (length != 1) {
    builder.AddCharacter('.');
    builder.AddString(decimal_rep + 1);
    }
    builder.AddCharacter('e');
    builder.AddCharacter((decimal_point >= 0) ? '+' : '-');
    int exponent = decimal_point - 1;
    if (exponent < 0) exponent = -exponent;
    builder.AddDecimalInteger(exponent);
    }
    return builder.Finalize();
    }
    }
    }
    - -

    不用过多解释, 已经很清晰了, FastD2I 就是 Fast Double to Integer的意思, 定义如下, 注释也很详尽:

    -
    // The fast double-to-(unsigned-)int conversion routine does not guarantee
    // rounding towards zero.
    // The result is undefined if x is infinite or NaN, or if the rounded
    // integer value is outside the range of type int.
    inline int FastD2I(double x) {
    DCHECK(x <= INT_MAX);
    DCHECK(x >= INT_MIN);
    return static_cast<int32_t>(x);
    }
    - -

    以上

    ]]>
    Technique -- cgit v1.2.3