非常教程

Erlang 20参考手册

asn1

4. Specialized Decodes

如果性能是最高优先级的,并且您在决定如何处理其余部分之前对ASN.1编码消息的有限部分感兴趣,则可以选择仅解码这个小部分。情况可能是服务器必须决定消息的收件人。收件人可能对整个邮件感兴趣,但服务器可能是您想要减少不必要负载的瓶颈。

而不是做两个完整的解码解码的正常情况),一个在服务器和一个在收件人,只需要做一个专门的解码(在服务器)和另一个完整的解码(在收件人)。本节介绍以下两个专用解码器,它们支持解决此类问题和类似问题:

  • 独家解码
  • 选定的解码

该函数仅在使用BER(选项ber)时才提供。

4.1独占解码

独占解码的基本思想是指定您想排除的部分消息被解码。这些部分保持编码,并作为二进制文件返回到值结构中。可以通过将它们传递给某个decode_part/2函数来对它们进行解码。大消息的性能增益很高。您可以执行独占解码和稍后的一个或多个部分解码,或者执行第二次完整解码而不是两个或更多完整解码。

程序

要执行独占解码:

  • 第1步:确定专用解码功能的名称。
  • 第2步:在配置文件中包含以下指示信息:
- The name of the exclusive decode function
- The name of the ASN.1 specification
- A notation that tells which parts of the message structure to be excluded from decode
  • 步骤3编译附加选项asn1config。编译器搜索与ASN.1规范具有相同名称但具有扩展名的配置文件.asn1config。该配置文件与用于编译一组文件不同。参见章节Writing an Exclusive Decode Instruction.

User Interface

独占解码的运行时用户界面由以下两个功能组成:

  • 独占解码的函数,其用户在配置文件中决定的名称
  • 编译器decode_part/2在选择独占解码时生成一个函数。该功能解码在独占解码期间未解码的部分。

这两个函数在下面进行描述。

例如,如果独占解码功能具有名称decode_exclusive和ASN.1编码的消息Bin要被独占解码,则该调用如下:

{ok,Excl_Message} = 'MyModule':decode_exclusive(Bin)      

结果Excl_Message与完整解码具有相同的结构,除了顶部类型未解码的部分。未解码的部分位于格式结构中的位置{Type_Key,Undecoded_Value}

每个解码后的未解码部分必须decode_part/2按如下方式输入功能:

{ok,Part_Message} = 'MyModule':decode_part(Type_Key,Undecoded_Value)

编写独占解码指令

该指令按以下格式写入配置文件中:

Exclusive_Decode_Instruction = {exclusive_decode,{Module_Name,Decode_Instructions}}.

Module_Name = atom()

Decode_Instructions = [Decode_Instruction]+

Decode_Instruction = {Exclusive_Decode_Function_Name,Type_List}

Exclusive_Decode_Function_Name = atom()

Type_List = [Top_Type,Element_List]

Element_List = [Element]+

Element = {Name,parts} |
          {Name,undecoded} |
          {Name,Element_List}

Top_Type = atom()

Name = atom()

该指令必须是以点结尾的有效Erlang术语。

描述Type_List了从顶级类型到每个未解码子组件的“路径”。路径的顶级类型是原子,它的名称。以下每个组件/类型的操作都由一个描述{Name,parts}, {Name,undecoded}, {Name,Element_List}

这些行动的用途和影响如下:

  • {Name,undecoded} - 说明在独占解码期间元素未解码。类型Name可以是任何ASN.1类型。元素Name的值在顶层类型的值结构中作为元组返回(如前一节所述)。
  • {Name,parts}- 类型Name可以是其中之一SEQUENCE OFSET OF。这个动作意味着不同的组件将Name被解码。该值Name作为元组返回(如前一节所述),其中第二个元素是二进制文件列表。这是因为Erlang中的a SEQUENCE OF或a 的表示形式SET OF是其内部类型的列表。该列表中的任何元素或整个列表都可以通过函数进行解码decode_part
  • {Name,Element_List}- 当一个或多个子类型Name被独占解码时使用此动作。

Name在这些操作中可以是a SEQUENCE OF或a 的组件名称SET OF,或a中的替代名称CHOICE

