// Copyright 2025 Google LLC
//
// 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
//
//     https://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.

// Code generated by protoc-gen-go_gapic. DO NOT EDIT.

package storage

import (
	"context"
	"fmt"
	"log/slog"
	"math"
	"net/url"
	"regexp"
	"strings"
	"time"

	iampb "cloud.google.com/go/iam/apiv1/iampb"
	storagepb "cloud.google.com/go/storage/internal/apiv2/storagepb"
	gax "github.com/googleapis/gax-go/v2"
	"google.golang.org/api/iterator"
	"google.golang.org/api/option"
	"google.golang.org/api/option/internaloption"
	gtransport "google.golang.org/api/transport/grpc"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/protobuf/proto"
)

var newClientHook clientHook

// CallOptions contains the retry settings for each method of Client.
type CallOptions struct {
	DeleteBucket              []gax.CallOption
	GetBucket                 []gax.CallOption
	CreateBucket              []gax.CallOption
	ListBuckets               []gax.CallOption
	LockBucketRetentionPolicy []gax.CallOption
	GetIamPolicy              []gax.CallOption
	SetIamPolicy              []gax.CallOption
	TestIamPermissions        []gax.CallOption
	UpdateBucket              []gax.CallOption
	ComposeObject             []gax.CallOption
	DeleteObject              []gax.CallOption
	RestoreObject             []gax.CallOption
	CancelResumableWrite      []gax.CallOption
	GetObject                 []gax.CallOption
	ReadObject                []gax.CallOption
	BidiReadObject            []gax.CallOption
	UpdateObject              []gax.CallOption
	WriteObject               []gax.CallOption
	BidiWriteObject           []gax.CallOption
	ListObjects               []gax.CallOption
	RewriteObject             []gax.CallOption
	StartResumableWrite       []gax.CallOption
	QueryWriteStatus          []gax.CallOption
	MoveObject                []gax.CallOption
}

func defaultGRPCClientOptions() []option.ClientOption {
	return []option.ClientOption{
		internaloption.WithDefaultEndpoint("storage.googleapis.com:443"),
		internaloption.WithDefaultEndpointTemplate("storage.UNIVERSE_DOMAIN:443"),
		internaloption.WithDefaultMTLSEndpoint("storage.mtls.googleapis.com:443"),
		internaloption.WithDefaultUniverseDomain("googleapis.com"),
		internaloption.WithDefaultAudience("https://storage.googleapis.com/"),
		internaloption.WithDefaultScopes(DefaultAuthScopes()...),
		internaloption.EnableJwtWithScope(),
		internaloption.EnableNewAuthLibrary(),
		option.WithGRPCDialOption(grpc.WithDefaultCallOptions(
			grpc.MaxCallRecvMsgSize(math.MaxInt32))),
	}
}

func defaultCallOptions() *CallOptions {
	return &CallOptions{
		DeleteBucket: []gax.CallOption{
			gax.WithTimeout(60000 * time.Millisecond),
			gax.WithRetry(func() gax.Retryer {
				return gax.OnCodes([]codes.Code{
					codes.DeadlineExceeded,
					codes.Unavailable,
				}, gax.Backoff{
					Initial:    1000 * time.Millisecond,
					Max:        60000 * time.Millisecond,
					Multiplier: 2.00,
				})
			}),
		},
		GetBucket: []gax.CallOption{
			gax.WithTimeout(60000 * time.Millisecond),
			gax.WithRetry(func() gax.Retryer {
				return gax.OnCodes([]codes.Code{
					codes.DeadlineExceeded,
					codes.Unavailable,
				}, gax.Backoff{
					Initial:    1000 * time.Millisecond,
					Max:        60000 * time.Millisecond,
					Multiplier: 2.00,
				})
			}),
		},
		CreateBucket: []gax.CallOption{
			gax.WithTimeout(60000 * time.Millisecond),
			gax.WithRetry(func() gax.Retryer {
				return gax.OnCodes([]codes.Code{
					codes.DeadlineExceeded,
					codes.Unavailable,
				}, gax.Backoff{
					Initial:    1000 * time.Millisecond,
					Max:        60000 * time.Millisecond,
					Multiplier: 2.00,
				})
			}),
		},
		ListBuckets: []gax.CallOption{
			gax.WithTimeout(60000 * time.Millisecond),
			gax.WithRetry(func() gax.Retryer {
				return gax.OnCodes([]codes.Code{
					codes.DeadlineExceeded,
					codes.Unavailable,
				}, gax.Backoff{
					Initial:    1000 * time.Millisecond,
					Max:        60000 * time.Millisecond,
					Multiplier: 2.00,
				})
			}),
		},
		LockBucketRetentionPolicy: []gax.CallOption{
			gax.WithTimeout(60000 * time.Millisecond),
			gax.WithRetry(func() gax.Retryer {
				return gax.OnCodes([]codes.Code{
					codes.DeadlineExceeded,
					codes.Unavailable,
				}, gax.Backoff{
					Initial:    1000 * time.Millisecond,
					Max:        60000 * time.Millisecond,
					Multiplier: 2.00,
				})
			}),
		},
		GetIamPolicy: []gax.CallOption{
			gax.WithTimeout(60000 * time.Millisecond),
			gax.WithRetry(func() gax.Retryer {
				return gax.OnCodes([]codes.Code{
					codes.DeadlineExceeded,
					codes.Unavailable,
				}, gax.Backoff{
					Initial:    1000 * time.Millisecond,
					Max:        60000 * time.Millisecond,
					Multiplier: 2.00,
				})
			}),
		},
		SetIamPolicy: []gax.CallOption{
			gax.WithTimeout(60000 * time.Millisecond),
			gax.WithRetry(func() gax.Retryer {
				return gax.OnCodes([]codes.Code{
					codes.DeadlineExceeded,
					codes.Unavailable,
				}, gax.Backoff{
					Initial:    1000 * time.Millisecond,
					Max:        60000 * time.Millisecond,
					Multiplier: 2.00,
				})
			}),
		},
		TestIamPermissions: []gax.CallOption{
			gax.WithTimeout(60000 * time.Millisecond),
			gax.WithRetry(func() gax.Retryer {
				return gax.OnCodes([]codes.Code{
					codes.DeadlineExceeded,
					codes.Unavailable,
				}, gax.Backoff{
					Initial:    1000 * time.Millisecond,
					Max:        60000 * time.Millisecond,
					Multiplier: 2.00,
				})
			}),
		},
		UpdateBucket: []gax.CallOption{
			gax.WithTimeout(60000 * time.Millisecond),
			gax.WithRetry(func() gax.Retryer {
				return gax.OnCodes([]codes.Code{
					codes.DeadlineExceeded,
					codes.Unavailable,
				}, gax.Backoff{
					Initial:    1000 * time.Millisecond,
					Max:        60000 * time.Millisecond,
					Multiplier: 2.00,
				})
			}),
		},
		ComposeObject: []gax.CallOption{
			gax.WithTimeout(60000 * time.Millisecond),
			gax.WithRetry(func() gax.Retryer {
				return gax.OnCodes([]codes.Code{
					codes.DeadlineExceeded,
					codes.Unavailable,
				}, gax.Backoff{
					Initial:    1000 * time.Millisecond,
					Max:        60000 * time.Millisecond,
					Multiplier: 2.00,
				})
			}),
		},
		DeleteObject: []gax.CallOption{
			gax.WithTimeout(60000 * time.Millisecond),
			gax.WithRetry(func() gax.Retryer {
				return gax.OnCodes([]codes.Code{
					codes.DeadlineExceeded,
					codes.Unavailable,
				}, gax.Backoff{
					Initial:    1000 * time.Millisecond,
					Max:        60000 * time.Millisecond,
					Multiplier: 2.00,
				})
			}),
		},
		RestoreObject: []gax.CallOption{
			gax.WithTimeout(60000 * time.Millisecond),
			gax.WithRetry(func() gax.Retryer {
				return gax.OnCodes([]codes.Code{
					codes.DeadlineExceeded,
					codes.Unavailable,
				}, gax.Backoff{
					Initial:    1000 * time.Millisecond,
					Max:        60000 * time.Millisecond,
					Multiplier: 2.00,
				})
			}),
		},
		CancelResumableWrite: []gax.CallOption{
			gax.WithTimeout(60000 * time.Millisecond),
			gax.WithRetry(func() gax.Retryer {
				return gax.OnCodes([]codes.Code{
					codes.DeadlineExceeded,
					codes.Unavailable,
				}, gax.Backoff{
					Initial:    1000 * time.Millisecond,
					Max:        60000 * time.Millisecond,
					Multiplier: 2.00,
				})
			}),
		},
		GetObject: []gax.CallOption{
			gax.WithTimeout(60000 * time.Millisecond),
			gax.WithRetry(func() gax.Retryer {
				return gax.OnCodes([]codes.Code{
					codes.DeadlineExceeded,
					codes.Unavailable,
				}, gax.Backoff{
					Initial:    1000 * time.Millisecond,
					Max:        60000 * time.Millisecond,
					Multiplier: 2.00,
				})
			}),
		},
		ReadObject: []gax.CallOption{
			gax.WithRetry(func() gax.Retryer {
				return gax.OnCodes([]codes.Code{
					codes.DeadlineExceeded,
					codes.Unavailable,
				}, gax.Backoff{
					Initial:    1000 * time.Millisecond,
					Max:        60000 * time.Millisecond,
					Multiplier: 2.00,
				})
			}),
		},
		BidiReadObject: []gax.CallOption{
			gax.WithRetry(func() gax.Retryer {
				return gax.OnCodes([]codes.Code{
					codes.DeadlineExceeded,
					codes.Unavailable,
				}, gax.Backoff{
					Initial:    1000 * time.Millisecond,
					Max:        60000 * time.Millisecond,
					Multiplier: 2.00,
				})
			}),
		},
		UpdateObject: []gax.CallOption{
			gax.WithTimeout(60000 * time.Millisecond),
			gax.WithRetry(func() gax.Retryer {
				return gax.OnCodes([]codes.Code{
					codes.DeadlineExceeded,
					codes.Unavailable,
				}, gax.Backoff{
					Initial:    1000 * time.Millisecond,
					Max:        60000 * time.Millisecond,
					Multiplier: 2.00,
				})
			}),
		},
		WriteObject: []gax.CallOption{
			gax.WithRetry(func() gax.Retryer {
				return gax.OnCodes([]codes.Code{
					codes.DeadlineExceeded,
					codes.Unavailable,
				}, gax.Backoff{
					Initial:    1000 * time.Millisecond,
					Max:        60000 * time.Millisecond,
					Multiplier: 2.00,
				})
			}),
		},
		BidiWriteObject: []gax.CallOption{
			gax.WithRetry(func() gax.Retryer {
				return gax.OnCodes([]codes.Code{
					codes.DeadlineExceeded,
					codes.Unavailable,
				}, gax.Backoff{
					Initial:    1000 * time.Millisecond,
					Max:        60000 * time.Millisecond,
					Multiplier: 2.00,
				})
			}),
		},
		ListObjects: []gax.CallOption{
			gax.WithTimeout(60000 * time.Millisecond),
			gax.WithRetry(func() gax.Retryer {
				return gax.OnCodes([]codes.Code{
					codes.DeadlineExceeded,
					codes.Unavailable,
				}, gax.Backoff{
					Initial:    1000 * time.Millisecond,
					Max:        60000 * time.Millisecond,
					Multiplier: 2.00,
				})
			}),
		},
		RewriteObject: []gax.CallOption{
			gax.WithTimeout(60000 * time.Millisecond),
			gax.WithRetry(func() gax.Retryer {
				return gax.OnCodes([]codes.Code{
					codes.DeadlineExceeded,
					codes.Unavailable,
				}, gax.Backoff{
					Initial:    1000 * time.Millisecond,
					Max:        60000 * time.Millisecond,
					Multiplier: 2.00,
				})
			}),
		},
		StartResumableWrite: []gax.CallOption{
			gax.WithTimeout(60000 * time.Millisecond),
			gax.WithRetry(func() gax.Retryer {
				return gax.OnCodes([]codes.Code{
					codes.DeadlineExceeded,
					codes.Unavailable,
				}, gax.Backoff{
					Initial:    1000 * time.Millisecond,
					Max:        60000 * time.Millisecond,
					Multiplier: 2.00,
				})
			}),
		},
		QueryWriteStatus: []gax.CallOption{
			gax.WithTimeout(60000 * time.Millisecond),
			gax.WithRetry(func() gax.Retryer {
				return gax.OnCodes([]codes.Code{
					codes.DeadlineExceeded,
					codes.Unavailable,
				}, gax.Backoff{
					Initial:    1000 * time.Millisecond,
					Max:        60000 * time.Millisecond,
					Multiplier: 2.00,
				})
			}),
		},
		MoveObject: []gax.CallOption{
			gax.WithTimeout(60000 * time.Millisecond),
			gax.WithRetry(func() gax.Retryer {
				return gax.OnCodes([]codes.Code{
					codes.DeadlineExceeded,
					codes.Unavailable,
				}, gax.Backoff{
					Initial:    1000 * time.Millisecond,
					Max:        60000 * time.Millisecond,
					Multiplier: 2.00,
				})
			}),
		},
	}
}

