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_exclusive
,Decode_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
消息的字节。部件buttonList
和actions
被排除在解码。只有state
和enabled
当被解码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_Window2
并selected_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规范,消息和配置
规格GUI
并MEDIA-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, driver
和asn1config
。省略选项driver
用于提供更高的价值decode
和decode_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类型和值可能与这些数字有很大不同。因此,在任何情况下,如果您打算使用这些解码器中的任何一种,执行一些测试以显示您是否将有利于您的目的,这一点非常重要。
最后的评论
- 使用选择性和独占性解码代替完整解码的收益越大,值越大,解码结构越深。