<?xml version="1.0" encoding="gb2312"?>
<rss version="2.0">
<channel>
<title><![CDATA[dyerac]]></title>
        <image>
        <title>http://hi.baidu.com</title>
        <link>http://hi.baidu.com</link>
        <url>http://img.baidu.com/img/logo-hi.gif</url>
        </image>
<description><![CDATA[td&#39;s home]]></description>
<link>http://hi.baidu.com/dyerac</link>
<language>zh-cn</language>
<generator>www.baidu.com</generator>
<ttl>5</ttl>


<item>
        <title><![CDATA[Don’t do it twice]]></title>
        <link><![CDATA[http://hi.baidu.com/dyerac/blog/item/7c16bbed7273acc3b31cb1df.html]]></link>
        <description><![CDATA[
		
		<p>From <a target="_blank" href="http://dyerac.com/index.php/2011/05/dont-do-it-twice/">http://dyerac.com/index.php/2011/05/dont-do-it-twice/</a></p><p>&nbsp;</p><p>在<a href="http://www.varnish-cache.org/trac/wiki/ArchitectNotes" target="_blank">varnish-cache</a>上
看到一篇讲Cache
swap的文章，很有意思。作者多年来一直从事kernel的开发工作，发现现代OS很多优化技术都没有被上层应用开发者利用，这种原始的编程思想被作者
戏称为1975 programming（意指还停留在1975年）。以Cache
swap为例，大部分cache都采用了RAM+Disk的双层结构，将热数据放在RAM中，不热的数据则swap到disk；这种将存储简单区分为
RAM和Disk两部分的想法就是典型的1975 programming。</p><p>事实上，因为virtual memory的出现，理论上在RAM数据中的冷数据由于长期未被引用，很可能早已经被OS pageout到disk了；这样的结果，使得理论上很优美的cache swap运行起来却是下面这样的逻辑：</p><p>（1）需要被swap的冷数据已经被OS pageout到disk，因此会先引起一个缺页中断，申请pages将数据重载到RAM（这一步可能还会需要先将别的数据pageout出去以获得空间）</p><p>（2）将载入的冷数据再重新写入到disk中</p><p>是不是看着就很蛋疼～</p><p>既然操作系统已经完成了一部分swap功能，为何应用层还需要重复实现呢？更可况1+1不但小于2，甚至还会小于1。Varnish就采用了与时俱
进2006 programming style，即应用完全不做swap管理，而是通过mmap方式申请了一大片virtual
memory并指定对应的backing file，全权交由操作系统来完成swap；这样既做到了高效，又简化了实现逻辑。</p><p>暂不论这种swap方式是否更好，但它却清晰的揭示了这样一个道理，<strong>做底层应用一定要熟知操作系统的特性</strong>。正是由于操作系统有大量不为人知的优化策略，会导致很多应用层自以为很妙的优化，不仅仅是重复工作，还会产生1+1&lt;1的问题。Don’t do it twice！这也是本文标题的意义所在。</p><p>换个思路想swap，如果觉得交给操作系统做操控性不强，那也可以在应用层控制swap，并限制in memory
cache不得被pageout，这种方法就明显比传统做法要好。IO也是另一个典型的例子。很多应用为了提高IO性能，将随机写操作以日志的方式转换成
顺序写，以提高性能。在这个时候，文件系统本身cache机制就起不到明显优化作用，反而有一定影响，因此选择dio可能会达到更好的效果。</p><p>最后作者除了swap机制外，还谈到了multi cpu的影响。由于CPU多级cache，可能导致内存连续的两个数据，因为cpu cacheline lock的原因，无法同时被不同线程访问。因此，尽量让线程访问自有变量，可以有效的阻止该现象的发生，提高性能。</p> <a href="http://hi.baidu.com/dyerac/blog/item/7c16bbed7273acc3b31cb1df.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/dyerac/blog/category/%C4%AC%C8%CF%B7%D6%C0%E0">默认分类</a>&nbsp;<a href="http://hi.baidu.com/dyerac/blog/item/7c16bbed7273acc3b31cb1df.html#comment">查看评论</a>]]></description>
        <pubDate>2011-05-07  00:31</pubDate>
        <category><![CDATA[默认分类]]></category>
        <author><![CDATA[dyerac]]></author>
		<guid>http://hi.baidu.com/dyerac/blog/item/7c16bbed7273acc3b31cb1df.html</guid>
</item>

<item>
        <title><![CDATA[用Reed-Solomon解决数据存储的容灾]]></title>
        <link><![CDATA[http://hi.baidu.com/dyerac/blog/item/24843efc0c84e897b801a028.html]]></link>
        <description><![CDATA[
		
		<p>From <a target="_blank" href="http://dyerac.com/index.php/2011/04/reed_solomon_for_data_replication/">http://dyerac.com/index.php/2011/04/reed_solomon_for_data_replication/</a></p><p>GFS以及其他很多分布式存储系统，都通过数据直接的replication来提供fault tolerance，这样做简单实用，但是却会浪费N倍的存储空间和IO。如果既想达到99.99999%的安全性，又不想浪费过多的存储空间，又有足够的设计/工程能力，可以考虑用Reed-Solomon算法来解决fault tolerance的问题；至少Google已经这么做了。</p><p>&nbsp;</p><p>可以这么定义Reed-Solomon解决的问题：将数据切分为D1到Dn共N份，每份有K bytes数据，根据这N份数据又可以得到C1～Cm共m份checksum（也是K bytes），那么这n+m个文件任意损坏m个文件，都可以通过余下的文件恢复出来。看上去和RAID 5很类似吧，不过RS是唯一可以解决任意组合的M个文件损坏都能够恢复的算法，其他的比如Hamming Codes或者基于parity都只能做到m&lt;=2的case；当然如果确定m＝2，EVENODD coding的性能会比RS更好。</p><p>&nbsp;</p><p>关于RS的细节可以参考A Tutorial on Reed–Solomon Coding for Fault-Tolerance in RAID-like Systems这篇paper，讲述得非常清晰。RS的效率问题主要是每次overwrite都需要更新对应checksum，recovery时也需要读取全局数据。但在Merge-Dump模型和Append-Only的情况下，overwrite的需求不存在；此外RS计算时，并不需要per bit的进行，而是可以w bits为一个单位进行操作，如果再限制总数据量的大小（比如RS replication都是per chunk的进行），那么额外的代价是可以被控制的。特别的，很多历史数据读取频率很低，因此完全可以后台慢慢的恢复这些数据。</p><p>&nbsp;</p><p>其实Hadoop已经基于RS实现了一个分布式的RAID系统，参见<a target="_blank" href="http://wiki.apache.org/hadoop/HDFS-RAID">http://wiki.apache.org/hadoop/HDFS-RAID</a>，其思想是用一个RAID master去实施raid和recovery的工作，并从nodemaster中获取损坏了的chunk信息；推荐配置是10个chunk配上4个checksum chunk，也就是节省到1.4倍的存储空间，并容忍四台机器宕机的情况。这是一个很好的思路，即我们完全可以在写数据时还是按照3份副本来完成，然后通过后天job去转换成RS编码后的data+checksum。读取时，可以允许跳过损坏的chunk，或者抛出异常，或者阻塞住并请求master发起一个job恢复数据直到完成。当然，master在调度chunk的时候要考虑到这n+m个chunk必须分开存储。</p> <a href="http://hi.baidu.com/dyerac/blog/item/24843efc0c84e897b801a028.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/dyerac/blog/category/%C4%AC%C8%CF%B7%D6%C0%E0">默认分类</a>&nbsp;<a href="http://hi.baidu.com/dyerac/blog/item/24843efc0c84e897b801a028.html#comment">查看评论</a>]]></description>
        <pubDate>2011-03-24  18:25</pubDate>
        <category><![CDATA[默认分类]]></category>
        <author><![CDATA[dyerac]]></author>
		<guid>http://hi.baidu.com/dyerac/blog/item/24843efc0c84e897b801a028.html</guid>
</item>

<item>
        <title><![CDATA[用vps搭建vpn]]></title>
        <link><![CDATA[http://hi.baidu.com/dyerac/blog/item/f245c7d1f45ca32d9a50274f.html]]></link>
        <description><![CDATA[
		
		<p>今天买了个linode的vps，装了一个pptpd搭了自己的vpn，原因嘛~你懂的</p><p>&nbsp;</p><p>有个一键安装的方法，非常简单：</p><p>------------</p><p>for pptpd</p><p>系统要求：CentOS 5 32bit/64bit。若VPS安装需要Xen虚拟化技术支持或者最新的OpenVZ技术支持。</p><p>安装方法，登陆SSH后输入以下命令：</p><p>wget http://www.diahosting.com/dload/pptpd.sh</p><p>sh pptpd.sh</p><p>安装完成后会提示vpn用户名和密码。</p><p>VPN用户管理：<br>
直接编辑/etc/ppp/chap-secrets文件，按照相同格式添加用户名和密码即可。 </p><p>注：选择CentOS 5.5 stable的那个版本，不然有一项会fail</p><p>-------------</p><p>&nbsp;for l2tpd</p><p>wget http://mirror.zeddicus.com/auto-l2tp/1.2/centos/l2tp.sh<br>chmod +x l2tp.sh<br>./l2tp.sh</p><p>--------------</p><p>&nbsp;</p><p>pptp和l2tp都可以用作vpn服务，个人感觉pptp似乎轻量级一些 ~ 但公司的路由器似乎不支持pptp协议，所以不得已让两个并存咯。此外要在windows上使用l2tp client，需要修改注册表定位“HKEY_Local_Machine \ System \ CurrentControl Set \ Services \ RasMan \ Parameters ”主键，为该主键添加以下键值：</p><div>键值：ProhibitIpSec</div><div>数据类型：reg_dword</div><div><p>值：1</p><p>然后重启。。。。不然会出现错误</p></div> <a href="http://hi.baidu.com/dyerac/blog/item/f245c7d1f45ca32d9a50274f.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/dyerac/blog/category/%C4%AC%C8%CF%B7%D6%C0%E0">默认分类</a>&nbsp;<a href="http://hi.baidu.com/dyerac/blog/item/f245c7d1f45ca32d9a50274f.html#comment">查看评论</a>]]></description>
        <pubDate>2011-03-17  23:22</pubDate>
        <category><![CDATA[默认分类]]></category>
        <author><![CDATA[dyerac]]></author>
		<guid>http://hi.baidu.com/dyerac/blog/item/f245c7d1f45ca32d9a50274f.html</guid>
</item>

<item>
        <title><![CDATA[【咆哮体程序员版】搞计算机当程序猿的人你伤不起！！！伤不起啊！！！（原创）]]></title>
        <link><![CDATA[http://hi.baidu.com/dyerac/blog/item/4ff2c42ac64ac32c5343c1aa.html]]></link>
        <description><![CDATA[
		
		<div><p>From <a target="_blank" href="http://dyerac.com/index.php/2011/04/cry_for_programmer/">http://dyerac.com/index.php/2011/04/cry_for_programmer/</a></p><p>“计算机要从娃娃抓起！”，有多少人因此走上了不归路！！！不归路！！！！！！</p><p><br>尼玛一上大学就找不到女朋
友！！！！！！班上男女比例八比一，八比一啊！！！！！！都塔玛建军节了！！！！！！就八比一还有学长来抢有木有！！！本科学长，研究生学长还有博士学
长！！！！！！玛德学长你们是兔子啊就这么喜欢啃嫩草！！！！还窝边草！！！！！！等熬过一年去迎新，一件行李十几个人竞争！！！！！！要打群架了有木
有！！！妹子吓哭了有木有！！！！！！</p><p>学了两年还在学数学物理！！！！！！傅里叶！！！拉普拉斯！！！尼玛两个
法国老头死了咋还这不安神呢！！！！！！编程作业Code得抄在纸上交！！！汇编啊！！！！！！随随便便就几百行啊！！！！抄次作业都要半个小时
啊！！！！！！课设还要插电路板！！！！！！一个板子插一千多根线，一千多根线！！！！！！连起来可以绕地球三圈半啊！！！！！！谁再说计算机好学劳资吐
一口血水腥死他！！！！！！汇编语言不会编听过没！！！随机过程随机过听过没！！！听过没！！！！！！<br><br>还有尼玛谁说学计算机就
会修电脑！！！！！！就会买电脑！！！！！！还免费的！！！！！！劳资用linux不懂windows可不可以！！！！！！劳资用windows也不懂
windows可不可以！！！！！！尼玛用办公室的电脑把文档Ctrl-C，回家用家里的电脑Ctrl-V当然不可以！！！！！！问我也不可
以！！！！！！重装系统也别找我！！！！！！劳资帮隔壁系妹子重装了10几次操作系统了啊！！！10几次啊！！！！！！10几次连手都还没牵到
啊！！！！！！劳资还是买块硬盘撞死算了！！！！！！<br><br>找工作也不容易！！！！！！面试书买了十几本有木有！！！天天研究各个公
司面经有木有！！！都快面瘫了！！！！！！玛德现在每个公司都学着Google考算法！！！！！！算你妹！！！！！！尼玛贵公司产品里只有算法
啊！！！！！！尼玛难道每个人都是搞ACM的啊！！！！！！还不如去SM！！！！！！NND面试官你要不是事先知道答案你做得出来吗！！！！！！你做得出
来吗！！！！！！<br><br>找到工作了也是民工！！！是码农！！！Robin都首富了，你都还没首付！！！！！！羞愧吗！！！！！！羞愧
吗！！！！！！工作了照样没有女朋友！！！研发部门的比例连八比一都没有！！！！！！新入职的mm上学期间都被下手了有木有！！！！！！上学期间下手的
mm入职后都被挖了墙角有木有！！！！！！卧槽都是程序猿，相煎那么急！！！！！！尼玛是个搞计算机的最后都去搞单反，搞摄影！！！！！！尼玛搞来搞去还
不就是为了搞mm！！！！！<br><br>工作压力也超大！！！！！！操着卖白粉的心，挣着卖白菜的钱！！！应用上线压力大，一分钟几十万收
入有木有！！！尼玛收入归老板，责任该你挡有木有！！！！！！凌晨三点跑去公司解决线上故障！！！大便便秘要带笔记本防止突发事件！！！一天收几百条报警
短信！！！有木有！！！有没有！！！有木有！！！！！！万一哪天ML时报警短信来了，吓出病了，找谁哭去！！！！！！找谁哭去！！！！！！<br><br>每个你用过的IT产品和应用，都是背后无数程序猿的血与泪！！每个程序猿上辈子都是折了护翼的卫生巾！！！你伤不起！！！伤不起！！！！！！！！！！！！</p></div><p></p> <a href="http://hi.baidu.com/dyerac/blog/item/4ff2c42ac64ac32c5343c1aa.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/dyerac/blog/category/%C4%AC%C8%CF%B7%D6%C0%E0">默认分类</a>&nbsp;<a href="http://hi.baidu.com/dyerac/blog/item/4ff2c42ac64ac32c5343c1aa.html#comment">查看评论</a>]]></description>
        <pubDate>2011-03-11  11:07</pubDate>
        <category><![CDATA[默认分类]]></category>
        <author><![CDATA[dyerac]]></author>
		<guid>http://hi.baidu.com/dyerac/blog/item/4ff2c42ac64ac32c5343c1aa.html</guid>
</item>

<item>
        <title><![CDATA[压缩算法和Hadoop]]></title>
        <link><![CDATA[http://hi.baidu.com/dyerac/blog/item/465c68254e6f477335a80f71.html]]></link>
        <description><![CDATA[
		
		<p>在海量存储系统中，好的压缩算法可以有效的降低存储开销，减轻运营成本。Hadoop基本上已经是主流的存储系统了，但是由于本身是Java实现，在压缩性能上受到语言的限制；此外该压缩算法还得支持Hadoop Mapreduce的split机制，不然压缩后文件只能被一个mapper处理，会大大的影响效率。Twitter之前实现过一个lzo的压缩框架，很好的将lzo引入到hadoop中。借助其原理我实现了一个更加通用的压缩框架，姑且叫做Nsm（Native Splittable Multi-method）Compression，可以方便的将一个Native（C/C++）实现的Encoder/Decoder整合到Hadoop Compression IO机制中，并支持Mapreduce时的切分；此外我还基于lzma2实现了一个更高效的通用日志压缩算法，压缩比为zlib的1/2，压缩速度为原lzma2的两倍。</p><p>&nbsp;</p><p>首先看最基本的压缩算法。Bigtable里提到了一种针对网页的long common string压缩，在网页按url聚集后可以达到十几倍的压缩比。然而在日志系统中，由于日志本身有过优化，很难出现网页那样大段重复的情况，也无法进行相似日志的聚集，long common string的优势体现不出来，反而不如zlib这样的短窗口压缩。另一方面，基于可读性的考虑，日志中整数，md5，timestamp，ip这样的数据往往以文本形式表示。因此，一个自然而然的想法是先把文件预处理，将上述数据由文本转换为二进制，再交给通用压缩算法进行压缩。有意思的是，经过预处理后的原文件虽然往往大小可以缩小一半，但再由通用压缩算法压缩后的结果，却和直接压缩的大小相差无几；这主要是通用压缩算法都会对结果进行哈夫曼或者算术编码，因此预处理的作用并不明显（往往只能降低10％左右）。</p><p>&nbsp;</p><p>虽然预处理对最终压缩比影响不大，但是由于预处理速度快，并减少了通用压缩算法要处理的数据量，因此往往可以提高压缩速度，特别是对lzma2这种慢速压缩算法。在我的实现里，预处理平均可以将原文件大小缩小到1/2，预处理+lzma2对比单纯使用lzma2，可以将压缩速度从2M/s提高到4M/s，压缩比由19%提高到17%；当然解压缩速度也从100M/s降低到50M/s，这也是个代价。<strong><span style="color: rgb(255, 0, 0);">预处理还有一个好处，就是可以把日志中不同类型的数据聚集在一起</span></strong>（类似按列存储的数据库），虽然我还没有实现，但相信会对压缩比有很大提升。（注：PPMd和Lzma2是我以为最好的通用文本压缩算法，不过预处理和PPMd结合的并不好，我猜测是因为PPMd是纯粹的算术编码，预处理分散了概率分布，起到了副作用；此外PPMd的解压缩度只有10M/s，也不可接受）。</p><p>&nbsp;</p><p>再看Hadoop的Compression IO机制，用户要实现一个自定义的Codec，用来创建Compressor, Decompressor, InputStream(DecompressStream)和OutputStream(CompressStream)；Hadoop使用该InputStream和OutputStream来读写文件。为了支持Mapreduce时的split，需要实现block based的InputStream/OutputStream，即以block为单位进行数据压缩，并且还能让每个split都恰好从block头开始，才能让解压缩器识别。所以，可以用额外的Indexer程序为每个压缩文件生成一个index文件，记录每个block的offset；然后再实现一个自定义的InputFormat来实现切分功能，切分逻辑很简单，就是读取文件对应的index，把父类方法完成的splits（通常是按chunk 64M划分）对齐到block的开始；对于没有index的压缩文件，则只能以其整体作为一个split。</p><p>&nbsp;</p><p>最后就来看框架本身的实现了。首先在Native实现中，定义了Encoder/Decoder两个接口，为了简单，Encoder每次都会把传入的数据独立encode成一个block，Decoder也只能接受一个完整block的数据。EncodeUtil管理所有的encoder，用户将原始数据和压缩方法ID传给Util，Util再找到对应的Encoder进行数据压缩，并添加一个block header；该header包括magic number，raw/encoded length, raw/encoded checksum和压缩算法ID。同样，也有一个对应的DecodeUtil管理所有的decoder，util首先解析block header，得到算法ID并验证checksum后，调用对应的Decoder进行解压。所以，想要添加一个新算法，只需要实现Encoder/Decoder并在Util中注册即可。</p><p>&nbsp;</p><p>而Hadoop端的实现也很简单，只用实现之前讲过的Codec，Compressor，Decompressor，InputStream，OutputStream，Indexer，InputFormat即可。布署的时候，添加Codec到hadoop-site.xml(core-site.xml)，如下</p><p>&lt;property&gt;<br>&lt;name&gt;io.compression.codecs&lt;/name&gt;<br>&lt;value&gt;org.apache.hadoop.io.compress.GzipCodec,org.apache.hadoop.io.compress.DefaultCodec,org.apache.hadoop.io.compress.BZip2Codec,com.hadoop.compression.nsm.NsmCodec&lt;/value&gt;<br>&lt;/property&gt;<br>&lt;property&gt;<br>&lt;name&gt;io.compression.codec.nsm.class&lt;/name&gt;<br>&lt;value&gt;com.hadoop.compression.nsm.NsmCodec&lt;/value&gt;<br>&lt;/property&gt;</p><p>以及在hadoop-site.xml(mapred-site.xml)中添加</p><p>&lt;property&gt;<br>  &lt;name&gt;mapred.child.env&lt;/name&gt;<br>  &lt;value&gt;JAVA_LIBRARY_PATH=/path/to/your/hadoop/lib/native&lt;/value&gt;<br>&lt;/property&gt;</p><p>&nbsp;</p><p>此外，还需要将相关的native lib(libnsm.so libp7z.so)添加到Hadoop的lib/native中，并修改hadoop-config.sh，将LD_LIBRARY_PATH=/path/to/your/hadoop/lib/native export即可（需要重启hadoop）。</p><p>&nbsp;</p><p>－－－－－－－－－－－－－－－</p><p>&nbsp;</p><p>另：预处理的实现和改进</p><p>由于日志本身不一定是格式化对齐的，即使对齐，一个field中也可能含有多个可转换的数据，所以这其实是一个模式识别－转换的问题；另一方面，日志本身又有一些通用的格式可以利用。在我的实现里，是预先定义好一些split token，并给0～255每个byte归于一种类型；在扫描过程中，每遇到一个token，就检查该段数据类型并进行相应的转换。这样把基于byte的状态机变成基于segment的状态机，实现上更加简单高效。</p><p>也正是因此，在该基础上把同一类型的segment存储在一起也很容易实现，只不过如何做到高效（时间，空间）想必在工程上也需要不小的功夫。</p> <a href="http://hi.baidu.com/dyerac/blog/item/465c68254e6f477335a80f71.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/dyerac/blog/category/Computer%20Science">Computer Science</a>&nbsp;<a href="http://hi.baidu.com/dyerac/blog/item/465c68254e6f477335a80f71.html#comment">查看评论</a>]]></description>
        <pubDate>2011-01-21  18:46</pubDate>
        <category><![CDATA[Computer Science]]></category>
        <author><![CDATA[dyerac]]></author>
		<guid>http://hi.baidu.com/dyerac/blog/item/465c68254e6f477335a80f71.html</guid>
</item>

<item>
        <title><![CDATA[Dremel论文阅读笔记(Interactive Analysis of Web-Scale Datasets)]]></title>
        <link><![CDATA[http://hi.baidu.com/dyerac/blog/item/f4c4211689317a0a972b43af.html]]></link>
        <description><![CDATA[
		
		<p>From <a target="_blank" href="http://dyerac.com/index.php/2011/04/dremel-review/">http://dyerac.com/index.php/2011/04/dremel-review/</a></p><p>Dremel是Google一个用来分析read-only nested data的系统，支持列式存储，SQL-liked查询，scalable and fault tolerance，最近粗粗的阅读了一遍，觉得还是有不少可取之处的。</p><p>&nbsp;</p><p>首先Dremel不是MR也不是Bigtable的替代品，它本身采取了不一样的存储格式和查询机制。Dremel是用来处理一些nested data，即有多层嵌套结构的复杂数据格式，这些数据很难摊开到一张Bigtable Table里，即使可以，也难以维持其locality（即很难做到按照嵌套路径的物理存储分离）。另外，Dremel没有将query转换成一系列MR job，而是用类似搜索引擎的distributed serving tree形式，因此它不需要MR额外的IO，性能更高（但是fault tolerence可能没有MR好）。因此，对于通常而言数据格式比较复杂，数据量不是太大（很少过P），响应时间又要求比较高的数据分析工作，Dremel还是非常适合的。</p><p>&nbsp;</p><p>我觉得Dremel的精华就在于它独特的数据格式，可以很好的处理nested data并保证locality。看下面这个例子：</p><p>&nbsp;message M{</p><p>&nbsp;&nbsp;&nbsp; required B;</p><p>&nbsp;&nbsp;&nbsp; repeated group C{</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; optional D;</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; repeated E;</p><p>&nbsp;&nbsp; }</p><p>&nbsp;&nbsp; </p><p>}</p><p>理想的情况是，用户单独访问B, C.D, C.E的时候，都不会读取其他额外的数据，因此上述数据应该存储在不同的文件里；另外一方面，由于数据格式是松散的（比如A里面有没有C，有多少个C都不确定；相同的field name可能出现在不同的嵌套层次里），必须有机制可以通过部分数据恢复出整体结构。Dremel里面引入了一个repetition level和definition level的辅助信息。repetition level用来表示当前value在其path的哪一级别重复，definition level用来表示当前path中，所有可能缺失（比如optional和repeated的field）但实际上被定义了的field个数。比如说，field C.D如果紧跟在上一个C.D之后，则rl为2；如果是当前C中第一个D，但C之前还有一个C，则rl为1；如果是第一个C的第一个D，则rl为0；但无论如何，dl均为2（因为C，D皆为可缺失类型）；而B的dl则为0，因为B是required的。另一方面，如果出现一个不含有D的C，则还是会补上一个NULL的值表示该C.D，其rl为0，dl为1。正如原文所说，用dl而不是一个bool来表示某field是否存在的好处在于，可以通过一个leaf path的dl，得知其parent paths的存在情况；这在恢复数据结构时非常有用。</p><p>&nbsp;</p><p>总结下dremel的格式，可以理解为记录下每个value的值，如果缺失则补上NULL；每个值（含NULL）都附加上rl和dl属性，用来区别重复记录的分布情况和整个path的缺失情况，用于恢复数据格式时使用。实际上，NULL并不需要被物理存储，其对应的dl就可以表示该value是否为NULL（当dl小于当前path的长度时即为NULL）。</p><p>&nbsp;</p><p>最后来看Dremel的query机制，整个query执行的时候以一个serving tree的形式执行，即root节点将query水平改写后分发给intermediate节点，intermediate节点再分发给leaf节点（可以不止3层），再由leaf节点完成实际查询计算工作，最后依次向上汇总。dremel有一个query dispatcher用来调度query，给每个query分配合适的资源（如多少层结构，多少个leaf节点），并监控各个server以提供fault tolerance功能。</p> <a href="http://hi.baidu.com/dyerac/blog/item/f4c4211689317a0a972b43af.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/dyerac/blog/category/Computer%20Science">Computer Science</a>&nbsp;<a href="http://hi.baidu.com/dyerac/blog/item/f4c4211689317a0a972b43af.html#comment">查看评论</a>]]></description>
        <pubDate>2011-01-13  16:14</pubDate>
        <category><![CDATA[Computer Science]]></category>
        <author><![CDATA[dyerac]]></author>
		<guid>http://hi.baidu.com/dyerac/blog/item/f4c4211689317a0a972b43af.html</guid>
</item>

<item>
        <title><![CDATA[C++实现OR Mapping以及编译期schema检查]]></title>
        <link><![CDATA[http://hi.baidu.com/dyerac/blog/item/51a1608f4aa5b0e8503d9264.html]]></link>
        <description><![CDATA[
		
		<p></p><p>From <a target="_blank" href="http://dyerac.com/index.php/2011/05/cpp_orm/">http://dyerac.com/index.php/2011/05/cpp_orm/</a></p><p>OR Mapping在java中已经很普遍了，但在C++里实现却有一定难度，主要是C++缺乏反射，annotation等一系列辅助机制。前段时间闲来无聊给公司的“Bigtable” API封装了一层OR Mapping接口，本身通过宏来实现的所以平平无奇，不过实现了编译期的schema检查，还算是有点亮点。而且整个框架只有300多行代码，小巧整洁。</p><p>&nbsp;</p><p>注：所谓编译期schema检查，指的是如果用户指定了Table的某一列需要存储为int类型，然后试图将某个非int字段（比如string）通过OR Mapping功能存入该列时，会在编译阶段就报错，而无需运行时才能发现问题。这样既可以提早暴露错误，又能够提高运行时的性能。</p><p>&nbsp;</p><p>一. 接口</p><p>整个OR Mapping框架的接口很简单，用户首先提供一个头文件用来说明table的schema，比如person_schema.h用来描述person表的schema，如下：</p><p>&nbsp;#include "mapping_base.h"<br><br>TABLE_SCHEMA(person, age, int)<br>TABLE_SCHEMA(person, birth, int64_t)<br>TABLE_SCHEMA(person, gender, bool)<br>TABLE_SCHEMA(person, height, double)<br>TABLE_SCHEMA(person, name, string)</p><p>其中，person表示table名，age表示列名，int表示该列存储的数据类型。接着，就可以在用户自定义类中绑定类属性和表之间的对应关系，如下，将类Person绑定到表person中，属性mID，mAge，mBirth等分别对应列id（primary key），列age，列birth等：</p><p>class Person : MappingBase<br>{<br>private:<br>&nbsp;&nbsp;&nbsp; string mID;<br>&nbsp;&nbsp;&nbsp; int mAge;<br>&nbsp;&nbsp;&nbsp; int64_t mBirth;<br>&nbsp;&nbsp;&nbsp; bool mIsMale;<br>&nbsp;&nbsp;&nbsp; double mHeight;<br>&nbsp;&nbsp;&nbsp; string mName;<br><br>public:<br><br>&nbsp;&nbsp;&nbsp; MAPPING_ENTITY(Person)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MAPPING_KEY(mID, id, person)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MAPPING_FIELD(mAge, age, person)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //error<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //MAPPING_FIELD(mAge, gender, person)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MAPPING_FIELD(mBirth, birth, person)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MAPPING_FIELD(mIsMale, gender, person)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MAPPING_FIELD(mHeight, height, person)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MAPPING_FIELD(mName, name, person)<br>&nbsp;&nbsp;&nbsp; }<br>};</p><p>这样就可以通过dbProxy的read和write接口，直接从数据库中读写Person对象了（read，write接口都接受一个MappingBase类型的参数）。如果用户妄想将int类型的mAge属性绑定到bool类型的gender类，则编译器会在编译阶段报错。</p><p>&nbsp;</p><p>二. 实现</p><p>OR Mapping的实现原理也很简单，MAPPING_ENTITY，MAPPING_KEY和MAPPING_FIELD其实就是一个宏语句，将该类的属性load/store到一个EntityInfo对象，而该对象则是一个简单的key-value map，key为列名，value为列值。dbProxy可以枚举该map，将其值写入到db，或者从db中读取对应的值填充到该map里。代码演示如下：</p><p>（1）基类MappingBase</p><p>class MappingBase<br>{<br>&nbsp;&nbsp;&nbsp; virtual void ToEntity(EntityInfo&amp; info) const = 0;<br>&nbsp;&nbsp;&nbsp; virtual void FromEntity(const EntityInfo &amp; info) = 0;<br>};</p><p>&nbsp;</p><p>（2）Mapping宏</p><p>// load &amp; store for entity<br>#define MAPPING_ENTITY(T)\<br>&nbsp;&nbsp;&nbsp; virtual void ToEntity(EntityInfo&amp; info) const \<br>&nbsp;&nbsp;&nbsp; { \<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const_cast&lt;T*&gt;(this)-&gt;entity&lt;IOWRAPPER_MAPPING_TO&gt;(info);&nbsp;&nbsp;&nbsp; \<br>&nbsp;&nbsp;&nbsp; } \<br>&nbsp;&nbsp;&nbsp; \<br>&nbsp;&nbsp;&nbsp; virtual void FromEntity(const EntityInfo &amp; info) \<br>&nbsp;&nbsp;&nbsp; { \<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;entity&lt;IOWRAPPER_MAPPING_FROM&gt;(const_cast&lt;EntityInfo&amp;&gt;(info));&nbsp;&nbsp;&nbsp; \<br>&nbsp;&nbsp;&nbsp; } \<br>&nbsp;&nbsp;&nbsp; \<br>&nbsp;&nbsp;&nbsp; template&lt;int Type&gt; \<br>&nbsp;&nbsp;&nbsp; void entity(EntityInfo &amp; info)<br><br>// load &amp; store for entity key<br>#define MAPPING_KEY(field, column, table) \<br>{\<br>&nbsp;&nbsp;&nbsp; TypeCheck&lt;IOWRAPPER_MAPPING_TYPE_string, typeof(field), typeof(*this)&gt; check_##field;\<br>&nbsp;&nbsp;&nbsp; KeyLoadStoreSelector&lt;Type&gt;::ResultType::Handle(field, info);\<br>}<br><br>// load &amp; store for entity field<br>#define MAPPING_FIELD(field, column, table) \<br>{\<br>&nbsp;&nbsp; &nbsp;TypeCheck&lt;SCHEMA_TYPE_##table##_##column, typeof(field), typeof(*this)&gt; check_##field;\<br>&nbsp;&nbsp;&nbsp; LoadStoreSelector&lt;Type, typeof(field)&gt;::ResultType::Handle(field, #column, info);\<br>}</p><p>&nbsp;</p><p>&nbsp;看到上面没头没脑的代码，估计比较晕菜，为什么MAPPING宏里面不是直接将列名和属性存入到Map（即EntityInfo）中，而是用了一堆乱七八糟的模版？主要是两个原因：</p><p>1. 模版TypeCheck是为了做编译期schema检查</p><p>2. 模版LoadStoreSelector则是通过一个入口实现load和store两套逻辑，这样用户只需要写一个宏比较方便；而且用模版而不用if-else也是为了性能考虑（和boost中的serialize机制类似）</p><p>&nbsp;</p><p>（3） 编译期schema检查 （TypeCheck）</p><p>哈哈，前面说了半天这个特性了，原理也非常简单。还记得用户提供的person_schema.h头文件来说明table schema吗，其实那个时候已经通过宏偷偷的生成了一个由表名和列名组成的常量，并根据设定的数据类型对该常量进行了赋值。因此，在mapping的时候，可以以该常量值作为参数，通过模版的特化与偏特化，以及typeof这个有用的关键字来进行类型比对了。具体case如下：</p><p>// declare table schema<br>#define TABLE_SCHEMA(table, column, type)\<br>&nbsp;&nbsp;&nbsp; static const int SCHEMA_TYPE_##table##_##column = IOWRAPPER_MAPPING_TYPE_##type;</p><p>&nbsp;</p><p>那么在mapping时，就可以利用SCHEMA_TYPE_##table##_##column这个常量进行检查<br>#define MAPPING_FIELD(field, column, table) \<br>{\<br>&nbsp;&nbsp; &nbsp;TypeCheck&lt;SCHEMA_TYPE_##table##_##column, typeof(field), typeof(*this)&gt; check_##field;\<br>&nbsp;&nbsp;&nbsp; LoadStoreSelector&lt;Type, typeof(field)&gt;::ResultType::Handle(field, #column, info);\<br>}</p><p>&nbsp;</p><p>具体检查的方法为（以double为例）</p><p>template&lt;int Type, typename T, class V&gt;<br>struct TypeCheck<br>{<br>&nbsp;&nbsp;&nbsp; TypeCheck()<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; T * ret = (void*) NULL;<br>&nbsp;&nbsp;&nbsp; }<br>};</p><p>//偏特化</p><p>template&lt;typename T, class V&gt;<br>struct TypeCheck&lt;IOWRAPPER_MAPPING_TYPE_double, T, V&gt;<br>{<br><br>&nbsp;&nbsp;&nbsp; TypeCheck()<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; T * ret = (double*) NULL;<br>&nbsp;&nbsp;&nbsp; }<br>};</p><p>&nbsp;</p><p>这样，假如用户想把一个int类型属性绑定给double属性的列，则上面的模版展开后会成为int * ret = (double*)NULL，自然会编译器报错。</p><p>&nbsp;</p><p>（4）LoadStoreSelector</p><p>这部分比较简单，就不细讲了，唯一要说的是，由于C++模版不支持方法的偏特化，所以这里用了一个struct的typedef这样怪怪的方式，不过这也是标准做法了，呵呵。</p><p>template&lt;int N, typename T&gt;<br>struct LoadStoreSelector<br>{<br>&nbsp;&nbsp;&nbsp; typedef NullStruct ResultType;<br>};<br><br>template&lt;typename T&gt;<br>struct LoadStoreSelector&lt;IOWRAPPER_MAPPING_TO, T&gt;<br>{<br>&nbsp;&nbsp;&nbsp; typedef StoreSelector ResultType;<br>};<br><br>template&lt;typename T&gt;<br>struct LoadStoreSelector&lt;IOWRAPPER_MAPPING_FROM, T&gt;<br>{<br>&nbsp;&nbsp;&nbsp; typedef LoadSelector ResultType;<br>};</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>----------------------------------------------</p><p>&nbsp;</p><p>最后附上所有的源代码，讲述不清楚的地方还是看代码最方便.</p><p>&nbsp;</p><p>1. mapping_base.h</p><p>/*<br>&nbsp;* mapping_base.h<br>&nbsp;*<br>&nbsp;*&nbsp; Created on: Aug 2, 2010<br>&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Author: dyerac<br>&nbsp;*/<br><br>#ifndef MAPPING_BASE_H_<br>#define MAPPING_BASE_H_<br><br>#include &lt;string&gt;<br>#include "proxy_item.h"<br><br>static const int IOWRAPPER_MAPPING_TYPE_int = 0;<br>static const int IOWRAPPER_MAPPING_TYPE_int64_t = 0;<br>static const int IOWRAPPER_MAPPING_TYPE_double = 1;<br>static const int IOWRAPPER_MAPPING_TYPE_bool = 2;<br>static const int IOWRAPPER_MAPPING_TYPE_string = 3;<br><br>static const int IOWRAPPER_MAPPING_TO = 1;<br>static const int IOWRAPPER_MAPPING_FROM = 2;<br><br>struct NullStruct;<br><br>/********************* Type Check *************************/<br>template&lt;int Size, typename T, class V&gt;<br>struct IntTypeCheck<br>{<br>&nbsp;&nbsp;&nbsp; IntTypeCheck()<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; T * ret = (int*) NULL;<br>&nbsp;&nbsp;&nbsp; }<br>};<br><br>template&lt;typename T, class V&gt;<br>struct IntTypeCheck&lt;sizeof(int64_t), T, V&gt;<br>{<br>&nbsp;&nbsp;&nbsp; IntTypeCheck()<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; T * ret = (int64_t*) NULL;<br><br>&nbsp;&nbsp;&nbsp; }<br>};<br><br>template&lt;int Type, typename T, class V&gt;<br>struct TypeCheck<br>{<br>&nbsp;&nbsp;&nbsp; TypeCheck()<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; T * ret = (void*) NULL;<br>&nbsp;&nbsp;&nbsp; }<br>};<br><br>template&lt;typename T, class V&gt;<br>struct TypeCheck&lt;IOWRAPPER_MAPPING_TYPE_int, T, V&gt;<br><br>{<br>&nbsp;&nbsp;&nbsp; TypeCheck()<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IntTypeCheck&lt;sizeof(T), T, V&gt; checker;<br>&nbsp;&nbsp;&nbsp; }<br>};<br><br>template&lt;typename T, class V&gt;<br>struct TypeCheck&lt;IOWRAPPER_MAPPING_TYPE_double, T, V&gt;<br>{<br><br>&nbsp;&nbsp;&nbsp; TypeCheck()<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; T * ret = (double*) NULL;<br>&nbsp;&nbsp;&nbsp; }<br>};<br><br>template&lt;typename T, class V&gt;<br>struct TypeCheck&lt;IOWRAPPER_MAPPING_TYPE_bool, T, V&gt;<br>{<br><br>&nbsp;&nbsp;&nbsp; TypeCheck()<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; T * ret = (bool*) NULL;<br>&nbsp;&nbsp;&nbsp; }<br>};<br><br>template&lt;typename T, class V&gt;<br>struct TypeCheck&lt;IOWRAPPER_MAPPING_TYPE_string, T, V&gt;<br>{<br><br>&nbsp;&nbsp;&nbsp; TypeCheck()<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; T * ret = (std::string*) NULL;<br>&nbsp;&nbsp;&nbsp; }<br>};<br><br>/********************* Load &amp; Store *************************/<br>struct LoadSelector<br>{<br>&nbsp;&nbsp;&nbsp; static void Handle(int &amp; field, const char * column,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const EntityInfo &amp; entity)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const EntityParam &amp; param = entity.GetParam(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; std::string(column));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (param.IsNull())<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LOG_DEBUG(sLogger, ("message", "no required column")("column", column));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; field = param.GetInt64Value();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; static void Handle(int64_t &amp; field, const char * column,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const EntityInfo &amp; entity)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const EntityParam &amp; param = entity.GetParam(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; std::string(column));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (param.IsNull())<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LOG_DEBUG(sLogger, ("message", "no required column")("column", column));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; field = param.GetInt64Value();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; static void Handle(double &amp; field, const char * column,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const EntityInfo &amp; entity)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const EntityParam &amp; param = entity.GetParam(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; std::string(column));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (param.IsNull())<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LOG_DEBUG(sLogger, ("message", "no required column")("column", column));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; field = param.GetDoubleValue();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; static void Handle(bool &amp; field, const char * column,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const EntityInfo &amp; entity)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const EntityParam &amp; param = entity.GetParam(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; std::string(column));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (param.IsNull())<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LOG_DEBUG(sLogger, ("message", "no required column")("column", column));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; field = param.GetBoolValue();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; static void Handle(std::string &amp; field, const char * column,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const EntityInfo &amp; entity)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const EntityParam &amp; param = entity.GetParam(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; std::string(column));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (param.IsNull())<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LOG_DEBUG(sLogger, ("message", "no required column")("column", column));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; field = param.GetStringValue();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>};<br><br>struct StoreSelector<br>{<br>&nbsp;&nbsp;&nbsp; static void Handle(const int field, const char * column,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EntityInfo &amp; entity)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EntityParam param;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; param.SetIntContent(column, field);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; entity.GetParamsForModify().push_back(param);<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; static void Handle(const int64_t field, const char * column,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EntityInfo &amp; entity)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EntityParam param;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; param.SetIntContent(column, field);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; entity.GetParamsForModify().push_back(param);<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; static void Handle(const double field, const char * column,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EntityInfo &amp; entity)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EntityParam param;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; param.SetDoubleContent(column, field);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; entity.GetParamsForModify().push_back(param);<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; static void Handle(const bool field, const char * column,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EntityInfo &amp; entity)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EntityParam param;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; param.SetBoolContent(column, field);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; entity.GetParamsForModify().push_back(param);<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; static void Handle(const std::string &amp; field, const char * column,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EntityInfo &amp; entity)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EntityParam param;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; param.SetStringContent(std::string(column), field);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; entity.GetParamsForModify().push_back(param);<br>&nbsp;&nbsp;&nbsp; }<br>};<br><br>template&lt;int N, typename T&gt;<br>struct LoadStoreSelector<br>{<br>&nbsp;&nbsp;&nbsp; typedef NullStruct ResultType;<br>};<br><br>template&lt;typename T&gt;<br>struct LoadStoreSelector&lt;IOWRAPPER_MAPPING_TO, T&gt;<br>{<br>&nbsp;&nbsp;&nbsp; typedef StoreSelector ResultType;<br>};<br><br>template&lt;typename T&gt;<br>struct LoadStoreSelector&lt;IOWRAPPER_MAPPING_FROM, T&gt;<br>{<br>&nbsp;&nbsp;&nbsp; typedef LoadSelector ResultType;<br>};<br><br>/*** for key **/<br><br>struct KeyStore<br>{<br>&nbsp;&nbsp;&nbsp; static void Handle(const std::string &amp; key,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EntityInfo &amp; info)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; info.SetEntityKey(key);<br>&nbsp;&nbsp;&nbsp; }<br>};<br><br>struct KeyLoad<br>{<br>&nbsp;&nbsp;&nbsp; static void Handle(std::string &amp; key,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const EntityInfo &amp; info)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; key = info.GetEntityKey();<br>&nbsp;&nbsp;&nbsp; }<br>};<br><br>template&lt;int N&gt;<br>struct KeyLoadStoreSelector<br>{<br>&nbsp;&nbsp;&nbsp; typedef NullStruct ResultType;<br>};<br><br>template&lt;&gt;<br>struct KeyLoadStoreSelector&lt;IOWRAPPER_MAPPING_TO&gt;<br>{<br>&nbsp;&nbsp;&nbsp; typedef KeyStore ResultType;<br>};<br><br>template&lt;&gt;<br>struct KeyLoadStoreSelector&lt;IOWRAPPER_MAPPING_FROM&gt;<br>{<br>&nbsp;&nbsp;&nbsp; typedef KeyLoad ResultType;<br>};<br><br>/********************* Useful Macro *************************/<br><br>// declare table schema<br>#define TABLE_SCHEMA(table, column, type)\<br>&nbsp;&nbsp;&nbsp; static const int SCHEMA_TYPE_##table##_##column = IOWRAPPER_MAPPING_TYPE_##type;<br><br>// load &amp; store for entity<br>#define MAPPING_ENTITY(T)\<br>&nbsp;&nbsp;&nbsp; virtual void ToEntity(EntityInfo&amp; info) const \<br>&nbsp;&nbsp;&nbsp; { \<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const_cast&lt;T*&gt;(this)-&gt;entity&lt;IOWRAPPER_MAPPING_TO&gt;(info);&nbsp;&nbsp;&nbsp; \<br>&nbsp;&nbsp;&nbsp; } \<br>&nbsp;&nbsp;&nbsp; \<br>&nbsp;&nbsp;&nbsp; virtual void FromEntity(const EntityInfo &amp; info) \<br>&nbsp;&nbsp;&nbsp; { \<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;entity&lt;IOWRAPPER_MAPPING_FROM&gt;(const_cast&lt;EntityInfo&amp;&gt;(info));&nbsp;&nbsp;&nbsp; \<br>&nbsp;&nbsp;&nbsp; } \<br>&nbsp;&nbsp;&nbsp; \<br>&nbsp;&nbsp;&nbsp; template&lt;int Type&gt; \<br>&nbsp;&nbsp;&nbsp; void entity(EntityInfo &amp; info)<br><br>// load &amp; store for entity key<br>#define MAPPING_KEY(field, column, table) \<br>{\<br>&nbsp;&nbsp;&nbsp; TypeCheck&lt;IOWRAPPER_MAPPING_TYPE_string, typeof(field), typeof(*this)&gt; check_##field;\<br>&nbsp;&nbsp;&nbsp; KeyLoadStoreSelector&lt;Type&gt;::ResultType::Handle(field, info);\<br>}<br><br>// load &amp; store for entity field<br>#define MAPPING_FIELD(field, column, table) \<br>{\<br>&nbsp;&nbsp; &nbsp;TypeCheck&lt;SCHEMA_TYPE_##table##_##column, typeof(field), typeof(*this)&gt; check_##field;\<br>&nbsp;&nbsp; &nbsp;LoadStoreSelector&lt;Type, typeof(field)&gt;::ResultType::Handle(field, #column, info);\<br>}<br><br>/********************* Basic Class *************************/<br>class MappingBase<br>{<br>&nbsp;&nbsp;&nbsp; virtual void ToEntity(EntityInfo&amp; info) const = 0;<br>&nbsp;&nbsp;&nbsp; virtual void FromEntity(const EntityInfo &amp; info) = 0;<br>};<br><br>}<br>}<br><br>#endif /* MAPPING_BASE_H_ */</p><p>&nbsp;</p><p>2. person_schema.h</p><p>/*<br>&nbsp;* person_schema.h<br>&nbsp;*<br>&nbsp;*&nbsp; Created on: Aug 3, 2010<br>&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Author: dyerac<br>&nbsp;*/<br><br>#ifndef PERSON_SCHEMA_H_<br>#define PERSON_SCHEMA_H_<br>#include "mapping_base.h"<br><br>TABLE_SCHEMA(person, age, int)<br>TABLE_SCHEMA(person, birth, int64_t)<br>TABLE_SCHEMA(person, gender, bool)<br>TABLE_SCHEMA(person, height, double)<br>TABLE_SCHEMA(person, name, string)<br><br>#endif /* PERSON_SCHEMA_H_ */</p><p>&nbsp;</p><p>&nbsp;</p><p>3.mapping_tester.cpp</p><p>/*<br>&nbsp;* mapping_tester.cpp<br>&nbsp;*<br>&nbsp;*&nbsp; Created on: Aug 3, 2010<br>&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Author: dyerac<br>&nbsp;*/<br><br>#include "mapping_base.h"<br>#include "person_schema.h"<br><br>using namespace std;<br><br>class Person : public MappingBase<br>{<br>private:<br>&nbsp;&nbsp;&nbsp; string mID;<br>&nbsp;&nbsp;&nbsp; int mAge;<br>&nbsp;&nbsp;&nbsp; int64_t mBirth;<br>&nbsp;&nbsp;&nbsp; bool mIsMale;<br>&nbsp;&nbsp;&nbsp; double mHeight;<br>&nbsp;&nbsp;&nbsp; string mName;<br><br>public:<br>&nbsp;&nbsp;&nbsp; Person(){};<br><br>&nbsp;&nbsp;&nbsp; Person(const string &amp; id, const int age, const int64_t birth,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const bool isMale, const double height, const string &amp; name) :<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mID(id), mAge(age), mBirth(birth), mIsMale(isMale), mHeight(height),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mName(name)<br>&nbsp;&nbsp;&nbsp; {<br><br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; bool Equals(const Person &amp; person) const<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bool ret = true;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret = (mID == person.mID) &amp;&amp; (mAge == person.mAge) &amp;&amp; (mBirth == person.mBirth)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;&amp; (mIsMale == person.mIsMale) &amp;&amp; (mHeight == person.mHeight) &amp;&amp; (mName == person.mName);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return ret;<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; void Dump(int level, FILE * src) const<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(src != NULL)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fprintf(src, "Person: id=%s, age=%d, birth=%ld, isMale=%d, height=%lf,"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; " name=%s\n",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mID.c_str(), mAge, mBirth, mIsMale, mHeight, mName.c_str());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; MAPPING_ENTITY(Person)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MAPPING_KEY(mID, id, person)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MAPPING_FIELD(mAge, age, person)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //error<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //MAPPING_FIELD(mAge, gender, person)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MAPPING_FIELD(mBirth, birth, person)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MAPPING_FIELD(mIsMale, gender, person)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MAPPING_FIELD(mHeight, height, person)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MAPPING_FIELD(mName, name, person)<br>&nbsp;&nbsp;&nbsp; }<br>};<br><br>void TestLoadStore()<br>{<br>&nbsp;&nbsp;&nbsp; EntityInfo saver;<br>&nbsp;&nbsp;&nbsp; Person src(string("4201041986"), 24, 19860119, true, 176.8, string("dyerac"));<br>&nbsp;&nbsp;&nbsp; Person dest;<br><br>&nbsp;&nbsp;&nbsp; src.ToEntity(saver);<br>&nbsp;&nbsp;&nbsp; dest.FromEntity(saver);<br><br>&nbsp;&nbsp;&nbsp; if(!src.Equals(dest))<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("not equal!\n");<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("equal!\n");<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; src.Dump(0, stdout);<br>&nbsp;&nbsp;&nbsp; dest.Dump(0, stdout);<br>}<br><br>int main(int argc, char ** argv)<br>{<br>&nbsp;&nbsp;&nbsp; TestLoadStore();<br>&nbsp;&nbsp;&nbsp; return 0;<br>}</p> <a href="http://hi.baidu.com/dyerac/blog/item/51a1608f4aa5b0e8503d9264.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/dyerac/blog/category/c%2B%2B">c++</a>&nbsp;<a href="http://hi.baidu.com/dyerac/blog/item/51a1608f4aa5b0e8503d9264.html#comment">查看评论</a>]]></description>
        <pubDate>2011-01-10  23:19</pubDate>
        <category><![CDATA[c++]]></category>
        <author><![CDATA[dyerac]]></author>
		<guid>http://hi.baidu.com/dyerac/blog/item/51a1608f4aa5b0e8503d9264.html</guid>
</item>

<item>
        <title><![CDATA[JNI使用笔记]]></title>
        <link><![CDATA[http://hi.baidu.com/dyerac/blog/item/695eef17316103104b90a7fd.html]]></link>
        <description><![CDATA[
		
		<p>From <a target="_blank" href="http://dyerac.com/index.php/2011/04/jni/">http://dyerac.com/index.php/2011/04/jni/</a></p><p>关于JNI的使用，看SUN本身的文档（The Java&#8482; Native Interface Programmer’s Guide and Specification）就行了，不过专门要写一篇mark一下的原因在于一个诡异的问题：</p><p>&nbsp;</p><p>在cpp中访问java里面类的成员时，如果是通过jobject参数本身获得其jclass值，然后再get field id，总是会报NoSuchFieldError；但是直接通过FindClass根据类名来获得jclass，然后再get field id就没问题，实在是诡异，网上也没有任何解答，还好被我试了出来.....&nbsp;</p><p>&nbsp;</p><p>顺便总结下jni的一些心得，不断更新ing：</p><p>（1）用directbuffer，这样可以直接访问java中申请的内存的地址，不需要通过jni的方法以拷贝数组的方式进行访问和修改</p><p>如java中，rawBuffer = ByteBuffer.allocateDirect(rawBufferSize);</p><p>cpp里面可以用GetDirectBufferAddress得到该buffer的指针，然后直接操作</p><p>&nbsp;</p><p>（2）用initIDs这样的方法，在cpp中用全局变量缓存java对象的field id和method id，避免每次都需要重新查询类的field和method table；该initIDs方法在java类的static代码块中，因此一旦类被jvm加载，就可以保证cpp里的field id和method id都得到重新计算。</p><p>&nbsp;</p><p>（3）shared stub 以及JNA</p><p>JNI最麻烦的地方在于，需要给每个native方法写一个jni的stub，工作重复量太大。jna是一个基于jni的框架，可以省去stub的功夫，直接调用native方法，如下：</p><p>package com.sun.jna.examples;<br><br>import com.sun.jna.Library;<br>import com.sun.jna.Native;<br>import com.sun.jna.Platform;<br><br>/** Simple example of JNA interface mapping and usage. */<br>public class HelloWorld {<br><br>&nbsp;&nbsp;&nbsp; // This is the standard, stable way of mapping, which supports extensive<br>&nbsp;&nbsp;&nbsp; // customization and mapping of Java to native types.<br>&nbsp;&nbsp;&nbsp; public interface CLibrary extends Library {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CLibrary INSTANCE = (CLibrary)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Native.loadLibrary((Platform.isWindows() ? "msvcrt" : "c"),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CLibrary.class);<br>&nbsp;&nbsp; &nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void printf(String format, Object... args);<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; public static void main(String[] args) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CLibrary.INSTANCE.printf("Hello, World\n");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i=0;i &lt; args.length;i++) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CLibrary.INSTANCE.printf("Argument %d: %s\n", i, args[i]);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>}</p><p>&nbsp;</p><p>确实很方便，但它是如何实现的呢？我没看过jna的源代码，只猜想它是建立在shared stub的技巧上的。所谓shared stub，就是我提供一个万能的stub，可以封装所有的native函数调用。如下：</p><p>public class C {<br>&nbsp;&nbsp;&nbsp; private static CFunction c_atol =<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new CFunction("msvcrt.dll", // native library name<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "atol",&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // C function name<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "C");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // calling convention<br>&nbsp;&nbsp;&nbsp; public static int atol(String str) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return c_atol.callInt(new Object[] {str});<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; ...<br>}<br><br>这个风格基本上就和jna很类似了（jna是传入class本身，接口更简单，不过通过反射和代理可以很容易的用shared stub实现）；下面来看它的实现。首先在C++中，通过dlopen系列函数，得到某个动态库的函数指针是非常容易的；问题的难点在于函数的签名是编译期未知的（在Java里面是知道的，但是在C++ stub里面不知道），具体的，是不知道有多少个参数，以及每个参数的类型。参数类型好解决，可以提供union类型，将所有参数都转换成该格式；而参数个数的问题，就需要用汇编代码操作C++函数的stack，填入参数。</p><p>&nbsp;typedef union {<br>&nbsp;&nbsp;&nbsp; jint i;<br>&nbsp;&nbsp;&nbsp; jfloat f;<br>&nbsp;&nbsp;&nbsp; void *p;<br>} word_t;</p><p>int asm_dispatch(void *func,&nbsp;&nbsp;&nbsp; // pointer to the C function<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int nwords,&nbsp;&nbsp;&nbsp; // number of words in args array<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; word_t *args, // start of the argument data<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int conv)&nbsp;&nbsp;&nbsp;&nbsp; // calling convention 0: C<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1: JNI<br>{<br>&nbsp;&nbsp;&nbsp; __asm {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov esi, args<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov edx, nwords<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // word address -&gt; byte address<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; shl edx, 2<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sub edx, 4<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jc args_done<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // push the last argument first<br>&nbsp;&nbsp;&nbsp; args_loop:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov eax, DWORD PTR [esi+edx]<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push eax<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sub edx, 4<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jge SHORT args_loop<br>&nbsp;&nbsp;&nbsp; args_done:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; call func<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // check for calling convention<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov edx, conv<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; or edx, edx<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jnz jni_call<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // pop the arguments<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov edx, nwords<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; shl edx, 2<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; add esp, edx<br>&nbsp;&nbsp;&nbsp; jni_call:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // done, return value in eax<br>&nbsp;&nbsp;&nbsp; }<br>}</p><p>总结shared stub的实现，就是提供一个万能的stub，在该stub里，通过jni传入的函数名（注意C/C++不同的convention方式）和库名，先找到对应函数地址，然后统一好参数的格式，操作stack压入参数再执行即可。shared stub使用起来非常方便，非常环保，当然性能上的损耗也是有代价的。</p> <a href="http://hi.baidu.com/dyerac/blog/item/695eef17316103104b90a7fd.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/dyerac/blog/category/%C4%AC%C8%CF%B7%D6%C0%E0">默认分类</a>&nbsp;<a href="http://hi.baidu.com/dyerac/blog/item/695eef17316103104b90a7fd.html#comment">查看评论</a>]]></description>
        <pubDate>2010-12-16  14:41</pubDate>
        <category><![CDATA[默认分类]]></category>
        <author><![CDATA[dyerac]]></author>
		<guid>http://hi.baidu.com/dyerac/blog/item/695eef17316103104b90a7fd.html</guid>
</item>

<item>
        <title><![CDATA[CRC校验的原理和实现]]></title>
        <link><![CDATA[http://hi.baidu.com/dyerac/blog/item/09f302de37da0e4595ee376e.html]]></link>
        <description><![CDATA[
		
		<p>网上大部分的中文资料都是垃圾，好不容易找到了两篇文章，就直接上了：</p><p>http://www.repairfaq.org/filipg/LINK/F_crc_v3.html</p><p>http://freesoftman.javaeye.com/blog/381635</p><p>&nbsp;</p><p>后面那篇相当于是第一篇的翻译，不过其中用一段code解释了table－driven crc不好理解的那个sumpoly （Sum all the Polys at various offsets that are to be XORed into the register in accordance with the control byte）</p><p>&nbsp;</p><p>除了table-driven的优化外，第一篇里面还提到了一些基于硬件的优化，比如说硬件先传送最低位，再传送最高位的时候，可以用reflected的stream进行计算等等。</p> <a href="http://hi.baidu.com/dyerac/blog/item/09f302de37da0e4595ee376e.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/dyerac/blog/category/%C4%AC%C8%CF%B7%D6%C0%E0">默认分类</a>&nbsp;<a href="http://hi.baidu.com/dyerac/blog/item/09f302de37da0e4595ee376e.html#comment">查看评论</a>]]></description>
        <pubDate>2010-12-10  19:32</pubDate>
        <category><![CDATA[默认分类]]></category>
        <author><![CDATA[dyerac]]></author>
		<guid>http://hi.baidu.com/dyerac/blog/item/09f302de37da0e4595ee376e.html</guid>
</item>

<item>
        <title><![CDATA[所有的爱情都是悲哀的，可尽管悲哀，依然是我们知道的最美好的事物]]></title>
        <link><![CDATA[http://hi.baidu.com/dyerac/blog/item/9fa3d03f60a162e055e7233b.html]]></link>
        <description><![CDATA[
		
		<div class="text-article" >
<p>色情与爱情的美妙结合，是琥珀的宣传口号之一。尽管已有耳闻，但最开始几幕台词的露骨还是让我大惊失色。不过这也算孟导的惯用手法了。在怪诞不已的前20分钟后，琥珀的剧情总算走上了正轨。</p>
<p>单 从故事性而言，琥珀讲的是一个很白烂的事情，任何偶像肥皂剧中都有可能出现；台词和演员所赋予的张力才是琥珀的亮点。一直觉得刘烨身上有种很偏执很狡黠的 特性，不过无极和黄金甲算是彻底把他废了。但今天的刘烨可谓如鱼得水，高辕那种玩世不恭游戏人间，其实内心孤独寂寞渴望爱情却又害怕爱情的心理让他演绎的 非常完美。王珞丹也摆脱了米莱的影子，甚至声线都有所改变。和原主角袁泉相比，王没有那种与生俱来的古典气质，但却很好的表现出了小优身上神经质、狂情少 女的一面。</p>
<p>床在剧中则作为道具被大量使用，以至于成为了琥珀主题的承载体和推动剂。在某一幕里舞台上甚至出现了七张床，不同的人在床上浓缩 着人生的悲欢离合，出生、死亡、相爱、做爱、争吵、背叛、哭泣、欢笑、想念、遗忘......想想也是，恋人们聚在一起最多的地方就是床了，它见证了最亲 密无间的相依相偎，也承受了同床异梦的尴尬凝重。枕边的人一个又一个，直到某天不再更迭；从此以后，每个寂静的夜里，都会伴着对方的呼吸轻轻入眠，把熟睡 后最软弱最无助的自己放心的交给彼此。</p>
<p>据说廖一梅在创造琥珀的剧本时，肚子里正怀着爱情的结晶。廖一直自诩愤世嫉俗悲观之人，但那个时候开 始，拒绝一切消极负面的事物，总努力的想要一个美好的世界展现在孩子面前。“因为有你，我害怕死去”，一定是感受到怀中生命跳动的喜悦，她才会把如此动人 的情话赋予给了高辕和小优，也给了琥珀一个亮丽光明的结局。</p>
<p>最后谢幕时，孟京辉和廖一梅都出现在了舞台上。循例介绍完每个演员后，孟导引用了他夫人的一句台词：“所有的爱情都是悲哀的，可尽管悲哀，依然是我们知道的最美好的事物。”</p>
<p>满场的热烈掌声和会心微笑。</p>
<p>&#160;</p>
<p>&#160;</p>
<p>&#160;</p>
<p>-------------------------------</p>
<p>写这篇文章的时候，越写越觉得自己矫情，都有点“为赋新词强说愁”的意味；但总觉得应该写点什么，只当是锻炼语文功底都成。其实琥珀对我而言，就是一个非常开心的夜晚 ^_^</p>
</div> <a href="http://hi.baidu.com/dyerac/blog/item/9fa3d03f60a162e055e7233b.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/dyerac/blog/category/%C4%AC%C8%CF%B7%D6%C0%E0">默认分类</a>&nbsp;<a href="http://hi.baidu.com/dyerac/blog/item/9fa3d03f60a162e055e7233b.html#comment">查看评论</a>]]></description>
        <pubDate>2010-03-24  01:16</pubDate>
        <category><![CDATA[默认分类]]></category>
        <author><![CDATA[dyerac]]></author>
		<guid>http://hi.baidu.com/dyerac/blog/item/9fa3d03f60a162e055e7233b.html</guid>
</item>


</channel>
</rss>