// internalClient is an interface that defines the methods available from Cloud Storage API.
type internalClient interface {
	Close() error
	setGoogleClientInfo(...string)
	Connection() *grpc.ClientConn
	DeleteBucket(context.Context, *storagepb.DeleteBucketRequest, ...gax.CallOption) error
	GetBucket(context.Context, *storagepb.GetBucketRequest, ...gax.CallOption) (*storagepb.Bucket, error)
	CreateBucket(context.Context, *storagepb.CreateBucketRequest, ...gax.CallOption) (*storagepb.Bucket, error)
	ListBuckets(context.Context, *storagepb.ListBucketsRequest, ...gax.CallOption) *BucketIterator
	LockBucketRetentionPolicy(context.Context, *storagepb.LockBucketRetentionPolicyRequest, ...gax.CallOption) (*storagepb.Bucket, error)
	GetIamPolicy(context.Context, *iampb.GetIamPolicyRequest, ...gax.CallOption) (*iampb.Policy, error)
	SetIamPolicy(context.Context, *iampb.SetIamPolicyRequest, ...gax.CallOption) (*iampb.Policy, error)
	TestIamPermissions(context.Context, *iampb.TestIamPermissionsRequest, ...gax.CallOption) (*iampb.TestIamPermissionsResponse, error)
	UpdateBucket(context.Context, *storagepb.UpdateBucketRequest, ...gax.CallOption) (*storagepb.Bucket, error)
	ComposeObject(context.Context, *storagepb.ComposeObjectRequest, ...gax.CallOption) (*storagepb.Object, error)
	DeleteObject(context.Context, *storagepb.DeleteObjectRequest, ...gax.CallOption) error
	RestoreObject(context.Context, *storagepb.RestoreObjectRequest, ...gax.CallOption) (*storagepb.Object, error)
	CancelResumableWrite(context.Context, *storagepb.CancelResumableWriteRequest, ...gax.CallOption) (*storagepb.CancelResumableWriteResponse, error)
	GetObject(context.Context, *storagepb.GetObjectRequest, ...gax.CallOption) (*storagepb.Object, error)
	ReadObject(context.Context, *storagepb.ReadObjectRequest, ...gax.CallOption) (storagepb.Storage_ReadObjectClient, error)
	BidiReadObject(context.Context, ...gax.CallOption) (storagepb.Storage_BidiReadObjectClient, error)
	UpdateObject(context.Context, *storagepb.UpdateObjectRequest, ...gax.CallOption) (*storagepb.Object, error)
	WriteObject(context.Context, ...gax.CallOption) (storagepb.Storage_WriteObjectClient, error)
	BidiWriteObject(context.Context, ...gax.CallOption) (storagepb.Storage_BidiWriteObjectClient, error)
	ListObjects(context.Context, *storagepb.ListObjectsRequest, ...gax.CallOption) *ObjectIterator
	RewriteObject(context.Context, *storagepb.RewriteObjectRequest, ...gax.CallOption) (*storagepb.RewriteResponse, error)
	StartResumableWrite(context.Context, *storagepb.StartResumableWriteRequest, ...gax.CallOption) (*storagepb.StartResumableWriteResponse, error)
	QueryWriteStatus(context.Context, *storagepb.QueryWriteStatusRequest, ...gax.CallOption) (*storagepb.QueryWriteStatusResponse, error)
	MoveObject(context.Context, *storagepb.MoveObjectRequest, ...gax.CallOption) (*storagepb.Object, error)
}

