github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/util/codec/codec.go (about) 1 // Copyright 2015 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package codec 15 16 import ( 17 "time" 18 19 "github.com/insionng/yougam/libraries/juju/errors" 20 "github.com/insionng/yougam/libraries/pingcap/tidb/mysql" 21 "github.com/insionng/yougam/libraries/pingcap/tidb/util/types" 22 ) 23 24 // First byte in the encoded value which specifies the encoding type. 25 const ( 26 NilFlag byte = 0 27 bytesFlag byte = 1 28 compactBytesFlag byte = 2 29 intFlag byte = 3 30 uintFlag byte = 4 31 floatFlag byte = 5 32 decimalFlag byte = 6 33 durationFlag byte = 7 34 maxFlag byte = 250 35 ) 36 37 func encode(b []byte, vals []types.Datum, comparable bool) ([]byte, error) { 38 for _, val := range vals { 39 switch val.Kind() { 40 case types.KindInt64: 41 b = append(b, intFlag) 42 b = EncodeInt(b, val.GetInt64()) 43 case types.KindUint64: 44 b = append(b, uintFlag) 45 b = EncodeUint(b, val.GetUint64()) 46 case types.KindFloat32, types.KindFloat64: 47 b = append(b, floatFlag) 48 b = EncodeFloat(b, val.GetFloat64()) 49 case types.KindString, types.KindBytes: 50 b = encodeBytes(b, val.GetBytes(), comparable) 51 case types.KindMysqlTime: 52 b = encodeBytes(b, []byte(val.GetMysqlTime().String()), comparable) 53 case types.KindMysqlDuration: 54 // duration may have negative value, so we cannot use String to encode directly. 55 b = append(b, durationFlag) 56 b = EncodeInt(b, int64(val.GetMysqlDuration().Duration)) 57 case types.KindMysqlDecimal: 58 b = append(b, decimalFlag) 59 b = EncodeDecimal(b, val.GetMysqlDecimal()) 60 case types.KindMysqlHex: 61 b = append(b, intFlag) 62 b = EncodeInt(b, int64(val.GetMysqlHex().ToNumber())) 63 case types.KindMysqlBit: 64 b = append(b, uintFlag) 65 b = EncodeUint(b, uint64(val.GetMysqlBit().ToNumber())) 66 case types.KindMysqlEnum: 67 b = append(b, uintFlag) 68 b = EncodeUint(b, uint64(val.GetMysqlEnum().ToNumber())) 69 case types.KindMysqlSet: 70 b = append(b, uintFlag) 71 b = EncodeUint(b, uint64(val.GetMysqlSet().ToNumber())) 72 case types.KindNull: 73 b = append(b, NilFlag) 74 case types.KindMinNotNull: 75 b = append(b, bytesFlag) 76 case types.KindMaxValue: 77 b = append(b, maxFlag) 78 default: 79 return nil, errors.Errorf("unsupport encode type %d", val.Kind()) 80 } 81 } 82 83 return b, nil 84 } 85 86 func encodeBytes(b []byte, v []byte, comparable bool) []byte { 87 if comparable { 88 b = append(b, bytesFlag) 89 b = EncodeBytes(b, v) 90 } else { 91 b = append(b, compactBytesFlag) 92 b = EncodeCompactBytes(b, v) 93 } 94 return b 95 } 96 97 // EncodeKey appends the encoded values to byte slice b, returns the appended 98 // slice. It guarantees the encoded value is in ascending order for comparison. 99 func EncodeKey(b []byte, v ...types.Datum) ([]byte, error) { 100 return encode(b, v, true) 101 } 102 103 // EncodeValue appends the encoded values to byte slice b, returning the appended 104 // slice. It does not guarantee the order for comparison. 105 func EncodeValue(b []byte, v ...types.Datum) ([]byte, error) { 106 return encode(b, v, false) 107 } 108 109 // Decode decodes values from a byte slice generated with EncodeKey or EncodeValue 110 // before. 111 func Decode(b []byte) ([]types.Datum, error) { 112 if len(b) < 1 { 113 return nil, errors.New("invalid encoded key") 114 } 115 116 var ( 117 err error 118 values = make([]types.Datum, 0, 1) 119 ) 120 121 for len(b) > 0 { 122 var d types.Datum 123 b, d, err = DecodeOne(b) 124 if err != nil { 125 return nil, errors.Trace(err) 126 } 127 128 values = append(values, d) 129 } 130 131 return values, nil 132 } 133 134 // DecodeOne decodes on datum from a byte slice generated with EncodeKey or EncodeValue. 135 func DecodeOne(b []byte) (remain []byte, d types.Datum, err error) { 136 if len(b) < 1 { 137 return nil, d, errors.New("invalid encoded key") 138 } 139 flag := b[0] 140 b = b[1:] 141 switch flag { 142 case intFlag: 143 var v int64 144 b, v, err = DecodeInt(b) 145 d.SetInt64(v) 146 case uintFlag: 147 var v uint64 148 b, v, err = DecodeUint(b) 149 d.SetUint64(v) 150 case floatFlag: 151 var v float64 152 b, v, err = DecodeFloat(b) 153 d.SetFloat64(v) 154 case bytesFlag: 155 var v []byte 156 b, v, err = DecodeBytes(b) 157 d.SetBytes(v) 158 case compactBytesFlag: 159 var v []byte 160 b, v, err = DecodeCompactBytes(b) 161 d.SetBytes(v) 162 case decimalFlag: 163 var v mysql.Decimal 164 b, v, err = DecodeDecimal(b) 165 d.SetValue(v) 166 case durationFlag: 167 var r int64 168 b, r, err = DecodeInt(b) 169 if err == nil { 170 // use max fsp, let outer to do round manually. 171 v := mysql.Duration{Duration: time.Duration(r), Fsp: mysql.MaxFsp} 172 d.SetValue(v) 173 } 174 case NilFlag: 175 default: 176 return b, d, errors.Errorf("invalid encoded key flag %v", flag) 177 } 178 if err != nil { 179 return b, d, errors.Trace(err) 180 } 181 return b, d, nil 182 }