github.com/sykesm/fabric@v1.1.0-preview.0.20200129034918-2aa12b1a0181/common/ledger/util/util.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  		 http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package util
    18  
    19  import (
    20  	"encoding/binary"
    21  	"fmt"
    22  
    23  	"github.com/golang/protobuf/proto"
    24  	"github.com/pkg/errors"
    25  )
    26  
    27  // EncodeOrderPreservingVarUint64 returns a byte-representation for a uint64 number such that
    28  // all zero-bits starting bytes are trimmed in order to reduce the length of the array
    29  // For preserving the order in a default bytes-comparison, first byte contains the number of remaining bytes.
    30  // The presence of first byte also allows to use the returned bytes as part of other larger byte array such as a
    31  // composite-key representation in db
    32  func EncodeOrderPreservingVarUint64(number uint64) []byte {
    33  	bytes := make([]byte, 8)
    34  	binary.BigEndian.PutUint64(bytes, number)
    35  	startingIndex := 0
    36  	size := 0
    37  	for i, b := range bytes {
    38  		if b != 0x00 {
    39  			startingIndex = i
    40  			size = 8 - i
    41  			break
    42  		}
    43  	}
    44  	sizeBytes := proto.EncodeVarint(uint64(size))
    45  	if len(sizeBytes) > 1 {
    46  		panic(fmt.Errorf("[]sizeBytes should not be more than one byte because the max number it needs to hold is 8. size=%d", size))
    47  	}
    48  	encodedBytes := make([]byte, size+1)
    49  	encodedBytes[0] = sizeBytes[0]
    50  	copy(encodedBytes[1:], bytes[startingIndex:])
    51  	return encodedBytes
    52  }
    53  
    54  // DecodeOrderPreservingVarUint64 decodes the number from the bytes obtained from method 'EncodeOrderPreservingVarUint64'.
    55  // It returns the decoded number, the number of bytes that are consumed in the process, and an error if the input bytes are invalid.
    56  func DecodeOrderPreservingVarUint64(bytes []byte) (uint64, int, error) {
    57  	s, numBytes := proto.DecodeVarint(bytes)
    58  
    59  	switch {
    60  	case numBytes != 1:
    61  		return 0, 0, errors.Errorf("number of consumed bytes from DecodeVarint is invalid, expected 1, but got %d", numBytes)
    62  	case s > 8:
    63  		return 0, 0, errors.Errorf("decoded size from DecodeVarint is invalid, expected <=8, but got %d", s)
    64  	case int(s) > len(bytes)-1:
    65  		return 0, 0, errors.Errorf("decoded size (%d) from DecodeVarint is more than available bytes (%d)", s, len(bytes)-1)
    66  	default:
    67  		// no error
    68  		size := int(s)
    69  		decodedBytes := make([]byte, 8)
    70  		copy(decodedBytes[8-size:], bytes[1:size+1])
    71  		numBytesConsumed := size + 1
    72  		return binary.BigEndian.Uint64(decodedBytes), numBytesConsumed, nil
    73  	}
    74  }