// Client is a client for interacting with Cloud Storage API.
// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls.
//
// API Overview and Naming SyntaxThe Cloud Storage gRPC API allows applications to read and write data through
// the abstractions of buckets and objects. For a description of these
// abstractions please see https://cloud.google.com/storage/docs (at https://cloud.google.com/storage/docs).
//
// Resources are named as follows:
//
//	Projects are referred to as they are defined by the Resource Manager API,
//	using strings like projects/123456 or projects/my-string-id.
//
//	Buckets are named using string names of the form:
//	projects/{project}/buckets/{bucket}
//	For globally unique buckets, _ may be substituted for the project.
//
//	Objects are uniquely identified by their name along with the name of the
//	bucket they belong to, as separate strings in this API. For example:
//
//	ReadObjectRequest {
//	  bucket: ‘projects/_/buckets/my-bucket’
//	  object: ‘my-object’
//	  }
//	  Note that object names can contain / characters, which are treated as
//	  any other character (no special directory semantics).
type Client struct {
	// The internal transport-dependent client.
	internalClient internalClient

	// The call options for this service.
	CallOptions *CallOptions
}

// Wrapper methods routed to the internal client.

// Close closes the connection to the API service. The user should invoke this when
// the client is no longer required.
func (c *Client) Close() error {
	return c.internalClient.Close()
}

// setGoogleClientInfo sets the name and version of the application in
// the `x-goog-api-client` header passed on each request. Intended for
// use by Google-written clients.
func (c *Client) setGoogleClientInfo(keyval ...string) {
	c.internalClient.setGoogleClientInfo(keyval...)
}

// Connection returns a connection to the API service.
//
// Deprecated: Connections are now pooled so this method does not always
// return the same resource.
func (c *Client) Connection() *grpc.ClientConn {
	return c.internalClient.Connection()
}

// DeleteBucket permanently deletes an empty bucket.
func (c *Client) DeleteBucket(ctx context.Context, req *storagepb.DeleteBucketRequest, opts ...gax.CallOption) error {
	return c.internalClient.DeleteBucket(ctx, req, opts...)
}

// GetBucket returns metadata for the specified bucket.
func (c *Client) GetBucket(ctx context.Context, req *storagepb.GetBucketRequest, opts ...gax.CallOption) (*storagepb.Bucket, error) {
	return c.internalClient.GetBucket(ctx, req, opts...)
}

// CreateBucket creates a new bucket.
func (c *Client) CreateBucket(ctx context.Context, req *storagepb.CreateBucketRequest, opts ...gax.CallOption) (*storagepb.Bucket, error) {
	return c.internalClient.CreateBucket(ctx, req, opts...)
}

// ListBuckets retrieves a list of buckets for a given project.
func (c *Client) ListBuckets(ctx context.Context, req *storagepb.ListBucketsRequest, opts ...gax.CallOption) *BucketIterator {
	return c.internalClient.ListBuckets(ctx, req, opts...)
}

// LockBucketRetentionPolicy locks retention policy on a bucket.
func (c *Client) LockBucketRetentionPolicy(ctx context.Context, req *storagepb.LockBucketRetentionPolicyRequest, opts ...gax.CallOption) (*storagepb.Bucket, error) {
	return c.internalClient.LockBucketRetentionPolicy(ctx, req, opts...)
}

// GetIamPolicy gets the IAM policy for a specified bucket.
// The resource field in the request should be
// projects/_/buckets/{bucket}.
func (c *Client) GetIamPolicy(ctx context.Context, req *iampb.GetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) {
	return c.internalClient.GetIamPolicy(ctx, req, opts...)
}

// SetIamPolicy updates an IAM policy for the specified bucket.
// The resource field in the request should be
// projects/_/buckets/{bucket}.
func (c *Client) SetIamPolicy(ctx context.Context, req *iampb.SetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) {
	return c.internalClient.SetIamPolicy(ctx, req, opts...)
}

// TestIamPermissions tests a set of permissions on the given bucket, object, or managed folder
// to see which, if any, are held by the caller.
// The resource field in the request should be
// projects/_/buckets/{bucket} for a bucket,
// projects/_/buckets/{bucket}/objects/{object} for an object, or
// projects/_/buckets/{bucket}/managedFolders/{managedFolder}
// for a managed folder.
func (c *Client) TestIamPermissions(ctx context.Context, req *iampb.TestIamPermissionsRequest, opts ...gax.CallOption) (*iampb.TestIamPermissionsResponse, error) {
	return c.internalClient.TestIamPermissions(ctx, req, opts...)
}

// UpdateBucket updates a bucket. Equivalent to JSON API’s storage.buckets.patch method.
func (c *Client) UpdateBucket(ctx context.Context, req *storagepb.UpdateBucketRequest, opts ...gax.CallOption) (*storagepb.Bucket, error) {
	return c.internalClient.UpdateBucket(ctx, req, opts...)
}

// ComposeObject concatenates a list of existing objects into a new object in the same
// bucket.
func (c *Client) ComposeObject(ctx context.Context, req *storagepb.ComposeObjectRequest, opts ...gax.CallOption) (*storagepb.Object, error) {
	return c.internalClient.ComposeObject(ctx, req, opts...)
}

// DeleteObject deletes an object and its metadata. Deletions are permanent if versioning
// is not enabled for the bucket, or if the generation parameter is used, or
// if soft delete (at https://cloud.google.com/storage/docs/soft-delete) is not
// enabled for the bucket.
// When this API is used to delete an object from a bucket that has soft
// delete policy enabled, the object becomes soft deleted, and the
// softDeleteTime and hardDeleteTime properties are set on the object.
// This API cannot be used to permanently delete soft-deleted objects.
// Soft-deleted objects are permanently deleted according to their
// hardDeleteTime.
//
// You can use the [RestoreObject][google.storage.v2.Storage.RestoreObject]
// API to restore soft-deleted objects until the soft delete retention period
// has passed.
//
// IAM Permissions:
//
// Requires storage.objects.delete
// IAM permission (at https://cloud.google.com/iam/docs/overview#permissions) on
// the bucket.
func (c *Client) DeleteObject(ctx context.Context, req *storagepb.DeleteObjectRequest, opts ...gax.CallOption) error {
	return c.internalClient.DeleteObject(ctx, req, opts...)
}

// RestoreObject restores a soft-deleted object.
func (c *Client) RestoreObject(ctx context.Context, req *storagepb.RestoreObjectRequest, opts ...gax.CallOption) (*storagepb.Object, error) {
	return c.internalClient.RestoreObject(ctx, req, opts...)
}

// CancelResumableWrite cancels an in-progress resumable upload.
//
// Any attempts to write to the resumable upload after cancelling the upload
// will fail.
//
// The behavior for currently in progress write operations is not guaranteed -
// they could either complete before the cancellation or fail if the
// cancellation completes first.
func (c *Client) CancelResumableWrite(ctx context.Context, req *storagepb.CancelResumableWriteRequest, opts ...gax.CallOption) (*storagepb.CancelResumableWriteResponse, error) {
	return c.internalClient.CancelResumableWrite(ctx, req, opts...)
}

// GetObject retrieves object metadata.
//
// IAM Permissions:
//
// Requires storage.objects.get
// IAM permission (at https://cloud.google.com/iam/docs/overview#permissions) on
// the bucket. To return object ACLs, the authenticated user must also have
// the storage.objects.getIamPolicy permission.
func (c *Client) GetObject(ctx context.Context, req *storagepb.GetObjectRequest, opts ...gax.CallOption) (*storagepb.Object, error) {
	return c.internalClient.GetObject(ctx, req, opts...)
}

// ReadObject retrieves object data.
//
// IAM Permissions:
//
// Requires storage.objects.get
// IAM permission (at https://cloud.google.com/iam/docs/overview#permissions) on
// the bucket.
func (c *Client) ReadObject(ctx context.Context, req *storagepb.ReadObjectRequest, opts ...gax.CallOption) (storagepb.Storage_ReadObjectClient, error) {
	return c.internalClient.ReadObject(ctx, req, opts...)
}

// BidiReadObject reads an object’s data.
//
// This is a bi-directional API with the added support for reading multiple
// ranges within one stream both within and across multiple messages.
// If the server encountered an error for any of the inputs, the stream will
// be closed with the relevant error code.
// Because the API allows for multiple outstanding requests, when the stream
// is closed the error response will contain a BidiReadObjectRangesError proto
// in the error extension describing the error for each outstanding read_id.
//
// IAM Permissions:
//
// # Requires storage.objects.get
//
// IAM permission (at https://cloud.google.com/iam/docs/overview#permissions) on
// the bucket.
//
// This API is currently in preview and is not yet available for general
// use.
func (c *Client) BidiReadObject(ctx context.Context, opts ...gax.CallOption) (storagepb.Storage_BidiReadObjectClient, error) {
	return c.internalClient.BidiReadObject(ctx, opts...)
}

