寻找共同点
大家好!
那些日子我花了一些时间在基础部件上工作,揭示了可能的惊喜,Raku 的 LDAP(轻量级目录访问协议)实现。
然而,现在谈论这个还为时尚早,所以我现在将有一些神秘的封面覆盖这个话题,因为我们有另一个 - 宇宙飞船!
航天器和LDAP之间的共同点是:LDAP规范使用一种称为符号的符号 ASN.1
,它允许使用特定的文本语法定义抽象类型,并在 ASN.1
编译器的帮助下,为特定的编程语言创建类型定义,以及什么是更多:此类型值的编码器和解码器,可以将您的值序列化为某些数据,例如,可以通过网络发送并在另一台计算机上很好地解析。
通过这种方式,您可以轻松地在应用程序中获得跨平台类型。编码器和解码器可以自动生成,不仅针对某些指定的编码格式,而且针对整个范围的二进制(例如 BER
,PER
和其他)和文本(例如SOAP
)编码格式。
因此,为了完成工作,我必须至少实现 ASN.1
Raku中的一些子集- 不是完整的规范,这很大,只关注LDAP规范中使用的功能。
“这听起来很有趣,但我们的宇宙飞船在哪里!?”,你可能会问。事实证明,这种 Rocket
类型是您在 ASN.1 Playground 网站上看到的第一件事,它让您可以免费访问 ASN.1
编译器,它可以作为参考!
ASN.1
和限制
这是花哨的代码:
World-Schema DEFINITIONS AUTOMATIC TAGS ::=
BEGIN
Rocket ::= SEQUENCE
{
name UTF8String (SIZE(1..16)),
message UTF8String DEFAULT "Hello World" ,
fuel ENUMERATED {solid, liquid, gas},
speed CHOICE
{
mph INTEGER,
kmph INTEGER
} OPTIONAL,
payload SEQUENCE OF UTF8String
}
END
让我们快速浏览一下这个定义:
Rocket
是一个SEQUENCE
- 一组某类型的有序值,可以看作是异构列表/数组或类。- 场
name
和message
有UTF8String
型,这是肯定的,一种字符串表示的ASN.1
。字段name已应用长度限制,(SIZE(1..16))
和message
具有指定的默认值DEFAULT "Hello World"
。 - 字段
fuel
有ENUMERATED
类型:它只是一个可供选择的标签枚举。 - 字段
speed
是一个CHOICE
,它是一种特殊类型,它描述了一个字段,该值可以是指定类型之一。不同的是ENUMERATED
,价值不仅仅是标签。OPTIONAL
如你所知,关键字意味着如果不存在,该字段可能会被省略。 - 字段
payload
是一个SEQUENCE
,但指定了类型。这意味着我们可以根据需要在这里拥有尽可能多的UTF8String
值。
这里我们将应用两个重要的限制:
- 我们将使用
Basic Encoding Rules(BER)
- 将ASN.1
类型编码指定为特定字节序列的规则。如上所述,有不同的格式,但我们将使用这一种。
Basic Encoding Rules
标准是基于一个所谓的“TLV编码”的事情-的类型的值被编码为字节序列表示:“ Ť AG”,“ 大号 ength”和“ V传递类型的某些值的ALUE”。让我们更仔细地看一下……以相反的顺序!
“值”是包含值的字节表示的部分。每种类型都有自己的编码模式(例如,INTEGER
编码方式不同 UTF8String
)。
“长度”是表示“值”部分中的字节数的数字。这允许我们很好地处理增量解析(通常也是!)。它也可以具有“未知”值,这允许我们以未知的长度流式传输数据,但我们将把它放在一边。
“标签”简单地说是一个字节或一些字节,我们可以用它来确定我们手头有什么类型。其确切值由标记规则的数量(“标记模式”)确定,并且存在好的或更差的不同模式。
并且,如果您已经等待某些段落的第二个限制,那么它是:
我们将 IMPLICIT
在这里使用 BER 的类型标记模式。正如您所猜测的那样,EXPLICIT
标记模式也同时存在 AUTOMATIC
(在上面的 Rocket 示例中使用)。
考虑到这一点,我们需要将 ASN.1
上面的类型更改为:
World-Schema DEFINITIONS IMPLICIT TAGS ::=
BEGIN
Rocket ::= SEQUENCE
{
name UTF8String (SIZE(1..16)),
message UTF8String DEFAULT "Hello World" ,
fuel ENUMERATED {solid, liquid, gas},
speed CHOICE
{
mph [0] INTEGER,
kmph [1] INTEGER
} OPTIONAL,
payload SEQUENCE OF UTF8String
}
END
注意 IMPLICIT TAGS
用于代替字段中的 AUTOMATIC TAGS
和 [$n]
字符串 speed
。
如果你看一下这个模式,事实证明,这是,其实,暧昧,因为 mph
和 kmph
都有 INTEGER
型。因此,如果我们 INTEGER
从字节流中读取了一个,它是 mph
值还是 kmph
值?如果我们谈论宇宙飞船,它会产生巨大的变化!
为了避免这种混淆,使用了特殊的标签,这里我们指定了我们想要的标签,因为与 AUTOMATIC
模式不同,IMPLICIT
它不适用于我们。
逐步建设。问题答案。
那么,我们可以用 Raku 中的所有功能做什么呢?虽然编译器可能很有趣,但是可以通过可扩展的方式编译成 Raku,并且包含了奇特的功能?必须有一些更简单的东西。
比方说,我们有一个适用于航天器的脚本。当然,我们需要一个类型来表示一个,特别是一个类,让我们称之为 Rocket
:
class Rocket {}
当然,我们想知道一些有关它的数据:
class Rocket {
has $.name;
has $.message is default("Hello World");
has $.fuel;
has $.speed;
has @.payload;
}
如果我们必须使我们的 Rocket
定义更明确,那么我们指定一些类型:
enum Fuel <Solid Liquid Gas>;
class Rocket {
has Str $.name;
has Str $.message is default("Hello World");
has Fuel $.fuel;
has $.speed;
has Str @.payload;
}
现在它开始提醒我们一些事情……
Str
类似UTF8String
,只是我们不能离开它这样,因为ASN.1
我们不仅有UTF8String
,而且BIT STRING
,OCTET STRING
和其他字符串类型。Fuel
枚举类似于ENUMERATED
类型。@.payload
中的@
符号告诉我们,这将是一个序列,而且Str
指定其元素的类型。- 但是虽然有一些类似的观点,但从我们
ASN.1
的观点来看,我们没有足够的数据。让我们一步一步解决这些问题!
我们怎么知道这完全Rocket是
ASN.1
序列类型?
通过应用角色:class Rocket does ASNSequence
。
我们怎么知道确切的字段顺序?
通过实现此角色的存根方法:method ASN-order { <$!name $!message $!fuel $!speed @!payload> }
我们怎么知道这
$.speed
是可选的?
我们只是应用它的特征!Traits 允许我们在代码部分上执行自定义代码,特别是 Attributes
。例如,虚构的API可以是这样的:has $.speed is optional
。
我们怎么知道 $.speed 是多少?
由于 CHOICE
类型是“特殊的”,但仍然是一流的(例如,你可以使它递归),我们需要在这里发挥作用:ASNChoice
来救援。
我们怎么知道
ASN.1
我们的 Str 类型是什么类型的字符串?
我们来写吧 has Str $.name is UTF8String;
。
我们如何指定字段的默认值?
虽然 Raku 已经具有内置 is default
特性,但对我们来说不好的是我们无法“很好地”检测到它。因此,我们必须引入另一个自定义特征,以满足我们的目的并应用内置特征:has Str $.message is default-value("Hello World");
让我们在一个包中回答所有这些问题:
role ASNSequence { #`[ Elves Special Magic Truly Happens Here ] }
role ASNChoice { #`[ And even here ] }
class SpeedChoice does ASNChoice {
method ASN-choice() {
# Description of: names, tags, types specificed by this CHOICE
{ mph => (0 => Int), kmph => (1 => Int) }
}
}
class Rocket does ASNSequence {
has Str $.name is UTF8String;
has Str $.message is default-value("Hello World") is UTF8String;
has Fuel $.fuel;
has SpeedChoice $.speed is optional;
has Str @.payload is UTF8String;
method ASN-order { <$!name $!message $!fuel $!speed @!payload> }
}
值可能类似于:
my $rocket = Rocket.new(
name => 'Falcon',
fuel => Solid,
speed => SpeedChoice.new((mph => 18000)),
payload => [ "Car", "GPS" ]);
答案越多,问题就越多
对于这个微小的例子(另一方面,它已经 ASN.1
展示了许多特性),实际上,我们需要在我们的应用程序中使用这个类的实例,并可能根据需要对其进行编码和解码。
那么精灵们对我们的数据秘密做了什么?让我们在下一篇文章中找到答案!