接上一篇的源码中的rlp 继续......
- 回顾
- 解码(类型判断,stream结构,数据长度)
- 编码(encbuf)
- 下期
上一文当中讲到了以太坊的基本定义和认识,同时讲解了部分源码中rlp包中的编解码内容。其中,用Go实现的typecache为快速定位查找定义的编解码函数的核心数据结构。
Map中的Key则为类型,Value为对应的编码器和解码器
通过Key得到的类型,使用genTypeInfo生成对应类型的编解码器函数。
接下来,就是具体Decoder和Writer的处理逻辑了。
通过定义makeDecoder函数,根据类型来分配不同的处理函数。
func makeDecoder(typ reflect.Type, tags tags) (dec decoder, err error) {
kind := typ.Kind() // 获取当前需要解析的类型和长度
switch {
case typ == rawValueType:
return decodeRawValue, nil
case typ.Implements(decoderInterface):
return decodeDecoder, nil
case kind != reflect.Ptr && reflect.PtrTo(typ).Implements(decoderInterface):
return decodeDecoderNoPtr, nil
case typ.AssignableTo(reflect.PtrTo(bigInt)):
return decodeBigInt, nil
case typ.AssignableTo(bigInt):
return decodeBigIntNoPtr, nil
case isUint(kind):
return decodeUint, nil
case kind == reflect.Bool:
return decodeBool, nil
case kind == reflect.String:
return decodeString, nil
case kind == reflect.Slice || kind == reflect.Array:
return makeListDecoder(typ, tags)
case kind == reflect.Struct:
return makeStructDecoder(typ) //对于不同的结构体类型进行处理
case kind == reflect.Ptr:
if tags.nilOK {
return makeOptionalPtrDecoder(typ)
}
return makePtrDecoder(typ)
case kind == reflect.Interface:
return decodeInterface, nil
default:
return nil, fmt.Errorf("rlp: type %v is not RLP-serializable", typ)
}
}
判断类型时的所有解码方法:
通过反射的方法,遍历fields,调用字段的解码器方法
先来看一下它的结构。
编码中的stream,是用来读取以流方式解码RLP的一个辅助类。在解码过程中,根据Kind()方法获取需要解码的对象的类型和长度,然后根据长度和类型进行数据的解码。
判断类型:如果是Byte类型,直接返回;如果是string类型,读取指定长度值后返回。
Stream的List()方法:
1、调用Kind方法获取类型和长度,如果类型不匹配那么就抛出错误;
2、把一个listpos对象压入到堆栈;
3、pos:记录当前这个list已经读取了多少字节的数据(初始为0);
4、size:记录这个list对象一共需要读取多少字节数据;
5、比对pos字段和size字段是否相等,不相等则抛异常;
Stream的ListEnd()方法:
1、stack长度为0,抛异常(没有数据);
2、当前读取的数据数量pos不等于声明的数据长度size,抛异常(“call of ListEnd not positioned at EOL”);
3、进行“pop”操作,如果当前stack不为空,pos再加上处理完的数据长度;
编码的过程与解码大致类似。
填充过程中使用了一个缓冲buf对象,encbuf:
下期文章将进入以太坊p2p 进行源码分析。
---------------------
另外,文章部分内容和图片来自ZtesoftCS的github,在此鸣谢。
有任何建议或问题,欢迎加微信一起学习交流,欢迎从事IT,热爱IT,喜欢深挖源代码的行业大牛加入,一起探讨。
个人微信号:bboyHan