// UpdateObject updates an object’s metadata.
// Equivalent to JSON API’s storage.objects.patch.
func (c *Client) UpdateObject(ctx context.Context, req *storagepb.UpdateObjectRequest, opts ...gax.CallOption) (*storagepb.Object, error) {
	return c.internalClient.UpdateObject(ctx, req, opts...)
}

// WriteObject stores a new object and metadata.
//
// An object can be written either in a single message stream or in a
// resumable sequence of message streams. To write using a single stream,
// the client should include in the first message of the stream an
// WriteObjectSpec describing the destination bucket, object, and any
// preconditions. Additionally, the final message must set ‘finish_write’ to
// true, or else it is an error.
//
// For a resumable write, the client should instead call
// StartResumableWrite(), populating a WriteObjectSpec into that request.
// They should then attach the returned upload_id to the first message of
// each following call to WriteObject. If the stream is closed before
// finishing the upload (either explicitly by the client or due to a network
// error or an error response from the server), the client should do as
// follows:
//
//	Check the result Status of the stream, to determine if writing can be
//	resumed on this stream or must be restarted from scratch (by calling
//	StartResumableWrite()). The resumable errors are DEADLINE_EXCEEDED,
//	INTERNAL, and UNAVAILABLE. For each case, the client should use binary
//	exponential backoff before retrying.  Additionally, writes can be
//	resumed after RESOURCE_EXHAUSTED errors, but only after taking
//	appropriate measures, which may include reducing aggregate send rate
//	across clients and/or requesting a quota increase for your project.
//
//	If the call to WriteObject returns ABORTED, that indicates
//	concurrent attempts to update the resumable write, caused either by
//	multiple racing clients or by a single client where the previous
//	request was timed out on the client side but nonetheless reached the
//	server. In this case the client should take steps to prevent further
//	concurrent writes (e.g., increase the timeouts, stop using more than
//	one process to perform the upload, etc.), and then should follow the
//	steps below for resuming the upload.
//
//	For resumable errors, the client should call QueryWriteStatus() and
//	then continue writing from the returned persisted_size. This may be
//	less than the amount of data the client previously sent. Note also that
//	it is acceptable to send data starting at an offset earlier than the
//	returned persisted_size; in this case, the service will skip data at
//	offsets that were already persisted (without checking that it matches
//	the previously written data), and write only the data starting from the
//	persisted offset. Even though the data isn’t written, it may still
//	incur a performance cost over resuming at the correct write offset.
//	This behavior can make client-side handling simpler in some cases.
//
//	Clients must only send data that is a multiple of 256 KiB per message,
//	unless the object is being finished with finish_write set to true.
//
// The service will not view the object as complete until the client has
// sent a WriteObjectRequest with finish_write set to true. Sending any
// requests on a stream after sending a request with finish_write set to
// true will cause an error. The client should check the response it
// receives to determine how much data the service was able to commit and
// whether the service views the object as complete.
//
// Attempting to resume an already finalized object will result in an OK
// status, with a WriteObjectResponse containing the finalized object’s
// metadata.
//
// Alternatively, the BidiWriteObject operation may be used to write an
// object with controls over flushing and the ability to fetch the ability to
// determine the current persisted size.
//
// IAM Permissions:
//
// Requires storage.objects.create
// IAM permission (at https://cloud.google.com/iam/docs/overview#permissions) on
// the bucket.
func (c *Client) WriteObject(ctx context.Context, opts ...gax.CallOption) (storagepb.Storage_WriteObjectClient, error) {
	return c.internalClient.WriteObject(ctx, opts...)
}

// BidiWriteObject stores a new object and metadata.
//
// This is similar to the WriteObject call with the added support for
// manual flushing of persisted state, and the ability to determine current
// persisted size without closing the stream.
//
// The client may specify one or both of the state_lookup and flush fields
// in each BidiWriteObjectRequest. If flush is specified, the data written
// so far will be persisted to storage. If state_lookup is specified, the
// service will respond with a BidiWriteObjectResponse that contains the
// persisted size. If both flush and state_lookup are specified, the flush
// will always occur before a state_lookup, so that both may be set in the
// same request and the returned state will be the state of the object
// post-flush. When the stream is closed, a BidiWriteObjectResponse will
// always be sent to the client, regardless of the value of state_lookup.
func (c *Client) BidiWriteObject(ctx context.Context, opts ...gax.CallOption) (storagepb.Storage_BidiWriteObjectClient, error) {
	return c.internalClient.BidiWriteObject(ctx, opts...)
}

// ListObjects retrieves a list of objects matching the criteria.
//
// IAM Permissions:
//
// The authenticated user requires storage.objects.list
// IAM permission (at https://cloud.google.com/iam/docs/overview#permissions)
// to use this method. To return object ACLs, the authenticated user must also
// have the storage.objects.getIamPolicy permission.
func (c *Client) ListObjects(ctx context.Context, req *storagepb.ListObjectsRequest, opts ...gax.CallOption) *ObjectIterator {
	return c.internalClient.ListObjects(ctx, req, opts...)
}

// RewriteObject rewrites a source object to a destination object. Optionally overrides
// metadata.
func (c *Client) RewriteObject(ctx context.Context, req *storagepb.RewriteObjectRequest, opts ...gax.CallOption) (*storagepb.RewriteResponse, error) {
	return c.internalClient.RewriteObject(ctx, req, opts...)
}

// StartResumableWrite starts a resumable write operation. This
// method is part of the Resumable
// upload (at https://cloud.google.com/storage/docs/resumable-uploads) feature.
// This allows you to upload large objects in multiple chunks, which is more
// resilient to network interruptions than a single upload. The validity
// duration of the write operation, and the consequences of it becoming
// invalid, are service-dependent.
//
// IAM Permissions:
//
// Requires storage.objects.create
// IAM permission (at https://cloud.google.com/iam/docs/overview#permissions) on
// the bucket.
func (c *Client) StartResumableWrite(ctx context.Context, req *storagepb.StartResumableWriteRequest, opts ...gax.CallOption) (*storagepb.StartResumableWriteResponse, error) {
	return c.internalClient.StartResumableWrite(ctx, req, opts...)
}

// QueryWriteStatus determines the persisted_size of an object that is being written. This
// method is part of the resumable
// upload (at https://cloud.google.com/storage/docs/resumable-uploads) feature.
// The returned value is the size of the object that has been persisted so
// far. The value can be used as the write_offset for the next Write()
// call.
//
// If the object does not exist, meaning if it was deleted, or the
// first Write() has not yet reached the service, this method returns the
// error NOT_FOUND.
//
// This method is useful for clients that buffer data and need to know which
// data can be safely evicted. The client can call QueryWriteStatus() at any
// time to determine how much data has been logged for this object.
// For any sequence of QueryWriteStatus() calls for a given
// object name, the sequence of returned persisted_size values are
// non-decreasing.
func (c *Client) QueryWriteStatus(ctx context.Context, req *storagepb.QueryWriteStatusRequest, opts ...gax.CallOption) (*storagepb.QueryWriteStatusResponse, error) {
	return c.internalClient.QueryWriteStatus(ctx, req, opts...)
}

// MoveObject moves the source object to the destination object in the same bucket.
func (c *Client) MoveObject(ctx context.Context, req *storagepb.MoveObjectRequest, opts ...gax.CallOption) (*storagepb.Object, error) {
	return c.internalClient.MoveObject(ctx, req, opts...)
}

// gRPCClient is a client for interacting with Cloud Storage API over gRPC transport.
//
// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls.
type gRPCClient struct {
	// Connection pool of gRPC connections to the service.
	connPool gtransport.ConnPool

	// Points back to the CallOptions field of the containing Client
	CallOptions **CallOptions

	// The gRPC API client.
	client storagepb.StorageClient

	// The x-goog-* metadata to be sent with each request.
	xGoogHeaders []string

	logger *slog.Logger
}

