2019独角兽企业重金招聘Python工程师标准>>>

BinaryOutputArchive类位于org.apache.jute包中,是序列化组件中的一个类。从字面意思理解就是输出类。这里类实现了OutputArchive接口。并且在构造函数中需要传递一个DataOutput接口的实现类。

在这个代码中有一段代码引起了我的注意:

 /*** create our own char encoder to utf8. This is faster * then string.getbytes(UTF8).* @param s the string to encode into utf8* @return utf8 byte sequence.*/final private ByteBuffer stringToByteBuffer(CharSequence s) {bb.clear();final int len = s.length();for (int i = 0; i < len; i++) {if (bb.remaining() < 3) {ByteBuffer n = ByteBuffer.allocate(bb.capacity() << 1);bb.flip();n.put(bb);bb = n;}char c = s.charAt(i);//一个字节if (c < 0x80) {bb.put((byte) c);}else if (c < 0x800) {bb.put((byte) (0xc0 | (c >> 6)));bb.put((byte) (0x80 | (c & 0x3f)));} else {bb.put((byte) (0xe0 | (c >> 12)));bb.put((byte) (0x80 | ((c >> 6) & 0x3f)));bb.put((byte) (0x80 | (c & 0x3f)));}}bb.flip();return bb;}

 根据代码的注释说这里的实现比java自身的要高效。那么究竟是怎么样呢?值得对比进行学习一番。

首先在对比之前要先弄清除他们在做什么。这里的方式实现了一个字符串转化正一个utf-8编码格式的字节数组。那么utf-8编码格式是规定怎么进行转换的呢?大体的规则如下:

  1.对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。

2.对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。

实例如下:

一个字节 0000 0000-0000 007F | 0xxxxxxx

两个字节 0000 0080-0000 07FF | 110xxxxx 10xxxxxx

三个字节 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx

四个字节 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

 根据上面的规则再去理解代码就方便一些了。stringToByteBuffer方法的逻辑大致是:

  1. 循环字符的长度。如果缓冲字节类ByteBuffer 的剩余空间不足则扩充1倍。(这里使用了位运算)

  2. 如果是1个字节直接保存

  3. 如果是2个字节,先把字符的前一部分根据规则放入第一个字节中,然后把后面一部分放入第二个字节中。0xc0-->11000000。用它或一个字符刚好满足上面规则中110xxxxx的要求。代码 c >> 6 让我理解了好半天(可能是我比较笨的原因吧)。最后我发现根据规则除了第一个字节,其他字节都是前两位为10.那么就只剩下6位了。所以移位计算的数字是以6的倍数来进行的。0x80-->10000000、0x3f-->00111111。代码 0x80 | (c & 0x3f) 刚好满足了第二个字节的条件

  4. 3字节的代码和2字节的代码比较类似

理解了stringToByteBuffer这个方法之后,我们再来看看java中的实现:

 public byte[] getBytes(String charsetName)throws UnsupportedEncodingException {if (charsetName == null) throw new NullPointerException();return StringCoding.encode(charsetName, value, 0, value.length);}

没什么好说的,调用了一个StringCoding的一个方法。继续跟踪代码:

static byte[] encode(String charsetName, char[] ca, int off, int len)throws UnsupportedEncodingException{StringEncoder se = deref(encoder);String csn = (charsetName == null) ? "ISO-8859-1" : charsetName;if ((se == null) || !(csn.equals(se.requestedCharsetName())|| csn.equals(se.charsetName()))) {se = null;try {Charset cs = lookupCharset(csn);if (cs != null)se = new StringEncoder(cs, csn);} catch (IllegalCharsetNameException x) {}if (se == null)throw new UnsupportedEncodingException (csn);set(encoder, se);}return se.encode(ca, off, len);}

 这段代码的大概的意思猜测是,先根据传递的字符集名称去查找字符集,然后根据字符集创建一个StringEncoder类型的对象。然后调用对象的encode方法。继续跟踪代码:

byte[] encode(char[] ca, int off, int len) {int en = scale(len, ce.maxBytesPerChar());byte[] ba = new byte[en];if (len == 0)return ba;if (ce instanceof ArrayEncoder) {int blen = ((ArrayEncoder)ce).encode(ca, off, len, ba);return safeTrim(ba, blen, cs, isTrusted);} else {ce.reset();ByteBuffer bb = ByteBuffer.wrap(ba);CharBuffer cb = CharBuffer.wrap(ca, off, len);try {CoderResult cr = ce.encode(cb, bb, true);if (!cr.isUnderflow())cr.throwException();cr = ce.flush(bb);if (!cr.isUnderflow())cr.throwException();} catch (CharacterCodingException x) {// Substitution is always enabled,// so this shouldn't happenthrow new Error(x);}return safeTrim(ba, bb.position(), cs, isTrusted);}}

 这段代码判断StringEncoder的类型。我写个测试用例byte[] x="a".getbytes("utf-8"); 跟踪代码到这里发现走的是 ArrayEncoder这里逻辑。继续跟踪发现ArrayEncoder有多个实现。其中有一个UTF_8类。这个类在nio包中。这里的代码只能通过反编译来看,没有找到源码包:

/*     */     public int encode(char[] paramArrayOfChar, int paramInt1, int paramInt2, byte[] paramArrayOfByte)
/*     */     {
/* 627 */       int i = paramInt1 + paramInt2;
/* 628 */       int j = 0;
/* 629 */       int k = j + Math.min(paramInt2, paramArrayOfByte.length);
/* 632 */       while ((j < k) && (paramArrayOfChar[paramInt1] < '')) {
/* 633 */         paramArrayOfByte[(j++)] = ((byte)paramArrayOfChar[(paramInt1++)]);
/*     */       }
/* 635 */       while (paramInt1 < i)
/*     */       {
/* 636 */         char c = paramArrayOfChar[(paramInt1++)];
/* 637 */         if (c < '-')
/*     */         {
/* 639 */           paramArrayOfByte[(j++)] = ((byte)c);
/*     */         }
/* 640 */         else if (c < 'ࠀ')
/*     */         {
/* 642 */           paramArrayOfByte[(j++)] = ((byte)(0xC0 | c >> '\006'));
/* 643 */           paramArrayOfByte[(j++)] = ((byte)(0x80 | c & 0x3F));
/*     */         }
/* 644 */         else if (Character.isSurrogate(c))
/*     */         {
/* 645 */           if (this.sgp == null) {
/* 646 */             this.sgp = new Surrogate.Parser();
/*     */           }
/* 647 */           int m = this.sgp.parse(c, paramArrayOfChar, paramInt1 - 1, i);
/* 648 */           if (m < 0)
/*     */           {
/* 649 */             if (malformedInputAction() != CodingErrorAction.REPLACE) {
/* 650 */               return -1;
/*     */             }
/* 651 */             paramArrayOfByte[(j++)] = replacement()[0];
/*     */           }
/*     */           else
/*     */           {
/* 653 */             paramArrayOfByte[(j++)] = ((byte)(0xF0 | m >> 18));
/* 654 */             paramArrayOfByte[(j++)] = ((byte)(0x80 | m >> 12 & 0x3F));
/* 655 */             paramArrayOfByte[(j++)] = ((byte)(0x80 | m >> 6 & 0x3F));
/* 656 */             paramArrayOfByte[(j++)] = ((byte)(0x80 | m & 0x3F));
/* 657 */             paramInt1++;
/*     */           }
/*     */         }
/*     */         else
/*     */         {
/* 661 */           paramArrayOfByte[(j++)] = ((byte)(0xE0 | c >> '\f'));
/* 662 */           paramArrayOfByte[(j++)] = ((byte)(0x80 | c >> '\006' & 0x3F));
/* 663 */           paramArrayOfByte[(j++)] = ((byte)(0x80 | c & 0x3F));
/*     */         }
/*     */       }
/* 666 */       return j;
/*     */     }

 和zookeeper中的差不多,都是在用位运算来实现utf-8的规则。

 对比代码的理解如下:

  1. zookeeper中实现了3字节的转换,而java的类实现了4字节的转换。

  2. 代码复杂度上java的比zookeeper要高

直观上我没有发现具体的实现上zookeeper会比较高。

到这里utf-8的实现就完毕了,但这里引起了我的思考:

  1. zookeeper为什么需要自己去实现序列化、实现utf-8的转换

  2. 如果自己去开发一个分布式的系统是否也需要实现这些呢

也许等我在不断的学习zookeeper的源码中会有新的体会和领悟!树立目标,坚持去做,不断的提升自己的理解和领悟,才能超越过去的自己!

转载于:https://my.oschina.net/u/2456287/blog/645180

通过Zoopkeeper-BinaryOutputArchive类学习utf-8的实现相关推荐

  1. Unity API常用方法和类学习笔记2

    Unity API常用方法和类学习笔记2 ------Mathf & Input & Vector & Random 类Mathf 一.静态变量 print(Mathf.Deg ...

  2. CStdioFile类学习

    CStdioFile 类学习笔记 2007-7-17 CStdioFile 类的声明保存再 afx.h 头文件中. CStdioFile 类继承自 CFile 类, CStdioFile 对象表示一个 ...

  3. Unity API常用方法和类学习笔记1

    Unity API常用方法和类学习笔记1 ------主要构架(Unity-Engine.GameObject.Component) 事件 一.事件执行顺序 二.测试代码 using System.C ...

  4. [黑马IOS自学第十篇]OC语言基础,面向对象概念,类学习

    一.类学习引入 //缺点 _result的结果一直在变化,得创建变量,在堆区一直申请空间 <span style="font-size:14px;">#import&l ...

  5. 精品网址整理——计算机类学习视频资料

    精品网址整理--计算机类学习视频资料     计算机方面学习视频资料 PHP视频教程从入门到精通 http://www.xuexinet.com/document/xuexidetail.asp?id ...

  6. QIODevice 类学习笔记

    QIODevice 类学习笔记 Isaaccwoo 2015年12月10日 一.       简介 QIODevice用于对输入输出设备进行管理.输入设备有两种类型,一种是随机访问设备(Random- ...

  7. QFrame类学习笔记

    QFrame类学习笔记 参考:https://wenku.baidu.com/view/759c1af565ce050877321322.html https://doc.qt.io/qt-5/qfr ...

  8. 位置式PID——小车类学习心得

    位置式PID--小车类学习心得 位置式PID:公式为:val=kp * err+ki * err_sum+kd*(err-err_last) err变量=期望值-实际值,也就是我们常说的误差 err_ ...

  9. Cesium BingMapsImageryProvider类学习

    在此有一个超图地图加载三维场景的示例: http://support.supermap.com.cn:8090/webgl/examples/webgl/editor.html#KeyboardCon ...

最新文章

  1. php libev pthreads,libuv 与 libev 的对比
  2. CSS题目系列(1) - 可滚动的Table
  3. input:file上传类型控制简单介绍
  4. 一款基于jquery的下拉点击改变背景图片
  5. jvm:运行时数据区
  6. Php在线字体woff转svg,在线字体格式转换ttf/otf/eot/woff/woff2格式工具
  7. 用fft对信号进行频谱分析实验报告_FFT分析频谱无泄露的条件
  8. linux模块导出符号 EXPORT_SYMBOL_GPLEXPORT_SYMBOL(转)
  9. C#中的主从Datagridview
  10. EurekaClient启动报错:Invocation of destroy method failed on bean with name 'scopedTarget.eurekaClient'
  11. [转帖]ASP.NET4中不要相信Request.Browser.Cookies,Form验证要用UseCookies
  12. Android测试工具-Emmagee使用
  13. win7 共享wifi
  14. SESSION的窃取
  15. C#语言入门详解1-12
  16. 计算机怎样关闭电脑桌面弹窗,永久关闭电脑弹窗广告的三种方法
  17. 大数据未来发展趋势,主要取决于这八个要素
  18. 【软件2.0】软件2.0时代来了!特斯拉AI负责人说:神经网络正在改变编程
  19. java解析bt协议详解_BT下载原理分析
  20. RHEL目录和文件操作:Linux文件系统管理

热门文章

  1. 非结构化WEB数据库与图书馆多媒体信息资源的组织利用
  2. [读书笔记] 史玉柱自述:我的营销心得
  3. 利用专用文件夹隐藏文件 (tasks、fonts)
  4. 计算机硬件教学设计高中信息,重大版信息技术七上《计算机硬件系统》教学设计.doc...
  5. vue的视图化创建项目_vue-cli3用图形化的方式创建项目
  6. arduino智能浇花系统_创新成果 | 养花神器——智能浇花机
  7. sql数据导入错误代码: 0x80004005_PL/SQL 非预定义异常、自定义异常处理、RAISE_APPLICATION_ERROR...
  8. python里面print是什么意思_python里print是什么意思
  9. c语言可以编制出功能复杂的程序,2018级《C语言程序设计》复习题及答案(5页)-原创力文档...
  10. MyEclipse工具的优化使用