首先让我们来学习一下ASN.1的Constructors类型 。
ASN.1 constructors 类型可以将简单的预定义类型封装成复杂的数据类型。而且,这种操作可以是嵌套的,比如,constructors中还可以包含其他的constructors类型。ASN.1的constructors类型有:
SEQUENCE是一个表示有序元素集合的数据类型。集合可能为空,集合中的元素可以是任意ASN.1类型,并且元素类型可以不同。集合中的元素还可以被命名。这类似于C/C++中的结构体。
SEQUENCE OF 是一个表示有序元素集合的数据类型,集合可以为空。集合中的元素可以是任意ASN.1类型,但是要求所有元素为同一类型。集合中的元素可以被命名。这很类似于C/C++中的有序数组/链表。
SET 是一个表示无序元素集合的数据类型,集合可以为空。集合中的元素可以是任意ASN.1类型,并且元素类型可以不同。集合中的元素可以被命名。这同样类似于C/C++中的结构体。
SET OF 是一个表示无序元素集合的数据类型,集合可以为空。集合中的元素可以是任意ASN.1类型,但是要求所有元素为同一类型。集合中的元素可以被命名。这很类似于C/C++中的无序数组/链表。
一眼看上去似乎SEQUENCE 和SET是一样的。其实不同就在于SEQUENCE是有序的,而SET不是。比如,在上面的例子中,SET在编码时,字段field2就可以比field1先发送,而对SEQUENCE,我们希望field1必须在field2之前先处理。
SEQUENCE OF 和SET OF的区别也就在这。
总而言之,SEQUENCE 和SEQUENCE OF依赖顺序来避免模糊。而SET 和SET OF就依赖数据类型或者每个元素的标签来唯一区分每个元素。
SEQUENCE 等集合中出现的关键字OPTIONAL表示集合中的那个元素不是必须的:也就是可以存在也可以省略。 例如:
这对SEQUENCE OF, SET 和SET OF都适用。
在某些情况下,给指定类型的某个元素设定一个默认值在编码时是非常有用的。
CHOICE类型是一个定义一个或者多个类型的联合的数据类型。这个联合中的字段可以被命名。每一个CHOICE的实例,都必须指定为这个联合中的一个类型。例如:
以上内容翻译自http://www.lkn.ei.tum.de/arbeiten/faq/man/tau42_help/ttcncase8.html。
/*******************************************休息一下***************************************
下面我们来研究eSNACC的C代码生成和C运行时库对ASN.1 constructors的处理办法:
一、因为ASN.1允许不命名集合中的字段,但是C语言要求变量必须有名字,所以eSNACC对SETs, SEQUENCEs, 和CHOICEs中的空字段都会自动命名。名字依赖于该字段的类型名。
二、对SEQUENCE (of)和SET (of)中用OPTIONAL指定的可选元素,大部分都以指针实现。当指针不为NULL时那么就代表这个元素存在。不过当元素是OCTET STRINGs, BIT STRINGs 和OBJECT IDENTIFIERs类型时例外,因为这些类型很小,而且底层实现还包含一个内部的指针,利用那个指针就可以表明元素是否存在。具体请参加本系列前面的文章:对比特串、字节串、OBJECT IDENTIFIERs的编码和解码等。
三、总结:eSNACC对ASN.1 constructors->C类型转换规则:
SEQUENCE —> C struct
SET —> C struct
SEQUENCE OF —> 双向链表AsnList
SET OF —> 双向链表AsnList
ENUMERATED —> C enum
CHOICE : 比较有意思,我们以后再专门讨论。
ASN.1到C struct和enum就是直接的对应关系,很简单。这里我们主要看看eSNACC的双向链表AsnList。
看一下AsnList的定义:
typedef struct AsnListNode
{
struct AsnListNode *prev;
struct AsnListNode *next;
void *data; /**//* this must be the last field of this structure */
} AsnListNode;
typedef struct AsnList
{
AsnListNode *first;
AsnListNode *last;
AsnListNode *curr;
int count; /**//* number of elements in list */
int dataSize; /**//* space required in each node for the data */
} AsnList;
要注意AsnList与我们一般写的链表可能不同的是:他专门设计了一个头AsnList,而真正存放节点内容的是AsnListNode。AsnList中包含了头结点、尾节点、当前节点指针,节点元素的数目和每一个节点元素的数据大小(也就是为data分配内存时的值)。
利用AsnList,我们就能很方便的得知包括链表头尾指针、总数等的信息了。也能很方便的对整个链表进行遍历访问等操作了。
要说明的是:first节点的prev指针和last的next指针总是NULL。而且AsnList的dataSize字段必须在生成链表或者使用前初始化时就必须赋予明确的值,因为只是每创建一个AsnListNode时对data分配的内存大小数。
对AsnList的分析,我准备就只讲这些了。之所以不展开函数实现,是因为里面就是数据结构中的链表操作算法,想必大家都相当熟悉,所以不怕老生常谈,也担心班门弄斧,就算了吧。
不过,你可能发现有些问题:
1、上文中提到过:SEQUENCE OF是有序的,而SET OF无序的。那我们在编译成C语言时要怎么对应呢?我们看看eSNACC:他丝毫没有提这个,压根就无视这个性质!这有问题吗?
从ASN.1语义上来说,确实是没有体现,但是其实是没有问题的。因为ASN.1中所要求的有序无序,是说明数据编码和传输的问题:有序就是要保证数据定义的顺序。而我们转为C语言,本来就是按数据定义的顺序而转的,而编码解码自然就只能是C定义的顺序了,也就是说最后无论是对SEQUENCE OF还是SET OF,我们都保证了他是有序的,这当然没有问题了。
2、AsnList中存放数据的都是void*类型,这就不知道数据的原始类型了呀?
是的,AsnList中确实只是存放了void*,这导致类型丢失了。也就是你如果不小心存了不同类型的数据,他也一言不发的纵容了你。这是类型不安全的。BTW,在eSNACC的C++库中,就不存在这个问题了,这个留待以后对eSNACC的C++运行时库剖析时再说。
好了,本篇就到此了。