// NewClient creates a new storage client based on gRPC.
// The returned client must be Closed when it is done being used to clean up its underlying connections.
//
// API Overview and Naming SyntaxThe Cloud Storage gRPC API allows applications to read and write data through
// the abstractions of buckets and objects. For a description of these
// abstractions please see https://cloud.google.com/storage/docs (at https://cloud.google.com/storage/docs).
//
// Resources are named as follows:
//
//	Projects are referred to as they are defined by the Resource Manager API,
//	using strings like projects/123456 or projects/my-string-id.
//
//	Buckets are named using string names of the form:
//	projects/{project}/buckets/{bucket}
//	For globally unique buckets, _ may be substituted for the project.
//
//	Objects are uniquely identified by their name along with the name of the
//	bucket they belong to, as separate strings in this API. For example:
//
//	ReadObjectRequest {
//	  bucket: ‘projects/_/buckets/my-bucket’
//	  object: ‘my-object’
//	  }
//	  Note that object names can contain / characters, which are treated as
//	  any other character (no special directory semantics).
func NewClient(ctx context.Context, opts ...option.ClientOption) (*Client, error) {
	clientOpts := defaultGRPCClientOptions()
	if newClientHook != nil {
		hookOpts, err := newClientHook(ctx, clientHookParams{})
		if err != nil {
			return nil, err
		}
		clientOpts = append(clientOpts, hookOpts...)
	}

	connPool, err := gtransport.DialPool(ctx, append(clientOpts, opts...)...)
	if err != nil {
		return nil, err
	}
	client := Client{CallOptions: defaultCallOptions()}

	c := &gRPCClient{
		connPool:    connPool,
		client:      storagepb.NewStorageClient(connPool),
		CallOptions: &client.CallOptions,
		logger:      internaloption.GetLogger(opts),
	}
	c.setGoogleClientInfo()

	client.internalClient = c

	return &client, nil
}

// Connection returns a connection to the API service.
//
// Deprecated: Connections are now pooled so this method does not always
// return the same resource.
func (c *gRPCClient) Connection() *grpc.ClientConn {
	return c.connPool.Conn()
}

// setGoogleClientInfo sets the name and version of the application in
// the `x-goog-api-client` header passed on each request. Intended for
// use by Google-written clients.
func (c *gRPCClient) setGoogleClientInfo(keyval ...string) {
	kv := append([]string{"gl-go", gax.GoVersion}, keyval...)
	kv = append(kv, "gapic", getVersionClient(), "gax", gax.Version, "grpc", grpc.Version)
	c.xGoogHeaders = []string{
		"x-goog-api-client", gax.XGoogHeader(kv...),
	}
}

// Close closes the connection to the API service. The user should invoke this when
// the client is no longer required.
func (c *gRPCClient) Close() error {
	return c.connPool.Close()
}

func (c *gRPCClient) DeleteBucket(ctx context.Context, req *storagepb.DeleteBucketRequest, opts ...gax.CallOption) error {
	routingHeaders := ""
	routingHeadersMap := make(map[string]string)
	if reg := regexp.MustCompile("(?P<bucket>.*)"); reg.MatchString(req.GetName()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetName())[1])) > 0 {
		routingHeadersMap["bucket"] = url.QueryEscape(reg.FindStringSubmatch(req.GetName())[1])
	}
	for headerName, headerValue := range routingHeadersMap {
		routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
	}
	routingHeaders = strings.TrimSuffix(routingHeaders, "&")
	hds := []string{"x-goog-request-params", routingHeaders}

	hds = append(c.xGoogHeaders, hds...)
	ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
	opts = append((*c.CallOptions).DeleteBucket[0:len((*c.CallOptions).DeleteBucket):len((*c.CallOptions).DeleteBucket)], opts...)
	err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
		var err error
		_, err = executeRPC(ctx, c.client.DeleteBucket, req, settings.GRPC, c.logger, "DeleteBucket")
		return err
	}, opts...)
	return err
}

func (c *gRPCClient) GetBucket(ctx context.Context, req *storagepb.GetBucketRequest, opts ...gax.CallOption) (*storagepb.Bucket, error) {
	routingHeaders := ""
	routingHeadersMap := make(map[string]string)
	if reg := regexp.MustCompile("(?P<bucket>.*)"); reg.MatchString(req.GetName()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetName())[1])) > 0 {
		routingHeadersMap["bucket"] = url.QueryEscape(reg.FindStringSubmatch(req.GetName())[1])
	}
	for headerName, headerValue := range routingHeadersMap {
		routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
	}
	routingHeaders = strings.TrimSuffix(routingHeaders, "&")
	hds := []string{"x-goog-request-params", routingHeaders}

	hds = append(c.xGoogHeaders, hds...)
	ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
	opts = append((*c.CallOptions).GetBucket[0:len((*c.CallOptions).GetBucket):len((*c.CallOptions).GetBucket)], opts...)
	var resp *storagepb.Bucket
	err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
		var err error
		resp, err = executeRPC(ctx, c.client.GetBucket, req, settings.GRPC, c.logger, "GetBucket")
		return err
	}, opts...)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func (c *gRPCClient) CreateBucket(ctx context.Context, req *storagepb.CreateBucketRequest, opts ...gax.CallOption) (*storagepb.Bucket, error) {
	routingHeaders := ""
	routingHeadersMap := make(map[string]string)
	if reg := regexp.MustCompile("(?P<project>.*)"); reg.MatchString(req.GetParent()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetParent())[1])) > 0 {
		routingHeadersMap["project"] = url.QueryEscape(reg.FindStringSubmatch(req.GetParent())[1])
	}
	if reg := regexp.MustCompile("(?P<project>.*)"); reg.MatchString(req.GetBucket().GetProject()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetBucket().GetProject())[1])) > 0 {
		routingHeadersMap["project"] = url.QueryEscape(reg.FindStringSubmatch(req.GetBucket().GetProject())[1])
	}
	for headerName, headerValue := range routingHeadersMap {
		routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
	}
	routingHeaders = strings.TrimSuffix(routingHeaders, "&")
	hds := []string{"x-goog-request-params", routingHeaders}

	hds = append(c.xGoogHeaders, hds...)
	ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
	opts = append((*c.CallOptions).CreateBucket[0:len((*c.CallOptions).CreateBucket):len((*c.CallOptions).CreateBucket)], opts...)
	var resp *storagepb.Bucket
	err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
		var err error
		resp, err = executeRPC(ctx, c.client.CreateBucket, req, settings.GRPC, c.logger, "CreateBucket")
		return err
	}, opts...)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func (c *gRPCClient) ListBuckets(ctx context.Context, req *storagepb.ListBucketsRequest, opts ...gax.CallOption) *BucketIterator {
	routingHeaders := ""
	routingHeadersMap := make(map[string]string)
	if reg := regexp.MustCompile("(?P<project>.*)"); reg.MatchString(req.GetParent()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetParent())[1])) > 0 {
		routingHeadersMap["project"] = url.QueryEscape(reg.FindStringSubmatch(req.GetParent())[1])
	}
	for headerName, headerValue := range routingHeadersMap {
		routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
	}
	routingHeaders = strings.TrimSuffix(routingHeaders, "&")
	hds := []string{"x-goog-request-params", routingHeaders}

	hds = append(c.xGoogHeaders, hds...)
	ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
	opts = append((*c.CallOptions).ListBuckets[0:len((*c.CallOptions).ListBuckets):len((*c.CallOptions).ListBuckets)], opts...)
	it := &BucketIterator{}
	req = proto.Clone(req).(*storagepb.ListBucketsRequest)
	it.InternalFetch = func(pageSize int, pageToken string) ([]*storagepb.Bucket, string, error) {
		resp := &storagepb.ListBucketsResponse{}
		if pageToken != "" {
			req.PageToken = pageToken
		}
		if pageSize > math.MaxInt32 {
			req.PageSize = math.MaxInt32
		} else if pageSize != 0 {
			req.PageSize = int32(pageSize)
		}
		err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
			var err error
			resp, err = executeRPC(ctx, c.client.ListBuckets, req, settings.GRPC, c.logger, "ListBuckets")
			return err
		}, opts...)
		if err != nil {
			return nil, "", err
		}

		it.Response = resp
		return resp.GetBuckets(), resp.GetNextPageToken(), nil
	}
	fetch := func(pageSize int, pageToken string) (string, error) {
		items, nextPageToken, err := it.InternalFetch(pageSize, pageToken)
		if err != nil {
			return "", err
		}
		it.items = append(it.items, items...)
		return nextPageToken, nil
	}

	it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf)
	it.pageInfo.MaxSize = int(req.GetPageSize())
	it.pageInfo.Token = req.GetPageToken()

	return it
}

