世界上共有多少种颜色

Python015

世界上共有多少种颜色,第1张

英语颜色大汇总: 红色 vermeil, ponceau 朱红 <br>pinksoft redrose bloom 粉红色 <br>plumcrimson梅红 rose 玫瑰红 peach blossom桃红 <cherry 樱桃红 salmon pink 桔红色 garnet石榴红 purplish reddate red 枣红色 lotus red 莲红色 fuchsia pink 浅莲红bean red 豆红 capsicum red辣椒红 Kaoliang red 高梁红 poppy red 芙蓉红 lakerouge red胭脂红 salmon 鲑鱼红 hawksbill turtle red 玳瑁红 cadmium orange 海螺红 ruby red 宝石红 agate red 玛瑙红 coral珊瑚红 bronze red金红 iron oxide red铁红 rust red铁锈红 chrome red铬红 brick red砖红 reddle 土红 purplish redwine red紫红(酒红) prunemulberry深紫红 henna棕红 dark reddull red暗红 fresh redblood redbright red鲜红scarlet绯红 silver pink米红 deep red深红 light red 淡红橙色 orange 黄色 yellow 深桔黄 deep orange 浅桔黄 light orangeclear orange 柠檬黄 lemon yellowlemon 玉米黄 maize 橄榄黄 olive yellow 稻草黄 straw yellow 芥末黄 mustard 杏黄 broze yellow 蛋黄 york yellowegg yellow 藤黄 rattan yellow 象牙黄 nude 日光黄 sunny yellow 土黄 earth yellow yellowish brown砂黄 sand yellow金黄 golden yellowgold 深黄 deep yellow 棕黄 tan 青黄 bluish yellow 灰黄 sallowgrey yellow 米黄 cream 嫩黄 yellow cream 鲜黄 cadmium yellow 鹅黄 light yellow 中黄 midium yellow paleyellowbuff 淡黄 primrosejasmine绿色 green 豆绿 pea greenbean green 浅豆绿 light bean green橄榄绿 olive greenolive茶绿 tea greenplantation 葱绿 onion green苹果绿 apple green 森林绿 forest green 苔藓绿 moss green 草地绿 grass green灰湖绿 agate green 水晶绿 crystal 玉绿 jade green 石绿 mineral green 松石绿 spearmint viridis 孔雀绿 peacock green 墨绿 green black jasper 墨玉绿 emerald black 深绿 petrol bottle greenChinese green暗绿 deep green 青绿 dark green 碧绿 azure greenviridity 蓝绿 blue green 黄绿 yellow green 灰绿 grey green褐绿 breen 中绿 medium greengolf green 浅绿 light green 淡绿 pale green靛青 ingigo 蓝色 blue 天蓝 蔚蓝 sky blue azure 月光蓝 moon blue 海洋蓝 ocean blue 海蓝 sea blue 湖蓝 acid blue 深湖蓝 vivid blue 中湖蓝 bright blue 冰雪蓝 ice-snow blue 孔雀蓝 peacock blue 宝石蓝sapphirejewelry blue粉末蓝 powder blue 藏蓝 purplish blue navy 海军蓝 navy blue 宝蓝 royal blue墨蓝 blue black 紫蓝 purplish blue 浅紫蓝 dutch blue 青蓝 ultramarine 深灰蓝 blue ashes 深蓝 dark blue deep blue 鲜蓝 clear blue 中蓝 medium blue 浅蓝light blue

Rust 是一种日益流行的编程语言,被视为硬件接口的最佳选择。通常会将其与 C 的抽象级别相比较。本文介绍了 Rust 如何通过多种方式处理按位运算,并提供了既安全又易于使用的解决方案。

在系统编程领域,你可能经常需要编写硬件驱动程序或直接与内存映射设备进行交互,而这些交互几乎总是通过硬件提供的内存映射寄存器来完成的。通常,你通过对某些固定宽度的数字类型进行按位运算来与这些寄存器进行交互。

例如,假设一个 8 位寄存器具有三个字段

字段名称下方的数字规定了该字段在寄存器中使用的位。要启用该寄存器,你将写入值 1(以二进制表示为 0000_0001)来设置 Enabled 字段的位。但是,通常情况下,你也不想干扰寄存器中的现有配置。假设你要在设备上启用中断功能,但也要确保设备保持启用状态。为此,必须将 Interrupt 字段的值与 Enabled 字段的值结合起来。你可以通过按位操作来做到这一点:

