github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/trie/encoding.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:45</date> 10 //</624450122779987968> 11 12 13 package trie 14 15 //trie键有三种不同的编码: 16 // 17 //keybytes编码包含实际的密钥,而不包含其他任何内容。此编码是 18 //大多数API函数的输入。 19 // 20 //十六进制编码包含键的每个半字节和可选尾随字节。 21 //值为0x10的“终止符”字节,指示键处的节点是否 22 //包含一个值。十六进制键编码用于内存中加载的节点,因为它 23 //方便进入。 24 // 25 //紧凑型编码由以太坊黄纸(称为“十六进制前缀”)定义 26 //并包含密钥的字节和标志。高尖的 27 //第一个字节包含标志;编码长度的奇数和 28 //第二个最低的编码键处的节点是否为值节点。低啃咬 29 //对于偶数个半字节和第一个半字节,第一个字节的为零。 30 //如果是奇数。所有剩余的笔尖(现在是偶数)都合适 31 //到剩余的字节。压缩编码用于存储在磁盘上的节点。 32 33 func hexToCompact(hex []byte) []byte { 34 terminator := byte(0) 35 if hasTerm(hex) { 36 terminator = 1 37 hex = hex[:len(hex)-1] 38 } 39 buf := make([]byte, len(hex)/2+1) 40 buf[0] = terminator << 5 //标志字节 41 if len(hex)&1 == 1 { 42 buf[0] |= 1 << 4 //奇数旗 43 buf[0] |= hex[0] //第一个半字节包含在第一个字节中 44 hex = hex[1:] 45 } 46 decodeNibbles(hex, buf[1:]) 47 return buf 48 } 49 50 func compactToHex(compact []byte) []byte { 51 base := keybytesToHex(compact) 52 //删除终止符标志 53 if base[0] < 2 { 54 base = base[:len(base)-1] 55 } 56 //应用奇数旗 57 chop := 2 - base[0]&1 58 return base[chop:] 59 } 60 61 func keybytesToHex(str []byte) []byte { 62 l := len(str)*2 + 1 63 var nibbles = make([]byte, l) 64 for i, b := range str { 65 nibbles[i*2] = b / 16 66 nibbles[i*2+1] = b % 16 67 } 68 nibbles[l-1] = 16 69 return nibbles 70 } 71 72 //十六进制字节将十六进制字节转换为键字节。 73 //这只能用于长度均匀的键。 74 func hexToKeybytes(hex []byte) []byte { 75 if hasTerm(hex) { 76 hex = hex[:len(hex)-1] 77 } 78 if len(hex)&1 != 0 { 79 panic("can't convert hex key of odd length") 80 } 81 key := make([]byte, len(hex)/2) 82 decodeNibbles(hex, key) 83 return key 84 } 85 86 func decodeNibbles(nibbles []byte, bytes []byte) { 87 for bi, ni := 0, 0; ni < len(nibbles); bi, ni = bi+1, ni+2 { 88 bytes[bi] = nibbles[ni]<<4 | nibbles[ni+1] 89 } 90 } 91 92 //prefixlen返回A和B的公共前缀的长度。 93 func prefixLen(a, b []byte) int { 94 var i, length = 0, len(a) 95 if len(b) < length { 96 length = len(b) 97 } 98 for ; i < length; i++ { 99 if a[i] != b[i] { 100 break 101 } 102 } 103 return i 104 } 105 106 //hasterm返回十六进制键是否具有终止符标志。 107 func hasTerm(s []byte) bool { 108 return len(s) > 0 && s[len(s)-1] == 16 109 } 110