4. Specialized Decodes

4专业解码

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

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

  • 独家解码

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

4.1独占解码

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

程序

要执行独占解码:

  • 第1步:确定专用解码功能的名称。

- 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_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在这些操作中可以是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规范的名称相同,但没有扩展名。

在该示例中,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

下图显示了哪些组件在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 / 1374045可选择的媒体网关-CONTROL8.3
decode_MegacoMessage_exclusive / 1621107独家媒体网关-CONTROL13.8
解码/ 24507457完成媒体网关-CONTROL100
selected_decode_Window1 / 1449585可选择的GUI7.6
selected_decode_Window2 / 1890666可选择的GUI15.1
decode_Window_status_exclusive / 11251878独家GUI21.3
解码/ 25889197完成GUI100

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

操作功能时间(微秒)ASN.1规范时间百分比与完整解码
完成解码/ 24507457媒体网关-CONTROL100
选择性和完整decode_MegacoMessage_selective / 14881502媒体网关-CONTROL108.3
Exclusive和decode_partdecode_MegacoMessage_exclusive / 15481034媒体网关-CONTROL112.3
完成解码/ 25889197GUI100
选择性和完整selected_decode_Window1 / 16337636GUI107.6
选择性和完整selected_decode_Window2 / 16795319GUI115.4
Exclusive和decode_partdecode_Window_status_exclusive / 16249200GUI106.1

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

最后的评论

  • 使用选择性和独占性解码代替完整解码的收益越大,值越大,解码结构越深。