# 1. 补码编码

• 原码：最高位为符号位，剩余位表示绝对值；
• 反码：除符号位外，对原码剩余位依次取反；
• 补码：对于正数，补码为其自身；对于负数，除符号位外对原码剩余位依次取反然后+1。

$0=[0000 \enspace 0000]_原=[1000 \enspace 0000]_原$

$1 + (-1) = [0000 \enspace 0001]_原 + [1000 \enspace 0001]_原 = [1000 \enspace 0010]原 = -2$

$1 + (-1) = [0000 \enspace 0001]_补 + [1111 \enspace 1111]_补 = [0000 \enspace 0000]_补 = 0$

• 左移1位（n << 1），无论正数还是负数，相当于乘以2；对于正数，若大于Integer.MAX_VALUE/2（1076741823），则会发生溢出，导致左移1位后为负数
• 右移31位（n >> 31），对于正数，则返回0x00000000；对于负数，则返回0xffffffff

# 2. ZigZag

$1 = [00 \enspace 00 \enspace 00 \enspace 01] \\ -1 = [ff \enspace ff \enspace ff \enspace ff] \\ 20151103 = [01 \enspace 33 \enspace 7b \enspace 3f]$

## 编码

n hex h(n) ZigZag (hex)
0 00 00 00 00 00 00 00 00 00
-1 ff ff ff ff 00 00 00 01 01
1 00 00 00 01 00 00 00 02 02
-2 ff ff ff fe 00 00 00 03 03
2 00 00 00 02 00 00 00 04 04
... ... ... ...
-64 ff ff ff c0 00 00 00 7f 7f
64 00 00 00 40 00 00 00 80 80 01
... ... ... ...

h(0) = 0x0 = [00]
h(64) = 0x80 = [80]
h(16384) = 0x8000 = [80 00]


input: int n
output: byte[] buf

loop
if 第七位满1或有进位:
n |= 0x80;
取低位的8位作为一个byte写入buf;
n >>>=7（无符号右移7位，在高位插0）;
else:
取低位的8位作为一个byte写入buf
end


ZigZag编码的Java实现（从org.apache.avro.io.BinaryData抠出来的）：

/** Encode an integer to the byte array at the given position. Will throw
* IndexOutOfBounds if it overflows. Users should ensure that there are at
* least 5 bytes left in the buffer before calling this method.
* @return The number of bytes written to the buffer, between 1 and 5.
*/
public static int encodeInt(int n, byte[] buf, int pos) {
// move sign to low-order bit, and flip others if negative
n = (n << 1) ^ (n >> 31);
int start = pos;
if ((n & ~0x7F) != 0) {
buf[pos++] = (byte)((n | 0x80) & 0xFF);
n >>>= 7;
if (n > 0x7F) {
buf[pos++] = (byte)((n | 0x80) & 0xFF);
n >>>= 7;
if (n > 0x7F) {
buf[pos++] = (byte)((n | 0x80) & 0xFF);
n >>>= 7;
if (n > 0x7F) {
buf[pos++] = (byte)((n | 0x80) & 0xFF);
n >>>= 7;
}
}
}
}
buf[pos++] = (byte) n;
return pos - start;
}


ZigZag是一种变长编码，当整数值较大时，hash值的十六进制的有效位会较长，对应地ZigZag码字会出现需要5 byte存储；比如，

ZigZag(Integer.MAX_VALUE)=[fe ff ff ff 0f]


## 解码

public static int readInt(byte[] buf, int pos) throws IOException {
int len = 1;
int b = buf[pos] & 0xff;
int n = b & 0x7f;
if (b > 0x7f) {
b = buf[pos + len++] & 0xff;
n ^= (b & 0x7f) << 7;
if (b > 0x7f) {
b = buf[pos + len++] & 0xff;
n ^= (b & 0x7f) << 14;
if (b > 0x7f) {
b = buf[pos + len++] & 0xff;
n ^= (b & 0x7f) << 21;
if (b > 0x7f) {
b = buf[pos + len++] & 0xff;
n ^= (b & 0x7f) << 28;
if (b > 0x7f) {
throw new IOException("Invalid int encoding");
}
}
}
}
}
pos += len;
return (n >>> 1) ^ -(n & 1); // back to two's-complement
}


ZigZag总结如下：

1. ZigZag仅从经验出发，认为较小的整数会有较大的概率出现，故设计编码策略：小整数对应的ZigZag码字短，大整数对应的ZigZag码字长。
2. 但是，在特定的场景下，比如，要传输的整数为大整数居多，ZigZag编码的压缩效率就不理想了。
posted @ 2016-06-08 16:50  Treant  阅读(15237)  评论(4编辑  收藏  举报