From 17e9059a904c1d9f58063aace4cc4feed4dfc5b7 Mon Sep 17 00:00:00 2001 From: muqiuhan Date: Tue, 29 Jul 2025 10:25:18 +0000 Subject: deploy: 255c40eb85d0b1877949bfcaf62465e82abf2b06 --- search.xml | 820 ++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 433 insertions(+), 387 deletions(-) (limited to 'search.xml') diff --git a/search.xml b/search.xml index 934e09b2..73ad7b08 100644 --- a/search.xml +++ b/search.xml @@ -656,6 +656,21 @@ Technique + + Fever of Unknown Origin + /2023/11/16/Fever-of-Unknown-Origin/ + Fever of unknown origin (FUO) has been recognized as a disease state for over100 years.
The first commonly accepted definition of FUO was published in 1961 and included fever greater than 101F (38.3C) on multiple occasions at least 3 week duration of fever without identifiable cause despite at least a 1 week inpatient evaluation.

+

Overtime, this definition was considered both too rigid and too ill defined.
Given the impracticality of prolonged hospitalizations and advancing technology for diagnosis, the duration of work up was shortened to 3 days inpatient evaluation and/or 3 outpatient clinic visits.

+ +

这是FUO(不明原因发烧)的综合指南,第一个普遍接受的 FUO 定义是1961 年发布的,包括多次发烧超过三十八度三,发烧持续时间至少 3 周,并且就算住院至少 1 周但仍无明确原因。后面这个定义被认为过于僵化且过于模糊。又改成了三天住院评估和/或三次门。除了有关评估持续时间的所有时间要求,还应该将 FUO 定义为完成最低限度检查后缺乏已知病因的发烧。

+

常见原因就感染性,自身免疫和结缔组织疾病,其他炎症过程和恶性肿瘤。结缔组织疾病和自身免疫性疾病是青少年 FUO 比儿童更常见的原因。
总体上有超过两百种病因可导致 FUO

+]]>
+ + Medicine + +
Functional Reactive Programming in F# /2024/09/12/Functional-Reactive-Programming-in-F/ @@ -782,21 +797,6 @@ Archive - - Fever of Unknown Origin - /2023/11/16/Fever-of-Unknown-Origin/ - Fever of unknown origin (FUO) has been recognized as a disease state for over100 years.
The first commonly accepted definition of FUO was published in 1961 and included fever greater than 101F (38.3C) on multiple occasions at least 3 week duration of fever without identifiable cause despite at least a 1 week inpatient evaluation.

-

Overtime, this definition was considered both too rigid and too ill defined.
Given the impracticality of prolonged hospitalizations and advancing technology for diagnosis, the duration of work up was shortened to 3 days inpatient evaluation and/or 3 outpatient clinic visits.

- -

这是FUO(不明原因发烧)的综合指南,第一个普遍接受的 FUO 定义是1961 年发布的,包括多次发烧超过三十八度三,发烧持续时间至少 3 周,并且就算住院至少 1 周但仍无明确原因。后面这个定义被认为过于僵化且过于模糊。又改成了三天住院评估和/或三次门。除了有关评估持续时间的所有时间要求,还应该将 FUO 定义为完成最低限度检查后缺乏已知病因的发烧。

-

常见原因就感染性,自身免疫和结缔组织疾病,其他炎症过程和恶性肿瘤。结缔组织疾病和自身免疫性疾病是青少年 FUO 比儿童更常见的原因。
总体上有超过两百种病因可导致 FUO

