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  }