首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 其他教程 > 互联网 >

Hadoop中VIntWritable编码形式解析

2012-07-16 
Hadoop中VIntWritable编码方式解析最近因为实验室的云计算项目,开始学习Hadoop,有时间就记录一下自己在学

Hadoop中VIntWritable编码方式解析

最近因为实验室的云计算项目,开始学习Hadoop,有时间就记录一下自己在学习过程中的一些小收获吧。


《Hadoop权威指南》在序列化这一节有个例子程序,叫做TextPair,代码略长,就不贴上来了,它implements了WritableComparable<TextPair>,将两个Text对象打包到一起。TextPair以静态内部类的形式实现了WritableComparator,这样,不从数据流中deserialize出对象也可以对TextPair进行比较了。实现的这个Comparator中的compare方法如下:

?

@Overridepublic int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {    try {int firstL1 = WritableUtils.decodeVIntSize(b1[s1])+ readVInt(b1, s1);int firstL2 = WritableUtils.decodeVIntSize(b2[s2])+ readVInt(b2, s2);int cmp = TEXT_COMPARATOR.compare(b1, s1, firstL1, b2, s2,firstL2);if (cmp != 0) {    return cmp;}return TEXT_COMPARATOR.compare(b1, s1 + firstL1, l1 - firstL1,b2, s2 + firstL2, l2 - firstL2);    } catch (IOException e) {throw new IllegalArgumentException(e);    }}
?


?中间有两行代码比较有意思,int firstL1 = WritableUtils.decodeVIntSize(b1[s1])+ readVInt(b1, s1); 以及它之后的那一行类似的。第一个方法,decodeVIntSize,先看代码:

?

public static int decodeVIntSize(byte value) {    if (value >= -112) {      return 1;    } else if (value < -120) {      return -119 - value;    }    return -111 - value;  }
?


?这个方法的功能是判断VInt的长度。这里还是稍微记一下VInt和Text,以免将来忘记。


public static int readVInt(byte[] bytes, int start) throws IOException { return (int) readVLong(bytes, start); }public static long readVLong(byte[] bytes, int start) throws IOException { int len = bytes[start]; if (len >= -112) { return len; //1 } boolean isNegative = (len < -120); //2 len = isNegative ? -(len + 120) : -(len + 112); //3 if (start+1+len>bytes.length) //4 throw new IOException( "Not enough number of bytes for a zero-compressed integer"); long i = 0; for (int idx = 0; idx < len; idx++) { //5 i = i << 8; i = i | (bytes[start+1+idx] & 0xFF); } return (isNegative ? (i ^ -1L) : i); //6 }?


?readVInt引用了readVLong方法,readVLong从字节序列中尝试读取出一个采用变长编码方案的Long。因此,关键就在这个readVLong中了。

在标号1的地方,如果第一个byte>=-112,就将这个byte返回,亦即代表这就是我们想要的那个数,可以再结合?decodeVIntSize这个方法,可以确定,在VInt或VLong中,一个字节能表示的数是从0~127, -112~-1的,当数字小于了-112就会做特殊处理了,这就是开始用两个字节来存放。

往下看,在标号2的地方,又做了判断。要注意到的是,代码走到这里,表示要去的那个Int或Long已经不是用一个byte来表示的了,也就是说,第一个byte是用作长度标识了,但是从标号2这里又可以看出,第一个byte还可以用来判断这个数字是正数还是负数,从标号三可以看出,正负数的长度存储是不同的,原因从后面的代码可以看出。另外,这里分别为正数和负数做了最大8个byte长度的限制。VLongWritable的最大长度就是9,这里减一即是8.

在到达标号4时候,这个数字的长度已经计算出来了,剩下的就是解码出数字了,但是,如果解码出数字所需的byte数目超出了这个bytes数组的boundary,就只能抛出异常了。

从标号5开始对剩下的数据进行解码,对java的byte处理有点了解的都应该看得懂,只做一个对自己的提醒,使用bytes[start+1+idx] & 0xFF是为了确保获得正确的byte值,以防java帮你自动转换。如果你的byte的most significant bit是1,也就是说明这个byte是表示的负数,如果将这个负数与一个Long进行位的或操作,这个byte的所有高于第9位的所有位都会被填充上1,这可不是我们想要的。

标号6,之前做的判断这里用到了,如果是负数,就对它取-1,也就是11111111...(64个1,-1的二进制表示),的反,这么做的原因是在写入这个VInt或VLong的时候做了相反的操作,具体为什么这样我也高步太懂,但是这么做所有的数据byte(就是除了第一个byte)的most significant bit都是0.


以上两个方法?decodeVIntSize + readVInt 就完美地解析出了一个完整Text的长度。


在WritableComparator中并没有写VIntWritable或VLongWritable的方法,这时候找到VIntWritable。

?

public void readFields(DataInput in) throws IOException {    value = WritableUtils.readVInt(in);  }  public void write(DataOutput out) throws IOException {    WritableUtils.writeVInt(out, value);  }
?


?嗯,大哥来了,WritableUtils,代码参上。同样,writeVInt引用了writeVLong

public static void writeVLong(DataOutput stream, long i) throws IOException {       if (i >= -112 && i <= 127) {      stream.writeByte((byte)i);      return;    }          int len = -112;    if (i < 0) {      i ^= -1L; // take one's complement'      len = -120;    }          long tmp = i;    while (tmp != 0) {      tmp = tmp >> 8;      len--;    }          stream.writeByte((byte)len);          len = (len < -120) ? -(len + 120) : -(len + 112);          for (int idx = len; idx != 0; idx--) {      int shiftbits = (idx - 1) * 8;      long mask = 0xFFL << shiftbits;      stream.writeByte((byte)((i & mask) >> shiftbits));    }  }
?


?如果已经看到这里,我想也没有太多好说的了,先判断可不可以用一个byte表示,然后分为正负数处理,之后再计算长度,最后将数字编码成byte写入流。其实我写到这里才觉得,如果先找到write方法,什么都一目了然了,但是都写了这么多了,哎。。。就将就这样吧。

最后把大哥的readVLong也贴出来,这个方法是直接从stream中而不是从byte数组中读取,所以略有不同,但是思想还是一样的。

?

public static long readVLong(DataInput stream) throws IOException {    byte firstByte = stream.readByte();    int len = decodeVIntSize(firstByte);    //这个函数应该还是比较熟悉了吧    if (len == 1) {      return firstByte;    }    long i = 0;    for (int idx = 0; idx < len-1; idx++) {      byte b = stream.readByte();      i = i << 8;      i = i | (b & 0xFF);    }    return (isNegativeVInt(firstByte) ? (i ^ -1L) : i);  }
?

?

内容比较乱,东西也比较简单,主要目的还是给自己理清一下思路,方便以后回头能直接了然于心。

热点排行