-]]>
- - Medicine - -
G-Machine /2024/01/23/G-Machine/ @@ -913,6 +913,20 @@ 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 @@ -952,20 +966,6 @@ 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 @@ -1408,6 +1408,102 @@ await prisma.$transaction(async tx => {
  • Github - robur-coop/orb: check opam package reproductibility.
  • Github - ocaml-multicore/dscheck: Experimental model checker for testing concurrent algorithms.
  • +]]> + + Technique + +
    + + OCaml News 2024-6 + /2024/07/23/OCaml-News-2024-6/ +
           ^o3
    ~/\_/\_|)
    |/=_=\|
    " "
    + +

    语言的发展

    +

    新消息

    +

    有价值的文章

    +

    有趣的项目

    ]]>
    Technique @@ -1555,102 +1651,6 @@ await prisma.$transaction(async tx => {
  • GitHub - ocaml-community/ocaml-mariadb: OCaml bindings to MariaDB, supporting the nonblocking API
  • Monolith (monolith.Monolith): Monolith offers facilities for testing an OCaml library by comparing its implementation (known as the candidate implementation) against a reference implementation.
  • -]]> - - Technique - -
    - - OCaml News 2024-6 - /2024/07/23/OCaml-News-2024-6/ -
           ^o3
    ~/\_/\_|)
    |/=_=\|
    " "
    - -

    语言的发展

    -

    新消息

    -

    有价值的文章

    -

    有趣的项目

    ]]>
    Technique @@ -1838,35 +1838,14 @@ await prisma.$transaction(async tx => {

    而这似乎并没有好的解决方案,理想中的实现应该是:

    module Styled = (Component: {
    type params // or type params = 'a
    let make: React.component<props>
    }) => {
    type props = {
    ...Component.props, // This can't work as it is not defined as record
    className?: string
    }
    @module("nativewind")
    external styled: React.component<Component.props> => React.component<props> = "styled"

    let make: React.component<props> = styled(Component.make)
    }
    -

    或者退一步:

    -
    type styledProps = {
    className?: string,
    // ...
    }

    @module("nativewind")
    external styled: React.component<'a> => React.component<'b> = "styled"

    module StyledText = {
    type props = {
    ...ReactNative.Text.props,
    ...styledProps
    }

    let make: React.component<props> = styled(ReactNative.Text.make)
    }
    - -

    但这在当前的 rescript 中,根本无法实现。

    -

    详细的讨论看这个帖子:

    - -]]> - - Technique - -
    - - Rust NewType 模式 - /2023/05/03/Rust-NewType-%E6%A8%A1%E5%BC%8F/ - New Type模式是一种软件设计模式,用于在已有类型的基础上创建一个新的类型。在Rust中,这通常是通过定义一个结构体,其中只包含一个单一成员。这个结构体(New Type)对外提供了一个新的、独立的类型,用于对原始类型增加额外的语义或限制。

    -

    加强类型安全

    struct Meters(f64);
    struct Feet(f64);

    let length_in_meters = Meters(100.0);
    let length_in_feet = Feet(328.084);

    // 编译器会防止以下代码执行,因为类型不匹配
    // let wrong_length = Meters(length_in_feet); // 编译错误

    // 正确的构造
    fn add_lengths(length1: Meters, length2: Meters) -> Meters {
    Meters(length1.0 + length2.0)
    }
    - -

    这个例子使用 newtype 模式避免将原始类型f64用于不同的量度,从而增强了类型的安全性。

    -

    实现特定 trait

    struct Kilometers(f64);

    impl Kilometers {
    fn to_miles(&self) -> f64 {
    self.0 * 0.621371
    }
    }

    let distance = Kilometers(10.0);
    println!("The distance in miles is {}", distance.to_miles());
    - -

    这里,Kilometers有一个方法to_miles,该方法是不会影响其他f64数据的。如果我们有另一个表示温度的f64类型,就不会意外调用到与距离相关的方法。

    -

    New Type模式同样适用于对Box<dyn SomeTrait>类型的包装,这可以在需要动态分派(动态调用实现了某个接口的不同类型的对象的方法)的时候提供便利。通过创建一个New Type来包装这样的Box<dyn SomeTrait>类型,可以提供自定义的方法或实现更多的trait,同时也可以让API更加清晰和易于使用。

    -

    零成本抽象

    在Rust中,New Type模式不仅是类型安全的,还是一种零成本抽象。这是因为Rust编译器在编译时期会进行足够的优化,以确保New Type的使用没有运行时开销。 Rust的零成本抽象原则确保了抽象不会引入额外的运行时成本。例如,当你使用Meters这样的New Type时,Rust确保:

    -
      -
    1. 无额外内存开销:Meters只包含一个f64,在内存中的表现和单独的f64是一样的。
    2. -
    3. 无额外运行时开销:使用Meters时,性能和直接使用f64完全相同。编译器会移除任何关于New Type的包装和解包的代码。
    4. -
    +

    或者退一步:

    +
    type styledProps = {
    className?: string,
    // ...
    }

    @module("nativewind")
    external styled: React.component<'a> => React.component<'b> = "styled"

    module StyledText = {
    type props = {
    ...ReactNative.Text.props,
    ...styledProps
    }

    let make: React.component<props> = styled(ReactNative.Text.make)
    }
    + +

    但这在当前的 rescript 中,根本无法实现。

    +

    详细的讨论看这个帖子:

    + ]]>
    Technique @@ -1894,6 +1873,27 @@ await prisma.$transaction(async tx => {

    现在生成的 TypeScript 文件将会从 Js.shim.ts import 类型:

    import type {Json_t as Js_Json_t} from '../../src/model/Js.shim.ts';
    +]]> + + Technique + +
    + + Rust NewType 模式 + /2023/05/03/Rust-NewType-%E6%A8%A1%E5%BC%8F/ + New Type模式是一种软件设计模式,用于在已有类型的基础上创建一个新的类型。在Rust中,这通常是通过定义一个结构体,其中只包含一个单一成员。这个结构体(New Type)对外提供了一个新的、独立的类型,用于对原始类型增加额外的语义或限制。

    +

    加强类型安全

    struct Meters(f64);
    struct Feet(f64);

    let length_in_meters = Meters(100.0);
    let length_in_feet = Feet(328.084);

    // 编译器会防止以下代码执行,因为类型不匹配
    // let wrong_length = Meters(length_in_feet); // 编译错误

    // 正确的构造
    fn add_lengths(length1: Meters, length2: Meters) -> Meters {
    Meters(length1.0 + length2.0)
    }
    + +

    这个例子使用 newtype 模式避免将原始类型f64用于不同的量度,从而增强了类型的安全性。

    +

    实现特定 trait

    struct Kilometers(f64);

    impl Kilometers {
    fn to_miles(&self) -> f64 {
    self.0 * 0.621371
    }
    }

    let distance = Kilometers(10.0);
    println!("The distance in miles is {}", distance.to_miles());
    + +

    这里,Kilometers有一个方法to_miles,该方法是不会影响其他f64数据的。如果我们有另一个表示温度的f64类型,就不会意外调用到与距离相关的方法。

    +

    New Type模式同样适用于对Box<dyn SomeTrait>类型的包装,这可以在需要动态分派(动态调用实现了某个接口的不同类型的对象的方法)的时候提供便利。通过创建一个New Type来包装这样的Box<dyn SomeTrait>类型,可以提供自定义的方法或实现更多的trait,同时也可以让API更加清晰和易于使用。

    +

    零成本抽象

    在Rust中,New Type模式不仅是类型安全的,还是一种零成本抽象。这是因为Rust编译器在编译时期会进行足够的优化,以确保New Type的使用没有运行时开销。 Rust的零成本抽象原则确保了抽象不会引入额外的运行时成本。例如,当你使用Meters这样的New Type时,Rust确保:

    +
      +
    1. 无额外内存开销:Meters只包含一个f64,在内存中的表现和单独的f64是一样的。
    2. +
    3. 无额外运行时开销:使用Meters时,性能和直接使用f64完全相同。编译器会移除任何关于New Type的包装和解包的代码。
    4. +
    ]]>
    Technique @@ -2447,30 +2447,6 @@ await prisma.$transaction(async tx => { Technique
    - - caml_release_runtime_system导致的死锁问题 - /2023/06/28/caml-release-runtime-system%E5%AF%BC%E8%87%B4%E7%9A%84%E6%AD%BB%E9%94%81%E9%97%AE%E9%A2%98/ - 给出一个test.ml:

    -
    Printf.eprintf "hello from OCaml\n%!"
    - -

    和test-ocaml-5.c:

    -
    #include <stdio.h>
    #include <stdlib.h>
    #include <caml/misc.h>
    #include <caml/callback.h>
    #include <caml/threads.h>

    int
    main (int argc, char *argv[])
    {
    fprintf (stderr, "starting up ...\n");
    caml_startup (argv);
    fprintf (stderr, "acquiring ...\n");
    caml_acquire_runtime_system ();
    fprintf (stderr, "acquired\n");
    // here is where I would be calling an OCaml callback
    // omitted for simplicity
    caml_release_runtime_system ();
    exit (0);
    }
    - -

    然后用OCaml5的ocaml native compiler编译一下(这里我用的是ocaml-variants.5.0.0+options):
    ocamlopt -g test-ocaml-5.c test.ml -o test-ocaml-5

    -

    运行test-ocaml-5 会出现:

    -
    starting up ...
    hello from OCaml
    acquiring ...
    Fatal error: Fatal error during lock: Resource deadlock avoided

    [1] 8998 IOT instruction (core dumped) ./test-ocaml-5
    - -

    这里我尝试了一下 4.14.0 和 4.14.1 , 都没有出现这个情况,而如果用threads编译的话:
    ocamlopt -g -I +unix unix.cmxa -I +threads threads.cmxa test-ocaml-5.c test.ml -o test-ocaml-5

    -

    无论在5.0还是4.14.x,都会挂起。

    -

    出现这个问题的一个可能原因是,在test-ocaml-5.c中,我在开头调用了caml_startup(),这会让当前线程获取锁,而caml_acquire_runtime_system()会再次获取它,caml_acquire_runtime_system() 应该在caml_release_runtime_system()之后调用:

    -
    int
    main (int argc, char *argv[])
    {
    fprintf (stderr, "starting up ...\n");
    caml_startup (argv);
    fprintf (stderr, "acquiring ...\n");
    caml_release_runtime_system ();
    caml_acquire_runtime_system ();
    fprintf (stderr, "acquired\n");
    exit (0);
    }
    - -

    运行结果为:

    -
    starting up ...
    hello from OCaml
    acquiring ...
    acquired
    ]]>
    - - Technique - -
    WiscKey: Separating Keys from Values in SSD-conscious Storage /2023/07/21/WiscKey-Separating-Keys-from-Values-in-SSD-conscious-Storage/ @@ -2509,6 +2485,30 @@ await prisma.$transaction(async tx => { Technique + + caml_release_runtime_system导致的死锁问题 + /2023/06/28/caml-release-runtime-system%E5%AF%BC%E8%87%B4%E7%9A%84%E6%AD%BB%E9%94%81%E9%97%AE%E9%A2%98/ + 给出一个test.ml:

    +
    Printf.eprintf "hello from OCaml\n%!"
    + +

    和test-ocaml-5.c:

    +
    #include <stdio.h>
    #include <stdlib.h>
    #include <caml/misc.h>
    #include <caml/callback.h>
    #include <caml/threads.h>

    int
    main (int argc, char *argv[])
    {
    fprintf (stderr, "starting up ...\n");
    caml_startup (argv);
    fprintf (stderr, "acquiring ...\n");
    caml_acquire_runtime_system ();
    fprintf (stderr, "acquired\n");
    // here is where I would be calling an OCaml callback
    // omitted for simplicity
    caml_release_runtime_system ();
    exit (0);
    }
    + +

    然后用OCaml5的ocaml native compiler编译一下(这里我用的是ocaml-variants.5.0.0+options):
    ocamlopt -g test-ocaml-5.c test.ml -o test-ocaml-5

    +

    运行test-ocaml-5 会出现:

    +
    starting up ...
    hello from OCaml
    acquiring ...
    Fatal error: Fatal error during lock: Resource deadlock avoided

    [1] 8998 IOT instruction (core dumped) ./test-ocaml-5
    + +

    这里我尝试了一下 4.14.0 和 4.14.1 , 都没有出现这个情况,而如果用threads编译的话:
    ocamlopt -g -I +unix unix.cmxa -I +threads threads.cmxa test-ocaml-5.c test.ml -o test-ocaml-5

    +

    无论在5.0还是4.14.x,都会挂起。

    +

    出现这个问题的一个可能原因是,在test-ocaml-5.c中,我在开头调用了caml_startup(),这会让当前线程获取锁,而caml_acquire_runtime_system()会再次获取它,caml_acquire_runtime_system() 应该在caml_release_runtime_system()之后调用:

    +
    int
    main (int argc, char *argv[])
    {
    fprintf (stderr, "starting up ...\n");
    caml_startup (argv);
    fprintf (stderr, "acquiring ...\n");
    caml_release_runtime_system ();
    caml_acquire_runtime_system ();
    fprintf (stderr, "acquired\n");
    exit (0);
    }
    + +

    运行结果为:

    +
    starting up ...
    hello from OCaml
    acquiring ...
    acquired
    ]]>
    + + Technique + +
    DADGAD 定弦 /2025/07/08/dadgad/ @@ -2684,21 +2684,6 @@ await prisma.$transaction(async tx => {
    options quiet splash amdgpu.dcdebugmask=0x10 amdgpu.sg_display=0

    对于其他使用 EFI 或 GRUB 启动的发行版也可以添加类似的参数尝试尝试。

    -]]> - - Technique - -
    - - 使用 [@poll error] 实现线程安全的数据结构 - /2023/08/15/poll-error-attribute-in-OCaml/ - OCaml的标准库提供了许多mutable的数据结构,比如Hashtbl, Queue, Stack之类的,但是这些数据结构都不是线程安全的。在 OCaml 4 和 OCaml 5 中,单个Domain中一次只能运行一个线程。换句话说,单个Domain中的线程仍然不会并行运行,除非在不同所的Domain中。

    -

    而在单个Domain中 OCaml 的 runtime 是通过在safe point期间半抢占式的切换线程。也就是说,线程切换只会发生在safe point期间。例如内存分配就是是safe point。这意味着在没有safe point的代码块内,可以在Domain内原子性地进行多次读写或访问操作,因为线程没被切换。

    -

    OCaml 编译器提供了一个名为 [@poll error] 的annotation,可以在函数中使用它来确保该函数不包含safe point。

    -

    所以通过使用 [@poll error] 就可以创建在Domain内原子性执行的函数,也就是说,基于此特性可以实现单个Domain内线程安全的数据结构,例如 thread-table 便是使用这个特性实现的 Hash Table。可以看看它的 add 函数的实现:

    -
    let[@poll error] add_atomically t buckets n i before after =
    t.rehash = 0 && buckets == t.buckets
    && before == Array.unsafe_get buckets i
    && begin
    Array.unsafe_set buckets i after;
    let length = t.length + 1 in
    t.length <- length;
    if n < length && n < max_buckets_div_2 then t.rehash <- n * 2;
    true
    end

    let rec add t k' v' =
    let h = Mix.int k' in
    maybe_rehash t;
    let buckets = t.buckets in
    let n = Array.length buckets in
    let i = h land (n - 1) in
    let before = Array.unsafe_get buckets i in
    let after = Cons (k', v', before) in
    if not (add_atomically t buckets n i before after) then add t k' v'
    - -

    相比使用 Stdlib.Mutex,这种无锁实现会有更好的性能(特别是对于只读操作),并且还允许例如信号处理之类的上下文操作。

    ]]>
    Technique @@ -2750,6 +2735,67 @@ await prisma.$transaction(async tx => {
  • “Retrying failing jobs” · BullMQ Guide
    https://docs.bullmq.io/guide/retrying-failing-jobs
  • BullMQ Guide & Patterns · Process Step Jobs (completed event only fires after process resolves)
    https://docs.bullmq.io/patterns/process-step-jobs
  • +]]> + + Technique + +
    + + NestJS 嵌套查询参数解析错误分析与解决方案 + /2025/07/29/nestjs-nested-object-dto/ + 在开发 NestJS 应用程序时,一个常见的场景是根据复杂的条件筛选资源。当使用嵌套查询参数来表达这些条件时,例如通过 URL http://localhost:3000/products/filter?filters[price][min]=100,可能会意外地遇到 property filters[price][min] should not exist 错误。本文将深入剖析此问题背后的原因,并提供针对 Express 和 Fastify 两种底层框架的解决方案。

    +

    问题背景

    假设我们正在构建一个电子商务平台的后端,需要实现一个商品筛选接口 /products/filter。该接口应允许客户端根据不同的属性(如价格 price、库存 stock)进行筛选,并支持对数值型属性(如价格)指定一个范围。

    +

    为了实现这一功能,我们定义了以下 NestJS 数据传输对象 (DTO) 结构:

    +
    import { ApiPropertyOptional } from "@nestjs/swagger";
    import {
    IsNumber,
    IsOptional,
    Min,
    ValidateNested,
    } from "class-validator";
    import { Type } from "class-transformer";

    class RangeFilterDto {
    @ApiPropertyOptional({ description: "最小值 (大于等于)" })
    @IsOptional()
    @Type(() => Number)
    @IsNumber()
    @Min(0)
    min?: number;

    @ApiPropertyOptional({ description: "最大值 (小于等于)" })
    @IsOptional()
    @Type(() => Number)
    @IsNumber()
    @Min(0)
    max?: number;
    }

    class ProductAttributeFilterDto {
    @ApiPropertyOptional({
    description: "价格范围",
    type: RangeFilterDto,
    })
    @IsOptional()
    @ValidateNested()
    @Type(() => RangeFilterDto)
    price?: RangeFilterDto;
    }

    export class ProductFilterDto {
    @ApiPropertyOptional({ description: "商品属性筛选条件" })
    @IsOptional()
    @ValidateNested()
    @Type(() => ProductAttributeFilterDto)
    filters?: ProductAttributeFilterDto;
    }
    + +

    对应的控制器代码如下:

    +
    import { Controller, Get, Query } from '@nestjs/common';
    import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
    import { ProductFilterDto } from './dtos/product-filter.dto';

    @ApiTags('products')
    @Controller('products')
    export class ProductsController {
    @Get("filter")
    @ApiOperation({ summary: "根据条件筛选商品" })
    @ApiResponse({ status: 200, description: "筛选商品成功" })
    @ApiResponse({ status: 400, description: "请求参数错误" })
    async getFilteredProducts(
    @Query() query: ProductFilterDto,
    ): Promise<any> {
    // 在实际应用中,这里会调用服务处理筛选逻辑
    console.log(query);
    return {
    message: "Products filtered successfully.",
    data: query,
    };
    }
    }
    + +

    当客户端通过以下 URL 发送请求时:

    +

    http://localhost:3000/products/filter?filters[price][min]=100

    +

    后端返回了 HTTP 400 错误,并附带以下日志:

    +
    [Nest] 12345 - 07/29/2025, 11:00:00 AM   ERROR [HttpExceptionFilter] [GET /products/filter?filters%5Bprice%5D%5Bmin%5D=100] HTTP 400 Error: property filters[price][min] should not exist
    BadRequestException: Bad Request Exception
    at ValidationPipe.exceptionFactory (/path/to/project/node_modules/@nestjs/common/pipes/validation.pipe.js:107:20)
    at ValidationPipe.transform (/path/to/project/node_modules/@nestjs/common/pipes/validation.pipe.js:74:30)
    ... (其他堆栈信息)
    + +

    错误分析

    此错误的核心在于 NestJS 的 @Query() 装饰器与 ValidationPipe 在处理 URL 查询字符串时的默认行为,未能正确地将客户端提供的嵌套方括号语法 (filters[price][min]) 解析为 DTO 所期望的 JavaScript 嵌套对象结构。

    +
      +
    1. DTO 的预期结构:
      ProductFilterDto 定义了 filters 属性,其类型为 ProductAttributeFilterDto。ProductAttributeFilterDto 又包含了 price 属性,类型为 RangeFilterDto,最终含有 min 和 max 属性。这意味着 DTO 期望接收的数据结构应为:{ filters: { price: { min: 100 } } }。

      +
    2. +
    3. URL 查询参数的扁平化解析:
      HTTP 协议的查询字符串本质上是扁平的键值对集合。虽然 key[nestedKey]=value 这种方括号语法被许多 Web 框架(如 PHP、Ruby on Rails)和库(如 qs)广泛用于表示嵌套数据,但这并非 HTTP 协议的标准。
      NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的请求。在默认配置下,Express 不会自动地、递归地将 filters[price][min] 这样的字符串键名解析成一个具有正确嵌套层级的 JavaScript 对象。相反,它会将其视为一个完整的、扁平的字符串键名 filters[price][min],并将其值 100 与之对应。

      +
    4. +
    5. ValidationPipe 的验证失败:
      当 ValidationPipe 接收到被扁平化解析后的查询参数时,它会在 ProductFilterDto 中寻找一个名为 filters[price][min] 的顶层属性。由于 DTO 中并未直接定义这样一个扁平的属性,并且 ValidationPipe 通常会启用 forbidNonWhitelisted: true 或 whitelist: true 选项来拒绝 DTO 中未明确声明的属性,因此验证过程失败,并抛出 property filters[price][min] should not exist 的错误。这表明 ValidationPipe 将 filters[price][min] 视为一个非法的、未在白名单中的属性,而不是预期的嵌套对象 filters 下的深层子属性。

      +
    6. +
    +

    简而言之,问题不在于 DTO 定义或 @Query() 装饰器本身,而在于底层 HTTP 框架对查询字符串的默认解析行为与 NestJS ValidationPipe 对复杂嵌套 DTO 结构的需求不匹配。

    +

    解决方案

    要解决此问题,关键在于配置 NestJS 应用的底层 HTTP 适配器,使其能够正确地解析包含方括号语法的嵌套查询参数,将其转换为嵌套的 JavaScript 对象。

    +
    针对 Express 适配器

    如果您的 NestJS 应用使用 Express 作为底层 HTTP 框架(这是新项目的默认配置),您需要在应用的入口文件(通常是 main.ts)中,通过 app.set('query parser', 'extended'); 显式启用 Express 的扩展查询字符串解析功能。此配置会指示 Express 使用 qs 库(Express 内置依赖)来处理查询字符串,该库能够正确处理嵌套结构。

    +

    示例 (main.ts):

    +
    import { NestFactory } from '@nestjs/core';
    import { AppModule } from './app.module';
    import { ValidationPipe } from '@nestjs/common';
    import { NestExpressApplication } from '@nestjs/platform-express'; // 导入此类型

    async function bootstrap() {
    const app = await NestFactory.create<NestExpressApplication>(AppModule);

    // 启用 Express 的扩展查询参数解析
    // 'simple' (默认) | 'extended'
    app.set('query parser', 'extended');

    app.useGlobalPipes(new ValidationPipe({
    whitelist: true, // 自动剥离 DTO 中未定义的属性
    forbidNonWhitelisted: true, // 如果传入 DTO 中未定义的属性,则抛出错误
    transform: true, // 自动将负载转换为 DTO 类的实例
    transformOptions: {
    enableImplicitConversion: true, // 允许隐式类型转换,配合 @Type()
    },
    }));

    // ... 其他配置
    await app.listen(3000);
    }
    bootstrap();
    + +
    针对 Fastify 适配器

    如果您的 NestJS 应用使用 Fastify 作为底层 HTTP 框架,您需要在创建 Fastify 适配器实例时,通过 querystringParser 选项提供一个自定义的查询字符串解析函数。通常,我们会利用 qs 这样的成熟库来完成递归解析。

    +

    首先,确保已安装 qs 库及其类型定义:
    npm install qs
    npm install -D @types/qs

    +

    示例 (main.ts):

    +
    import { NestFactory } from '@nestjs/core';
    import { AppModule } from './app.module';
    import { ValidationPipe } from '@nestjs/common';
    import {
    FastifyAdapter,
    NestFastifyApplication,
    } from '@nestjs/platform-fastify';
    import * as qs from 'qs'; // 引入 qs 库

    async function bootstrap() {
    const app = await NestFactory.create<NestFastifyApplication>(
    AppModule,
    new FastifyAdapter({
    // 配置 Fastify 的查询字符串解析器
    querystringParser: (str) => qs.parse(str),
    }),
    );

    app.useGlobalPipes(new ValidationPipe({
    whitelist: true,
    forbidNonWhitelisted: true,
    transform: true,
    transformOptions: {
    enableImplicitConversion: true,
    },
    }));

    // ... 其他配置
    await app.listen(3000);
    }
    bootstrap();
    + +

    通过上述配置,无论是使用 Express 还是 Fastify,NestJS 应用都将能够正确地将 ?filters[price][min]=100 解析为 { filters: { price: { min: '100' } } }。随后,在 ValidationPipe 的 transform 阶段,@Type(() => Number) 装饰器会确保 min 的值从字符串 '100' 转换为数字 100,从而顺利通过验证并注入到控制器方法中。

    +

    引用

    +]]>
    + + Technique + +
    + + 使用 [@poll error] 实现线程安全的数据结构 + /2023/08/15/poll-error-attribute-in-OCaml/ + OCaml的标准库提供了许多mutable的数据结构,比如Hashtbl, Queue, Stack之类的,但是这些数据结构都不是线程安全的。在 OCaml 4 和 OCaml 5 中,单个Domain中一次只能运行一个线程。换句话说,单个Domain中的线程仍然不会并行运行,除非在不同所的Domain中。

    +

    而在单个Domain中 OCaml 的 runtime 是通过在safe point期间半抢占式的切换线程。也就是说,线程切换只会发生在safe point期间。例如内存分配就是是safe point。这意味着在没有safe point的代码块内,可以在Domain内原子性地进行多次读写或访问操作,因为线程没被切换。

    +

    OCaml 编译器提供了一个名为 [@poll error] 的annotation,可以在函数中使用它来确保该函数不包含safe point。

    +

    所以通过使用 [@poll error] 就可以创建在Domain内原子性执行的函数,也就是说,基于此特性可以实现单个Domain内线程安全的数据结构,例如 thread-table 便是使用这个特性实现的 Hash Table。可以看看它的 add 函数的实现:

    +
    let[@poll error] add_atomically t buckets n i before after =
    t.rehash = 0 && buckets == t.buckets
    && before == Array.unsafe_get buckets i
    && begin
    Array.unsafe_set buckets i after;
    let length = t.length + 1 in
    t.length <- length;
    if n < length && n < max_buckets_div_2 then t.rehash <- n * 2;
    true
    end

    let rec add t k' v' =
    let h = Mix.int k' in
    maybe_rehash t;
    let buckets = t.buckets in
    let n = Array.length buckets in
    let i = h land (n - 1) in
    let before = Array.unsafe_get buckets i in
    let after = Cons (k', v', before) in
    if not (add_atomically t buckets n i before after) then add t k' v'
    + +

    相比使用 Stdlib.Mutex,这种无锁实现会有更好的性能(特别是对于只读操作),并且还允许例如信号处理之类的上下文操作。

    ]]>
    Technique @@ -2816,40 +2862,6 @@ await prisma.$transaction(async tx => {
    /* The tick thread: posts a SIGPREEMPTION signal periodically */

    static void * caml_thread_tick(void * arg)
    {
    struct timeval timeout;
    sigset_t mask;

    /* Block all signals so that we don't try to execute an OCaml signal handler*/
    sigfillset(&mask);
    pthread_sigmask(SIG_BLOCK, &mask, NULL);
    while(! caml_tick_thread_stop) {
    /* select() seems to be the most efficient way to suspend the
    thread for sub-second intervals */
    timeout.tv_sec = 0;
    timeout.tv_usec = Thread_timeout * 1000;
    select(0, NULL, NULL, NULL, &timeout);
    /* The preemption signal should never cause a callback, so don't
    go through caml_handle_signal(), just record signal delivery via
    caml_record_signal(). */
    caml_record_signal(SIGPREEMPTION);
    }
    return NULL;
    }

    这是因为Multicore OCaml的GC目前需要一个进程(或一个Domain)中的所有线程一起参与以避免并发访问。如果一个线程在system call上被阻塞,那么整个Domain就会被卡住,直到该线程可以参与当前的垃圾收集。为了避免这个问题,tick 线程可以代替被阻塞的线程执行垃圾收集操作。

    -]]> - - 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 @@ -2922,6 +2934,40 @@ 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 @@ -2950,6 +2996,18 @@ await prisma.$transaction(async tx => {

    一切重新沉寂下来。

    +]]> + + gallery + +
    + + 二〇二三年六月二十九日 + /2023/06/29/%E4%BA%8C%E3%80%87%E4%BA%8C%E4%B8%89%E5%B9%B4%E5%85%AD%E6%9C%88%E4%BA%8C%E5%8D%81%E4%B9%9D%E6%97%A5/ + 我能走遍大街小巷,但我走不出时间,这里有太多东西网住我了,每一个不知所措的老人家,每一个满身泥土的农民,每一只嬉戏打闹的猫狗和每一寸土地下的家人。

    +

    我知道,能明确感受到,远处的风力发电机每转一圈我对这里的牵挂就更深一点,太阳每次升到我头顶都会把我的身体压进泥土里一点。

    +

    夕阳下的街头空无一人,窗户间的缝隙吹来寒风,隔壁回锅肉的香味混在其中,困顿中还有美好的可能,所以别让自己太过消沉。

    +

    所以别让自己太过消沉。

    ]]>
    gallery @@ -2965,18 +3023,6 @@ await prisma.$transaction(async tx => {

    这世上也不是所有事都算得准的。

    云仰望着太阳,羡慕他的炙热,或许太阳也在仰望着云,渴求他的停留。

    地球距离太阳1.5亿公里,如无意外,两者此生不会相遇,但从此以后,我与世间美好再也不会分离。

    -]]> - - gallery - -
    - - 二〇二三年六月二十九日 - /2023/06/29/%E4%BA%8C%E3%80%87%E4%BA%8C%E4%B8%89%E5%B9%B4%E5%85%AD%E6%9C%88%E4%BA%8C%E5%8D%81%E4%B9%9D%E6%97%A5/ - 我能走遍大街小巷,但我走不出时间,这里有太多东西网住我了,每一个不知所措的老人家,每一个满身泥土的农民,每一只嬉戏打闹的猫狗和每一寸土地下的家人。

    -

    我知道,能明确感受到,远处的风力发电机每转一圈我对这里的牵挂就更深一点,太阳每次升到我头顶都会把我的身体压进泥土里一点。

    -

    夕阳下的街头空无一人,窗户间的缝隙吹来寒风,隔壁回锅肉的香味混在其中,困顿中还有美好的可能,所以别让自己太过消沉。

    -

    所以别让自己太过消沉。

    ]]>
    gallery @@ -3084,6 +3130,18 @@ await prisma.$transaction(async tx => {

    那在这之前呢?

    在这之前我刚吃完一碗辣椒炒肉,

    香

    +]]> + + gallery + +
    + + 二〇二五年二月二十四日 + /2025/02/24/%E4%BA%8C%E3%80%87%E4%BA%8C%E4%BA%94%E5%B9%B4%E4%BA%8C%E6%9C%88%E4%BA%8C%E5%8D%81%E5%9B%9B%E6%97%A5/ + 街上的人躲进建筑,道路上的车变得缓慢。路边的黑猫坐在屋檐下张望,在雨天,它也有了思考的地方与时间。除了淅淅沥沥的雨声再无其他。

    +

    衣服买大了,他们走路好急,地铁上只有老人没看手机,天怎么黑那么快是我起晚了吗,那个蹦蹦跳跳的女孩耳机里在听什么歌,我还在原地吗,我要再等等吗。

    +

    家就像厚酒之后突然明晰的想象,时间不语,只是沉默,我沉默地走进时间,看着我和你的年龄相差越来越小。

    +

    这雨天,世界都温柔的可以掐出水来。

    ]]>
    gallery @@ -3098,18 +3156,6 @@ await prisma.$transaction(async tx => {

    时间带走了一个人,带走了一个由无数粒子组成的,短暂观察了这个宇宙一瞬的意识,它们会在时间中无数次聚散,再无数次见这灿烂的一切。

    梦里明明有六趣,觉后空空无大千,

    又是一个难捱的冬天,为了脚不被冻住,我们只能起舞。

    -]]> - - gallery - -
    - - 二〇二五年二月二十四日 - /2025/02/24/%E4%BA%8C%E3%80%87%E4%BA%8C%E4%BA%94%E5%B9%B4%E4%BA%8C%E6%9C%88%E4%BA%8C%E5%8D%81%E5%9B%9B%E6%97%A5/ - 街上的人躲进建筑,道路上的车变得缓慢。路边的黑猫坐在屋檐下张望,在雨天,它也有了思考的地方与时间。除了淅淅沥沥的雨声再无其他。

    -

    衣服买大了,他们走路好急,地铁上只有老人没看手机,天怎么黑那么快是我起晚了吗,那个蹦蹦跳跳的女孩耳机里在听什么歌,我还在原地吗,我要再等等吗。

    -

    家就像厚酒之后突然明晰的想象,时间不语,只是沉默,我沉默地走进时间,看着我和你的年龄相差越来越小。

    -

    这雨天,世界都温柔的可以掐出水来。

    ]]>
    gallery @@ -3294,22 +3340,13 @@ await prisma.$transaction(async tx => { gallery -
    - - 二〇二四年五月七日 - /2024/05/07/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%BA%94%E6%9C%88%E4%B8%83%E6%97%A5/ - 我在写信
    寄给羊水
    信里提到
    宇宙称呼它为灰
    我们叫地球
    还提到
    风车不能骑
    但是石头可以打水漂
    你呀
    到时别忘了
    用小小的哭声款待我

    -
    -

    我们应该坐在一起发呆
    发很久的呆
    然后我说人类好无聊啊
    这个地球完蛋了
    你点点头

    -]]>
    - - gallery - -
    - - 二〇二四年八月一日 - /2024/08/02/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E5%85%AB%E6%9C%88%E4%B8%80%E6%97%A5/ - 阿公烧烟成老瘾,
    阿公饮酒会面红,
    他说日子要开心,
    他说人生要尽兴。

    +
    + + 二〇二四年五月七日 + /2024/05/07/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%BA%94%E6%9C%88%E4%B8%83%E6%97%A5/ + 我在写信
    寄给羊水
    信里提到
    宇宙称呼它为灰
    我们叫地球
    还提到
    风车不能骑
    但是石头可以打水漂
    你呀
    到时别忘了
    用小小的哭声款待我

    +
    +

    我们应该坐在一起发呆
    发很久的呆
    然后我说人类好无聊啊
    这个地球完蛋了
    你点点头

    ]]>
    gallery @@ -3330,6 +3367,15 @@ await prisma.$transaction(async tx => {

    当有了一个目标,需要的不是豪言壮志,不是任何心理建设,而是尽快上手去做。
    做一件事,无关心态,只要去做就是了。发挥最大的主观能动性,遇到问题就解决,碰到障碍就破开,需要帮助就求助。别因为所谓的自信就盲目冒进,也别因为所谓的自卑就胆小退缩,那俩玩意都不存在。

    尊重事情本身的客观发展规律,什么样的人匹配什么样的事,什么样的条件匹配什么样的理想。别把心态想得太重要,更别花太多时间精力去建设心态,客观规律不会因为心态就发生奇迹般地转变。

    你最好趁早学会尊重客观规律。

    +]]> + + gallery + +
    + + 二〇二四年八月一日 + /2024/08/02/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E5%85%AB%E6%9C%88%E4%B8%80%E6%97%A5/ + 阿公烧烟成老瘾,
    阿公饮酒会面红,
    他说日子要开心,
    他说人生要尽兴。

    ]]>
    gallery @@ -3429,19 +3475,6 @@ await prisma.$transaction(async tx => { 二零二三年一月二十一日 /2023/01/21/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%B8%89%E5%B9%B4%E4%B8%80%E6%9C%88%E4%BA%8C%E5%8D%81%E4%B8%80%E6%97%A5/ 我踏着烟火若隐若现, 我生来就是为了把这个世界的消息带到下一个世界去。

    -]]>
    - - gallery - -
    - - 二零二三年九月十三日 - /2023/09/13/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%B8%89%E5%B9%B4%E4%B9%9D%E6%9C%88%E5%8D%81%E4%B8%89%E6%97%A5/ - “站好! 要拍了啊,倒数三秒,都不要眨眼。” 婚礼结束后, 门口路过的陌生男人拿着相机,充当起了临时的摄影师。

    -

    镜头里,她和抱着雪狮的希文站在中间,她手边是依次排开的五个哥哥,希文手边是暮秋, 培元, 世琪, 婧灵五人。
    在他们后排,其他人也各自找好位置挺直脊背站着。

    -

    “拍了啊,倒数,三、二、一……茄子!”

    -

    咔擦,画面定格,幸福定格,大家终于从苦难里彻底毕业了。

    -

    萤火虫的微光与星空交相辉映,黑夜中潜滋暗长的花香沁人心脾,晚风挠过波光粼粼的海面,彼此相爱的人相拥而眠。

    ]]>
    gallery @@ -3460,6 +3493,19 @@ await prisma.$transaction(async tx => {

    韩暮秋,息止安所。

    +]]> + + gallery + +
    + + 二零二三年九月十三日 + /2023/09/13/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%B8%89%E5%B9%B4%E4%B9%9D%E6%9C%88%E5%8D%81%E4%B8%89%E6%97%A5/ + “站好! 要拍了啊,倒数三秒,都不要眨眼。” 婚礼结束后, 门口路过的陌生男人拿着相机,充当起了临时的摄影师。

    +

    镜头里,她和抱着雪狮的希文站在中间,她手边是依次排开的五个哥哥,希文手边是暮秋, 培元, 世琪, 婧灵五人。
    在他们后排,其他人也各自找好位置挺直脊背站着。

    +

    “拍了啊,倒数,三、二、一……茄子!”

    +

    咔擦,画面定格,幸福定格,大家终于从苦难里彻底毕业了。

    +

    萤火虫的微光与星空交相辉映,黑夜中潜滋暗长的花香沁人心脾,晚风挠过波光粼粼的海面,彼此相爱的人相拥而眠。

    ]]>
    gallery @@ -3559,6 +3605,19 @@ await prisma.$transaction(async tx => {

    接收到的评论不必急于改变,先存起来,让它们陪着自己,跟着时间走,路上会慢慢的和其他的事情连结起来,这样就能择其善者而从之,其不善者而改之了。

    应当早日从这喧扰的世界冷静下来,让脑子里满是憧憬和情爱的灵魂得到一丝陈酿,理性点抬头看看世界上方的二氧化碳,自己晃晃头打破能回到最初的样子再重来的梦。

    爱你们。

    +]]> + + gallery + +
    + + 二零二三年十一月六日 + /2023/11/06/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%B8%89%E5%B9%B4%E5%8D%81%E4%B8%80%E6%9C%88%E5%85%AD%E6%97%A5/ + 寒风打得他踉跄地摔进田野中,他颤抖着,疑惑的抬起头,云层捂住了月亮,摇曳着露出一角烛火般的月光,烛火同他的身体一般颤抖,那是落日的余晖,也是等待朝阳的希望。

    +

    世界同可怜的孩子一起闭上了眼睛,世界不那么开心,但是

    +
    +

    这个世界,我真的爱你

    +
    ]]>
    gallery @@ -3577,19 +3636,6 @@ await prisma.$transaction(async tx => {

    岁月中的某个文明中的某个人,曾经也骑着骆驼向远方,又是哪个人,吟着佛门心语站在我身旁,我听见大漠的念经声。

    我不是个有耐心的人, 抄经书对我来说是种折磨, 我只能选择把经文抄在大地上, 黄沙上, 用自己的双腿一点点写, 用耳去倾听狂风卷积的声音。

    我生来就是为了把这个世界的消息和秘密带到下一个世界去。

    -]]> - - gallery - -
    - - 二零二三年十一月六日 - /2023/11/06/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%B8%89%E5%B9%B4%E5%8D%81%E4%B8%80%E6%9C%88%E5%85%AD%E6%97%A5/ - 寒风打得他踉跄地摔进田野中,他颤抖着,疑惑的抬起头,云层捂住了月亮,摇曳着露出一角烛火般的月光,烛火同他的身体一般颤抖,那是落日的余晖,也是等待朝阳的希望。

    -

    世界同可怜的孩子一起闭上了眼睛,世界不那么开心,但是

    -
    -

    这个世界,我真的爱你

    -
    ]]>
    gallery @@ -3667,18 +3713,18 @@ await prisma.$transaction(async tx => {
    - 二零二零年十二月九日 - /2020/12/09/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E9%9B%B6%E5%B9%B4%E5%8D%81%E4%BA%8C%E6%9C%88%E4%B9%9D%E6%97%A5/ - 我们走在朝阳的洪流上, 一步一个脚印, 踏散阳光, 向南是北。

    + 二零二二年十月一日 + /2022/10/01/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%BA%8C%E5%B9%B4%E5%8D%81%E6%9C%88%E4%B8%80%E6%97%A5/ + 巨大而又空洞的表达欲,失语后渴望把自己潦草在这个时代里。

    ]]>
    gallery
    - 二零二二年十月一日 - /2022/10/01/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%BA%8C%E5%B9%B4%E5%8D%81%E6%9C%88%E4%B8%80%E6%97%A5/ - 巨大而又空洞的表达欲,失语后渴望把自己潦草在这个时代里。

    + 二零二零年十二月九日 + /2020/12/09/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E9%9B%B6%E5%B9%B4%E5%8D%81%E4%BA%8C%E6%9C%88%E4%B9%9D%E6%97%A5/ + 我们走在朝阳的洪流上, 一步一个脚印, 踏散阳光, 向南是北。

    ]]>
    gallery @@ -3965,34 +4011,6 @@ await prisma.$transaction(async tx => {

    参考

    -]]>
    - - Medicine - -
    - - 奥司他韦 - /2023/03/05/%E5%A5%A5%E5%8F%B8%E4%BB%96%E9%9F%A6/ - 奥司他韦.jpg

    -

    目前市场上销售的达菲为罗氏制药独家生产的抗流感药物,其通用名称为磷酸奥司他韦(Oseltamivirphosphate)。奥司他韦(Oseltamivir)于1999年在瑞士上市,2001年10月在我国上市。达菲是一种非常有效的流感治疗用药,并且可以大大减少并发症(主要是气管与支气管炎、肺炎、咽炎等)的发生和抗生素的使用,因而是目前治疗流感的最常用药物之一,也是公认的抗禽流感、甲型H1N1病毒最有效的药物之一。

    -

    主要原理

    奥司他韦口服后经肝脏和肠道酯酶迅速催化转化为其活性代谢物奥司他韦羧酸,奥司他韦羧酸的构型与神经氨酸的过渡态相似,能够竞争性地与流感病毒神经氨酸酶(NA,neu-raminidase,也有称作神经氨酸苷酶)的活动位点结合,因而是一种强效的高选择性的流感病毒NA抑制剂(NAIs),它主要通过干扰病毒从被感染的宿主细胞中释放,从而减少甲型或乙型流感病毒的传播。

    -

    特殊人群

      -
    1. 对不同程度的肾功能不全患者给予100mg磷酸奥司他韦,每日两次,服用五天,显示活性代谢产物水平与降低肾功能成反比。对肌酐清除率小于30ml/min的患者建议做剂量调整。目前没有研究数据指导肾功能衰竭患者的用药(肌酐清除小于10ml/nin),所以对该人群用药时要慎重。

      -
    2. -
    3. 口服磷酸奥司他韦后。肝功能不全患者并没有象预期那样体内奥司他韦水平增高或其活性代谢产物水平降低。

      -
    4. -
    5. 给予相同剂量的磷酸奥司他韦,同年轻人相比,老年人(年龄在65—78岁间)的稳态代谢物水平比年轻人高25~35%,而两个人群药物半衰期很相似。考虑到药物暴露量和耐受力,老年人不必调整剂量。

      -
    6. -
    7. 对一小组5~18岁儿童给予单剂2mg/kg的粉末剂,口服建立药代曲线,数据显示儿童年龄越小,对药物前体和其活性代谢产物的清除越快,人体对每mg/kg计量单位的承受越少。比如给予5-8岁儿童2mg/kg的剂量,若要达到可比性。即相当于要给予成年人单剂75mg奥司他韦(大约为1mg/kg)。年龄相差越小,儿童与成年人对每一单位mg/kg剂量的代谢差别越小。比如大于12岁的儿童与成年人在药代动力学方面就已经很相似了。

      -
    8. -
    -

    不良反应

    在罗氏提交美国联邦食品和药品管理局的申报材料中指出,奥司他韦(达菲)主要的不良反应显示为消化道的不适,包括恶心、呕吐、腹泻、腹痛等,其次是呼吸系统的不良反应,包括支气管炎、咳嗽等,此外还有中枢神经系统的不良反应,如眩晕、头痛、失眠、疲劳等。

    -

    在2004年1月,FDA还发出对于奥司他韦的消费警讯,声称由于1岁以内幼儿血脑屏障发育不完全,奥司他韦应用于幼儿可能造成脑内药物浓度过高,形成潜在的安全问题。

    -

    2005年有日本媒体报道日本青少年服用奥斯他韦后自杀并有精神异常反应,此后日本先后报道数十例此类不良反应,此后世界各地媒体纷纷转载报道,引起公众关注。2005年11月FDA就这一反应作出报告,认为没有证据证明奥斯他韦可以导致精神异常,日本的不良反应病例系大众媒体报道后经心理暗示作用引起的群体性臆症。

    -

    其他

    需要注意的是,奥司他韦是通过抑制病毒的复制而起到治疗流感作用的,如果人体没有感染病毒,吃了“达菲”也是没有任何作用的。也就是说,“达菲”是在体内存在病毒的前提下起到杀灭病毒的作用,它不可能抵抗病毒侵入人体。

    -

    参考

    ]]>
    Medicine @@ -4054,6 +4072,34 @@ await prisma.$transaction(async tx => { Technique
    + + 奥司他韦 + /2023/03/05/%E5%A5%A5%E5%8F%B8%E4%BB%96%E9%9F%A6/ + 奥司他韦.jpg

    +

    目前市场上销售的达菲为罗氏制药独家生产的抗流感药物,其通用名称为磷酸奥司他韦(Oseltamivirphosphate)。奥司他韦(Oseltamivir)于1999年在瑞士上市,2001年10月在我国上市。达菲是一种非常有效的流感治疗用药,并且可以大大减少并发症(主要是气管与支气管炎、肺炎、咽炎等)的发生和抗生素的使用,因而是目前治疗流感的最常用药物之一,也是公认的抗禽流感、甲型H1N1病毒最有效的药物之一。

    +

    主要原理

    奥司他韦口服后经肝脏和肠道酯酶迅速催化转化为其活性代谢物奥司他韦羧酸,奥司他韦羧酸的构型与神经氨酸的过渡态相似,能够竞争性地与流感病毒神经氨酸酶(NA,neu-raminidase,也有称作神经氨酸苷酶)的活动位点结合,因而是一种强效的高选择性的流感病毒NA抑制剂(NAIs),它主要通过干扰病毒从被感染的宿主细胞中释放,从而减少甲型或乙型流感病毒的传播。

    +

    特殊人群

      +
    1. 对不同程度的肾功能不全患者给予100mg磷酸奥司他韦,每日两次,服用五天,显示活性代谢产物水平与降低肾功能成反比。对肌酐清除率小于30ml/min的患者建议做剂量调整。目前没有研究数据指导肾功能衰竭患者的用药(肌酐清除小于10ml/nin),所以对该人群用药时要慎重。

      +
    2. +
    3. 口服磷酸奥司他韦后。肝功能不全患者并没有象预期那样体内奥司他韦水平增高或其活性代谢产物水平降低。

      +
    4. +
    5. 给予相同剂量的磷酸奥司他韦,同年轻人相比,老年人(年龄在65—78岁间)的稳态代谢物水平比年轻人高25~35%,而两个人群药物半衰期很相似。考虑到药物暴露量和耐受力,老年人不必调整剂量。

      +
    6. +
    7. 对一小组5~18岁儿童给予单剂2mg/kg的粉末剂,口服建立药代曲线,数据显示儿童年龄越小,对药物前体和其活性代谢产物的清除越快,人体对每mg/kg计量单位的承受越少。比如给予5-8岁儿童2mg/kg的剂量,若要达到可比性。即相当于要给予成年人单剂75mg奥司他韦(大约为1mg/kg)。年龄相差越小,儿童与成年人对每一单位mg/kg剂量的代谢差别越小。比如大于12岁的儿童与成年人在药代动力学方面就已经很相似了。

      +
    8. +
    +

    不良反应

    在罗氏提交美国联邦食品和药品管理局的申报材料中指出,奥司他韦(达菲)主要的不良反应显示为消化道的不适,包括恶心、呕吐、腹泻、腹痛等,其次是呼吸系统的不良反应,包括支气管炎、咳嗽等,此外还有中枢神经系统的不良反应,如眩晕、头痛、失眠、疲劳等。

    +

    在2004年1月,FDA还发出对于奥司他韦的消费警讯,声称由于1岁以内幼儿血脑屏障发育不完全,奥司他韦应用于幼儿可能造成脑内药物浓度过高,形成潜在的安全问题。

    +

    2005年有日本媒体报道日本青少年服用奥斯他韦后自杀并有精神异常反应,此后日本先后报道数十例此类不良反应,此后世界各地媒体纷纷转载报道,引起公众关注。2005年11月FDA就这一反应作出报告,认为没有证据证明奥斯他韦可以导致精神异常,日本的不良反应病例系大众媒体报道后经心理暗示作用引起的群体性臆症。

    +

    其他

    需要注意的是,奥司他韦是通过抑制病毒的复制而起到治疗流感作用的,如果人体没有感染病毒,吃了“达菲”也是没有任何作用的。也就是说,“达菲”是在体内存在病毒的前提下起到杀灭病毒的作用,它不可能抵抗病毒侵入人体。

    +

    参考

    +]]>
    + + Medicine + +
    如何管理用户界面中的危险操作 /2024/11/26/%E5%A6%82%E4%BD%95%E7%AE%A1%E7%90%86%E7%94%A8%E6%88%B7%E7%95%8C%E9%9D%A2%E4%B8%AD%E7%9A%84%E5%8D%B1%E9%99%A9%E6%93%8D%E4%BD%9C/ @@ -4389,55 +4435,6 @@ await prisma.$transaction(async tx => { Medicine - - 灯笼草 - /2023/06/22/%E7%81%AF%E7%AC%BC%E8%8D%89/ - 今天在菜园子里看到了野生的灯笼草,都结果了:

    -

    -

    灯笼果别名为小果酸浆、秘鲁苦蘵、打头泡、灯笼草等,茄科酸浆属多年生草本植物。
    灯笼果茎直立,叶较厚,阔卵形或心脏形,两面密生柔毛;花单独腋生,花萼阔钟状,花冠阔钟状,黄色而喉部有紫色斑纹,花丝及花药蓝紫色,花药长约3毫米。果萼薄纸质,淡绿色或淡黄色,浆果成熟时黄色,种子黄色,圆盘状,夏季开花结果。生于田间、路旁、村边。我国南北各地均有分市。

    -

    不要与醋栗混淆

    -

    醋栗长这样:

    -

    -
    -

    性味

      -
    • 《陆川本草》:”甘淡,微寒。”

      -
    • -
    • 《南宁市药物志》:”苦,寒,微甘。”

      -
    • -
    -

    功能主治

    -

    清热,行气,止痛,消肿。治感冒,痄腮,喉痛,咳嗽,腹胀,疝气,天疱疮。

    -
    -
      -
    • 《陆川本草》:”行气,消胀,利尿。治腹胀,睾丸炎,疝气。”

      -
    • -
    • 《南宁市药物志》:”清热杀虫,止痛消肿。治热眼,喉痛,咳嗽;外敷毒疮,并熏洗阴囊肿大。”

      -
    • -
    • 《生草药手册》:”内服治伤寒或小肠疝气。外洗治天疱疮。”

      -
    • -
    • 《中国药植图鉴》:”功同酸浆。”

      -
    • -
    • 广州空军《常用中草药手册》:”治感冒发热,腮腺炎,支气管炎,疱疹,疖疮,疝气痛。”

      -
    • -
    -

    用法用量

      -
    • 内服:煎汤,3 ~ 5钱。
    • -
    • 外用:捣敷或煎水洗。
    • -
    -

    引用

      -
    • 《本草纲目》旧版草部第十六卷,草之五:燕京野果名红姑娘,外垂降囊,中含赤子如珠,酸甘可食盈盈绕砌,与翠草同芳,亦自可爱。捣计服治黄病(即黄胆性肝炎)多效,治上气咳嗽风热,明目,付小儿内辟等多种疾病。东北地区种植较广泛,其他地区种植较少,仍属稀特蔬菜。

      -
    • -
    • 《别录》:“酸浆,生荆、楚川泽及人家田园中。五月采,阴干。”陶弘景:“酸浆,处处人家多有。叶亦可食。子作房,房中有子,如梅李大,皆黄赤色。”《唐本草》:“灯笼草,所在有之。八月采。枝干高三、四尺,有花,红色,状若灯笼,内有子,红色可爱。根、茎、花、叶并入药用。”

      -
    • -
    • 《梦溪笔谈》:“苦耽,即本草酸浆也。河西番界中酸浆有盈丈者。”《本草衍义》:“酸浆,今天下皆有之。苗如天茄子,开小白花,结青壳,熟则深红,壳中子大如樱,亦红色,樱中复有细子,如落苏之子,食之有青草气。此即苦耽也。”

      -
    • -
    -]]>
    - - Medicine - Life - -
    气胸 /2023/03/01/%E6%B0%94%E8%83%B8/ @@ -4491,6 +4488,55 @@ await prisma.$transaction(async tx => { Medicine + + 灯笼草 + /2023/06/22/%E7%81%AF%E7%AC%BC%E8%8D%89/ + 今天在菜园子里看到了野生的灯笼草,都结果了:

    +

    +

    灯笼果别名为小果酸浆、秘鲁苦蘵、打头泡、灯笼草等,茄科酸浆属多年生草本植物。
    灯笼果茎直立,叶较厚,阔卵形或心脏形,两面密生柔毛;花单独腋生,花萼阔钟状,花冠阔钟状,黄色而喉部有紫色斑纹,花丝及花药蓝紫色,花药长约3毫米。果萼薄纸质,淡绿色或淡黄色,浆果成熟时黄色,种子黄色,圆盘状,夏季开花结果。生于田间、路旁、村边。我国南北各地均有分市。

    +

    不要与醋栗混淆

    +

    醋栗长这样:

    +

    +
    +

    性味

      +
    • 《陆川本草》:”甘淡,微寒。”

      +
    • +
    • 《南宁市药物志》:”苦,寒,微甘。”

      +
    • +
    +

    功能主治

    +

    清热,行气,止痛,消肿。治感冒,痄腮,喉痛,咳嗽,腹胀,疝气,天疱疮。

    +
    +
      +
    • 《陆川本草》:”行气,消胀,利尿。治腹胀,睾丸炎,疝气。”

      +
    • +
    • 《南宁市药物志》:”清热杀虫,止痛消肿。治热眼,喉痛,咳嗽;外敷毒疮,并熏洗阴囊肿大。”

      +
    • +
    • 《生草药手册》:”内服治伤寒或小肠疝气。外洗治天疱疮。”

      +
    • +
    • 《中国药植图鉴》:”功同酸浆。”

      +
    • +
    • 广州空军《常用中草药手册》:”治感冒发热,腮腺炎,支气管炎,疱疹,疖疮,疝气痛。”

      +
    • +
    +

    用法用量

      +
    • 内服:煎汤,3 ~ 5钱。
    • +
    • 外用:捣敷或煎水洗。
    • +
    +

    引用

      +
    • 《本草纲目》旧版草部第十六卷,草之五:燕京野果名红姑娘,外垂降囊,中含赤子如珠,酸甘可食盈盈绕砌,与翠草同芳,亦自可爱。捣计服治黄病(即黄胆性肝炎)多效,治上气咳嗽风热,明目,付小儿内辟等多种疾病。东北地区种植较广泛,其他地区种植较少,仍属稀特蔬菜。

      +
    • +
    • 《别录》:“酸浆,生荆、楚川泽及人家田园中。五月采,阴干。”陶弘景:“酸浆,处处人家多有。叶亦可食。子作房,房中有子,如梅李大,皆黄赤色。”《唐本草》:“灯笼草,所在有之。八月采。枝干高三、四尺,有花,红色,状若灯笼,内有子,红色可爱。根、茎、花、叶并入药用。”

      +
    • +
    • 《梦溪笔谈》:“苦耽,即本草酸浆也。河西番界中酸浆有盈丈者。”《本草衍义》:“酸浆,今天下皆有之。苗如天茄子,开小白花,结青壳,熟则深红,壳中子大如樱,亦红色,樱中复有细子,如落苏之子,食之有青草气。此即苦耽也。”

      +
    • +
    +]]>
    + + Medicine + Life + +
    直系血亲之间不能直接输血 /2024/08/12/%E7%9B%B4%E7%B3%BB%E8%A1%80%E4%BA%B2%E4%B9%8B%E9%97%B4%E4%B8%8D%E8%83%BD%E7%9B%B4%E6%8E%A5%E8%BE%93%E8%A1%80/ @@ -4961,6 +5007,17 @@ await prisma.$transaction(async tx => { 一般7~14天可好转 +]]> + + Medicine + Life + + + + 藿香正气水和藿香正气口服液的区别 + /2024/09/01/%E8%97%BF%E9%A6%99%E6%AD%A3%E6%B0%94%E6%B0%B4%E5%92%8C%E8%97%BF%E9%A6%99%E6%AD%A3%E6%B0%94%E5%8F%A3%E6%9C%8D%E6%B6%B2%E7%9A%84%E5%8C%BA%E5%88%AB/ + 虽然都是口服液体制剂,且看起来单支容量都一样,但是藿香正气水里含有 40%-50% 的乙醇,而藿香正气口服液不含有乙醇。同时藿香正气水不能和头孢一起服用(因为含有酒精),而藿香正气口服液可以和头孢一起服用。在选用之前,还希望大家能仔细看药品名称。

    +

    正气水是酊剂,用的是生半夏,口服液用的是法半夏,比正气水多了炒的白术,生姜和大枣,两者的功效都是解表化湿,理气和中。藿香正气水由水煮及酒浸制而成,疗效最明显,但口感较差。由于霍香正气水药效比较峻猛,小儿和年老体虚者服用时应有医生指导。藿香正气口服液是藿香正气的换代产品。

    ]]>
    Medicine @@ -5028,17 +5085,6 @@ await prisma.$transaction(async tx => { Medicine
    - - 藿香正气水和藿香正气口服液的区别 - /2024/09/01/%E8%97%BF%E9%A6%99%E6%AD%A3%E6%B0%94%E6%B0%B4%E5%92%8C%E8%97%BF%E9%A6%99%E6%AD%A3%E6%B0%94%E5%8F%A3%E6%9C%8D%E6%B6%B2%E7%9A%84%E5%8C%BA%E5%88%AB/ - 虽然都是口服液体制剂,且看起来单支容量都一样,但是藿香正气水里含有 40%-50% 的乙醇,而藿香正气口服液不含有乙醇。同时藿香正气水不能和头孢一起服用(因为含有酒精),而藿香正气口服液可以和头孢一起服用。在选用之前,还希望大家能仔细看药品名称。

    -

    正气水是酊剂,用的是生半夏,口服液用的是法半夏,比正气水多了炒的白术,生姜和大枣,两者的功效都是解表化湿,理气和中。藿香正气水由水煮及酒浸制而成,疗效最明显,但口感较差。由于霍香正气水药效比较峻猛,小儿和年老体虚者服用时应有医生指导。藿香正气口服液是藿香正气的换代产品。

    -]]>
    - - Medicine - Life - -
    血常规化验结果阅读指南 /2023/10/12/%E8%A1%80%E5%B8%B8%E8%A7%84%E5%8C%96%E9%AA%8C%E7%BB%93%E6%9E%9C%E9%98%85%E8%AF%BB%E6%8C%87%E5%8D%97/ -- cgit v1.2.3