github.com/m3db/m3@v1.5.0/src/metrics/aggregation/id.go (about)

     1  // Copyright (c) 2018 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package aggregation
    22  
    23  import (
    24  	"encoding/json"
    25  	"fmt"
    26  
    27  	"github.com/m3db/m3/src/metrics/generated/proto/aggregationpb"
    28  )
    29  
    30  const (
    31  	// IDLen is the length of the ID.
    32  	// The IDLen will be 1 when maxTypeID <= 63.
    33  	IDLen = (maxTypeID)/64 + 1
    34  
    35  	// ID uses an array of int64 to represent aggregation types.
    36  	idBitShift = 6
    37  	idBitMask  = 63
    38  )
    39  
    40  var (
    41  	// DefaultID is a default ID.
    42  	DefaultID ID
    43  )
    44  
    45  // ID represents a compressed view of Types.
    46  type ID [IDLen]uint64
    47  
    48  // NewIDFromProto creates an ID from proto.
    49  func NewIDFromProto(input []aggregationpb.AggregationType) (ID, error) {
    50  	aggTypes, err := NewTypesFromProto(input)
    51  	if err != nil {
    52  		return DefaultID, err
    53  	}
    54  
    55  	// TODO(cw): consider pooling these compressors,
    56  	// this allocates one extra slice of length one per call.
    57  	id, err := NewIDCompressor().Compress(aggTypes)
    58  	if err != nil {
    59  		return DefaultID, err
    60  	}
    61  	return id, nil
    62  }
    63  
    64  // IsDefault checks if the ID is the default aggregation type.
    65  func (id ID) IsDefault() bool {
    66  	return id == DefaultID
    67  }
    68  
    69  // Equal checks whether two IDs are considered equal.
    70  func (id ID) Equal(other ID) bool {
    71  	return id == other
    72  }
    73  
    74  // Contains checks if the given aggregation type is contained in the aggregation id.
    75  func (id ID) Contains(aggType Type) bool {
    76  	if !aggType.IsValid() {
    77  		return false
    78  	}
    79  	idx := int(aggType) >> idBitShift   // aggType / 64
    80  	offset := uint(aggType) & idBitMask // aggType % 64
    81  	return (id[idx] & (1 << offset)) > 0
    82  }
    83  
    84  // Types returns the aggregation types defined by the id.
    85  func (id ID) Types() (Types, error) {
    86  	return NewIDDecompressor().Decompress(id)
    87  }
    88  
    89  // String is a string representation of the ID.
    90  func (id ID) String() string {
    91  	aggTypes, err := id.Types()
    92  	if err != nil {
    93  		return fmt.Sprintf("[invalid ID: %v]", err)
    94  	}
    95  	return aggTypes.String()
    96  }
    97  
    98  // MarshalJSON returns the JSON encoding of an ID.
    99  func (id ID) MarshalJSON() ([]byte, error) {
   100  	aggTypes, err := id.Types()
   101  	if err != nil {
   102  		return nil, fmt.Errorf("invalid aggregation id %v: %v", id, err)
   103  	}
   104  	return json.Marshal(aggTypes)
   105  }
   106  
   107  // UnmarshalJSON unmarshals JSON-encoded data into an ID.
   108  func (id *ID) UnmarshalJSON(data []byte) error {
   109  	var aggTypes Types
   110  	if err := json.Unmarshal(data, &aggTypes); err != nil {
   111  		return err
   112  	}
   113  	tid, err := CompressTypes(aggTypes...)
   114  	if err != nil {
   115  		return fmt.Errorf("invalid aggregation types %v: %v", aggTypes, err)
   116  	}
   117  	*id = tid
   118  	return nil
   119  }
   120  
   121  func (id ID) MarshalYAML() (interface{}, error) {
   122  	aggTypes, err := id.Types()
   123  	if err != nil {
   124  		return nil, fmt.Errorf("invalid aggregation id %v: %v", id, err)
   125  	}
   126  	return aggTypes, nil
   127  }
   128  
   129  // UnmarshalYAML unmarshals YAML-encoded data into an ID.
   130  func (id *ID) UnmarshalYAML(unmarshal func(interface{}) error) error {
   131  	var aggTypes Types
   132  	if err := unmarshal(&aggTypes); err != nil {
   133  		return err
   134  	}
   135  	tid, err := CompressTypes(aggTypes...)
   136  	if err != nil {
   137  		return fmt.Errorf("invalid aggregation types %v: %v", aggTypes, err)
   138  	}
   139  	*id = tid
   140  	return nil
   141  }
   142  
   143  // ToProto converts the aggregation id to a protobuf message in place.
   144  func (id ID) ToProto(pb *aggregationpb.AggregationID) {
   145  	pb.Id = id[0]
   146  }
   147  
   148  // FromProto converts the protobuf message to an aggregation id in place.
   149  func (id *ID) FromProto(pb aggregationpb.AggregationID) {
   150  	(*id)[0] = pb.Id
   151  }
   152  
   153  // CompressTypes compresses a list of aggregation types to an ID.
   154  func CompressTypes(aggTypes ...Type) (ID, error) {
   155  	return NewIDCompressor().Compress(aggTypes)
   156  }
   157  
   158  // MustCompressTypes compresses a list of aggregation types to
   159  // an ID, it panics if an error was encountered.
   160  func MustCompressTypes(aggTypes ...Type) ID {
   161  	res, err := CompressTypes(aggTypes...)
   162  	if err != nil {
   163  		panic(err)
   164  	}
   165  	return res
   166  }
   167  
   168  func init() {
   169  	if IDLen != 1 {
   170  		// changing this const requires extensive surgery
   171  		panic(fmt.Sprintf("id length %d cannot be represented by a single integer", IDLen))
   172  	}
   173  }