查看源代码 专用解码
当性能至关重要,并且在决定如何处理 ASN.1 编码消息的其余部分之前,只对消息的有限部分感兴趣时,可以选择仅解码消息的一部分。这种情况可能发生在需要决定消息接收者的服务器上。接收者可能对整个消息感兴趣,但服务器可能是您想要避免不必要负载的瓶颈。
无需进行两次完整解码(通常的解码情况),一次在服务器中,一次在接收者中,只需进行一次专用解码(在服务器中)和另一次完整解码(在接收者中)。本节描述以下专用解码功能
- 独占解码
- 选择性解码
此功能仅在使用 BER
(选项 ber
)时提供。
独占解码
独占解码的基本思想是指定您希望从解码中排除的消息部分。这些部分保持编码状态,并以二进制形式在值结构中返回。未解码的部分可以在以后通过调用 decode_part/2
函数进行解码。
步骤
要执行独占解码,请执行以下步骤:
步骤 1: 确定独占解码的函数名称。
步骤 2: 在配置文件中包含以下说明
- 独占解码函数的名称
- ASN.1 规范的名称
- 一个表示从解码中排除的消息结构部分的符号
步骤 3: 使用附加选项
asn1config
进行编译。编译器会搜索与 ASN.1 规范同名但扩展名为.asn1config
的配置文件。此配置文件与用于编译一组文件的配置文件不同。请参阅 编写独占解码指令 部分。
用户界面
独占解码的运行时用户界面包括以下两个函数
- 用于独占解码的函数,其名称由用户在配置文件中决定
- 当启用独占解码时,由 ASN.1 编译器生成的
decode_part/2
函数。此函数解码在独占解码期间未解码的部分。
以下将介绍这两个函数。
例如,如果独占解码函数的名称为 decode_exclusive
,并且要独占解码的 ASN.1 编码消息为 Bin
,则调用如下:
{ok,ExclMessage} = 'MyModule':decode_exclusive(Bin)
结果 ExclMessage
具有与完整解码相同的结构,只是顶级类型中未解码的部分除外。未解码的部分以 {TypeKey,UndecodedValue}
格式存在于结构中的相应位置。
每个要解码的未解码部分都必须馈送到 decode_part/2
函数,如下所示:
{ok,PartMessage} = 'MyModule':decode_part(TypeKey, UndecodedValue)
编写独占解码指令
此指令以以下格式写入配置文件:
ExclusiveDecodeInstruction = {exclusive_decode,{ModuleName,DecodeInstructions}}.
ModuleName = atom()
DecodeInstructions = [DecodeInstruction]+
DecodeInstruction = {ExclusiveDecodeFunctionName,TypeList}
ExclusiveDecodeFunctionName = atom()
TypeList = [TopType,ElementList]
ElementList = [Element]+
Element = {Name,parts} |
{Name,undecoded} |
{Name,ElementList}
TopType = atom()
Name = atom()
该指令必须是一个以点号结尾的有效 Erlang 项。
在 TypeList
中,描述了从顶级类型到每个未解码子组件的路径。TopType
是 ASN.1 规范中顶级类型的名称。ElementList
中每个组件的操作由以下之一描述:
{Name,parts}
{Name,undecoded}
{Name,ElementList}
这些操作的用途和效果如下:
{Name,undecoded}
- 使元素保持未解码状态。Name
的类型可以是任何 ASN.1 类型。Name
元素的值以元组形式(如前一节所述)在顶级类型的值结构中返回。{Name,parts}
-Name
的类型必须是SEQUENCE OF
或SET OF
。该操作意味着Name
的不同组件保持未解码状态。Name
的值以元组形式(如前一节所述)返回,其中第二个元素是二进制列表。这是因为SEQUENCE OF
或SET OF
在 Erlang 中的表示形式是其内部类型的列表。可以通过函数decodepart
解码此列表中的任何元素或整个列表。{Name,ElementList}
- 当Name
的一个或多个子类型被独占解码时,使用此操作。
这些操作中的 Name
可以是 SEQUENCE OF
或 SET OF
的组件名称,也可以是 CHOICE
中的一个备选方案的名称。
示例
在此示例中,使用了以下 ASN.1 规范中的定义:
GUI DEFINITIONS AUTOMATIC TAGS ::= BEGIN
Action ::= SEQUENCE {
number INTEGER DEFAULT 15,
handle Handle DEFAULT {number 12, on TRUE}
}
Key ::= Button
Handle ::= Key
Button ::= SEQUENCE {
number INTEGER,
on BOOLEAN
}
Window ::= CHOICE {
vsn INTEGER,
status Status
}
Status ::= SEQUENCE {
state INTEGER,
buttonList SEQUENCE OF Button,
enabled BOOLEAN OPTIONAL,
actions CHOICE {
possibleActions SEQUENCE OF Action,
noOfActions INTEGER
}
}
END
如果 Button
是一个顶级类型,并且需要从解码中排除组件 number
,则配置文件中指令中的 TypeList
为 ['Button',[{number,undecoded}]]
。如果您调用解码函数 decode_Button_exclusive
,则 DecodeInstruction
为 {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
从解码中排除。当调用 decode__Window_exclusive
时,仅解码 state
和 enabled
。
下面是一个模块示例。请注意,选项 no_ok_wrapper
用于使示例更简洁。
1> asn1ct:compile('GUI', [ber,asn1config,no_ok_wrapper]).
ok
2> rr('GUI').
['Action','Button','Status']
3> ButtonMsg = #'Button'{number=123,on=true}.
#'Button'{number = 123,on = true}
4> ButtonBytes = 'GUI':encode('Button', ButtonMsg).
<<48,6,128,1,123,129,1,255>>
5> ExclusiveMsgButton = 'GUI':decode_Button_exclusive(ButtonBytes).
#'Button'{number = {'Button_number',<<128,1,123>>},
on = true}
6> {UndecKey,UndecBytes} = ExclusiveMsgButton#'Button'.number.
{'Button_number',<<128,1,123>>}
7> 'GUI':decode_part(UndecKey, UndecBytes).
123
8> WindowMsg =
{status,{'Status',35,
[{'Button',3,true},
{'Button',4,false},
{'Button',5,true},
{'Button',6,true},
{'Button',7,false}],
false,
{possibleActions,[{'Action',16,{'Button',17,true}}]}}}.
{status,#'Status'{state = 35,
buttonList = [#'Button'{number = 3,on = true},
#'Button'{number = 4,on = false},
#'Button'{number = 5,on = true},
#'Button'{number = 6,on = true},
#'Button'{number = 7,on = false}],
enabled = false,
actions = {possibleActions,[#'Action'{number = 16,
handle = #'Button'{number = 17,on = true}}]}}}
9> WindowBytes = 'GUI':encode('Window', WindowMsg).
<<161,65,128,1,35,161,40,48,6,128,1,3,129,1,255,48,6,128,
1,4,129,1,0,48,6,128,1,5,129,...>>
10> {status,#'Status'{buttonList={UndecWindowKey,UndecWindowParts}}} =
'GUI':decode_Window_exclusive(WindowBytes).
{status,#'Status'{state = 35,
buttonList = {'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>>]},
enabled = false,
actions = {'Status_actions',<<163,15,160,13,48,11,128,
1,16,161,6,128,1,17,129,
1,255>>}}}
11> 'GUI':decode_part(UndecWindowKey, UndecWindowParts).
[#'Button'{number = 3,on = true},
#'Button'{number = 4,on = false},
#'Button'{number = 5,on = true},
#'Button'{number = 6,on = true},
#'Button'{number = 7,on = false}]
12> 'GUI':decode_part(UndecWindowKey, hd(UndecWindowParts)).
#'Button'{number = 3,on = true}
13> {status,#'Status'{actions={ChoiceKey,ChoiceUndec}}} = v(10).
{status,#'Status'{state = 35,
buttonList = {'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>>]},
enabled = false,
actions = {'Status_actions',<<163,15,160,13,48,11,128,
1,16,161,6,128,1,17,129,
1,255>>}}}
14> 'GUI':decode_part(ChoiceKey, ChoiceUndec).
{possibleActions,[#'Action'{number = 16,
handle = #'Button'{number = 17,on = true}}]}
选择性解码
选择性解码解码构造值的单个子类型。这是提取子值的最快方法。选择性解码通常用于当您想检查例如版本号以决定如何处理整个值时。
步骤
要执行选择性解码,请执行以下步骤:
步骤 1: 在配置文件中包含以下说明
- 用户函数的名称
- ASN.1 规范的名称
- 一个表示要解码的类型部分的符号
步骤 2: 使用附加选项
asn1config
进行编译。编译器会搜索与 ASN.1 规范同名但扩展名为.asn1config
的配置文件。在同一个文件中,您还可以为独占解码提供配置规范。生成的 Erlang 模块保留了通常的编码/解码功能,并添加了专用的解码功能。
用户界面
唯一新的用户界面函数是用户在配置文件中提供的函数。
例如,如果配置文件包含规范 {selective_decode,{'ModuleName',[{selected_decode_Window,TypeList}]}}
,则通过 {ok,Result} = 'ModuleName':selected_decode_Window(EncodedBinary).
执行选择性解码。
编写选择性解码指令
可以在配置文件中描述一个或多个选择性解码函数。使用以下符号:
SelectiveDecodeInstruction = {selective_decode,{ModuleName,DecodeInstructions}}.
ModuleName = atom()
DecodeInstructions = [DecodeInstruction]+
DecodeInstruction = {SelectiveDecodeFunctionName,TypeList}
SelectiveDecodeFunctionName = atom()
TypeList = [TopType|ElementList]
ElementList = Name|ListSelector
Name = atom()
ListSelector = [integer()]
该指令必须是一个以点号结尾的有效 Erlang 项。
ModuleName
与 ASN.1 规范的名称相同,但不带扩展名。DecodeInstruction
是一个元组,包含您选择的函数名称和从顶级类型到您要解码的单个类型的组件。请确保选择的函数名称与任何生成的函数名称不同。TypeList
的第一个元素是编码消息的顶级类型。在ElementList
中,它后面跟着每个通向所选类型的组件名称。- 在
ElementList
中的每个名称都必须是构造类型,除了最后一个名称,它可以是任何类型。 ListSelector
可以选择SEQUENCE OF
或SET OF
中编码的组件之一。也可以在该组件中进一步选择,并选取要解码的子类型。因此,在TypeList
中:['Window',status,buttonList,[1],number]
,组件buttonList
必须是SEQUENCE OF
或SET OF
类型。
在示例中,选择了 SEQUENCE OF
buttonList
中第一个编码元素的组件 number
。这适用于 编写独占解码指令 部分中的 ASN.1 规范。
示例
在此示例中,使用了与 编写独占解码指令 部分相同的 ASN.1 规范。以下是一个有效的选择性解码指令:
{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]}
获取类型为 Action
的 handle
组件中的 number
组件。如果值为 ValAction = {'Action',17,{'Button',4711,false}}
,则内部值 4711 将由 selected_decode_Action
选取。在 Erlang 终端中,它看起来如下所示:
1> asn1ct:compile('GUI', [ber,asn1config,no_ok_wrapper]).
ok
2> ValAction = {'Action',17,{'Button',4711,false}}.
{'Action',17,{'Button',4711,false}}
3> Bytes = 'GUI':encode('Action',ValAction).
<<48,18,2,1,17,160,13,172,11,171,9,48,7,128,2,18,103,129,1,0>>
4> 'GUI':selected_decode_Action(Bytes).
4711
第三个指令 ['Window',status,actions,possibleActions,[1],handle,number]
的工作方式如下:
- 步骤 1: 从
Window
类型开始。 - 步骤 2: 获取类型为
Status
的Window
的组件status
。 - 步骤 3: 获取类型为
Status
的actions。 - 步骤 4: 获取内部定义的
CHOICE
类型的possibleActions
。 - 步骤 5: 通过
[1]
进入SEQUENCE OF
的第一个组件。该组件的类型为Action
。 - 步骤 6: 获取组件
handle
。 - 步骤 7: 获取类型为
Button
的组件number
。
下图显示了 TypeList
['Window',status,actions,possibleActions,[1],handle,number]
中包含的组件
在下图中,只有标记的元素由 selected_decode_Window2
解码
通过以下示例,您可以检查 selected_decode_Window2
和 selected_decode_Window1
都解码了值 Val
的预期子值
1> Val = {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> Bin = 'GUI':encode('Window',Val).
<<161,89,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).
13
5> 'GUI':selected_decode_Window2(Bin).
18