当前位置:首页 > > 充电吧
[导读]LevelDB默认使用的是小端字节序存储,低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。 编码分为变长的EncodeVarint和固定大小的EncodeFixed两种,每种又分32位和6

LevelDB默认使用的是小端字节序存储,低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。 
编码分为变长的EncodeVarint和固定大小的EncodeFixed两种,每种又分32位和64位。
一.EncodeFixed

void EncodeFixed32(char* buf, uint32_t value) {
#if __BYTE_ORDER == __LITTLE_ENDIAN
  memcpy(buf, &value, sizeof(value));
#else
  buf[0] = value & 0xff;
  buf[1] = (value >> 8) & 0xff;
  buf[2] = (value >> 16) & 0xff;
  buf[3] = (value >> 24) & 0xff;
#endif
}
void EncodeFixed64(char* buf, uint64_t value) {
#if __BYTE_ORDER == __LITTLE_ENDIAN
  memcpy(buf, &value, sizeof(value));
#else
  buf[0] = value & 0xff;
  buf[1] = (value >> 8) & 0xff;
  buf[2] = (value >> 16) & 0xff;
  buf[3] = (value >> 24) & 0xff;
  buf[4] = (value >> 32) & 0xff;
  buf[5] = (value >> 40) & 0xff;
  buf[6] = (value >> 48) & 0xff;
  buf[7] = (value >> 56) & 0xff;
#endif
}

这两个函数非常简单,功能就是判断系统是否为小端存储,如果是,直接将value拷贝到buf,如果不是,则将value转换为小端字节序存储到buf中。
解码函数DecodeFixed32和DecodeFixed64是编码的逆过程,也很简单。
二.EncodeVarint
为什么要把整型(int)编码成变长整型(varint)呢?是为了尽可能的节约存储空间。
varint是一种紧凑的表示数字的方法,它用一个或多个字节来表示一个数字,值越小的数字使用越少的字节数。比如int32类型的数字,一般需要4个字节。但是采用Varint,对于很小的int32类型的数字,则可以用1个字节来表示。当然凡事都有好的也有不好的一面,采用varint表示法,大的数字则可能需要5个字节来表示。从统计的角度来说,一般不会所有消息中的数字都是大数,因此大多数情况下,采用varint后,可以用更小的字节数来表示数字信息。
varint中的每个字节的最高位(bit)有特殊含义,如果该位为1,表示后续的字节也是这个数字的一部分,如果该位为0,则结束。其他的7位(bit)都表示数字。7位能表示的最大数是127,因此小于128的数字都可以用一个字节表示。大于等于128的数字,比如说300,会用两个字节在内存中表示为:
低               高
1010 1100 0000 0010
实现过程如下:
300的二进制为100101100,取低7位也就是010 1100放在内存低字节中,由于第二个字节也是数字的一部分,因此内存低字节的最高位为1,则完整的内存低字节为1010 1100。300的高2位也就是10放到内存的高字节中,因为数字到该字节结束,因此该字节包括最高位的其他6位都用0填充,则完整的内存高字节为0000 0010。
正常情况下,int需要32位,varint用一个字节的最高位作为标识位,所以,一个字节只能存储7位,如果整数特别大,可能需要5个字节才能存放(5*8-5(标志位)>32),下面if语句的第五个分支就是处理这种情况。

char* EncodeVarint32(char* dst, uint32_t v) {
  // Operate on characters as unsigneds
  unsigned char* ptr = reinterpret_cast(dst);
  static const int B = 128;
  if (v < (1<<7)) {               //如果v小于128
    *(ptr++) = v;
  } else if (v < (1<>7;              //把300(0000 0001 0010 1100)右移7位得到000 0000 0000 0001 0,给内存高字节。
  } else if (v < (1<>7) | B;
    *(ptr++) = v>>14;
  } else if (v < (1<>7) | B;
    *(ptr++) = (v>>14) | B;
    *(ptr++) = v>>21;
  } else {
    *(ptr++) = v | B;
    *(ptr++) = (v>>7) | B;
    *(ptr++) = (v>>14) | B;
    *(ptr++) = (v>>21) | B;
    *(ptr++) = v>>28;
  }
  return reinterpret_cast(ptr);
}

对于64位整型,我们最多需要10个字节(10*8-10(标志位)>64),如果像EncodeVarint32一样写代码,需要有10个if分支,大牛肯定没这么勤快。其实EncodeVarint32也可以像EncodeVarint64这么写。

char* EncodeVarint64(char* dst, uint64_t v) {
  static const int B = 128;
  unsigned char* ptr = reinterpret_cast(dst);
  while (v >= B) {
    *(ptr++) = (v & (B-1)) | B;
    v >>= 7;
  }
  *(ptr++) = static_cast(v);
  return reinterpret_cast(ptr);
}

下面的函数是计算整型编码后的长度,也就是varint的长度。


int VarintLength(uint64_t v) {
  int len = 1;
  while (v >= 128) {
    v >>= 7;
    len++;
  }
  return len;
}

三.varint解码

解了编码的原理,再来看解码就很轻松了,直接调用GetVarint32Ptr函数,该函数处理value < 128的情况,即varint只占一个字节的情况,对于varint 大于一个字节的情况,GetVarint32Ptr调用GetVarint32PtrFallback来处理。

inline const char* GetVarint32Ptr(const char* p,
                                  const char* limit,
                                  uint32_t* value) {
  if (p < limit) {
    uint32_t result = *(reinterpret_cast(p));
    if ((result & 128) == 0) {
      *value = result;
      return p + 1;
    }
  }
  return GetVarint32PtrFallback(p, limit, value);
}

在GetVarint32Ptr和GetVarint32PtrFallback函数中,参数p是指向一个包含varint的字符串,limit在调用的时候都是赋值为limit= p + 5, 这是因为varint最多占用5个字节。value用于存储返回的int值。

const char* GetVarint32PtrFallback(const char* p,
                                   const char* limit,
                                   uint32_t* value) {
  uint32_t result = 0;
  for (uint32_t shift = 0; shift <= 28 && p < limit; shift += 7) {
    uint32_t byte = *(reinterpret_cast(p));
    p++;
    if (byte & 128) {
      // More bytes are present
      result |= ((byte & 127) << shift);
    } else {
      result |= (byte << shift);
      *value = result;
      return reinterpret_cast(p);
    }
  }
  return NULL;
}

64位的解码与32位的类似。


本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