示例

在这个例子中,使用了以下ASN.1规范的定义:

GUI DEFINITIONS AUTOMATIC TAGS ::=

BEGIN

Action ::= SEQUENCE 
 { 
   number  INTEGER DEFAULT 15,
   handle  [0] Handle DEFAULT {number  12, on  TRUE}
 }

Key ::= [11] EXPLICIT Button
Handle ::= [12] Key
Button ::= SEQUENCE 
 {
   number  INTEGER,
   on  BOOLEAN
 }

Window ::= CHOICE 
 {
   vsn INTEGER,
   status E
 }

Status ::= SEQUENCE 
 {
   state INTEGER,
   buttonList SEQUENCE OF Button,
   enabled BOOLEAN OPTIONAL,
   actions CHOICE {
     possibleActions SEQUENCE OF Action,
     noOfActions INTEGER
   }
 }


END

如果Button是顶级类型,并且需要number从解码中排除组件,Type_List则在配置文件中的指令是['Button',[{number,undecoded}]]。如果你打电话解码功能decode_Button_exclusiveDecode_Instruction{decode_Button_exclusive,['Button',[{number,undecoded}]]}

另一种顶级类型是Window其子类型的子组件动作Status和组件的部分buttonList将被解码。对于这种类型,该函数被命名decode__Window_exclusive。完整的Exclusive_Decode_Instruction配置如下:

{exclusive_decode,{'GUI',
	[{decode_Window_exclusive,['Window',[{status,[{buttonList,parts},{actions,undecoded}]}]]},
	 {decode_Button_exclusive,['Button',[{number,undecoded}]]}]}}.

下图显示了Window:status消息的字节。部件buttonListactions被排除在解码。只有stateenabled当被解码decode__Window_exclusive被调用。

图4.1:窗口的字节:状态消息

编译GUI.asn包含配置文件的过程如下:

unix> erlc -bber +asn1config GUI.asn

erlang> asn1ct:compile('GUI', [ber,asn1config]).

该模块可以使用如下:

1> Button_Msg = {'Button',123,true}.
{'Button',123,true}
2> {ok,Button_Bytes} = 'GUI':encode('Button',Button_Msg).
{ok,[<<48>>,
     [6],
     [<<128>>,
      [1],
      123],
     [<<129>>,
      [1],
      255]]}
3> {ok,Exclusive_Msg_Button} = 'GUI':decode_Button_exclusive(list_to_binary(Button_Bytes)).
{ok,{'Button',{'Button_number',<<28,1,123>>},
         true}}
4> 'GUI':decode_part('Button_number',<<128,1,123>>).
{ok,123}
5> Window_Msg = 
{'Window',{status,{'Status',35,
              [{'Button',3,true},
               {'Button',4,false},
               {'Button',5,true},
               {'Button',6,true},
               {'Button',7,false},
               {'Button',8,true},
               {'Button',9,true},
               {'Button',10,false},
               {'Button',11,true},
               {'Button',12,true},
               {'Button',13,false},
               {'Button',14,true}],
              false,
              {possibleActions,[{'Action',16,{'Button',17,true}}]}}}}. 
{'Window',{status,{'Status',35,
              [{'Button',3,true},
               {'Button',4,false},
               {'Button',5,true},
               {'Button',6,true},
               {'Button',7,false},
               {'Button',8,true},
               {'Button',9,true},
               {'Button',10,false},
               {'Button',11,true},
               {'Button',12,true},
               {'Button',13,false},
               {'Button',14,true}],
              false,
              {possibleActions,[{'Action',16,{'Button',17,true}}]}}}}