func (c *gRPCClient) LockBucketRetentionPolicy(ctx context.Context, req *storagepb.LockBucketRetentionPolicyRequest, opts ...gax.CallOption) (*storagepb.Bucket, error) {
	routingHeaders := ""
	routingHeadersMap := make(map[string]string)
	if reg := regexp.MustCompile("(?P<bucket>.*)"); reg.MatchString(req.GetBucket()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetBucket())[1])) > 0 {
		routingHeadersMap["bucket"] = url.QueryEscape(reg.FindStringSubmatch(req.GetBucket())[1])
	}
	for headerName, headerValue := range routingHeadersMap {
		routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
	}
	routingHeaders = strings.TrimSuffix(routingHeaders, "&")
	hds := []string{"x-goog-request-params", routingHeaders}

	hds = append(c.xGoogHeaders, hds...)
	ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
	opts = append((*c.CallOptions).LockBucketRetentionPolicy[0:len((*c.CallOptions).LockBucketRetentionPolicy):len((*c.CallOptions).LockBucketRetentionPolicy)], opts...)
	var resp *storagepb.Bucket
	err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
		var err error
		resp, err = executeRPC(ctx, c.client.LockBucketRetentionPolicy, req, settings.GRPC, c.logger, "LockBucketRetentionPolicy")
		return err
	}, opts...)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func (c *gRPCClient) GetIamPolicy(ctx context.Context, req *iampb.GetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) {
	routingHeaders := ""
	routingHeadersMap := make(map[string]string)
	if reg := regexp.MustCompile("(?P<bucket>.*)"); reg.MatchString(req.GetResource()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetResource())[1])) > 0 {
		routingHeadersMap["bucket"] = url.QueryEscape(reg.FindStringSubmatch(req.GetResource())[1])
	}
	for headerName, headerValue := range routingHeadersMap {
		routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
	}
	routingHeaders = strings.TrimSuffix(routingHeaders, "&")
	hds := []string{"x-goog-request-params", routingHeaders}

	hds = append(c.xGoogHeaders, hds...)
	ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
	opts = append((*c.CallOptions).GetIamPolicy[0:len((*c.CallOptions).GetIamPolicy):len((*c.CallOptions).GetIamPolicy)], opts...)
	var resp *iampb.Policy
	err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
		var err error
		resp, err = executeRPC(ctx, c.client.GetIamPolicy, req, settings.GRPC, c.logger, "GetIamPolicy")
		return err
	}, opts...)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func (c *gRPCClient) SetIamPolicy(ctx context.Context, req *iampb.SetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) {
	routingHeaders := ""
	routingHeadersMap := make(map[string]string)
	if reg := regexp.MustCompile("(?P<bucket>.*)"); reg.MatchString(req.GetResource()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetResource())[1])) > 0 {
		routingHeadersMap["bucket"] = url.QueryEscape(reg.FindStringSubmatch(req.GetResource())[1])
	}
	for headerName, headerValue := range routingHeadersMap {
		routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
	}
	routingHeaders = strings.TrimSuffix(routingHeaders, "&")
	hds := []string{"x-goog-request-params", routingHeaders}

	hds = append(c.xGoogHeaders, hds...)
	ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
	opts = append((*c.CallOptions).SetIamPolicy[0:len((*c.CallOptions).SetIamPolicy):len((*c.CallOptions).SetIamPolicy)], opts...)
	var resp *iampb.Policy
	err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
		var err error
		resp, err = executeRPC(ctx, c.client.SetIamPolicy, req, settings.GRPC, c.logger, "SetIamPolicy")
		return err
	}, opts...)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func (c *gRPCClient) TestIamPermissions(ctx context.Context, req *iampb.TestIamPermissionsRequest, opts ...gax.CallOption) (*iampb.TestIamPermissionsResponse, error) {
	routingHeaders := ""
	routingHeadersMap := make(map[string]string)
	if reg := regexp.MustCompile("(?P<bucket>.*)"); reg.MatchString(req.GetResource()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetResource())[1])) > 0 {
		routingHeadersMap["bucket"] = url.QueryEscape(reg.FindStringSubmatch(req.GetResource())[1])
	}
	if reg := regexp.MustCompile("(?P<bucket>projects/[^/]+/buckets/[^/]+)/objects(?:/.*)?"); reg.MatchString(req.GetResource()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetResource())[1])) > 0 {
		routingHeadersMap["bucket"] = url.QueryEscape(reg.FindStringSubmatch(req.GetResource())[1])
	}
	if reg := regexp.MustCompile("(?P<bucket>projects/[^/]+/buckets/[^/]+)/managedFolders(?:/.*)?"); reg.MatchString(req.GetResource()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetResource())[1])) > 0 {
		routingHeadersMap["bucket"] = url.QueryEscape(reg.FindStringSubmatch(req.GetResource())[1])
	}
	for headerName, headerValue := range routingHeadersMap {
		routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
	}
	routingHeaders = strings.TrimSuffix(routingHeaders, "&")
	hds := []string{"x-goog-request-params", routingHeaders}

	hds = append(c.xGoogHeaders, hds...)
	ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
	opts = append((*c.CallOptions).TestIamPermissions[0:len((*c.CallOptions).TestIamPermissions):len((*c.CallOptions).TestIamPermissions)], opts...)
	var resp *iampb.TestIamPermissionsResponse
	err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
		var err error
		resp, err = executeRPC(ctx, c.client.TestIamPermissions, req, settings.GRPC, c.logger, "TestIamPermissions")
		return err
	}, opts...)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func (c *gRPCClient) UpdateBucket(ctx context.Context, req *storagepb.UpdateBucketRequest, opts ...gax.CallOption) (*storagepb.Bucket, error) {
	routingHeaders := ""
	routingHeadersMap := make(map[string]string)
	if reg := regexp.MustCompile("(?P<bucket>.*)"); reg.MatchString(req.GetBucket().GetName()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetBucket().GetName())[1])) > 0 {
		routingHeadersMap["bucket"] = url.QueryEscape(reg.FindStringSubmatch(req.GetBucket().GetName())[1])
	}
	for headerName, headerValue := range routingHeadersMap {
		routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
	}
	routingHeaders = strings.TrimSuffix(routingHeaders, "&")
	hds := []string{"x-goog-request-params", routingHeaders}

	hds = append(c.xGoogHeaders, hds...)
	ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
	opts = append((*c.CallOptions).UpdateBucket[0:len((*c.CallOptions).UpdateBucket):len((*c.CallOptions).UpdateBucket)], opts...)
	var resp *storagepb.Bucket
	err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
		var err error
		resp, err = executeRPC(ctx, c.client.UpdateBucket, req, settings.GRPC, c.logger, "UpdateBucket")
		return err
	}, opts...)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func (c *gRPCClient) ComposeObject(ctx context.Context, req *storagepb.ComposeObjectRequest, opts ...gax.CallOption) (*storagepb.Object, error) {
	routingHeaders := ""
	routingHeadersMap := make(map[string]string)
	if reg := regexp.MustCompile("(?P<bucket>.*)"); reg.MatchString(req.GetDestination().GetBucket()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetDestination().GetBucket())[1])) > 0 {
		routingHeadersMap["bucket"] = url.QueryEscape(reg.FindStringSubmatch(req.GetDestination().GetBucket())[1])
	}
	for headerName, headerValue := range routingHeadersMap {
		routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
	}
	routingHeaders = strings.TrimSuffix(routingHeaders, "&")
	hds := []string{"x-goog-request-params", routingHeaders}

	hds = append(c.xGoogHeaders, hds...)
	ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
	opts = append((*c.CallOptions).ComposeObject[0:len((*c.CallOptions).ComposeObject):len((*c.CallOptions).ComposeObject)], opts...)
	var resp *storagepb.Object
	err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
		var err error
		resp, err = executeRPC(ctx, c.client.ComposeObject, req, settings.GRPC, c.logger, "ComposeObject")
		return err
	}, opts...)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func (c *gRPCClient) DeleteObject(ctx context.Context, req *storagepb.DeleteObjectRequest, opts ...gax.CallOption) error {
	routingHeaders := ""
	routingHeadersMap := make(map[string]string)
	if reg := regexp.MustCompile("(?P<bucket>.*)"); reg.MatchString(req.GetBucket()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetBucket())[1])) > 0 {
		routingHeadersMap["bucket"] = url.QueryEscape(reg.FindStringSubmatch(req.GetBucket())[1])
	}
	for headerName, headerValue := range routingHeadersMap {
		routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
	}
	routingHeaders = strings.TrimSuffix(routingHeaders, "&")
	hds := []string{"x-goog-request-params", routingHeaders}

	hds = append(c.xGoogHeaders, hds...)
	ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
	opts = append((*c.CallOptions).DeleteObject[0:len((*c.CallOptions).DeleteObject):len((*c.CallOptions).DeleteObject)], opts...)
	err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
		var err error
		_, err = executeRPC(ctx, c.client.DeleteObject, req, settings.GRPC, c.logger, "DeleteObject")
		return err
	}, opts...)
	return err
}

