update vendor
Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
This commit is contained in:
parent
19a32db84d
commit
94d1cfbfbf
10501 changed files with 2307943 additions and 29279 deletions
118
vendor/google.golang.org/grpc/encoding/encoding.go
generated
vendored
Normal file
118
vendor/google.golang.org/grpc/encoding/encoding.go
generated
vendored
Normal file
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
*
|
||||
* Copyright 2017 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
// Package encoding defines the interface for the compressor and codec, and
|
||||
// functions to register and retrieve compressors and codecs.
|
||||
//
|
||||
// This package is EXPERIMENTAL.
|
||||
package encoding
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Identity specifies the optional encoding for uncompressed streams.
|
||||
// It is intended for grpc internal use only.
|
||||
const Identity = "identity"
|
||||
|
||||
// Compressor is used for compressing and decompressing when sending or
|
||||
// receiving messages.
|
||||
type Compressor interface {
|
||||
// Compress writes the data written to wc to w after compressing it. If an
|
||||
// error occurs while initializing the compressor, that error is returned
|
||||
// instead.
|
||||
Compress(w io.Writer) (io.WriteCloser, error)
|
||||
// Decompress reads data from r, decompresses it, and provides the
|
||||
// uncompressed data via the returned io.Reader. If an error occurs while
|
||||
// initializing the decompressor, that error is returned instead.
|
||||
Decompress(r io.Reader) (io.Reader, error)
|
||||
// Name is the name of the compression codec and is used to set the content
|
||||
// coding header. The result must be static; the result cannot change
|
||||
// between calls.
|
||||
Name() string
|
||||
}
|
||||
|
||||
var registeredCompressor = make(map[string]Compressor)
|
||||
|
||||
// RegisterCompressor registers the compressor with gRPC by its name. It can
|
||||
// be activated when sending an RPC via grpc.UseCompressor(). It will be
|
||||
// automatically accessed when receiving a message based on the content coding
|
||||
// header. Servers also use it to send a response with the same encoding as
|
||||
// the request.
|
||||
//
|
||||
// NOTE: this function must only be called during initialization time (i.e. in
|
||||
// an init() function), and is not thread-safe. If multiple Compressors are
|
||||
// registered with the same name, the one registered last will take effect.
|
||||
func RegisterCompressor(c Compressor) {
|
||||
registeredCompressor[c.Name()] = c
|
||||
}
|
||||
|
||||
// GetCompressor returns Compressor for the given compressor name.
|
||||
func GetCompressor(name string) Compressor {
|
||||
return registeredCompressor[name]
|
||||
}
|
||||
|
||||
// Codec defines the interface gRPC uses to encode and decode messages. Note
|
||||
// that implementations of this interface must be thread safe; a Codec's
|
||||
// methods can be called from concurrent goroutines.
|
||||
type Codec interface {
|
||||
// Marshal returns the wire format of v.
|
||||
Marshal(v interface{}) ([]byte, error)
|
||||
// Unmarshal parses the wire format into v.
|
||||
Unmarshal(data []byte, v interface{}) error
|
||||
// Name returns the name of the Codec implementation. The returned string
|
||||
// will be used as part of content type in transmission. The result must be
|
||||
// static; the result cannot change between calls.
|
||||
Name() string
|
||||
}
|
||||
|
||||
var registeredCodecs = make(map[string]Codec)
|
||||
|
||||
// RegisterCodec registers the provided Codec for use with all gRPC clients and
|
||||
// servers.
|
||||
//
|
||||
// The Codec will be stored and looked up by result of its Name() method, which
|
||||
// should match the content-subtype of the encoding handled by the Codec. This
|
||||
// is case-insensitive, and is stored and looked up as lowercase. If the
|
||||
// result of calling Name() is an empty string, RegisterCodec will panic. See
|
||||
// Content-Type on
|
||||
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for
|
||||
// more details.
|
||||
//
|
||||
// NOTE: this function must only be called during initialization time (i.e. in
|
||||
// an init() function), and is not thread-safe. If multiple Compressors are
|
||||
// registered with the same name, the one registered last will take effect.
|
||||
func RegisterCodec(codec Codec) {
|
||||
if codec == nil {
|
||||
panic("cannot register a nil Codec")
|
||||
}
|
||||
contentSubtype := strings.ToLower(codec.Name())
|
||||
if contentSubtype == "" {
|
||||
panic("cannot register Codec with empty string result for String()")
|
||||
}
|
||||
registeredCodecs[contentSubtype] = codec
|
||||
}
|
||||
|
||||
// GetCodec gets a registered Codec by content-subtype, or nil if no Codec is
|
||||
// registered for the content-subtype.
|
||||
//
|
||||
// The content-subtype is expected to be lowercase.
|
||||
func GetCodec(contentSubtype string) Codec {
|
||||
return registeredCodecs[contentSubtype]
|
||||
}
|
117
vendor/google.golang.org/grpc/encoding/gzip/gzip.go
generated
vendored
Normal file
117
vendor/google.golang.org/grpc/encoding/gzip/gzip.go
generated
vendored
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
*
|
||||
* Copyright 2017 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
// Package gzip implements and registers the gzip compressor
|
||||
// during the initialization.
|
||||
// This package is EXPERIMENTAL.
|
||||
package gzip
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"sync"
|
||||
|
||||
"google.golang.org/grpc/encoding"
|
||||
)
|
||||
|
||||
// Name is the name registered for the gzip compressor.
|
||||
const Name = "gzip"
|
||||
|
||||
func init() {
|
||||
c := &compressor{}
|
||||
c.poolCompressor.New = func() interface{} {
|
||||
return &writer{Writer: gzip.NewWriter(ioutil.Discard), pool: &c.poolCompressor}
|
||||
}
|
||||
encoding.RegisterCompressor(c)
|
||||
}
|
||||
|
||||
type writer struct {
|
||||
*gzip.Writer
|
||||
pool *sync.Pool
|
||||
}
|
||||
|
||||
// SetLevel updates the registered gzip compressor to use the compression level specified (gzip.HuffmanOnly is not supported).
|
||||
// NOTE: this function must only be called during initialization time (i.e. in an init() function),
|
||||
// and is not thread-safe.
|
||||
//
|
||||
// The error returned will be nil if the specified level is valid.
|
||||
func SetLevel(level int) error {
|
||||
if level < gzip.DefaultCompression || level > gzip.BestCompression {
|
||||
return fmt.Errorf("grpc: invalid gzip compression level: %d", level)
|
||||
}
|
||||
c := encoding.GetCompressor(Name).(*compressor)
|
||||
c.poolCompressor.New = func() interface{} {
|
||||
w, err := gzip.NewWriterLevel(ioutil.Discard, level)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &writer{Writer: w, pool: &c.poolCompressor}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *compressor) Compress(w io.Writer) (io.WriteCloser, error) {
|
||||
z := c.poolCompressor.Get().(*writer)
|
||||
z.Writer.Reset(w)
|
||||
return z, nil
|
||||
}
|
||||
|
||||
func (z *writer) Close() error {
|
||||
defer z.pool.Put(z)
|
||||
return z.Writer.Close()
|
||||
}
|
||||
|
||||
type reader struct {
|
||||
*gzip.Reader
|
||||
pool *sync.Pool
|
||||
}
|
||||
|
||||
func (c *compressor) Decompress(r io.Reader) (io.Reader, error) {
|
||||
z, inPool := c.poolDecompressor.Get().(*reader)
|
||||
if !inPool {
|
||||
newZ, err := gzip.NewReader(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &reader{Reader: newZ, pool: &c.poolDecompressor}, nil
|
||||
}
|
||||
if err := z.Reset(r); err != nil {
|
||||
c.poolDecompressor.Put(z)
|
||||
return nil, err
|
||||
}
|
||||
return z, nil
|
||||
}
|
||||
|
||||
func (z *reader) Read(p []byte) (n int, err error) {
|
||||
n, err = z.Reader.Read(p)
|
||||
if err == io.EOF {
|
||||
z.pool.Put(z)
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *compressor) Name() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
type compressor struct {
|
||||
poolCompressor sync.Pool
|
||||
poolDecompressor sync.Pool
|
||||
}
|
110
vendor/google.golang.org/grpc/encoding/proto/proto.go
generated
vendored
Normal file
110
vendor/google.golang.org/grpc/encoding/proto/proto.go
generated
vendored
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
*
|
||||
* Copyright 2018 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
// Package proto defines the protobuf codec. Importing this package will
|
||||
// register the codec.
|
||||
package proto
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"google.golang.org/grpc/encoding"
|
||||
)
|
||||
|
||||
// Name is the name registered for the proto compressor.
|
||||
const Name = "proto"
|
||||
|
||||
func init() {
|
||||
encoding.RegisterCodec(codec{})
|
||||
}
|
||||
|
||||
// codec is a Codec implementation with protobuf. It is the default codec for gRPC.
|
||||
type codec struct{}
|
||||
|
||||
type cachedProtoBuffer struct {
|
||||
lastMarshaledSize uint32
|
||||
proto.Buffer
|
||||
}
|
||||
|
||||
func capToMaxInt32(val int) uint32 {
|
||||
if val > math.MaxInt32 {
|
||||
return uint32(math.MaxInt32)
|
||||
}
|
||||
return uint32(val)
|
||||
}
|
||||
|
||||
func marshal(v interface{}, cb *cachedProtoBuffer) ([]byte, error) {
|
||||
protoMsg := v.(proto.Message)
|
||||
newSlice := make([]byte, 0, cb.lastMarshaledSize)
|
||||
|
||||
cb.SetBuf(newSlice)
|
||||
cb.Reset()
|
||||
if err := cb.Marshal(protoMsg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := cb.Bytes()
|
||||
cb.lastMarshaledSize = capToMaxInt32(len(out))
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (codec) Marshal(v interface{}) ([]byte, error) {
|
||||
if pm, ok := v.(proto.Marshaler); ok {
|
||||
// object can marshal itself, no need for buffer
|
||||
return pm.Marshal()
|
||||
}
|
||||
|
||||
cb := protoBufferPool.Get().(*cachedProtoBuffer)
|
||||
out, err := marshal(v, cb)
|
||||
|
||||
// put back buffer and lose the ref to the slice
|
||||
cb.SetBuf(nil)
|
||||
protoBufferPool.Put(cb)
|
||||
return out, err
|
||||
}
|
||||
|
||||
func (codec) Unmarshal(data []byte, v interface{}) error {
|
||||
protoMsg := v.(proto.Message)
|
||||
protoMsg.Reset()
|
||||
|
||||
if pu, ok := protoMsg.(proto.Unmarshaler); ok {
|
||||
// object can unmarshal itself, no need for buffer
|
||||
return pu.Unmarshal(data)
|
||||
}
|
||||
|
||||
cb := protoBufferPool.Get().(*cachedProtoBuffer)
|
||||
cb.SetBuf(data)
|
||||
err := cb.Unmarshal(protoMsg)
|
||||
cb.SetBuf(nil)
|
||||
protoBufferPool.Put(cb)
|
||||
return err
|
||||
}
|
||||
|
||||
func (codec) Name() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
var protoBufferPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &cachedProtoBuffer{
|
||||
Buffer: proto.Buffer{},
|
||||
lastMarshaledSize: 16,
|
||||
}
|
||||
},
|
||||
}
|
100
vendor/google.golang.org/grpc/encoding/proto/proto_benchmark_test.go
generated
vendored
Normal file
100
vendor/google.golang.org/grpc/encoding/proto/proto_benchmark_test.go
generated
vendored
Normal file
|
@ -0,0 +1,100 @@
|
|||
// +build go1.7
|
||||
|
||||
/*
|
||||
*
|
||||
* Copyright 2014 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"google.golang.org/grpc/encoding"
|
||||
"google.golang.org/grpc/test/codec_perf"
|
||||
)
|
||||
|
||||
func setupBenchmarkProtoCodecInputs(payloadBaseSize uint32) []proto.Message {
|
||||
payloadBase := make([]byte, payloadBaseSize)
|
||||
// arbitrary byte slices
|
||||
payloadSuffixes := [][]byte{
|
||||
[]byte("one"),
|
||||
[]byte("two"),
|
||||
[]byte("three"),
|
||||
[]byte("four"),
|
||||
[]byte("five"),
|
||||
}
|
||||
protoStructs := make([]proto.Message, 0)
|
||||
|
||||
for _, p := range payloadSuffixes {
|
||||
ps := &codec_perf.Buffer{}
|
||||
ps.Body = append(payloadBase, p...)
|
||||
protoStructs = append(protoStructs, ps)
|
||||
}
|
||||
|
||||
return protoStructs
|
||||
}
|
||||
|
||||
// The possible use of certain protobuf APIs like the proto.Buffer API potentially involves caching
|
||||
// on our side. This can add checks around memory allocations and possible contention.
|
||||
// Example run: go test -v -run=^$ -bench=BenchmarkProtoCodec -benchmem
|
||||
func BenchmarkProtoCodec(b *testing.B) {
|
||||
// range of message sizes
|
||||
payloadBaseSizes := make([]uint32, 0)
|
||||
for i := uint32(0); i <= 12; i += 4 {
|
||||
payloadBaseSizes = append(payloadBaseSizes, 1<<i)
|
||||
}
|
||||
// range of SetParallelism
|
||||
parallelisms := make([]int, 0)
|
||||
for i := uint32(0); i <= 16; i += 4 {
|
||||
parallelisms = append(parallelisms, int(1<<i))
|
||||
}
|
||||
for _, s := range payloadBaseSizes {
|
||||
for _, p := range parallelisms {
|
||||
protoStructs := setupBenchmarkProtoCodecInputs(s)
|
||||
name := fmt.Sprintf("MinPayloadSize:%v/SetParallelism(%v)", s, p)
|
||||
b.Run(name, func(b *testing.B) {
|
||||
codec := &codec{}
|
||||
b.SetParallelism(p)
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
benchmarkProtoCodec(codec, protoStructs, pb, b)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkProtoCodec(codec *codec, protoStructs []proto.Message, pb *testing.PB, b *testing.B) {
|
||||
counter := 0
|
||||
for pb.Next() {
|
||||
counter++
|
||||
ps := protoStructs[counter%len(protoStructs)]
|
||||
fastMarshalAndUnmarshal(codec, ps, b)
|
||||
}
|
||||
}
|
||||
|
||||
func fastMarshalAndUnmarshal(codec encoding.Codec, protoStruct proto.Message, b *testing.B) {
|
||||
marshaledBytes, err := codec.Marshal(protoStruct)
|
||||
if err != nil {
|
||||
b.Errorf("codec.Marshal(_) returned an error")
|
||||
}
|
||||
res := codec_perf.Buffer{}
|
||||
if err := codec.Unmarshal(marshaledBytes, &res); err != nil {
|
||||
b.Errorf("codec.Unmarshal(_) returned an error")
|
||||
}
|
||||
}
|
129
vendor/google.golang.org/grpc/encoding/proto/proto_test.go
generated
vendored
Normal file
129
vendor/google.golang.org/grpc/encoding/proto/proto_test.go
generated
vendored
Normal file
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
*
|
||||
* Copyright 2018 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"google.golang.org/grpc/encoding"
|
||||
"google.golang.org/grpc/test/codec_perf"
|
||||
)
|
||||
|
||||
func marshalAndUnmarshal(t *testing.T, codec encoding.Codec, expectedBody []byte) {
|
||||
p := &codec_perf.Buffer{}
|
||||
p.Body = expectedBody
|
||||
|
||||
marshalledBytes, err := codec.Marshal(p)
|
||||
if err != nil {
|
||||
t.Errorf("codec.Marshal(_) returned an error")
|
||||
}
|
||||
|
||||
if err := codec.Unmarshal(marshalledBytes, p); err != nil {
|
||||
t.Errorf("codec.Unmarshal(_) returned an error")
|
||||
}
|
||||
|
||||
if !bytes.Equal(p.GetBody(), expectedBody) {
|
||||
t.Errorf("Unexpected body; got %v; want %v", p.GetBody(), expectedBody)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBasicProtoCodecMarshalAndUnmarshal(t *testing.T) {
|
||||
marshalAndUnmarshal(t, codec{}, []byte{1, 2, 3})
|
||||
}
|
||||
|
||||
// Try to catch possible race conditions around use of pools
|
||||
func TestConcurrentUsage(t *testing.T) {
|
||||
const (
|
||||
numGoRoutines = 100
|
||||
numMarshUnmarsh = 1000
|
||||
)
|
||||
|
||||
// small, arbitrary byte slices
|
||||
protoBodies := [][]byte{
|
||||
[]byte("one"),
|
||||
[]byte("two"),
|
||||
[]byte("three"),
|
||||
[]byte("four"),
|
||||
[]byte("five"),
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
codec := codec{}
|
||||
|
||||
for i := 0; i < numGoRoutines; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for k := 0; k < numMarshUnmarsh; k++ {
|
||||
marshalAndUnmarshal(t, codec, protoBodies[k%len(protoBodies)])
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// TestStaggeredMarshalAndUnmarshalUsingSamePool tries to catch potential errors in which slices get
|
||||
// stomped on during reuse of a proto.Buffer.
|
||||
func TestStaggeredMarshalAndUnmarshalUsingSamePool(t *testing.T) {
|
||||
codec1 := codec{}
|
||||
codec2 := codec{}
|
||||
|
||||
expectedBody1 := []byte{1, 2, 3}
|
||||
expectedBody2 := []byte{4, 5, 6}
|
||||
|
||||
proto1 := codec_perf.Buffer{Body: expectedBody1}
|
||||
proto2 := codec_perf.Buffer{Body: expectedBody2}
|
||||
|
||||
var m1, m2 []byte
|
||||
var err error
|
||||
|
||||
if m1, err = codec1.Marshal(&proto1); err != nil {
|
||||
t.Errorf("codec.Marshal(%v) failed", proto1)
|
||||
}
|
||||
|
||||
if m2, err = codec2.Marshal(&proto2); err != nil {
|
||||
t.Errorf("codec.Marshal(%v) failed", proto2)
|
||||
}
|
||||
|
||||
if err = codec1.Unmarshal(m1, &proto1); err != nil {
|
||||
t.Errorf("codec.Unmarshal(%v) failed", m1)
|
||||
}
|
||||
|
||||
if err = codec2.Unmarshal(m2, &proto2); err != nil {
|
||||
t.Errorf("codec.Unmarshal(%v) failed", m2)
|
||||
}
|
||||
|
||||
b1 := proto1.GetBody()
|
||||
b2 := proto2.GetBody()
|
||||
|
||||
for i, v := range b1 {
|
||||
if expectedBody1[i] != v {
|
||||
t.Errorf("expected %v at index %v but got %v", i, expectedBody1[i], v)
|
||||
}
|
||||
}
|
||||
|
||||
for i, v := range b2 {
|
||||
if expectedBody2[i] != v {
|
||||
t.Errorf("expected %v at index %v but got %v", i, expectedBody2[i], v)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue