...

Source file src/github.com/gogo/protobuf/test/issue411/ids.go

Documentation: github.com/gogo/protobuf/test/issue411

     1  package issue411
     2  
     3  import (
     4  	"encoding/base64"
     5  	"encoding/binary"
     6  	"fmt"
     7  	"strconv"
     8  )
     9  
    10  // TraceID is a random 128bit identifier for a trace
    11  type TraceID struct {
    12  	Low  uint64 `json:"lo"`
    13  	High uint64 `json:"hi"`
    14  }
    15  
    16  // SpanID is a random 64bit identifier for a span
    17  type SpanID uint64
    18  
    19  // ------- TraceID -------
    20  
    21  // NewTraceID creates a new TraceID from two 64bit unsigned ints.
    22  func NewTraceID(high, low uint64) TraceID {
    23  	return TraceID{High: high, Low: low}
    24  }
    25  
    26  func (t TraceID) String() string {
    27  	if t.High == 0 {
    28  		return fmt.Sprintf("%x", t.Low)
    29  	}
    30  	return fmt.Sprintf("%x%016x", t.High, t.Low)
    31  }
    32  
    33  // TraceIDFromString creates a TraceID from a hexadecimal string
    34  func TraceIDFromString(s string) (TraceID, error) {
    35  	var hi, lo uint64
    36  	var err error
    37  	if len(s) > 32 {
    38  		return TraceID{}, fmt.Errorf("TraceID cannot be longer than 32 hex characters: %s", s)
    39  	} else if len(s) > 16 {
    40  		hiLen := len(s) - 16
    41  		if hi, err = strconv.ParseUint(s[0:hiLen], 16, 64); err != nil {
    42  			return TraceID{}, err
    43  		}
    44  		if lo, err = strconv.ParseUint(s[hiLen:], 16, 64); err != nil {
    45  			return TraceID{}, err
    46  		}
    47  	} else {
    48  		if lo, err = strconv.ParseUint(s, 16, 64); err != nil {
    49  			return TraceID{}, err
    50  		}
    51  	}
    52  	return TraceID{High: hi, Low: lo}, nil
    53  }
    54  
    55  // MarshalText is called by encoding/json, which we do not want people to use.
    56  func (t TraceID) MarshalText() ([]byte, error) {
    57  	return nil, fmt.Errorf("unsupported method TraceID.MarshalText; please use github.com/gogo/protobuf/jsonpb for marshalling")
    58  }
    59  
    60  // UnmarshalText is called by encoding/json, which we do not want people to use.
    61  func (t *TraceID) UnmarshalText(text []byte) error {
    62  	return fmt.Errorf("unsupported method TraceID.UnmarshalText; please use github.com/gogo/protobuf/jsonpb for marshalling")
    63  }
    64  
    65  // Size returns the size of this datum in protobuf. It is always 16 bytes.
    66  func (t *TraceID) Size() int {
    67  	return 16
    68  }
    69  
    70  // Marshal converts trace ID into a binary representation. Called by protobuf serialization.
    71  func (t TraceID) Marshal() ([]byte, error) {
    72  	b := make([]byte, t.Size())
    73  	_, err := t.MarshalTo(b)
    74  	return b, err
    75  }
    76  
    77  // MarshalTo converts trace ID into a binary representation. Called by protobuf serialization.
    78  func (t *TraceID) MarshalTo(data []byte) (n int, err error) {
    79  	var b [16]byte
    80  	binary.BigEndian.PutUint64(b[:8], uint64(t.High))
    81  	binary.BigEndian.PutUint64(b[8:], uint64(t.Low))
    82  	return marshalBytes(data, b[:])
    83  }
    84  
    85  // Unmarshal inflates this trace ID from binary representation. Called by protobuf serialization.
    86  func (t *TraceID) Unmarshal(data []byte) error {
    87  	if len(data) < 16 {
    88  		return fmt.Errorf("buffer is too short")
    89  	}
    90  	t.High = binary.BigEndian.Uint64(data[:8])
    91  	t.Low = binary.BigEndian.Uint64(data[8:])
    92  	return nil
    93  }
    94  
    95  func marshalBytes(dst []byte, src []byte) (n int, err error) {
    96  	if len(dst) < len(src) {
    97  		return 0, fmt.Errorf("buffer is too short")
    98  	}
    99  	return copy(dst, src), nil
   100  }
   101  
   102  // MarshalJSON converts trace id into a base64 string enclosed in quotes.
   103  // Used by protobuf JSON serialization.
   104  // Example: {high:2, low:1} => "AAAAAAAAAAIAAAAAAAAAAQ==".
   105  func (t TraceID) MarshalJSON() ([]byte, error) {
   106  	var b [16]byte
   107  	_, err := t.MarshalTo(b[:]) // can only error on incorrect buffer size
   108  	if err != nil {
   109  		return []byte{}, err
   110  	}
   111  	s := make([]byte, 24+2)
   112  	base64.StdEncoding.Encode(s[1:25], b[:])
   113  	s[0], s[25] = '"', '"'
   114  	return s, nil
   115  }
   116  
   117  // UnmarshalJSON inflates trace id from base64 string, possibly enclosed in quotes.
   118  // User by protobuf JSON serialization.
   119  func (t *TraceID) UnmarshalJSON(data []byte) error {
   120  	s := string(data)
   121  	if l := len(s); l > 2 && s[0] == '"' && s[l-1] == '"' {
   122  		s = s[1 : l-1]
   123  	}
   124  	b, err := base64.StdEncoding.DecodeString(s)
   125  	if err != nil {
   126  		return fmt.Errorf("cannot unmarshal TraceID from string '%s': %v", string(data), err)
   127  	}
   128  	return t.Unmarshal(b)
   129  }
   130  
   131  // ------- SpanID -------
   132  
   133  // NewSpanID creates a new SpanID from a 64bit unsigned int.
   134  func NewSpanID(v uint64) SpanID {
   135  	return SpanID(v)
   136  }
   137  
   138  func (s SpanID) String() string {
   139  	return fmt.Sprintf("%x", uint64(s))
   140  }
   141  
   142  // SpanIDFromString creates a SpanID from a hexadecimal string
   143  func SpanIDFromString(s string) (SpanID, error) {
   144  	if len(s) > 16 {
   145  		return SpanID(0), fmt.Errorf("SpanID cannot be longer than 16 hex characters: %s", s)
   146  	}
   147  	id, err := strconv.ParseUint(s, 16, 64)
   148  	if err != nil {
   149  		return SpanID(0), err
   150  	}
   151  	return SpanID(id), nil
   152  }
   153  
   154  // MarshalText is called by encoding/json, which we do not want people to use.
   155  func (s SpanID) MarshalText() ([]byte, error) {
   156  	return nil, fmt.Errorf("unsupported method SpanID.MarshalText; please use github.com/gogo/protobuf/jsonpb for marshalling")
   157  }
   158  
   159  // UnmarshalText is called by encoding/json, which we do not want people to use.
   160  func (s *SpanID) UnmarshalText(text []byte) error {
   161  	return fmt.Errorf("unsupported method SpanID.UnmarshalText; please use github.com/gogo/protobuf/jsonpb for marshalling")
   162  }
   163  
   164  // Size returns the size of this datum in protobuf. It is always 8 bytes.
   165  func (s *SpanID) Size() int {
   166  	return 8
   167  }
   168  
   169  // Marshal converts span ID into a binary representation. Called by protobuf serialization.
   170  func (s SpanID) Marshal() ([]byte, error) {
   171  	b := make([]byte, s.Size())
   172  	_, err := s.MarshalTo(b)
   173  	return b, err
   174  }
   175  
   176  // MarshalTo converts span ID into a binary representation. Called by protobuf serialization.
   177  func (s *SpanID) MarshalTo(data []byte) (n int, err error) {
   178  	var b [8]byte
   179  	binary.BigEndian.PutUint64(b[:], uint64(*s))
   180  	return marshalBytes(data, b[:])
   181  }
   182  
   183  // Unmarshal inflates span ID from a binary representation. Called by protobuf serialization.
   184  func (s *SpanID) Unmarshal(data []byte) error {
   185  	if len(data) < 8 {
   186  		return fmt.Errorf("buffer is too short")
   187  	}
   188  	*s = NewSpanID(binary.BigEndian.Uint64(data))
   189  	return nil
   190  }
   191  
   192  // MarshalJSON converts span id into a base64 string enclosed in quotes.
   193  // Used by protobuf JSON serialization.
   194  // Example: {1} => "AAAAAAAAAAE=".
   195  func (s SpanID) MarshalJSON() ([]byte, error) {
   196  	var b [8]byte
   197  	_, err := s.MarshalTo(b[:]) // can only error on incorrect buffer size
   198  	if err != nil {
   199  		return []byte{}, err
   200  	}
   201  	v := make([]byte, 12+2)
   202  	base64.StdEncoding.Encode(v[1:13], b[:])
   203  	v[0], v[13] = '"', '"'
   204  	return v, nil
   205  }
   206  
   207  // UnmarshalJSON inflates span id from base64 string, possibly enclosed in quotes.
   208  // User by protobuf JSON serialization.
   209  //
   210  // There appears to be a bug in gogoproto, as this function is only called for numeric values.
   211  // https://github.com/gogo/protobuf/issues/411#issuecomment-393856837
   212  func (s *SpanID) UnmarshalJSON(data []byte) error {
   213  	str := string(data)
   214  	if l := len(str); l > 2 && str[0] == '"' && str[l-1] == '"' {
   215  		str = str[1 : l-1]
   216  	}
   217  	b, err := base64.StdEncoding.DecodeString(str)
   218  	if err != nil {
   219  		return fmt.Errorf("cannot unmarshal SpanID from string '%s': %v", string(data), err)
   220  	}
   221  	return s.Unmarshal(b)
   222  }
   223  

View as plain text