通过将 1 和 2(1 左移一位得到)进行“或”(|)运算得到二进制值 0000_0011 。你可以将其写入寄存器,使其保持启用状态,但也启用中断功能。

你的头脑中要记住很多事情,特别是当你要在一个完整的系统上和可能有数百个之多的寄存器打交道时。在实践上,你可以使用助记符来执行此操作,助记符可跟踪字段在寄存器中的位置以及字段的宽度(即它的上边界是什么)

下面是这些助记符之一的示例。它们是 C 语言的宏,用右侧的代码替换它们的出现的地方。这是上面列出的寄存器的简写。& 的左侧是该字段的起始位置,而右侧则限制该字段所占的位:

然后,你可以使用这些来抽象化寄存器值的操作,如下所示:

这就是现在的做法。实际上,这就是大多数驱动程序在 Linux 内核中的使用方式。

有没有更好的办法?如果能够基于对现代编程语言研究得出新的类型系统,就可能能够获得安全性和可表达性的好处。也就是说,如何使用更丰富、更具表现力的类型系统来使此过程更安全、更持久?

继续用上面的寄存器作为例子:

你想如何用 Rust 类型来表示它呢?

你将以类似的方式开始,为每个字段的偏移定义常量(即,距最低有效位有多远)及其掩码。掩码是一个值,其二进制表示形式可用于更新或读取寄存器内部的字段:

最后,你将使用一个 Register 类型,该类型会封装一个与你的寄存器宽度匹配的数字类型。 Register 具有 update 函数,可使用给定字段来更新寄存器:

使用 Rust,你可以使用数据结构来表示字段,将它们与特定的寄存器联系起来,并在与硬件交互时提供简洁明了的工效。这个例子使用了 Rust 提供的最基本的功能。无论如何,添加的结构都会减轻上述 C 示例中的某些晦涩的地方。现在,字段是个带有名字的事物,而不是从模糊的按位运算符派生而来的数字,并且寄存器是具有状态的类型 —— 这在硬件上多了一层抽象。

用 Rust 重写的第一个版本很好,但是并不理想。你必须记住要带上掩码和偏移量,并且要手工进行临时计算,这容易出错。人类不擅长精确且重复的任务 —— 我们往往会感到疲劳或失去专注力,这会导致错误。一次一个寄存器地手动记录掩码和偏移量几乎可以肯定会以糟糕的结局而告终。这是最好留给机器的任务。

其次,从结构上进行思考:如果有一种方法可以让字段的类型携带掩码和偏移信息呢?如果可以在编译时就发现硬件寄存器的访问和交互的实现代码中存在错误,而不是在运行时才发现,该怎么办?也许你可以依靠一种在编译时解决问题的常用策略,例如类型。

你可以使用 typenum 来修改前面的示例,该库在类型级别提供数字和算术。在这里,你将使用掩码和偏移量对 Field 类型进行参数化,使其可用于任何 Field 实例,而无需将其包括在调用处:

现在,当重新访问 Field 的构造函数时,你可以忽略掩码和偏移量参数,因为类型中包含该信息:

看起来不错,但是……如果你在给定的值是否适合该字段方面犯了错误,会发生什么?考虑一个简单的输入错误,你在其中放置了 10 而不是 1:

在上面的代码中,预期结果是什么?好吧,代码会将启用位设置为 0,因为 10&1 = 0。那真不幸;最好在尝试写入之前知道你要写入字段的值是否适合该字段。事实上,我认为截掉错误字段值的高位是一种 1未定义的行为(哈)。

如何以一般方式检查字段的值是否适合其规定的位置?需要更多类型级别的数字!

你可以在 Field 中添加 Width 参数,并使用它来验证给定的值是否适合该字段:

现在,只有给定值适合时,你才能构造一个 Field !否则,你将得到 None 信号,该信号指示发生了错误,而不是截掉该值的高位并静默写入意外的值。

但是请注意,这将在运行时环境中引发错误。但是,我们事先知道我们想写入的值,还记得吗?鉴于此,我们可以教编译器完全拒绝具有无效字段值的程序 —— 我们不必等到运行它!

这次,你将向 new 的新实现 new_checked 中添加一个特征绑定(where 子句),该函数要求输入值小于或等于给定字段用 Width 所能容纳的最大可能值:

只有拥有此属性的数字才实现此特征,因此,如果使用不适合的数字,它将无法编译。让我们看一看!