6> {ok,Window_Bytes}='GUI':encode('Window',Window_Msg).
{ok,[<<161>>,
     [127],
     [<<128>>, ...


8> {ok,{status,{'Status',Int,{Type_Key_SeqOf,Val_SEQOF},
BoolOpt,{Type_Key_Choice,Val_Choice}}}}=
'GUI':decode_Window_status_exclusive(list_to_binary(Window_Bytes)).
{ok,{status,{'Status',35,
        {'Status_buttonList',[<<48,6,128,1,3,129,1,255>>,
                              <<48,6,128,1,4,129,1,0>>,
                              <<48,6,128,1,5,129,1,255>>,
                              <<48,6,128,1,6,129,1,255>>,
                              <<48,6,128,1,7,129,1,0>>,
                              <<48,6,128,1,8,129,1,255>>,
                              <<48,6,128,1,9,129,1,255>>,
                              <<48,6,128,1,10,129,1,0>>,
                              <<48,6,128,1,11,129,1,255>>,
                              <<48,6,128,1,12,129,1,255>>,
                              <<48,6,128,1,13,129,1,0>>,
                              <<48,6,128,1,14,129,1,255>>]},
        false,
        {'Status_actions',
<<163,21,160,19,48,17,2,1,16,160,12,172,10,171,8,48,6,128,1,...>>}}}}
10> 'GUI':decode_part(Type_Key_SeqOf,Val_SEQOF).
{ok,[{'Button',3,true},
     {'Button',4,false},
     {'Button',5,true},
     {'Button',6,true},
     {'Button',7,false},
     {'Button',8,true},
     {'Button',9,true},
     {'Button',10,false},
     {'Button',11,true},
     {'Button',12,true},
     {'Button',13,false},
     {'Button',14,true}]}
11> 'GUI':decode_part(Type_Key_SeqOf,hd(Val_SEQOF)).
{ok,{'Button',3,true}}
12> 'GUI':decode_part(Type_Key_Choice,Val_Choice).  
{ok,{possibleActions,[{'Action',16,{'Button',17,true}}]}}

4.2选择性解码

这种专门的解码解码构造值的子类型,是提取子值的最快方法。这种解码通常用于想要检查(例如)版本号的情况,以便能够决定如何处理整个值。结果将返回为{ok,Value}{error,Reason}

程序

要执行选择性解码:

  • 第1步:在配置文件中包含以下说明:
- The name of the user function
- The name of the ASN.1 specification
- A notation that tells which part of the type to be decoded
  • 第2步:编译附加选项asn1config。编译器搜索与ASN.1规范具有相同名称但具有扩展名的配置文件.asn1config。在同一个文件中,您还可以提供独占解码的配置规范。生成的Erlang模块具有保留编码/解码和添加专用解码功能的常用功能。

用户界面

唯一的新用户界面功能是用户在配置文件中提供的功能。该功能由ModuleName:FunctionName符号开始。

例如,如果配置文件包含规范,{selective_decode,{'ModuleName',[{selected_decode_Window,TypeList}]}}则通过进行选择性解码{ok,Result}='ModuleName':selected_decode_Window(EncodedBinary).

编写选择性解码指令

一个或多个选择性解码功能可以在配置文件中描述。使用以下表示法:

Selective_Decode_Instruction = {selective_decode,{Module_Name,Decode_Instructions}}.

Module_Name = atom()

Decode_Instructions = [Decode_Instruction]+

Decode_Instruction = {Selective_Decode_Function_Name,Type_List}

Selective_Decode_Function_Name = atom()

Type_List = [Top_Type|Element_List]

Element_List = Name|List_Selector

Name = atom()

List_Selector = [integer()]

该指令必须是以点结尾的有效Erlang术语。

  • Module_Name 与ASN.1规范的名称相同,但没有扩展名。
  • Decode_Instruction是一个元组,其中包含您选择的函数名称和顶级类型的组件,这些组件将导致您想要解码的单一类型。确保选择与任何生成的函数不同的函数名称。
  • 第一个元素Type_List是编码消息的最高类型。在Element_List它后面跟着每个导致选定类型的组件名称。
  • Element_List除了最后一个名称,其中的每个名称都必须是构造类型,可以是任何类型。
  • List_Selector使得可以选择aa SEQUENCE OF或a 中的编码组件之一SET OF。也可以在该组件中继续并选择一个子类型进行解码。所以,在Type_List:中['Window',status,buttonList,[1],number],组件buttonList必须是type SEQUENCE OFSET OF

在该示例中,number选择了第一个编码元素的组成SEQUENCE OF buttonList部分。这适用于部分中的ASN.1规范Writing an Exclusive Decode Instruction

另一个例子

在这个例子中,使用了与Section中相同的ASN.1规范Writing an Exclusive Decode Instruction。以下是有效的选择性解码指令:

{selective_decode,
    {'GUI',
        [{selected_decode_Window1,
            ['Window',status,buttonList, 
             [1],
             number]},
 {selected_decode_Action,
     ['Action',handle,number]},
 {selected_decode_Window2,
     ['Window',
      status,
      actions,
      possibleActions,
      [1],
      handle,number]}]}}.

第一条指令{selected_decode_Window1,['Window',status,buttonList,[1],number]}在前一节中介绍。

第二条指令{selected_decode_Action,['Action',handle,number]}将类型的组件number中的handle组件Action。如果该值是ValAction = {'Action',17,{'Button',4711,false}},内部值4711将被选择selected_decode_Action。在Erlang终端中,它看起来如下所示:

ValAction = {'Action',17,{'Button',4711,false}}.
{'Action',17,{'Button',4711,false}}
7> {ok,Bytes}='GUI':encode('Action',ValAction).
...
8> BinBytes = list_to_binary(Bytes).
<<48,18,2,1,17,160,13,172,11,171,9,48,7,128,2,18,103,129,1,0>>
9> 'GUI':selected_decode_Action(BinBytes).
{ok,4711}
10>

第三条指令的['Window',status,actions,possibleActions,[1],handle,number]工作原理如下:

  • 第1步:以类型开始Window
  • 步骤2:注意到成分statusWindow即类型的Status
  • 第3步:采取类型的行动Status
  • 第4步:采取possibleActions内部定义的CHOICE类型。
  • 第5步:进入SEQUENCE OFby 的第一个组件[1]。该组件是类型的Action
  • 第6步:采取组件handle
  • 第7步:采取number类型的组成部分Button

下图显示了哪些组件在TypeList ['Window',status,actions,possibleActions,[1],handle,number]

图4.2:在配置文件中指定的元素用于窗口消息中子值的选择性解码

在下图中,只有被标记的元素被解码为selected_decode_Window2

图4.3:窗口字节:状态消息

通过以下示例,您可以检查两者selected_decode_Window2selected_decode_Window1解码值的预期子值Val

1> Val = {'Window',{status,{'Status',12,
                    [{'Button',13,true},
                     {'Button',14,false},
                     {'Button',15,true},
                     {'Button',16,false}],
                    true,
                    {possibleActions,[{'Action',17,{'Button',18,false}},
                                      {'Action',19,{'Button',20,true}},
                                      {'Action',21,{'Button',22,false}}]}}}}
2> {ok,Bytes}='GUI':encode('Window',Val).
...
3> Bin = list_to_binary(Bytes).
<<161,101,128,1,12,161,32,48,6,128,1,13,129,1,255,48,6,128,1,14,129,1,0,48,6,128,1,15,129,...>>
4> 'GUI':selected_decode_Window1(Bin).
{ok,13}
5> 'GUI':selected_decode_Window2(Bin).
{ok,18}

请注意,馈送到选择性解码函数的值必须是二进制。

4.3性能

为了说明使用专用解码器可能的性能增益,已经执行了一些措施。选择性,排他性和完整解码(正常情况)之间结果的相关数字取决于类型的结构,消息的大小以及指定选择性和排它性解码的级别。

ASN.1规范,消息和配置

规格GUIMEDIA-GATEWAY-CONTROL在测试中使用。

对于GUI规范的配置如下:

{selective_decode,
  {'GUI',
    [{selected_decode_Window1,
         ['Window',
          status,buttonList,
          [1],
          number]},
     {selected_decode_Window2,
         ['Window',
          status,
          actions,
          possibleActions,
          [1],
          handle,number]}]}}.
     {exclusive_decode,
         {'GUI',
            [{decode_Window_status_exclusive,
                ['Window',
                 [{status,
                     [{buttonList,parts},
                      {actions,undecoded}]}]]}]}}.

MEDIA-GATEWAY-CONTROL配置情况如下:

{exclusive_decode,
  {'MEDIA-GATEWAY-CONTROL',
    [{decode_MegacoMessage_exclusive,
        ['MegacoMessage',
         [{authHeader,undecoded},
          {mess,
             [{mId,undecoded},
              {messageBody,undecoded}]}]]}]}}.
{selective_decode,
  {'MEDIA-GATEWAY-CONTROL',
    [{decode_MegacoMessage_selective,
         ['MegacoMessage',mess,version]}]}}.

相应的值如下:

{'Window',{status,{'Status',12,
              [{'Button',13,true},
               {'Button',14,false},
               {'Button',15,true},
               {'Button',16,false},
               {'Button',13,true},
               {'Button',14,false},
               {'Button',15,true},
               {'Button',16,false},
               {'Button',13,true},
               {'Button',14,false},
               {'Button',15,true},
               {'Button',16,false}],
              true,
              {possibleActions,
                 [{'Action',17,{'Button',18,false}},
                  {'Action',19,{'Button',20,true}},
                  {'Action',21,{'Button',22,false}},
                  {'Action',17,{'Button',18,false}},
                  {'Action',19,{'Button',20,true}},
                  {'Action',21,{'Button',22,false}},
                  {'Action',17,{'Button',18,false}},
                  {'Action',19,{'Button',20,true}},
                  {'Action',21,{'Button',22,false}},
                  {'Action',17,{'Button',18,false}},
                  {'Action',19,{'Button',20,true}},
                  {'Action',21,{'Button',22,false}},
                  {'Action',17,{'Button',18,false}},
                  {'Action',19,{'Button',20,true}},
                  {'Action',21,{'Button',22,false}},
                  {'Action',17,{'Button',18,false}},
                  {'Action',19,{'Button',20,true}},
                  {'Action',21,{'Button',22,false}}]}}}}


{'MegacoMessage',asn1_NOVALUE,
  {'Message',1,
    {ip4Address,
      {'IP4Address',[125,125,125,111],55555}},
  {transactions,
    [{transactionReply,
      {'TransactionReply',50007,asn1_NOVALUE,
       {actionReplies,
        [{'ActionReply',0,asn1_NOVALUE,asn1_NOVALUE,
          [{auditValueReply,{auditResult,{'AuditResult',
            {'TerminationID',[],[255,255,255]},
             [{mediaDescriptor,
               {'MediaDescriptor',asn1_NOVALUE,
                {multiStream,
                 [{'StreamDescriptor',1,
                   {'StreamParms',
                    {'LocalControlDescriptor',
                     sendRecv,
                     asn1_NOVALUE,
                     asn1_NOVALUE,
                     [{'PropertyParm',
                       [0,11,0,7],
                       [[52,48]],
                       asn1_NOVALUE}]},
                    {'LocalRemoteDescriptor',
                     [[{'PropertyParm',
                        [0,0,176,1],
                        [[48]],
                        asn1_NOVALUE},
                       {'PropertyParm',
                         [0,0,176,8],
                         [[73,78,32,73,80,52,32,49,50,53,46,49,
                           50,53,46,49,50,53,46,49,49,49]],
                         asn1_NOVALUE},
                       {'PropertyParm',
                         [0,0,176,15],
                         [[97,117,100,105,111,32,49,49,49,49,32,
                           82,84,80,47,65,86,80,32,32,52]],
                         asn1_NOVALUE},
                       {'PropertyParm',
                         [0,0,176,12],
                         [[112,116,105,109,101,58,51,48]],
                         asn1_NOVALUE}]]},
                    {'LocalRemoteDescriptor',
                     [[{'PropertyParm',
                         [0,0,176,1],
                         [[48]],
                         asn1_NOVALUE},
                       {'PropertyParm',
                         [0,0,176,8],
                         [[73,78,32,73,80,52,32,49,50,52,46,49,50,
                           52,46,49,50,52,46,50,50,50]],
                         asn1_NOVALUE},
                       {'PropertyParm',
                         [0,0,176,15],
                         [[97,117,100,105,111,32,50,50,50,50,32,82,
                           84,80,47,65,86,80,32,32,52]],
                         asn1_NOVALUE},
                       {'PropertyParm',
                         [0,0,176,12],
                         [[112,116,105,109,101,58,51,48]],
                         asn1_NOVALUE}]]}}}]}}},
              {packagesDescriptor,
               [{'PackagesItem',[0,11],1},
                {'PackagesItem',[0,11],1}]},
              {statisticsDescriptor,
               [{'StatisticsParameter',[0,12,0,4],[[49,50,48,48]]},
                {'StatisticsParameter',[0,11,0,2],[[54,50,51,48,48]]},
                {'StatisticsParameter',[0,12,0,5],[[55,48,48]]},
                {'StatisticsParameter',[0,11,0,3],[[52,53,49,48,48]]},
                {'StatisticsParameter',[0,12,0,6],[[48,46,50]]},
                {'StatisticsParameter',[0,12,0,7],[[50,48]]},
                {'StatisticsParameter',[0,12,0,8],[[52,48]]}]}]}}}]}]}}}]}}}

编码值的大小为458字节GUI和464字节MEDIA-GATEWAY-CONTROL

结果

测试中的ASN.1规范是用选项ber_bin, optimize, driverasn1config。省略选项driver用于提供更高的价值decodedecode_part。这些测试并未使用NIF重新运行,但预计比链接的驱动程序的性能提高约5%。

测试程序在该值上运行10000次解码,从而产生经过时间以微秒为单位的输出,用于解码总数。

功能

时间(微秒)

解码类型

ASN.1规范

时间百分比与完整解码

decode_MegacoMessage_selective / 1

374045

可选择的

媒体网关-CONTROL

8.3

decode_MegacoMessage_exclusive / 1

621107

独家

媒体网关-CONTROL

13.8

解码/ 2

4507457

完成

媒体网关-CONTROL

100

selected_decode_Window1 / 1

449585

可选择的

GUI

7.6

selected_decode_Window2 / 1

890666

可选择的

GUI

15.1

decode_Window_status_exclusive / 1

1251878

独家

GUI

21.3

解码/ 2

5889197

完成

GUI

100

知道完全解码,decode_part排除部分之后的排他解码和选择解码之间的关系以及完整解码之间的关系也是有意义的。有些情况可以与此模拟进行比较,例如,检查一个子值,然后检查整个值。下表显示了该测试的数据。循环次数和时间单位与前一次测试相同。

操作

功能

时间(微秒)

ASN.1规范

时间百分比与完整解码

完成

解码/ 2

4507457

媒体网关-CONTROL

100

选择性和完整

decode_MegacoMessage_selective / 1

4881502

媒体网关-CONTROL

108.3

Exclusive和decode_part

decode_MegacoMessage_exclusive / 1

5481034

媒体网关-CONTROL

112.3

完成

解码/ 2

5889197

GUI

100

选择性和完整

selected_decode_Window1 / 1

6337636

GUI

107.6

选择性和完整

selected_decode_Window2 / 1

6795319

GUI

115.4

Exclusive和decode_part

decode_Window_status_exclusive / 1

6249200

GUI

106.1

其他ASN.1类型和值可能与这些数字有很大不同。因此,在任何情况下,如果您打算使用这些解码器中的任何一种,执行一些测试以显示您是否将有利于您的目的,这一点非常重要。

最后的评论

  • 使用选择性和独占性解码代替完整解码的收益越大,值越大,解码结构越深。
  • 如果您只对一个子值感兴趣,请使用选择性解码而不是独占解码。
  • decode_part如果将部件发送到不同的服务器进行解码,或者如果您在某些情况下对所有部件不感兴趣,则解码之后的独占解码很有吸引力。
  • 最快的选择性解码是当解码类型是原始类型并且在顶部类型的结构中不那么深时。selected_decode_Window2解码高构建值,这解释了为什么这个操作相对较慢。
  • 它可以根据具体情况而异,其中选择性/完整解码或独占/部分解码的组合是最快的。
Erlang 20

Erlang 是一种通用的面向并发的编程语言,可应付大规模开发活动的程序设计语言和运行环境。

主页 https://www.erlang.org/
源码 https://github.com/erlang/otp
版本 20
发布版本 20.1