func (c *gRPCClient) RestoreObject(ctx context.Context, req *storagepb.RestoreObjectRequest, opts ...gax.CallOption) (*storagepb.Object, error) {
	routingHeaders := ""
	routingHeadersMap := make(map[string]string)
	if reg := regexp.MustCompile("(?P<bucket>.*)"); reg.MatchString(req.GetBucket()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetBucket())[1])) > 0 {
		routingHeadersMap["bucket"] = url.QueryEscape(reg.FindStringSubmatch(req.GetBucket())[1])
	}
	for headerName, headerValue := range routingHeadersMap {
		routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
	}
	routingHeaders = strings.TrimSuffix(routingHeaders, "&")
	hds := []string{"x-goog-request-params", routingHeaders}

	hds = append(c.xGoogHeaders, hds...)
	ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
	opts = append((*c.CallOptions).RestoreObject[0:len((*c.CallOptions).RestoreObject):len((*c.CallOptions).RestoreObject)], opts...)
	var resp *storagepb.Object
	err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
		var err error
		resp, err = executeRPC(ctx, c.client.RestoreObject, req, settings.GRPC, c.logger, "RestoreObject")
		return err
	}, opts...)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func (c *gRPCClient) CancelResumableWrite(ctx context.Context, req *storagepb.CancelResumableWriteRequest, opts ...gax.CallOption) (*storagepb.CancelResumableWriteResponse, error) {
	routingHeaders := ""
	routingHeadersMap := make(map[string]string)
	if reg := regexp.MustCompile("(?P<bucket>projects/[^/]+/buckets/[^/]+)(?:/.*)?"); reg.MatchString(req.GetUploadId()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetUploadId())[1])) > 0 {
		routingHeadersMap["bucket"] = url.QueryEscape(reg.FindStringSubmatch(req.GetUploadId())[1])
	}
	for headerName, headerValue := range routingHeadersMap {
		routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
	}
	routingHeaders = strings.TrimSuffix(routingHeaders, "&")
	hds := []string{"x-goog-request-params", routingHeaders}

	hds = append(c.xGoogHeaders, hds...)
	ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
	opts = append((*c.CallOptions).CancelResumableWrite[0:len((*c.CallOptions).CancelResumableWrite):len((*c.CallOptions).CancelResumableWrite)], opts...)
	var resp *storagepb.CancelResumableWriteResponse
	err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
		var err error
		resp, err = executeRPC(ctx, c.client.CancelResumableWrite, req, settings.GRPC, c.logger, "CancelResumableWrite")
		return err
	}, opts...)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func (c *gRPCClient) GetObject(ctx context.Context, req *storagepb.GetObjectRequest, opts ...gax.CallOption) (*storagepb.Object, error) {
	routingHeaders := ""
	routingHeadersMap := make(map[string]string)
	if reg := regexp.MustCompile("(?P<bucket>.*)"); reg.MatchString(req.GetBucket()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetBucket())[1])) > 0 {
		routingHeadersMap["bucket"] = url.QueryEscape(reg.FindStringSubmatch(req.GetBucket())[1])
	}
	for headerName, headerValue := range routingHeadersMap {
		routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
	}
	routingHeaders = strings.TrimSuffix(routingHeaders, "&")
	hds := []string{"x-goog-request-params", routingHeaders}

	hds = append(c.xGoogHeaders, hds...)
	ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
	opts = append((*c.CallOptions).GetObject[0:len((*c.CallOptions).GetObject):len((*c.CallOptions).GetObject)], opts...)
	var resp *storagepb.Object
	err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
		var err error
		resp, err = executeRPC(ctx, c.client.GetObject, req, settings.GRPC, c.logger, "GetObject")
		return err
	}, opts...)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func (c *gRPCClient) ReadObject(ctx context.Context, req *storagepb.ReadObjectRequest, opts ...gax.CallOption) (storagepb.Storage_ReadObjectClient, error) {
	routingHeaders := ""
	routingHeadersMap := make(map[string]string)
	if reg := regexp.MustCompile("(?P<bucket>.*)"); reg.MatchString(req.GetBucket()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetBucket())[1])) > 0 {
		routingHeadersMap["bucket"] = url.QueryEscape(reg.FindStringSubmatch(req.GetBucket())[1])
	}
	for headerName, headerValue := range routingHeadersMap {
		routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
	}
	routingHeaders = strings.TrimSuffix(routingHeaders, "&")
	hds := []string{"x-goog-request-params", routingHeaders}

	hds = append(c.xGoogHeaders, hds...)
	ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
	opts = append((*c.CallOptions).ReadObject[0:len((*c.CallOptions).ReadObject):len((*c.CallOptions).ReadObject)], opts...)
	var resp storagepb.Storage_ReadObjectClient
	err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
		var err error
		c.logger.DebugContext(ctx, "api streaming client request", "serviceName", serviceName, "rpcName", "ReadObject")
		resp, err = c.client.ReadObject(ctx, req, settings.GRPC...)
		c.logger.DebugContext(ctx, "api streaming client response", "serviceName", serviceName, "rpcName", "ReadObject")
		return err
	}, opts...)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func (c *gRPCClient) BidiReadObject(ctx context.Context, opts ...gax.CallOption) (storagepb.Storage_BidiReadObjectClient, error) {
	ctx = gax.InsertMetadataIntoOutgoingContext(ctx, c.xGoogHeaders...)
	var resp storagepb.Storage_BidiReadObjectClient
	opts = append((*c.CallOptions).BidiReadObject[0:len((*c.CallOptions).BidiReadObject):len((*c.CallOptions).BidiReadObject)], opts...)
	err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
		var err error
		c.logger.DebugContext(ctx, "api streaming client request", "serviceName", serviceName, "rpcName", "BidiReadObject")
		resp, err = c.client.BidiReadObject(ctx, settings.GRPC...)
		c.logger.DebugContext(ctx, "api streaming client response", "serviceName", serviceName, "rpcName", "BidiReadObject")
		return err
	}, opts...)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func (c *gRPCClient) UpdateObject(ctx context.Context, req *storagepb.UpdateObjectRequest, opts ...gax.CallOption) (*storagepb.Object, error) {
	routingHeaders := ""
	routingHeadersMap := make(map[string]string)
	if reg := regexp.MustCompile("(?P<bucket>.*)"); reg.MatchString(req.GetObject().GetBucket()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetObject().GetBucket())[1])) > 0 {
		routingHeadersMap["bucket"] = url.QueryEscape(reg.FindStringSubmatch(req.GetObject().GetBucket())[1])
	}
	for headerName, headerValue := range routingHeadersMap {
		routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
	}
	routingHeaders = strings.TrimSuffix(routingHeaders, "&")
	hds := []string{"x-goog-request-params", routingHeaders}

	hds = append(c.xGoogHeaders, hds...)
	ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
	opts = append((*c.CallOptions).UpdateObject[0:len((*c.CallOptions).UpdateObject):len((*c.CallOptions).UpdateObject)], opts...)
	var resp *storagepb.Object
	err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
		var err error
		resp, err = executeRPC(ctx, c.client.UpdateObject, req, settings.GRPC, c.logger, "UpdateObject")
		return err
	}, opts...)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func (c *gRPCClient) WriteObject(ctx context.Context, opts ...gax.CallOption) (storagepb.Storage_WriteObjectClient, error) {
	ctx = gax.InsertMetadataIntoOutgoingContext(ctx, c.xGoogHeaders...)
	var resp storagepb.Storage_WriteObjectClient
	opts = append((*c.CallOptions).WriteObject[0:len((*c.CallOptions).WriteObject):len((*c.CallOptions).WriteObject)], opts...)
	err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
		var err error
		c.logger.DebugContext(ctx, "api streaming client request", "serviceName", serviceName, "rpcName", "WriteObject")
		resp, err = c.client.WriteObject(ctx, settings.GRPC...)
		c.logger.DebugContext(ctx, "api streaming client response", "serviceName", serviceName, "rpcName", "WriteObject")
		return err
	}, opts...)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func (c *gRPCClient) BidiWriteObject(ctx context.Context, opts ...gax.CallOption) (storagepb.Storage_BidiWriteObjectClient, error) {
	ctx = gax.InsertMetadataIntoOutgoingContext(ctx, c.xGoogHeaders...)
	var resp storagepb.Storage_BidiWriteObjectClient
	opts = append((*c.CallOptions).BidiWriteObject[0:len((*c.CallOptions).BidiWriteObject):len((*c.CallOptions).BidiWriteObject)], opts...)
	err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
		var err error
		c.logger.DebugContext(ctx, "api streaming client request", "serviceName", serviceName, "rpcName", "BidiWriteObject")
		resp, err = c.client.BidiWriteObject(ctx, settings.GRPC...)
		c.logger.DebugContext(ctx, "api streaming client response", "serviceName", serviceName, "rpcName", "BidiWriteObject")
		return err
	}, opts...)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func (c *gRPCClient) ListObjects(ctx context.Context, req *storagepb.ListObjectsRequest, opts ...gax.CallOption) *ObjectIterator {
	routingHeaders := ""
	routingHeadersMap := make(map[string]string)
	if reg := regexp.MustCompile("(?P<bucket>.*)"); reg.MatchString(req.GetParent()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetParent())[1])) > 0 {
		routingHeadersMap["bucket"] = url.QueryEscape(reg.FindStringSubmatch(req.GetParent())[1])
	}
	for headerName, headerValue := range routingHeadersMap {
		routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
	}
	routingHeaders = strings.TrimSuffix(routingHeaders, "&")
	hds := []string{"x-goog-request-params", routingHeaders}

	hds = append(c.xGoogHeaders, hds...)
	ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
	opts = append((*c.CallOptions).ListObjects[0:len((*c.CallOptions).ListObjects):len((*c.CallOptions).ListObjects)], opts...)
	it := &ObjectIterator{}
	req = proto.Clone(req).(*storagepb.ListObjectsRequest)
	it.InternalFetch = func(pageSize int, pageToken string) ([]*storagepb.Object, string, error) {
		resp := &storagepb.ListObjectsResponse{}
		if pageToken != "" {
			req.PageToken = pageToken
		}
		if pageSize > math.MaxInt32 {
			req.PageSize = math.MaxInt32
		} else if pageSize != 0 {
			req.PageSize = int32(pageSize)
		}
		err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
			var err error
			resp, err = executeRPC(ctx, c.client.ListObjects, req, settings.GRPC, c.logger, "ListObjects")
			return err
		}, opts...)
		if err != nil {
			return nil, "", err
		}

		it.Response = resp
		return resp.GetObjects(), resp.GetNextPageToken(), nil
	}
	fetch := func(pageSize int, pageToken string) (string, error) {
		items, nextPageToken, err := it.InternalFetch(pageSize, pageToken)
		if err != nil {
			return "", err
		}
		it.items = append(it.items, items...)
		return nextPageToken, nil
	}

	it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf)
	it.pageInfo.MaxSize = int(req.GetPageSize())
	it.pageInfo.Token = req.GetPageToken()

	return it
}

