c 语言指数序列,C 语言的数据序列化

数据结构的序列化是个很有效的工具。这几天在点窜本来的资本治理模块,碰着畴前做的几个数据文件解析的子模块,改得很烦,就从头思虑序列化的方案了。

Java 和 .Net 等,由于有完整的数据元信息,语言便提供了完美的序列化解决方案。C++

对此在语言设计上有所缺陷,所以并没有出格好的,被所有人接管的方案。

现存的 C++ serialization 方案多近似于 MFC 在二十年前的做法。尔后,boost

提供了一个看起来更完整的方案( boost.serialization )。所谓更完备,我指的长短侵入。 boost

的解决方案用起来感受更现代,看起来更标致。给人一种“不需要修改已有的 C++ 代码,就可以把本不撑持 serialize

的类加上这个特征”的心理快感。换句话说, 就是这件事情我能做的,至于真正做的工作会碰到甚么,那就不得而知了。

好吧,诚恳说,我不喜好用大量苦力(或是高聪明的结晶?)聚积起来的代码。不论是他人出的力,仍是我本身出的。别的,我但愿有一个 C

的解决方案,而不是 C++ 的。

所以从昨天起头,就抽了点时间来弄定这件事。

问题来至于 C/C++ 语言未供应类型元信息,那我们从底子上去解决好了。

获得元信息的方案不在于使用奇异的宏,我这个呆板的 C 程序员,也用不起现代的 template 手艺。其实自定义一下 C

的结构描写体例(缔造个小说话),写个小法式去解析一下就好了。 再用这个程序去生成 .h 文件,而供 serialization

库使用的元数据。

假如暂不考虑让此外动态语言更便利的阐发序列化后的数据(留赐与后再扩大),其实,需要做序列化的对象仅仅只有两个数据类型:

值类型和对其它类型的援用。

对值类型,我们可以简单的做内存拷贝,只需要知道值类型的长度。

而引用,在内存中,则是简单的指针。序列化后则是相对数据块的偏移量。这里使用 base 1 ,这样可以许可 0

照旧暗示空引用。

我将引用分成两类,别离称号为外引用,和内引用。

所谓外引用,指这个引用指向一个不需要被序列化的数据块。在做序列化时,库会把这个外引用翻译成一个原子(凡是表示为一个独一的字符串)。数据睁开时,再将原子还原成外引用。

比如:文件或窗口句柄就能够被翻译成携带有文件名信息,或窗口题目信息的原子(这个取决于具体实现)。

内引用:指引用的数据类型也为序列化模块所知。被引用的数据一样需要被插足序列化的数据块中。

如果能准确的处理好所有的内引用关系,被引用的数据实际上是内存地址无关的。

在我定义的需求中,序列化模块应当尽量的合并掉值相同的数据块。比如当字符串的值相同时,在序列化成绩中就只应该存在一份。如果需要序列化一颗树,相同值的的叶子节点,

或完全相同的子树,都应该被合并掉。

另外,序列化模块应该正确的处理环状结构。比如可以正确的序列化一个轮回链表,或是复杂的有向图。

斟酌到时间复杂度不能跨越多项式时候。不考虑合并值沟通且拓扑构造相同的有环图。(尽办理论上是可以合并它们的)

因为有上面两个需求,proto buff 等现成的库是用不了的。

btw, 还有一个小需求:数组类型的长度可以不固定,是由同个布局中另外一个整型变量的值决议的。

我花了一天一晚初步实现上面的器材。元信息的定义和处理并不复杂。序列化库的接口也很简洁:

传入数据指针,数据类型的元信息,让库去填充一个数据块即可。其实,序列化的对象就是一个有向有环图。整个实现的难点在于对相同节点的合并。

一最先没太想清晰,本着先做对再优化的原则,使用了个最苯的算法。(最坏情况下,可能有 O(N!)

的时间复杂度)即一入手下手其实不合并相同节点。遍历处理完全个图以后, 计较每一个节点的 hash

值,然后排序,去失落完全相同的节点,并反向修改引用它们的节点。然后频频这个过程,直到有效节点数不再削减。最后再把所有有效节点放在输出流中,

终究调剂一下所有的内部引用。

今天和同事会商了一下,感觉可以改良这个算法。

建立两个纠合,一个为有效节点的原子调集,另一个为原始数据块的引用荟萃。

先在遍历原始数据块,同时把原始数据块的每一个节点逐一加入原始数据块集合中(相同的原始指针会在这个步调被去掉),并生成且则对象利便我们做进一步处理。这个且自对象上,

可以附加遍历状况标识表记标帜,hash 值,长度,等等信息。

当一个节点上的所有引用均被处理完,这个节点的数据可以转换为一个原子,到场有效节点的原子集合。

当碰到成环的情形,只能是由于当前节点引用了一个先前并没有处理完的节点。这个时辰,由于环的存在,阿谁致使环呈现的先前的未处理完的节点,可以必定不会被公用了。所以,我们可以认为,此节点可以直接被转换为一个原子,插手有效节点鸠合。换句话说,今后也不会再泛起另一个数据块和这个原子完全相同。

用此算法,我们可以只经由过程一次遍历,找到所有的有效节点,并合并掉一切值相同的节点。

最后,一次性将有用节点拼成一个持续的数据块,并批改引用纪录便可。

实现的要点在于,要界说一个内存堆,并从堆中申请序列化进程中利用到的姑且内存。然后在序列化完成后直接断根整个堆。若是不如许做,全部过程当中的各个琐细而复杂的数据结构

的生命期经管会要命的。

同时,需要定义一个 context 结构,用于记实繁杂的序列化流程中的各类数据。并使用 longjmp

做异常处理(首要用于处置惩罚用户供给的 buffer 空间不敷的环境)。这会使程序变得很简练。

至于代码,今朝还没完全写完。贫乏序列化成果展开的部门,和数据描述语言的解析器(测试代码临时用手工解析)。

不过序列化部份根基可用了。加上测试代码,大约用 500 行搞定。看来还算是个小玩意。

写下本文,记录下思绪。

31 号 弥补:

以上对图的不异节点归并算法,是有缺点的。在处置环问题时,不克不及做最年夜可能的合并。

好比:

+--------------+

V |

A ---> B ---> C ----> D

| ^

+-------------+

在此种情况下,固然 A 和 D 节点的拓扑结构雷同,如果值相等,是可以被合并的。但此算法对此力所不及。

不外考虑到实现的简洁和不错的时间机能,可以容忍这个缺陷。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值