new_checked 将无法生成一个程序,因为该字段的值有错误的高位。你的输入错误不会在运行时环境中才爆炸,因为你永远无法获得一个可以运行的工件。

就使内存映射的硬件进行交互的安全性而言,你已经接近 Rust 的极致。但是,你在 C 的第一个示例中所写的内容比最终得到的一锅粥的类型参数更简洁。当你谈论潜在可能有数百甚至数千个寄存器时,这样做是否容易处理?

早些时候,我认为手工计算掩码有问题,但我又做了同样有问题的事情 —— 尽管是在类型级别。虽然使用这种方法很不错,但要达到编写任何代码的地步,则需要大量样板和手动转录(我在这里谈论的是类型的同义词)。

我们的团队想要像 TockOS mmio 寄存器 之类的东西,而以最少的手动转录生成类型安全的实现。我们得出的结果是一个宏,该宏生成必要的样板以获得类似 Tock 的 API 以及基于类型的边界检查。要使用它,请写下一些有关寄存器的信息,其字段、宽度和偏移量以及可选的 枚举 类的值(你应该为字段可能具有的值赋予“含义”):

由此,你可以生成寄存器和字段类型,如上例所示,其中索引:Width、Mask 和 Offset 是从一个字段定义的 WIDTH 和 OFFSET 部分的输入值派生的。另外,请注意,所有这些数字都是 “类型数字”;它们将直接进入你的 Field 定义!

生成的代码通过为寄存器及字段指定名称来为寄存器及其相关字段提供名称空间。这很绕口,看起来是这样的:

生成的 API 包含名义上期望的读取和写入的原语,以获取原始寄存器的值,但它也有办法获取单个字段的值、执行集合操作以及确定是否设置了任何(或全部)位集合的方法。你可以阅读 完整生成的 API 上的文档。

将这些定义用于实际设备会是什么样?代码中是否会充斥着类型参数,从而掩盖了视图中的实际逻辑?

不会!通过使用类型同义词和类型推断,你实际上根本不必考虑程序的类型层面部分。你可以直接与硬件交互,并自动获得与边界相关的保证。

一旦到位,使用这些寄存器就像 read() 和 modify() 一样简单:

当我们使用运行时值时,我们使用如前所述的 选项 。这里我使用的是 unwrap,但是在一个输入未知的真实程序中,你可能想检查一下从新调用中返回的 某些东西 : 1 2

根据你的个人痛苦忍耐程度,你可能已经注意到这些错误几乎是无法理解的。看一下我所说的不那么微妙的提醒:

expected struct typenum::B0, found struct typenum::B1 部分是有意义的,但是 typenum::UInt<typenum::UInt, typenum::UInt... 到底是什么呢?好吧,typenum 将数字表示为二进制 cons 单元!像这样的错误使操作变得很困难,尤其是当你将多个这些类型级别的数字限制在狭窄的范围内时,你很难知道它在说哪个数字。当然,除非你一眼就能将巴洛克式二进制表示形式转换为十进制表示形式。

在第 U100 次试图从这个混乱中破译出某些含义之后,我们的一个队友简直《 疯了,地狱了,不要再忍受了(Mad As Hell And Wasn’t Going To Take It Anymore)》,并做了一个小工具 tnfilt,从这种命名空间的二进制 cons 单元的痛苦中解脱出来。tnfilt 将 cons 单元格式的表示法替换为可让人看懂的十进制数字。我们认为其他人也会遇到类似的困难,所以我们分享了 tnfilt 。你可以像这样使用它:

它将上面的输出转换为如下所示:

现在这才有意义!

当在软件与硬件进行交互时,普遍使用内存映射寄存器,并且有无数种方法来描述这些交互,每种方法在易用性和安全性上都有不同的权衡。我们发现使用类型级编程来取得内存映射寄存器交互的编译时检查可以为我们提供制作更安全软件的必要信息。该代码可在 bounded-registers crate(Rust 包)中找到。

我们的团队从安全性较高的一面开始,然后尝试找出如何将易用性滑块移近易用端。从这些雄心壮志中,“边界寄存器”就诞生了,我们在 Auxon 公司的冒险中遇到内存映射设备的任何时候都可以使用它。

此内容最初发布在 Auxon Engineering 博客 上,并经许可进行编辑和重新发布。

via: https://opensource.com/article/20/1/c-vs-rust-abstractions

作者: Dan Pittman 选题: lujun9972 译者: wxy 校对: wxy