Tokyo Cabinet TCHDB源码阅读——关于变长变量的高效存取(节约存储空间)

来源:互联网 发布:联想ssd数据迁移 编辑:程序博客网 时间:2024/05/22 00:19

TC中,很多地方为了节约存储空间,在保存变量时不会直接存放变量类型长度的值(比如4字节或8字节的值)到文件中,它会探测变量用来表示值的有效字节数,然后把这些有意义的字节保存起来,在后面读取该变量时,TC会把该变量的所有有效字节都读出来,从而计算出该变量所表示的值。

为了实现这个功能,TC的做法是:把变量的每个字节当成一个有符号数,最高位仅做为符号位使用,前面7位才用来表示真正的值,这样一来,文件中的4个字节的数仅有28位用来保存长度信息,如果我们要保存0xFFFFFFFF这个值,就需要在文件中占用5个字节,然而,这种情况的值毕竟是少数(4字节整型数只有超过2^28时才会在文件中占用5字节,其它情况都会少于或等于4字节),我们一般存放的值都很小,就只需要少于4个字节的存储长度,比如存放小于2^7的所有值只需要1个字节空间,存放2^72^14之间的值仅需要2字节等等,通过这中方式节省的空间会远远少于我们浪费的空间,因此这种做法在节省文件空间方面还是很有效的。

上面大概讲了原理,下面我们来看看TC中的代码,看看TC具体怎么实现的,我会在下面的代码中给出注释说明。TC主要通过2个宏来分别完成存放和读取变长变量,这2个宏又分别对应32位版本(最多存取4字节的值)和64位的版本(最多存放8字节的值),我仅分析32位的情况,64位情况类似。

1、  把一个变量按变长方式放到buffer

/* set a buffer for a variable length number */

#define TCSETVNUMBUF(TC_len, TC_buf, TC_num) / // TC_num为正整数

  do { /

    int _TC_num = (TC_num); /

    if(_TC_num == 0){ /  // 存放的变量为0

      ((signed char *)(TC_buf))[0] = 0; /

      (TC_len) = 1; /  // 返回存放了1个字节长度

    } else { /

      (TC_len) = 0; /

      while(_TC_num > 0){ /  // 变量大于0,就一直循环存放

        int _TC_rem = _TC_num & 0x7f; / // 取变量低7位存放

        _TC_num >>= 7; / // 变量右移7

        if(_TC_num > 0){ / // 去掉低7位后变量是否还有值?

// 按负数存放取得的变量低7位值,减一确保为负数,因为中间可能有0

          ((signed char *)(TC_buf))[(TC_len)] = -_TC_rem - 1; /         

} else { /  // 保留最后7位,按正数的形式

          ((signed char *)(TC_buf))[(TC_len)] = _TC_rem; /

        } /

        (TC_len)++; / // 存放的长度加1,表示用了多少字节来存放变量

      } /

    } /

  } while(false)

2、  buffer中读出按变长方式存放的变量值

/* read a variable length buffer */

#define TCREADVNUMBUF(TC_buf, TC_num, TC_step) /

  do { /

    TC_num = 0; /

    int _TC_base = 1; /

    int _TC_i = 0; / // buffer第一个成员开始读取

    while(true){ /

      if(((signed char *)(TC_buf))[_TC_i] >= 0){ / // 该字节大于0,则是最后字节

        TC_num += ((signed char *)(TC_buf))[_TC_i] * _TC_base; / // 累加

        break; / // 退出结束

      } /

        // 该字节小于0,则不是最后字节

      TC_num += _TC_base * (((signed char *)(TC_buf))[_TC_i] + 1) * -1; / //累加

      _TC_base <<= 7; /

      _TC_i++; / // 移动buffer

    } /

    (TC_step) = _TC_i + 1; / // 读取长度加1,表示读了多少字节的变量

 

  } while(false)

原创粉丝点击