查看源代码 专用解码

当性能至关重要,并且在决定如何处理 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 OFSET OF。该操作意味着 Name 的不同组件保持未解码状态。Name 的值以元组形式(如前一节所述)返回,其中第二个元素是二进制列表。这是因为 SEQUENCE OFSET OF 在 Erlang 中的表示形式是其内部类型的列表。可以通过函数 decodepart 解码此列表中的任何元素或整个列表。

  • {Name,ElementList} - 当 Name 的一个或多个子类型被独占解码时,使用此操作。

这些操作中的 Name 可以是 SEQUENCE OFSET 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 消息的字节。组件 buttonListactions 从解码中排除。当调用 decode__Window_exclusive 时,仅解码 stateenabled

Bytes of a Window:status Message

下面是一个模块示例。请注意,选项 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 OFSET OF 中编码的组件之一。也可以在该组件中进一步选择,并选取要解码的子类型。因此,在 TypeList 中:['Window',status,buttonList,[1],number],组件 buttonList 必须是 SEQUENCE OFSET 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]} 获取类型为 Actionhandle 组件中的 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: 获取类型为 StatusWindow 的组件 status
  • 步骤 3: 获取类型为 Statusactions
  • 步骤 4: 获取内部定义的 CHOICE 类型的 possibleActions
  • 步骤 5: 通过 [1] 进入 SEQUENCE OF 的第一个组件。该组件的类型为 Action
  • 步骤 6: 获取组件 handle
  • 步骤 7: 获取类型为 Button 的组件 number

下图显示了 TypeList ['Window',status,actions,possibleActions,[1],handle,number] 中包含的组件

Elements Specified in Configuration File for Selective Decode of a Subvalue in a Window Message

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

Bytes of a Window:status Message

通过以下示例,您可以检查 selected_decode_Window2selected_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