使用 std::string 作为通用 uint8_t 缓冲区

Posted

技术标签:

【中文标题】使用 std::string 作为通用 uint8_t 缓冲区【英文标题】:Using std::string as a generic uint8_t buffer 【发布时间】:2018-11-25 18:42:32 【问题描述】:

我正在查看 Chromium 的源代码,以研究他们如何实现 MediaRecorder API,该 API 将原始麦克风输入流编码/记录为特定格式。

我遇到了interesting codes from their source。简而言之:

bool DoEncode(float* data_in, std::string* data_out) 
    ...
    data_out->resize(MAX_DATA_BTYES_OR_SOMETHING);
    opus_encode_float(
        data_in,
        reinterpret_cast<uint8_t*>(base::data(*data_out))
    );
    ...

所以DoEncode(C++方法)这里接受一个float数组并将其转换为编码字节流,实际操作在opus_encode_float()(这是一个纯C函数)中完成。

有趣的是,Google Chromium 团队使用std::string 代替std::vector&lt;uint_8&gt; 作为字节数组,他们甚至手动转换为uint8_t 缓冲区。

为什么 Google Chromium 团队的人会这样做,是否存在使用 std::string 对通用字节缓冲区比使用像 std::vector&lt;uint8_t&gt; 等其他缓冲区更有用的情况?

【问题讨论】:

改用std::vectot&lt;uint8_t&gt;怎么样? @πάνταῥεῖ 我不是在寻找建议。提到的代码由 Google Chromium 团队完成。我有点想知道他们为什么要这样编码。 真的only differences 是API 的外观以及操作优化。充其量,我们可以推测 Chromium 团队决定他们希望对数据使用字符串操作而不是收集操作。但事情就是这样——这纯粹是猜测。还有一点要记住:仅仅因为它像 Chromium 或 Google 一样多产并不意味着它是完美的。有时,这些项目会做一些奇怪或明显不正确的事情。 @Qix 感谢您的评论。所以毕竟没有理由不使用std::vector的东西。 【参考方案1】:

Chromium 编码风格(见下文)禁止在没有充分理由的情况下使用无符号整数类型。外部 API 不是这样的原因。有符号和无符号字符的大小都是 1,为什么不呢。

我查看了 opus 编码器 API,似乎早期版本使用签名字符:

[out]   data    char*: Output payload (at least max_data_bytes long)

虽然 API 现在使用无符号字符,但描述仍然是指有符号字符。所以std::string for chars 对于早期的 API 来说更方便,并且 Chromium 团队在 API 更新后并没有更改已经使用的容器,他们在一行中使用 cast 而不是更新其他数十行。

整数类型

您不应该使用无符号整数类型,例如uint32_t,除非有正当理由,例如表示位模式而不是数字,或者您需要定义溢出模2^N。特别是,不要使用无符号类型来表示一个数字永远不会是负数。相反,为此使用断言。

如果您的代码是一个返回大小的容器,请务必使用能够适应您的容器的任何可能用途的类型。如有疑问,请使用较大的类型而不是较小的类型。

在转换整数类型时要小心。整数转换和提升可能会导致未定义的行为,从而导致安全漏洞和其他问题。

关于无符号整数

无符号整数适用于表示位域和模运算。由于历史意外,C++ 标准还使用无符号整数来表示容器的大小——标准机构的许多成员认为这是一个错误,但实际上目前无法修复。无符号算术不模拟简单整数的行为,而是由标准定义以模拟模算术(围绕上溢/下溢)这一事实,这意味着编译器无法诊断出一类重要的错误。在其他情况下,定义的行为会阻碍优化。

也就是说,混合整数类型的符号性会导致同样大类的问题。我们可以提供的最佳建议:尽量使用迭代器和容器而不是指针和大小,尽量不要混合有符号,并尽量避免无符号类型(表示位域或模运算除外)。不要仅仅使用无符号类型来断言变量是非负的。

【讨论】:

这对我来说很有意义。所以这只是历史和维护的原因。我认为使用std::string 背后可能有一个特殊的秘密:) 非常感谢! 所以,例外的答案是“因为为什么不”? The Chromium code style forbids using unsigned integral types 有趣;这是有原因的吗? @Qix Shorty,无符号类型用于位模式。由于无符号类型不能溢出,因此很难诊断溢出问题。请看我更新的答案。 但有符号整数溢出是未定义的。这比定义明确的溢出更可取吗?我在这里没有看到任何具体原因。【参考方案2】:

我们只能理论化。

我的猜测:他们想使用 std::string 中存在但可能不适用于 std::vector&lt;uint8_t&gt; 的内置 SSO 优化。

【讨论】:

对请求的 4000 字节缓冲区进行短字符串优化?

以上是关于使用 std::string 作为通用 uint8_t 缓冲区的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C++ 中将 uint8_t 的向量转换为 std::string?

Cython:python int 到 uint8_t

我啥时候应该使用 std::string / std::string_view 作为参数/返回类型

如何使用 std::string 作为 QHash 的键?

使用 std::string 作为 std::map 的键

为啥我可以在 std::map<std::string, int> 中使用 const char* 作为键