legacy_export.go raw

   1  // Copyright 2019 The Go Authors. All rights reserved.
   2  // Use of this source code is governed by a BSD-style
   3  // license that can be found in the LICENSE file.
   4  
   5  package impl
   6  
   7  import (
   8  	"encoding/binary"
   9  	"encoding/json"
  10  	"hash/crc32"
  11  	"math"
  12  	"reflect"
  13  
  14  	"google.golang.org/protobuf/internal/errors"
  15  	"google.golang.org/protobuf/reflect/protoreflect"
  16  	"google.golang.org/protobuf/runtime/protoiface"
  17  )
  18  
  19  // These functions exist to support exported APIs in generated protobufs.
  20  // While these are deprecated, they cannot be removed for compatibility reasons.
  21  
  22  // LegacyEnumName returns the name of enums used in legacy code.
  23  func (Export) LegacyEnumName(ed protoreflect.EnumDescriptor) string {
  24  	return legacyEnumName(ed)
  25  }
  26  
  27  // LegacyMessageTypeOf returns the protoreflect.MessageType for m,
  28  // with name used as the message name if necessary.
  29  func (Export) LegacyMessageTypeOf(m protoiface.MessageV1, name protoreflect.FullName) protoreflect.MessageType {
  30  	if mv := (Export{}).protoMessageV2Of(m); mv != nil {
  31  		return mv.ProtoReflect().Type()
  32  	}
  33  	return legacyLoadMessageType(reflect.TypeOf(m), name)
  34  }
  35  
  36  // UnmarshalJSONEnum unmarshals an enum from a JSON-encoded input.
  37  // The input can either be a string representing the enum value by name,
  38  // or a number representing the enum number itself.
  39  func (Export) UnmarshalJSONEnum(ed protoreflect.EnumDescriptor, b []byte) (protoreflect.EnumNumber, error) {
  40  	if b[0] == '"' {
  41  		var name protoreflect.Name
  42  		if err := json.Unmarshal(b, &name); err != nil {
  43  			return 0, errors.New("invalid input for enum %v: %s", ed.FullName(), b)
  44  		}
  45  		ev := ed.Values().ByName(name)
  46  		if ev == nil {
  47  			return 0, errors.New("invalid value for enum %v: %s", ed.FullName(), name)
  48  		}
  49  		return ev.Number(), nil
  50  	} else {
  51  		var num protoreflect.EnumNumber
  52  		if err := json.Unmarshal(b, &num); err != nil {
  53  			return 0, errors.New("invalid input for enum %v: %s", ed.FullName(), b)
  54  		}
  55  		return num, nil
  56  	}
  57  }
  58  
  59  // CompressGZIP compresses the input as a GZIP-encoded file.
  60  // The current implementation does no compression.
  61  func (Export) CompressGZIP(in []byte) (out []byte) {
  62  	// RFC 1952, section 2.3.1.
  63  	var gzipHeader = [10]byte{0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}
  64  
  65  	// RFC 1951, section 3.2.4.
  66  	var blockHeader [5]byte
  67  	const maxBlockSize = math.MaxUint16
  68  	numBlocks := 1 + len(in)/maxBlockSize
  69  
  70  	// RFC 1952, section 2.3.1.
  71  	var gzipFooter [8]byte
  72  	binary.LittleEndian.PutUint32(gzipFooter[0:4], crc32.ChecksumIEEE(in))
  73  	binary.LittleEndian.PutUint32(gzipFooter[4:8], uint32(len(in)))
  74  
  75  	// Encode the input without compression using raw DEFLATE blocks.
  76  	out = make([]byte, 0, len(gzipHeader)+len(blockHeader)*numBlocks+len(in)+len(gzipFooter))
  77  	out = append(out, gzipHeader[:]...)
  78  	for blockHeader[0] == 0 {
  79  		blockSize := maxBlockSize
  80  		if blockSize > len(in) {
  81  			blockHeader[0] = 0x01 // final bit per RFC 1951, section 3.2.3.
  82  			blockSize = len(in)
  83  		}
  84  		binary.LittleEndian.PutUint16(blockHeader[1:3], uint16(blockSize))
  85  		binary.LittleEndian.PutUint16(blockHeader[3:5], ^uint16(blockSize))
  86  		out = append(out, blockHeader[:]...)
  87  		out = append(out, in[:blockSize]...)
  88  		in = in[blockSize:]
  89  	}
  90  	out = append(out, gzipFooter[:]...)
  91  	return out
  92  }
  93