func (c *gRPCClient) RewriteObject(ctx context.Context, req *storagepb.RewriteObjectRequest, opts ...gax.CallOption) (*storagepb.RewriteResponse, error) {
	routingHeaders := ""
	routingHeadersMap := make(map[string]string)
	if reg := regexp.MustCompile("(.*)"); reg.MatchString(req.GetSourceBucket()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetSourceBucket())[1])) > 0 {
		routingHeadersMap["source_bucket"] = url.QueryEscape(reg.FindStringSubmatch(req.GetSourceBucket())[1])
	}
	if reg := regexp.MustCompile("(?P<bucket>.*)"); reg.MatchString(req.GetDestinationBucket()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetDestinationBucket())[1])) > 0 {
		routingHeadersMap["bucket"] = url.QueryEscape(reg.FindStringSubmatch(req.GetDestinationBucket())[1])
	}
	for headerName, headerValue := range routingHeadersMap {
		routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
	}
	routingHeaders = strings.TrimSuffix(routingHeaders, "&")
	hds := []string{"x-goog-request-params", routingHeaders}

	hds = append(c.xGoogHeaders, hds...)
	ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
	opts = append((*c.CallOptions).RewriteObject[0:len((*c.CallOptions).RewriteObject):len((*c.CallOptions).RewriteObject)], opts...)
	var resp *storagepb.RewriteResponse
	err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
		var err error
		resp, err = executeRPC(ctx, c.client.RewriteObject, req, settings.GRPC, c.logger, "RewriteObject")
		return err
	}, opts...)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func (c *gRPCClient) StartResumableWrite(ctx context.Context, req *storagepb.StartResumableWriteRequest, opts ...gax.CallOption) (*storagepb.StartResumableWriteResponse, error) {
	routingHeaders := ""
	routingHeadersMap := make(map[string]string)
	if reg := regexp.MustCompile("(?P<bucket>.*)"); reg.MatchString(req.GetWriteObjectSpec().GetResource().GetBucket()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetWriteObjectSpec().GetResource().GetBucket())[1])) > 0 {
		routingHeadersMap["bucket"] = url.QueryEscape(reg.FindStringSubmatch(req.GetWriteObjectSpec().GetResource().GetBucket())[1])
	}
	for headerName, headerValue := range routingHeadersMap {
		routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
	}
	routingHeaders = strings.TrimSuffix(routingHeaders, "&")
	hds := []string{"x-goog-request-params", routingHeaders}

	hds = append(c.xGoogHeaders, hds...)
	ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
	opts = append((*c.CallOptions).StartResumableWrite[0:len((*c.CallOptions).StartResumableWrite):len((*c.CallOptions).StartResumableWrite)], opts...)
	var resp *storagepb.StartResumableWriteResponse
	err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
		var err error
		resp, err = executeRPC(ctx, c.client.StartResumableWrite, req, settings.GRPC, c.logger, "StartResumableWrite")
		return err
	}, opts...)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func (c *gRPCClient) QueryWriteStatus(ctx context.Context, req *storagepb.QueryWriteStatusRequest, opts ...gax.CallOption) (*storagepb.QueryWriteStatusResponse, error) {
	routingHeaders := ""
	routingHeadersMap := make(map[string]string)
	if reg := regexp.MustCompile("(?P<bucket>projects/[^/]+/buckets/[^/]+)(?:/.*)?"); reg.MatchString(req.GetUploadId()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetUploadId())[1])) > 0 {
		routingHeadersMap["bucket"] = url.QueryEscape(reg.FindStringSubmatch(req.GetUploadId())[1])
	}
	for headerName, headerValue := range routingHeadersMap {
		routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
	}
	routingHeaders = strings.TrimSuffix(routingHeaders, "&")
	hds := []string{"x-goog-request-params", routingHeaders}

	hds = append(c.xGoogHeaders, hds...)
	ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
	opts = append((*c.CallOptions).QueryWriteStatus[0:len((*c.CallOptions).QueryWriteStatus):len((*c.CallOptions).QueryWriteStatus)], opts...)
	var resp *storagepb.QueryWriteStatusResponse
	err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
		var err error
		resp, err = executeRPC(ctx, c.client.QueryWriteStatus, req, settings.GRPC, c.logger, "QueryWriteStatus")
		return err
	}, opts...)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func (c *gRPCClient) MoveObject(ctx context.Context, req *storagepb.MoveObjectRequest, opts ...gax.CallOption) (*storagepb.Object, error) {
	routingHeaders := ""
	routingHeadersMap := make(map[string]string)
	if reg := regexp.MustCompile("(?P<bucket>.*)"); reg.MatchString(req.GetBucket()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetBucket())[1])) > 0 {
		routingHeadersMap["bucket"] = url.QueryEscape(reg.FindStringSubmatch(req.GetBucket())[1])
	}
	for headerName, headerValue := range routingHeadersMap {
		routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
	}
	routingHeaders = strings.TrimSuffix(routingHeaders, "&")
	hds := []string{"x-goog-request-params", routingHeaders}

	hds = append(c.xGoogHeaders, hds...)
	ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
	opts = append((*c.CallOptions).MoveObject[0:len((*c.CallOptions).MoveObject):len((*c.CallOptions).MoveObject)], opts...)
	var resp *storagepb.Object
	err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
		var err error
		resp, err = executeRPC(ctx, c.client.MoveObject, req, settings.GRPC, c.logger, "MoveObject")
		return err
	}, opts...)
	if err != nil {
		return nil, err
	}
	return resp, nil
}
