bump cloud.google.com/go v0.44.3:
full diff: https://github.com/googleapis/google-cloud-go/compare/v0.23.0...v0.44.3
bump googleapis/gax-go v2.0.5
full diff: https://github.com/googleapis/gax-go/compare/v2.0.0...v2.0.5
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
Signed-off-by: Tibor Vass <tibor@docker.com>
| ... | ... |
@@ -118,8 +118,8 @@ github.com/bsphere/le_go 7a984a84b5492ae539b79b62fb4a |
| 118 | 118 |
golang.org/x/oauth2 bf48bf16ab8d622ce64ec6ce98d2c98f916b6303 |
| 119 | 119 |
google.golang.org/api de943baf05a022a8f921b544b7827bacaba1aed5 |
| 120 | 120 |
go.opencensus.io c3ed530f775d85e577ca652cb052a52c078aad26 # v0.11.0 |
| 121 |
-cloud.google.com/go 0fd7230b2a7505833d5f69b75cbd6c9582401479 # v0.23.0 |
|
| 122 |
-github.com/googleapis/gax-go 317e0006254c44a0ac427cc52a0e083ff0b9622f # v2.0.0 |
|
| 121 |
+cloud.google.com/go ceeb313ad77b789a7fa5287b36a1d127b69b7093 # v0.44.3 |
|
| 122 |
+github.com/googleapis/gax-go bd5b16380fd03dc758d11cef74ba2e3bc8b0e8c2 # v2.0.5 |
|
| 123 | 123 |
google.golang.org/genproto 3f1135a288c9a07e340ae8ba4cc6c7065a3160e8 |
| 124 | 124 |
|
| 125 | 125 |
# containerd |
| ... | ... |
@@ -8,7 +8,7 @@ Go packages for [Google Cloud Platform](https://cloud.google.com) services. |
| 8 | 8 |
import "cloud.google.com/go" |
| 9 | 9 |
``` |
| 10 | 10 |
|
| 11 |
-To install the packages on your system, |
|
| 11 |
+To install the packages on your system, *do not clone the repo*. Instead use |
|
| 12 | 12 |
|
| 13 | 13 |
``` |
| 14 | 14 |
$ go get -u cloud.google.com/go/... |
| ... | ... |
@@ -19,263 +19,44 @@ make backwards-incompatible changes. |
| 19 | 19 |
|
| 20 | 20 |
**NOTE:** Github repo is a mirror of [https://code.googlesource.com/gocloud](https://code.googlesource.com/gocloud). |
| 21 | 21 |
|
| 22 |
- * [News](#news) |
|
| 23 |
- * [Supported APIs](#supported-apis) |
|
| 24 |
- * [Go Versions Supported](#go-versions-supported) |
|
| 25 |
- * [Authorization](#authorization) |
|
| 26 |
- * [Cloud Datastore](#cloud-datastore-) |
|
| 27 |
- * [Cloud Storage](#cloud-storage-) |
|
| 28 |
- * [Cloud Pub/Sub](#cloud-pub-sub-) |
|
| 29 |
- * [Cloud BigQuery](#cloud-bigquery-) |
|
| 30 |
- * [Stackdriver Logging](#stackdriver-logging-) |
|
| 31 |
- * [Cloud Spanner](#cloud-spanner-) |
|
| 32 |
- |
|
| 33 |
- |
|
| 34 |
-## News |
|
| 35 |
- |
|
| 36 |
-_May 18, 2018_ |
|
| 37 |
- |
|
| 38 |
-*v0.23.0* |
|
| 39 |
- |
|
| 40 |
-- bigquery: Add DDL stats to query statistics. |
|
| 41 |
-- bigtable: |
|
| 42 |
- - cbt: Add cells-per-column limit for row lookup. |
|
| 43 |
- - cbt: Make it possible to combine read filters. |
|
| 44 |
-- dlp: v2beta2 client removed. Use the v2 client instead. |
|
| 45 |
-- firestore, spanner: Fix compilation errors due to protobuf changes. |
|
| 46 |
- |
|
| 47 |
-_May 8, 2018_ |
|
| 48 |
- |
|
| 49 |
-*v0.22.0* |
|
| 50 |
- |
|
| 51 |
-- bigtable: |
|
| 52 |
- - cbt: Support cells per column limit for row read. |
|
| 53 |
- - bttest: Correctly handle empty RowSet. |
|
| 54 |
- - Fix ReadModifyWrite operation in emulator. |
|
| 55 |
- - Fix API path in GetCluster. |
|
| 56 |
- |
|
| 57 |
-- bigquery: |
|
| 58 |
- - BEHAVIOR CHANGE: Retry on 503 status code. |
|
| 59 |
- - Add dataset.DeleteWithContents. |
|
| 60 |
- - Add SchemaUpdateOptions for query jobs. |
|
| 61 |
- - Add Timeline to QueryStatistics. |
|
| 62 |
- - Add more stats to ExplainQueryStage. |
|
| 63 |
- - Support Parquet data format. |
|
| 64 |
- |
|
| 65 |
-- datastore: |
|
| 66 |
- - Support omitempty for times. |
|
| 67 |
- |
|
| 68 |
-- dlp: |
|
| 69 |
- - **BREAKING CHANGE:** Remove v1beta1 client. Please migrate to the v2 client, |
|
| 70 |
- which is now out of beta. |
|
| 71 |
- - Add v2 client. |
|
| 72 |
- |
|
| 73 |
-- firestore: |
|
| 74 |
- - BEHAVIOR CHANGE: Treat set({}, MergeAll) as valid.
|
|
| 75 |
- |
|
| 76 |
-- iam: |
|
| 77 |
- - Support JWT signing via SignJwt callopt. |
|
| 78 |
- |
|
| 79 |
-- profiler: |
|
| 80 |
- - BEHAVIOR CHANGE: PollForSerialOutput returns an error when context.Done. |
|
| 81 |
- - BEHAVIOR CHANGE: Increase the initial backoff to 1 minute. |
|
| 82 |
- - Avoid returning empty serial port output. |
|
| 83 |
- |
|
| 84 |
-- pubsub: |
|
| 85 |
- - BEHAVIOR CHANGE: Don't backoff during next retryable error once stream is healthy. |
|
| 86 |
- - BEHAVIOR CHANGE: Don't backoff on EOF. |
|
| 87 |
- - pstest: Support Acknowledge and ModifyAckDeadline RPCs. |
|
| 88 |
- |
|
| 89 |
-- redis: |
|
| 90 |
- - Add v1 beta Redis client. |
|
| 91 |
- |
|
| 92 |
-- spanner: |
|
| 93 |
- - Support SessionLabels. |
|
| 94 |
- |
|
| 95 |
-- speech: |
|
| 96 |
- - Add api v1 beta1 client. |
|
| 97 |
- |
|
| 98 |
-- storage: |
|
| 99 |
- - BEHAVIOR CHANGE: Retry reads when retryable error occurs. |
|
| 100 |
- - Fix delete of object in requester-pays bucket. |
|
| 101 |
- - Support KMS integration. |
|
| 102 |
- |
|
| 103 |
-_April 9, 2018_ |
|
| 104 |
- |
|
| 105 |
-*v0.21.0* |
|
| 106 |
- |
|
| 107 |
-- bigquery: |
|
| 108 |
- - Add OpenCensus tracing. |
|
| 109 |
- |
|
| 110 |
-- firestore: |
|
| 111 |
- - **BREAKING CHANGE:** If a document does not exist, return a DocumentSnapshot |
|
| 112 |
- whose Exists method returns false. DocumentRef.Get and Transaction.Get |
|
| 113 |
- return the non-nil DocumentSnapshot in addition to a NotFound error. |
|
| 114 |
- **DocumentRef.GetAll and Transaction.GetAll return a non-nil |
|
| 115 |
- DocumentSnapshot instead of nil.** |
|
| 116 |
- - Add DocumentIterator.Stop. **Call Stop whenever you are done with a |
|
| 117 |
- DocumentIterator.** |
|
| 118 |
- - Added Query.Snapshots and DocumentRef.Snapshots, which provide realtime |
|
| 119 |
- notification of updates. See https://cloud.google.com/firestore/docs/query-data/listen. |
|
| 120 |
- - Canceling an RPC now always returns a grpc.Status with codes.Canceled. |
|
| 121 |
- |
|
| 122 |
-- spanner: |
|
| 123 |
- - Add `CommitTimestamp`, which supports inserting the commit timestamp of a |
|
| 124 |
- transaction into a column. |
|
| 125 |
- |
|
| 126 |
-_March 22, 2018_ |
|
| 127 |
- |
|
| 128 |
-*v0.20.0* |
|
| 129 |
- |
|
| 130 |
-- bigquery: Support SchemaUpdateOptions for load jobs. |
|
| 131 |
- |
|
| 132 |
-- bigtable: |
|
| 133 |
- - Add SampleRowKeys. |
|
| 134 |
- - cbt: Support union, intersection GCPolicy. |
|
| 135 |
- - Retry admin RPCS. |
|
| 136 |
- - Add trace spans to retries. |
|
| 137 |
- |
|
| 138 |
-- datastore: Add OpenCensus tracing. |
|
| 139 |
- |
|
| 140 |
-- firestore: |
|
| 141 |
- - Fix queries involving Null and NaN. |
|
| 142 |
- - Allow Timestamp protobuffers for time values. |
|
| 143 |
- |
|
| 144 |
-- logging: Add a WriteTimeout option. |
|
| 145 |
- |
|
| 146 |
-- spanner: Support Batch API. |
|
| 147 |
- |
|
| 148 |
-- storage: Add OpenCensus tracing. |
|
| 149 |
- |
|
| 150 |
- |
|
| 151 |
-_February 26, 2018_ |
|
| 152 |
- |
|
| 153 |
-*v0.19.0* |
|
| 154 |
- |
|
| 155 |
-- bigquery: |
|
| 156 |
- - Support customer-managed encryption keys. |
|
| 157 |
- |
|
| 158 |
-- bigtable: |
|
| 159 |
- - Improved emulator support. |
|
| 160 |
- - Support GetCluster. |
|
| 161 |
- |
|
| 162 |
-- datastore: |
|
| 163 |
- - Add general mutations. |
|
| 164 |
- - Support pointer struct fields. |
|
| 165 |
- - Support transaction options. |
|
| 166 |
- |
|
| 167 |
-- firestore: |
|
| 168 |
- - Add Transaction.GetAll. |
|
| 169 |
- - Support document cursors. |
|
| 170 |
- |
|
| 171 |
-- logging: |
|
| 172 |
- - Support concurrent RPCs to the service. |
|
| 173 |
- - Support per-entry resources. |
|
| 174 |
- |
|
| 175 |
-- profiler: |
|
| 176 |
- - Add config options to disable heap and thread profiling. |
|
| 177 |
- - Read the project ID from $GOOGLE_CLOUD_PROJECT when it's set. |
|
| 178 |
- |
|
| 179 |
-- pubsub: |
|
| 180 |
- - BEHAVIOR CHANGE: Release flow control after ack/nack (instead of after the |
|
| 181 |
- callback returns). |
|
| 182 |
- - Add SubscriptionInProject. |
|
| 183 |
- - Add OpenCensus instrumentation for streaming pull. |
|
| 184 |
- |
|
| 185 |
-- storage: |
|
| 186 |
- - Support CORS. |
|
| 187 |
- |
|
| 188 |
- |
|
| 189 |
-_January 18, 2018_ |
|
| 190 |
- |
|
| 191 |
-*v0.18.0* |
|
| 192 |
- |
|
| 193 |
-- bigquery: |
|
| 194 |
- - Marked stable. |
|
| 195 |
- - Schema inference of nullable fields supported. |
|
| 196 |
- - Added TimePartitioning to QueryConfig. |
|
| 197 |
- |
|
| 198 |
-- firestore: Data provided to DocumentRef.Set with a Merge option can contain |
|
| 199 |
- Delete sentinels. |
|
| 200 |
- |
|
| 201 |
-- logging: Clients can accept parent resources other than projects. |
|
| 202 |
- |
|
| 203 |
-- pubsub: |
|
| 204 |
- - pubsub/pstest: A lighweight fake for pubsub. Experimental; feedback welcome. |
|
| 205 |
- - Support updating more subscription metadata: AckDeadline, |
|
| 206 |
- RetainAckedMessages and RetentionDuration. |
|
| 207 |
- |
|
| 208 |
-- oslogin/apiv1beta: New client for the Cloud OS Login API. |
|
| 209 |
- |
|
| 210 |
-- rpcreplay: A package for recording and replaying gRPC traffic. |
|
| 211 |
- |
|
| 212 |
-- spanner: |
|
| 213 |
- - Add a ReadWithOptions that supports a row limit, as well as an index. |
|
| 214 |
- - Support query plan and execution statistics. |
|
| 215 |
- - Added [OpenCensus](http://opencensus.io) support. |
|
| 216 |
- |
|
| 217 |
-- storage: Clarify checksum validation for gzipped files (it is not validated |
|
| 218 |
- when the file is served uncompressed). |
|
| 219 |
- |
|
| 220 |
- |
|
| 221 |
-_December 11, 2017_ |
|
| 222 |
- |
|
| 223 |
-*v0.17.0* |
|
| 224 |
- |
|
| 225 |
-- firestore BREAKING CHANGES: |
|
| 226 |
- - Remove UpdateMap and UpdateStruct; rename UpdatePaths to Update. |
|
| 227 |
- Change |
|
| 228 |
- `docref.UpdateMap(ctx, map[string]interface{}{"a.b", 1})`
|
|
| 229 |
- to |
|
| 230 |
- `docref.Update(ctx, []firestore.Update{{Path: "a.b", Value: 1}})`
|
|
| 231 |
- |
|
| 232 |
- Change |
|
| 233 |
- `docref.UpdateStruct(ctx, []string{"Field"}, aStruct)`
|
|
| 234 |
- to |
|
| 235 |
- `docref.Update(ctx, []firestore.Update{{Path: "Field", Value: aStruct.Field}})`
|
|
| 236 |
- - Rename MergePaths to Merge; require args to be FieldPaths |
|
| 237 |
- - A value stored as an integer can be read into a floating-point field, and vice versa. |
|
| 238 |
-- bigtable/cmd/cbt: |
|
| 239 |
- - Support deleting a column. |
|
| 240 |
- - Add regex option for row read. |
|
| 241 |
-- spanner: Mark stable. |
|
| 242 |
-- storage: |
|
| 243 |
- - Add Reader.ContentEncoding method. |
|
| 244 |
- - Fix handling of SignedURL headers. |
|
| 245 |
-- bigquery: |
|
| 246 |
- - If Uploader.Put is called with no rows, it returns nil without making a |
|
| 247 |
- call. |
|
| 248 |
- - Schema inference supports the "nullable" option in struct tags for |
|
| 249 |
- non-required fields. |
|
| 250 |
- - TimePartitioning supports "Field". |
|
| 251 |
- |
|
| 252 |
- |
|
| 253 |
-[Older news](https://github.com/GoogleCloudPlatform/google-cloud-go/blob/master/old-news.md) |
|
| 254 |
- |
|
| 255 | 22 |
## Supported APIs |
| 256 | 23 |
|
| 257 |
-Google API | Status | Package |
|
| 258 |
-[BigQuery][cloud-bigquery] | stable | [`cloud.google.com/go/bigquery`][cloud-bigquery-ref] |
|
| 259 |
-[Bigtable][cloud-bigtable] | stable | [`cloud.google.com/go/bigtable`][cloud-bigtable-ref] |
|
| 260 |
-[Container][cloud-container] | alpha | [`cloud.google.com/go/container/apiv1`][cloud-container-ref] |
|
| 261 |
-[Data Loss Prevention][cloud-dlp]| alpha | [`cloud.google.com/go/dlp/apiv2beta1`][cloud-dlp-ref] |
|
| 262 |
-[Datastore][cloud-datastore] | stable | [`cloud.google.com/go/datastore`][cloud-datastore-ref] |
|
| 263 |
-[Debugger][cloud-debugger] | alpha | [`cloud.google.com/go/debugger/apiv2`][cloud-debugger-ref] |
|
| 264 |
-[ErrorReporting][cloud-errors] | alpha | [`cloud.google.com/go/errorreporting`][cloud-errors-ref] |
|
| 265 |
-[Firestore][cloud-firestore] | beta | [`cloud.google.com/go/firestore`][cloud-firestore-ref] |
|
| 266 |
-[Language][cloud-language] | stable | [`cloud.google.com/go/language/apiv1`][cloud-language-ref] |
|
| 267 |
-[Logging][cloud-logging] | stable | [`cloud.google.com/go/logging`][cloud-logging-ref] |
|
| 268 |
-[Monitoring][cloud-monitoring] | beta | [`cloud.google.com/go/monitoring/apiv3`][cloud-monitoring-ref] |
|
| 269 |
-[OS Login][cloud-oslogin] | alpha | [`cloud.google.com/compute/docs/oslogin/rest`][cloud-oslogin-ref] |
|
| 270 |
-[Pub/Sub][cloud-pubsub] | beta | [`cloud.google.com/go/pubsub`][cloud-pubsub-ref] |
|
| 271 |
-[Spanner][cloud-spanner] | stable | [`cloud.google.com/go/spanner`][cloud-spanner-ref] |
|
| 272 |
-[Speech][cloud-speech] | stable | [`cloud.google.com/go/speech/apiv1`][cloud-speech-ref] |
|
| 273 |
-[Storage][cloud-storage] | stable | [`cloud.google.com/go/storage`][cloud-storage-ref] |
|
| 274 |
-[Translation][cloud-translation] | stable | [`cloud.google.com/go/translate`][cloud-translation-ref] |
|
| 275 |
-[Video Intelligence][cloud-video]| beta | [`cloud.google.com/go/videointelligence/apiv1beta1`][cloud-video-ref] |
|
| 276 |
-[Vision][cloud-vision] | stable | [`cloud.google.com/go/vision/apiv1`][cloud-vision-ref] |
|
| 277 |
- |
|
| 24 |
+Google API | Status | Package |
|
| 25 |
+------------------------------------------------|--------------|----------------------------------------------------------- |
|
| 26 |
+[Asset][cloud-asset] | alpha | [`cloud.google.com/go/asset/v1beta`][cloud-asset-ref] |
|
| 27 |
+[BigQuery][cloud-bigquery] | stable | [`cloud.google.com/go/bigquery`][cloud-bigquery-ref] |
|
| 28 |
+[Bigtable][cloud-bigtable] | stable | [`cloud.google.com/go/bigtable`][cloud-bigtable-ref] |
|
| 29 |
+[Cloudtasks][cloud-tasks] | stable | [`cloud.google.com/go/cloudtasks/apiv2`][cloud-tasks-ref] |
|
| 30 |
+[Container][cloud-container] | stable | [`cloud.google.com/go/container/apiv1`][cloud-container-ref] |
|
| 31 |
+[ContainerAnalysis][cloud-containeranalysis] | beta | [`cloud.google.com/go/containeranalysis/apiv1beta1`][cloud-containeranalysis-ref] |
|
| 32 |
+[Dataproc][cloud-dataproc] | stable | [`cloud.google.com/go/dataproc/apiv1`][cloud-dataproc-ref] |
|
| 33 |
+[Datastore][cloud-datastore] | stable | [`cloud.google.com/go/datastore`][cloud-datastore-ref] |
|
| 34 |
+[Debugger][cloud-debugger] | alpha | [`cloud.google.com/go/debugger/apiv2`][cloud-debugger-ref] |
|
| 35 |
+[Dialogflow][cloud-dialogflow] | alpha | [`cloud.google.com/go/dialogflow/apiv2`][cloud-dialogflow-ref] |
|
| 36 |
+[Data Loss Prevention][cloud-dlp] | alpha | [`cloud.google.com/go/dlp/apiv2`][cloud-dlp-ref] |
|
| 37 |
+[ErrorReporting][cloud-errors] | alpha | [`cloud.google.com/go/errorreporting`][cloud-errors-ref] |
|
| 38 |
+[Firestore][cloud-firestore] | stable | [`cloud.google.com/go/firestore`][cloud-firestore-ref] |
|
| 39 |
+[IAM][cloud-iam] | stable | [`cloud.google.com/go/iam`][cloud-iam-ref] |
|
| 40 |
+[IoT][cloud-iot] | alpha | [`cloud.google.com/iot/apiv1`][cloud-iot-ref] |
|
| 41 |
+[KMS][cloud-kms] | stable | [`cloud.google.com/go/kms`][cloud-kms-ref] |
|
| 42 |
+[Natural Language][cloud-natural-language] | stable | [`cloud.google.com/go/language/apiv1`][cloud-natural-language-ref] |
|
| 43 |
+[Logging][cloud-logging] | stable | [`cloud.google.com/go/logging`][cloud-logging-ref] |
|
| 44 |
+[Monitoring][cloud-monitoring] | alpha | [`cloud.google.com/go/monitoring/apiv3`][cloud-monitoring-ref] |
|
| 45 |
+[OS Login][cloud-oslogin] | alpha | [`cloud.google.com/go/oslogin/apiv1`][cloud-oslogin-ref] |
|
| 46 |
+[Pub/Sub][cloud-pubsub] | stable | [`cloud.google.com/go/pubsub`][cloud-pubsub-ref] |
|
| 47 |
+[Phishing Protection][cloud-phishingprotection] | alpha | [`cloud.google.com/go/phishingprotection/apiv1betad1`][cloud-phishingprotection-ref] |
|
| 48 |
+[reCAPTCHA Enterprise][cloud-recaptcha] | alpha | [`cloud.google.com/go/recaptchaenterprise/apiv1betad1`][cloud-recaptcha-ref] |
|
| 49 |
+[Memorystore][cloud-memorystore] | alpha | [`cloud.google.com/go/redis/apiv1`][cloud-memorystore-ref] |
|
| 50 |
+[Scheduler][cloud-scheduler] | stable | [`cloud.google.com/go/scheduler/apiv1`][cloud-scheduler-ref] |
|
| 51 |
+[Spanner][cloud-spanner] | stable | [`cloud.google.com/go/spanner`][cloud-spanner-ref] |
|
| 52 |
+[Speech][cloud-speech] | stable | [`cloud.google.com/go/speech/apiv1`][cloud-speech-ref] |
|
| 53 |
+[Storage][cloud-storage] | stable | [`cloud.google.com/go/storage`][cloud-storage-ref] |
|
| 54 |
+[Talent][cloud-talent] | alpha | [`cloud.google.com/go/talent/apiv4beta1`][cloud-talent-ref] |
|
| 55 |
+[Text To Speech][cloud-texttospeech] | alpha | [`cloud.google.com/go/texttospeech/apiv1`][cloud-texttospeech-ref] |
|
| 56 |
+[Trace][cloud-trace] | alpha | [`cloud.google.com/go/trace/apiv2`][cloud-trace-ref] |
|
| 57 |
+[Translate][cloud-translate] | stable | [`cloud.google.com/go/translate`][cloud-translate-ref] |
|
| 58 |
+[Video Intelligence][cloud-video] | alpha | [`cloud.google.com/go/videointelligence/apiv1beta1`][cloud-video-ref] |
|
| 59 |
+[Vision][cloud-vision] | stable | [`cloud.google.com/go/vision/apiv1`][cloud-vision-ref] |
|
| 278 | 60 |
|
| 279 | 61 |
> **Alpha status**: the API is still being actively developed. As a |
| 280 | 62 |
> result, it might change in backward-incompatible ways and is not recommended |
| ... | ... |
@@ -288,23 +69,16 @@ Google API | Status | Package |
| 288 | 288 |
> **Stable status**: the API is mature and ready for production use. We will |
| 289 | 289 |
> continue addressing bugs and feature requests. |
| 290 | 290 |
|
| 291 |
-Documentation and examples are available at |
|
| 292 |
-https://godoc.org/cloud.google.com/go |
|
| 293 |
- |
|
| 294 |
-Visit or join the |
|
| 295 |
-[google-api-go-announce group](https://groups.google.com/forum/#!forum/google-api-go-announce) |
|
| 296 |
-for updates on these packages. |
|
| 291 |
+Documentation and examples are available at [godoc.org/cloud.google.com/go](godoc.org/cloud.google.com/go) |
|
| 297 | 292 |
|
| 298 | 293 |
## Go Versions Supported |
| 299 | 294 |
|
| 300 | 295 |
We support the two most recent major versions of Go. If Google App Engine uses |
| 301 |
-an older version, we support that as well. You can see which versions are |
|
| 302 |
-currently supported by looking at the lines following `go:` in |
|
| 303 |
-[`.travis.yml`](.travis.yml). |
|
| 296 |
+an older version, we support that as well. |
|
| 304 | 297 |
|
| 305 | 298 |
## Authorization |
| 306 | 299 |
|
| 307 |
-By default, each API will use [Google Application Default Credentials][default-creds] |
|
| 300 |
+By default, each API will use [Google Application Default Credentials](https://developers.google.com/identity/protocols/application-default-credentials) |
|
| 308 | 301 |
for authorization credentials used in calling the API endpoints. This will allow your |
| 309 | 302 |
application to run in many environments without requiring explicit configuration. |
| 310 | 303 |
|
| ... | ... |
@@ -316,12 +90,12 @@ client, err := storage.NewClient(ctx) |
| 316 | 316 |
To authorize using a |
| 317 | 317 |
[JSON key file](https://cloud.google.com/iam/docs/managing-service-account-keys), |
| 318 | 318 |
pass |
| 319 |
-[`option.WithServiceAccountFile`](https://godoc.org/google.golang.org/api/option#WithServiceAccountFile) |
|
| 319 |
+[`option.WithCredentialsFile`](https://godoc.org/google.golang.org/api/option#WithCredentialsFile) |
|
| 320 | 320 |
to the `NewClient` function of the desired package. For example: |
| 321 | 321 |
|
| 322 | 322 |
[snip]:# (auth-JSON) |
| 323 | 323 |
```go |
| 324 |
-client, err := storage.NewClient(ctx, option.WithServiceAccountFile("path/to/keyfile.json"))
|
|
| 324 |
+client, err := storage.NewClient(ctx, option.WithCredentialsFile("path/to/keyfile.json"))
|
|
| 325 | 325 |
``` |
| 326 | 326 |
|
| 327 | 327 |
You can exert more control over authorization by using the |
| ... | ... |
@@ -335,249 +109,6 @@ tokenSource := ... |
| 335 | 335 |
client, err := storage.NewClient(ctx, option.WithTokenSource(tokenSource)) |
| 336 | 336 |
``` |
| 337 | 337 |
|
| 338 |
-## Cloud Datastore [](https://godoc.org/cloud.google.com/go/datastore) |
|
| 339 |
- |
|
| 340 |
-- [About Cloud Datastore][cloud-datastore] |
|
| 341 |
-- [Activating the API for your project][cloud-datastore-activation] |
|
| 342 |
-- [API documentation][cloud-datastore-docs] |
|
| 343 |
-- [Go client documentation](https://godoc.org/cloud.google.com/go/datastore) |
|
| 344 |
-- [Complete sample program](https://github.com/GoogleCloudPlatform/golang-samples/tree/master/datastore/tasks) |
|
| 345 |
- |
|
| 346 |
-### Example Usage |
|
| 347 |
- |
|
| 348 |
-First create a `datastore.Client` to use throughout your application: |
|
| 349 |
- |
|
| 350 |
-[snip]:# (datastore-1) |
|
| 351 |
-```go |
|
| 352 |
-client, err := datastore.NewClient(ctx, "my-project-id") |
|
| 353 |
-if err != nil {
|
|
| 354 |
- log.Fatal(err) |
|
| 355 |
-} |
|
| 356 |
-``` |
|
| 357 |
- |
|
| 358 |
-Then use that client to interact with the API: |
|
| 359 |
- |
|
| 360 |
-[snip]:# (datastore-2) |
|
| 361 |
-```go |
|
| 362 |
-type Post struct {
|
|
| 363 |
- Title string |
|
| 364 |
- Body string `datastore:",noindex"` |
|
| 365 |
- PublishedAt time.Time |
|
| 366 |
-} |
|
| 367 |
-keys := []*datastore.Key{
|
|
| 368 |
- datastore.NameKey("Post", "post1", nil),
|
|
| 369 |
- datastore.NameKey("Post", "post2", nil),
|
|
| 370 |
-} |
|
| 371 |
-posts := []*Post{
|
|
| 372 |
- {Title: "Post 1", Body: "...", PublishedAt: time.Now()},
|
|
| 373 |
- {Title: "Post 2", Body: "...", PublishedAt: time.Now()},
|
|
| 374 |
-} |
|
| 375 |
-if _, err := client.PutMulti(ctx, keys, posts); err != nil {
|
|
| 376 |
- log.Fatal(err) |
|
| 377 |
-} |
|
| 378 |
-``` |
|
| 379 |
- |
|
| 380 |
-## Cloud Storage [](https://godoc.org/cloud.google.com/go/storage) |
|
| 381 |
- |
|
| 382 |
-- [About Cloud Storage][cloud-storage] |
|
| 383 |
-- [API documentation][cloud-storage-docs] |
|
| 384 |
-- [Go client documentation](https://godoc.org/cloud.google.com/go/storage) |
|
| 385 |
-- [Complete sample programs](https://github.com/GoogleCloudPlatform/golang-samples/tree/master/storage) |
|
| 386 |
- |
|
| 387 |
-### Example Usage |
|
| 388 |
- |
|
| 389 |
-First create a `storage.Client` to use throughout your application: |
|
| 390 |
- |
|
| 391 |
-[snip]:# (storage-1) |
|
| 392 |
-```go |
|
| 393 |
-client, err := storage.NewClient(ctx) |
|
| 394 |
-if err != nil {
|
|
| 395 |
- log.Fatal(err) |
|
| 396 |
-} |
|
| 397 |
-``` |
|
| 398 |
- |
|
| 399 |
-[snip]:# (storage-2) |
|
| 400 |
-```go |
|
| 401 |
-// Read the object1 from bucket. |
|
| 402 |
-rc, err := client.Bucket("bucket").Object("object1").NewReader(ctx)
|
|
| 403 |
-if err != nil {
|
|
| 404 |
- log.Fatal(err) |
|
| 405 |
-} |
|
| 406 |
-defer rc.Close() |
|
| 407 |
-body, err := ioutil.ReadAll(rc) |
|
| 408 |
-if err != nil {
|
|
| 409 |
- log.Fatal(err) |
|
| 410 |
-} |
|
| 411 |
-``` |
|
| 412 |
- |
|
| 413 |
-## Cloud Pub/Sub [](https://godoc.org/cloud.google.com/go/pubsub) |
|
| 414 |
- |
|
| 415 |
-- [About Cloud Pubsub][cloud-pubsub] |
|
| 416 |
-- [API documentation][cloud-pubsub-docs] |
|
| 417 |
-- [Go client documentation](https://godoc.org/cloud.google.com/go/pubsub) |
|
| 418 |
-- [Complete sample programs](https://github.com/GoogleCloudPlatform/golang-samples/tree/master/pubsub) |
|
| 419 |
- |
|
| 420 |
-### Example Usage |
|
| 421 |
- |
|
| 422 |
-First create a `pubsub.Client` to use throughout your application: |
|
| 423 |
- |
|
| 424 |
-[snip]:# (pubsub-1) |
|
| 425 |
-```go |
|
| 426 |
-client, err := pubsub.NewClient(ctx, "project-id") |
|
| 427 |
-if err != nil {
|
|
| 428 |
- log.Fatal(err) |
|
| 429 |
-} |
|
| 430 |
-``` |
|
| 431 |
- |
|
| 432 |
-Then use the client to publish and subscribe: |
|
| 433 |
- |
|
| 434 |
-[snip]:# (pubsub-2) |
|
| 435 |
-```go |
|
| 436 |
-// Publish "hello world" on topic1. |
|
| 437 |
-topic := client.Topic("topic1")
|
|
| 438 |
-res := topic.Publish(ctx, &pubsub.Message{
|
|
| 439 |
- Data: []byte("hello world"),
|
|
| 440 |
-}) |
|
| 441 |
-// The publish happens asynchronously. |
|
| 442 |
-// Later, you can get the result from res: |
|
| 443 |
-... |
|
| 444 |
-msgID, err := res.Get(ctx) |
|
| 445 |
-if err != nil {
|
|
| 446 |
- log.Fatal(err) |
|
| 447 |
-} |
|
| 448 |
- |
|
| 449 |
-// Use a callback to receive messages via subscription1. |
|
| 450 |
-sub := client.Subscription("subscription1")
|
|
| 451 |
-err = sub.Receive(ctx, func(ctx context.Context, m *pubsub.Message) {
|
|
| 452 |
- fmt.Println(m.Data) |
|
| 453 |
- m.Ack() // Acknowledge that we've consumed the message. |
|
| 454 |
-}) |
|
| 455 |
-if err != nil {
|
|
| 456 |
- log.Println(err) |
|
| 457 |
-} |
|
| 458 |
-``` |
|
| 459 |
- |
|
| 460 |
-## Cloud BigQuery [](https://godoc.org/cloud.google.com/go/bigquery) |
|
| 461 |
- |
|
| 462 |
-- [About Cloud BigQuery][cloud-bigquery] |
|
| 463 |
-- [API documentation][cloud-bigquery-docs] |
|
| 464 |
-- [Go client documentation][cloud-bigquery-ref] |
|
| 465 |
-- [Complete sample programs](https://github.com/GoogleCloudPlatform/golang-samples/tree/master/bigquery) |
|
| 466 |
- |
|
| 467 |
-### Example Usage |
|
| 468 |
- |
|
| 469 |
-First create a `bigquery.Client` to use throughout your application: |
|
| 470 |
-[snip]:# (bq-1) |
|
| 471 |
-```go |
|
| 472 |
-c, err := bigquery.NewClient(ctx, "my-project-ID") |
|
| 473 |
-if err != nil {
|
|
| 474 |
- // TODO: Handle error. |
|
| 475 |
-} |
|
| 476 |
-``` |
|
| 477 |
- |
|
| 478 |
-Then use that client to interact with the API: |
|
| 479 |
-[snip]:# (bq-2) |
|
| 480 |
-```go |
|
| 481 |
-// Construct a query. |
|
| 482 |
-q := c.Query(` |
|
| 483 |
- SELECT year, SUM(number) |
|
| 484 |
- FROM [bigquery-public-data:usa_names.usa_1910_2013] |
|
| 485 |
- WHERE name = "William" |
|
| 486 |
- GROUP BY year |
|
| 487 |
- ORDER BY year |
|
| 488 |
-`) |
|
| 489 |
-// Execute the query. |
|
| 490 |
-it, err := q.Read(ctx) |
|
| 491 |
-if err != nil {
|
|
| 492 |
- // TODO: Handle error. |
|
| 493 |
-} |
|
| 494 |
-// Iterate through the results. |
|
| 495 |
-for {
|
|
| 496 |
- var values []bigquery.Value |
|
| 497 |
- err := it.Next(&values) |
|
| 498 |
- if err == iterator.Done {
|
|
| 499 |
- break |
|
| 500 |
- } |
|
| 501 |
- if err != nil {
|
|
| 502 |
- // TODO: Handle error. |
|
| 503 |
- } |
|
| 504 |
- fmt.Println(values) |
|
| 505 |
-} |
|
| 506 |
-``` |
|
| 507 |
- |
|
| 508 |
- |
|
| 509 |
-## Stackdriver Logging [](https://godoc.org/cloud.google.com/go/logging) |
|
| 510 |
- |
|
| 511 |
-- [About Stackdriver Logging][cloud-logging] |
|
| 512 |
-- [API documentation][cloud-logging-docs] |
|
| 513 |
-- [Go client documentation][cloud-logging-ref] |
|
| 514 |
-- [Complete sample programs](https://github.com/GoogleCloudPlatform/golang-samples/tree/master/logging) |
|
| 515 |
- |
|
| 516 |
-### Example Usage |
|
| 517 |
- |
|
| 518 |
-First create a `logging.Client` to use throughout your application: |
|
| 519 |
-[snip]:# (logging-1) |
|
| 520 |
-```go |
|
| 521 |
-ctx := context.Background() |
|
| 522 |
-client, err := logging.NewClient(ctx, "my-project") |
|
| 523 |
-if err != nil {
|
|
| 524 |
- // TODO: Handle error. |
|
| 525 |
-} |
|
| 526 |
-``` |
|
| 527 |
- |
|
| 528 |
-Usually, you'll want to add log entries to a buffer to be periodically flushed |
|
| 529 |
-(automatically and asynchronously) to the Stackdriver Logging service. |
|
| 530 |
-[snip]:# (logging-2) |
|
| 531 |
-```go |
|
| 532 |
-logger := client.Logger("my-log")
|
|
| 533 |
-logger.Log(logging.Entry{Payload: "something happened!"})
|
|
| 534 |
-``` |
|
| 535 |
- |
|
| 536 |
-Close your client before your program exits, to flush any buffered log entries. |
|
| 537 |
-[snip]:# (logging-3) |
|
| 538 |
-```go |
|
| 539 |
-err = client.Close() |
|
| 540 |
-if err != nil {
|
|
| 541 |
- // TODO: Handle error. |
|
| 542 |
-} |
|
| 543 |
-``` |
|
| 544 |
- |
|
| 545 |
-## Cloud Spanner [](https://godoc.org/cloud.google.com/go/spanner) |
|
| 546 |
- |
|
| 547 |
-- [About Cloud Spanner][cloud-spanner] |
|
| 548 |
-- [API documentation][cloud-spanner-docs] |
|
| 549 |
-- [Go client documentation](https://godoc.org/cloud.google.com/go/spanner) |
|
| 550 |
- |
|
| 551 |
-### Example Usage |
|
| 552 |
- |
|
| 553 |
-First create a `spanner.Client` to use throughout your application: |
|
| 554 |
- |
|
| 555 |
-[snip]:# (spanner-1) |
|
| 556 |
-```go |
|
| 557 |
-client, err := spanner.NewClient(ctx, "projects/P/instances/I/databases/D") |
|
| 558 |
-if err != nil {
|
|
| 559 |
- log.Fatal(err) |
|
| 560 |
-} |
|
| 561 |
-``` |
|
| 562 |
- |
|
| 563 |
-[snip]:# (spanner-2) |
|
| 564 |
-```go |
|
| 565 |
-// Simple Reads And Writes |
|
| 566 |
-_, err = client.Apply(ctx, []*spanner.Mutation{
|
|
| 567 |
- spanner.Insert("Users",
|
|
| 568 |
- []string{"name", "email"},
|
|
| 569 |
- []interface{}{"alice", "a@example.com"})})
|
|
| 570 |
-if err != nil {
|
|
| 571 |
- log.Fatal(err) |
|
| 572 |
-} |
|
| 573 |
-row, err := client.Single().ReadRow(ctx, "Users", |
|
| 574 |
- spanner.Key{"alice"}, []string{"email"})
|
|
| 575 |
-if err != nil {
|
|
| 576 |
- log.Fatal(err) |
|
| 577 |
-} |
|
| 578 |
-``` |
|
| 579 |
- |
|
| 580 |
- |
|
| 581 | 338 |
## Contributing |
| 582 | 339 |
|
| 583 | 340 |
Contributions are welcome. Please, see the |
| ... | ... |
@@ -592,32 +123,23 @@ for more information. |
| 592 | 592 |
|
| 593 | 593 |
[cloud-datastore]: https://cloud.google.com/datastore/ |
| 594 | 594 |
[cloud-datastore-ref]: https://godoc.org/cloud.google.com/go/datastore |
| 595 |
-[cloud-datastore-docs]: https://cloud.google.com/datastore/docs |
|
| 596 |
-[cloud-datastore-activation]: https://cloud.google.com/datastore/docs/activate |
|
| 597 | 595 |
|
| 598 | 596 |
[cloud-firestore]: https://cloud.google.com/firestore/ |
| 599 | 597 |
[cloud-firestore-ref]: https://godoc.org/cloud.google.com/go/firestore |
| 600 |
-[cloud-firestore-docs]: https://cloud.google.com/firestore/docs |
|
| 601 |
-[cloud-firestore-activation]: https://cloud.google.com/firestore/docs/activate |
|
| 602 | 598 |
|
| 603 | 599 |
[cloud-pubsub]: https://cloud.google.com/pubsub/ |
| 604 | 600 |
[cloud-pubsub-ref]: https://godoc.org/cloud.google.com/go/pubsub |
| 605 |
-[cloud-pubsub-docs]: https://cloud.google.com/pubsub/docs |
|
| 606 | 601 |
|
| 607 | 602 |
[cloud-storage]: https://cloud.google.com/storage/ |
| 608 | 603 |
[cloud-storage-ref]: https://godoc.org/cloud.google.com/go/storage |
| 609 |
-[cloud-storage-docs]: https://cloud.google.com/storage/docs |
|
| 610 |
-[cloud-storage-create-bucket]: https://cloud.google.com/storage/docs/cloud-console#_creatingbuckets |
|
| 611 | 604 |
|
| 612 | 605 |
[cloud-bigtable]: https://cloud.google.com/bigtable/ |
| 613 | 606 |
[cloud-bigtable-ref]: https://godoc.org/cloud.google.com/go/bigtable |
| 614 | 607 |
|
| 615 | 608 |
[cloud-bigquery]: https://cloud.google.com/bigquery/ |
| 616 |
-[cloud-bigquery-docs]: https://cloud.google.com/bigquery/docs |
|
| 617 | 609 |
[cloud-bigquery-ref]: https://godoc.org/cloud.google.com/go/bigquery |
| 618 | 610 |
|
| 619 | 611 |
[cloud-logging]: https://cloud.google.com/logging/ |
| 620 |
-[cloud-logging-docs]: https://cloud.google.com/logging/docs |
|
| 621 | 612 |
[cloud-logging-ref]: https://godoc.org/cloud.google.com/go/logging |
| 622 | 613 |
|
| 623 | 614 |
[cloud-monitoring]: https://cloud.google.com/monitoring/ |
| ... | ... |
@@ -630,17 +152,16 @@ for more information. |
| 630 | 630 |
[cloud-language-ref]: https://godoc.org/cloud.google.com/go/language/apiv1 |
| 631 | 631 |
|
| 632 | 632 |
[cloud-oslogin]: https://cloud.google.com/compute/docs/oslogin/rest |
| 633 |
-[cloud-oslogin-ref]: https://cloud.google.com/compute/docs/oslogin/rest |
|
| 633 |
+[cloud-oslogin-ref]: https://cloud.google.com/go/oslogin/apiv1 |
|
| 634 | 634 |
|
| 635 | 635 |
[cloud-speech]: https://cloud.google.com/speech |
| 636 | 636 |
[cloud-speech-ref]: https://godoc.org/cloud.google.com/go/speech/apiv1 |
| 637 | 637 |
|
| 638 | 638 |
[cloud-spanner]: https://cloud.google.com/spanner/ |
| 639 | 639 |
[cloud-spanner-ref]: https://godoc.org/cloud.google.com/go/spanner |
| 640 |
-[cloud-spanner-docs]: https://cloud.google.com/spanner/docs |
|
| 641 | 640 |
|
| 642 |
-[cloud-translation]: https://cloud.google.com/translation |
|
| 643 |
-[cloud-translation-ref]: https://godoc.org/cloud.google.com/go/translation |
|
| 641 |
+[cloud-translate]: https://cloud.google.com/translate |
|
| 642 |
+[cloud-translate-ref]: https://godoc.org/cloud.google.com/go/translate |
|
| 644 | 643 |
|
| 645 | 644 |
[cloud-video]: https://cloud.google.com/video-intelligence/ |
| 646 | 645 |
[cloud-video-ref]: https://godoc.org/cloud.google.com/go/videointelligence/apiv1beta1 |
| ... | ... |
@@ -657,4 +178,50 @@ for more information. |
| 657 | 657 |
[cloud-dlp]: https://cloud.google.com/dlp/ |
| 658 | 658 |
[cloud-dlp-ref]: https://godoc.org/cloud.google.com/go/dlp/apiv2beta1 |
| 659 | 659 |
|
| 660 |
-[default-creds]: https://developers.google.com/identity/protocols/application-default-credentials |
|
| 660 |
+[cloud-dataproc]: https://cloud.google.com/dataproc/ |
|
| 661 |
+[cloud-dataproc-ref]: https://godoc.org/cloud.google.com/go/dataproc/apiv1 |
|
| 662 |
+ |
|
| 663 |
+[cloud-iam]: https://cloud.google.com/iam/ |
|
| 664 |
+[cloud-iam-ref]: https://godoc.org/cloud.google.com/go/iam |
|
| 665 |
+ |
|
| 666 |
+[cloud-kms]: https://cloud.google.com/kms/ |
|
| 667 |
+[cloud-kms-ref]: https://godoc.org/cloud.google.com/go/kms/apiv1 |
|
| 668 |
+ |
|
| 669 |
+[cloud-natural-language]: https://cloud.google.com/natural-language/ |
|
| 670 |
+[cloud-natural-language-ref]: https://godoc.org/cloud.google.com/go/language/apiv1 |
|
| 671 |
+ |
|
| 672 |
+[cloud-memorystore]: https://cloud.google.com/memorystore/ |
|
| 673 |
+[cloud-memorystore-ref]: https://godoc.org/cloud.google.com/go/redis/apiv1 |
|
| 674 |
+ |
|
| 675 |
+[cloud-texttospeech]: https://cloud.google.com/texttospeech/ |
|
| 676 |
+[cloud-texttospeech-ref]: https://godoc.org/cloud.google.com/go/texttospeech/apiv1 |
|
| 677 |
+ |
|
| 678 |
+[cloud-trace]: https://cloud.google.com/trace/ |
|
| 679 |
+[cloud-trace-ref]: https://godoc.org/cloud.google.com/go/trace/apiv2 |
|
| 680 |
+ |
|
| 681 |
+[cloud-dialogflow]: https://cloud.google.com/dialogflow-enterprise/ |
|
| 682 |
+[cloud-dialogflow-ref]: https://godoc.org/cloud.google.com/go/dialogflow/apiv2 |
|
| 683 |
+ |
|
| 684 |
+[cloud-containeranalysis]: https://cloud.google.com/container-registry/docs/container-analysis |
|
| 685 |
+[cloud-containeranalysis-ref]: https://godoc.org/cloud.google.com/go/devtools/containeranalysis/apiv1beta1 |
|
| 686 |
+ |
|
| 687 |
+[cloud-asset]: https://cloud.google.com/security-command-center/docs/how-to-asset-inventory |
|
| 688 |
+[cloud-asset-ref]: https://godoc.org/cloud.google.com/go/asset/apiv1 |
|
| 689 |
+ |
|
| 690 |
+[cloud-tasks]: https://cloud.google.com/tasks/ |
|
| 691 |
+[cloud-tasks-ref]: https://godoc.org/cloud.google.com/go/cloudtasks/apiv2 |
|
| 692 |
+ |
|
| 693 |
+[cloud-scheduler]: https://cloud.google.com/scheduler |
|
| 694 |
+[cloud-scheduler-ref]: https://godoc.org/cloud.google.com/go/scheduler/apiv1 |
|
| 695 |
+ |
|
| 696 |
+[cloud-iot]: https://cloud.google.com/iot-core/ |
|
| 697 |
+[cloud-iot-ref]: https://godoc.org/cloud.google.com/go/iot/apiv1 |
|
| 698 |
+ |
|
| 699 |
+[cloud-phishingprotection]: https://cloud.google.com/phishing-protection/ |
|
| 700 |
+[cloud-phishingprotection-ref]: https://cloud.google.com/go/phishingprotection/apiv1beta1 |
|
| 701 |
+ |
|
| 702 |
+[cloud-recaptcha]: https://cloud.google.com/recaptcha-enterprise/ |
|
| 703 |
+[cloud-recaptcha-ref]: https://cloud.google.com/go/recaptchaenterprise/apiv1beta1 |
|
| 704 |
+ |
|
| 705 |
+[cloud-talent]: https://cloud.google.com/solutions/talent-solution/ |
|
| 706 |
+[cloud-talent-ref]: https://godoc.org/cloud.google.com/go/talent/apiv4beta1 |
| 661 | 707 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,100 @@ |
| 0 |
+// Copyright 2014 Google LLC |
|
| 1 |
+// |
|
| 2 |
+// Licensed under the Apache License, Version 2.0 (the "License"); |
|
| 3 |
+// you may not use this file except in compliance with the License. |
|
| 4 |
+// You may obtain a copy of the License at |
|
| 5 |
+// |
|
| 6 |
+// http://www.apache.org/licenses/LICENSE-2.0 |
|
| 7 |
+// |
|
| 8 |
+// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
+// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
+// See the License for the specific language governing permissions and |
|
| 12 |
+// limitations under the License. |
|
| 13 |
+ |
|
| 14 |
+/* |
|
| 15 |
+Package cloud is the root of the packages used to access Google Cloud |
|
| 16 |
+Services. See https://godoc.org/cloud.google.com/go for a full list |
|
| 17 |
+of sub-packages. |
|
| 18 |
+ |
|
| 19 |
+ |
|
| 20 |
+Client Options |
|
| 21 |
+ |
|
| 22 |
+All clients in sub-packages are configurable via client options. These options are |
|
| 23 |
+described here: https://godoc.org/google.golang.org/api/option. |
|
| 24 |
+ |
|
| 25 |
+ |
|
| 26 |
+Authentication and Authorization |
|
| 27 |
+ |
|
| 28 |
+All the clients in sub-packages support authentication via Google Application Default |
|
| 29 |
+Credentials (see https://cloud.google.com/docs/authentication/production), or |
|
| 30 |
+by providing a JSON key file for a Service Account. See the authentication examples |
|
| 31 |
+in this package for details. |
|
| 32 |
+ |
|
| 33 |
+ |
|
| 34 |
+Timeouts and Cancellation |
|
| 35 |
+ |
|
| 36 |
+By default, all requests in sub-packages will run indefinitely, retrying on transient |
|
| 37 |
+errors when correctness allows. To set timeouts or arrange for cancellation, use |
|
| 38 |
+contexts. See the examples for details. |
|
| 39 |
+ |
|
| 40 |
+Do not attempt to control the initial connection (dialing) of a service by setting a |
|
| 41 |
+timeout on the context passed to NewClient. Dialing is non-blocking, so timeouts |
|
| 42 |
+would be ineffective and would only interfere with credential refreshing, which uses |
|
| 43 |
+the same context. |
|
| 44 |
+ |
|
| 45 |
+ |
|
| 46 |
+Connection Pooling |
|
| 47 |
+ |
|
| 48 |
+Connection pooling differs in clients based on their transport. Cloud |
|
| 49 |
+clients either rely on HTTP or gRPC transports to communicate |
|
| 50 |
+with Google Cloud. |
|
| 51 |
+ |
|
| 52 |
+Cloud clients that use HTTP (bigquery, compute, storage, and translate) rely on the |
|
| 53 |
+underlying HTTP transport to cache connections for later re-use. These are cached to |
|
| 54 |
+the default http.MaxIdleConns and http.MaxIdleConnsPerHost settings in |
|
| 55 |
+http.DefaultTransport. |
|
| 56 |
+ |
|
| 57 |
+For gRPC clients (all others in this repo), connection pooling is configurable. Users |
|
| 58 |
+of cloud client libraries may specify option.WithGRPCConnectionPool(n) as a client |
|
| 59 |
+option to NewClient calls. This configures the underlying gRPC connections to be |
|
| 60 |
+pooled and addressed in a round robin fashion. |
|
| 61 |
+ |
|
| 62 |
+ |
|
| 63 |
+Using the Libraries with Docker |
|
| 64 |
+ |
|
| 65 |
+Minimal docker images like Alpine lack CA certificates. This causes RPCs to appear to |
|
| 66 |
+hang, because gRPC retries indefinitely. See https://github.com/googleapis/google-cloud-go/issues/928 |
|
| 67 |
+for more information. |
|
| 68 |
+ |
|
| 69 |
+ |
|
| 70 |
+Debugging |
|
| 71 |
+ |
|
| 72 |
+To see gRPC logs, set the environment variable GRPC_GO_LOG_SEVERITY_LEVEL. See |
|
| 73 |
+https://godoc.org/google.golang.org/grpc/grpclog for more information. |
|
| 74 |
+ |
|
| 75 |
+For HTTP logging, set the GODEBUG environment variable to "http2debug=1" or "http2debug=2". |
|
| 76 |
+ |
|
| 77 |
+ |
|
| 78 |
+Client Stability |
|
| 79 |
+ |
|
| 80 |
+Clients in this repository are considered alpha or beta unless otherwise |
|
| 81 |
+marked as stable in the README.md. Semver is not used to communicate stability |
|
| 82 |
+of clients. |
|
| 83 |
+ |
|
| 84 |
+Alpha and beta clients may change or go away without notice. |
|
| 85 |
+ |
|
| 86 |
+Clients marked stable will maintain compatibility with future versions for as |
|
| 87 |
+long as we can reasonably sustain. Incompatible changes might be made in some |
|
| 88 |
+situations, including: |
|
| 89 |
+ |
|
| 90 |
+- Security bugs may prompt backwards-incompatible changes. |
|
| 91 |
+ |
|
| 92 |
+- Situations in which components are no longer feasible to maintain without |
|
| 93 |
+making breaking changes, including removal. |
|
| 94 |
+ |
|
| 95 |
+- Parts of the client surface may be outright unstable and subject to change. |
|
| 96 |
+These parts of the surface will be labeled with the note, "It is EXPERIMENTAL |
|
| 97 |
+and subject to change or removal without notice." |
|
| 98 |
+*/ |
|
| 99 |
+package cloud // import "cloud.google.com/go" |
| 0 | 9 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,472 @@ |
| 0 |
+// Copyright 2018 Google Inc. All Rights Reserved. |
|
| 1 |
+// |
|
| 2 |
+// Licensed under the Apache License, Version 2.0 (the "License"); |
|
| 3 |
+// you may not use this file except in compliance with the License. |
|
| 4 |
+// You may obtain a copy of the License at |
|
| 5 |
+// |
|
| 6 |
+// http://www.apache.org/licenses/LICENSE-2.0 |
|
| 7 |
+// |
|
| 8 |
+// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
+// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
+// See the License for the specific language governing permissions and |
|
| 12 |
+// limitations under the License. |
|
| 13 |
+ |
|
| 14 |
+/* |
|
| 15 |
+ * Line tables |
|
| 16 |
+ */ |
|
| 17 |
+ |
|
| 18 |
+package gosym |
|
| 19 |
+ |
|
| 20 |
+import ( |
|
| 21 |
+ "encoding/binary" |
|
| 22 |
+ "sync" |
|
| 23 |
+) |
|
| 24 |
+ |
|
| 25 |
+// A LineTable is a data structure mapping program counters to line numbers. |
|
| 26 |
+// |
|
| 27 |
+// In Go 1.1 and earlier, each function (represented by a Func) had its own LineTable, |
|
| 28 |
+// and the line number corresponded to a numbering of all source lines in the |
|
| 29 |
+// program, across all files. That absolute line number would then have to be |
|
| 30 |
+// converted separately to a file name and line number within the file. |
|
| 31 |
+// |
|
| 32 |
+// In Go 1.2, the format of the data changed so that there is a single LineTable |
|
| 33 |
+// for the entire program, shared by all Funcs, and there are no absolute line |
|
| 34 |
+// numbers, just line numbers within specific files. |
|
| 35 |
+// |
|
| 36 |
+// For the most part, LineTable's methods should be treated as an internal |
|
| 37 |
+// detail of the package; callers should use the methods on Table instead. |
|
| 38 |
+type LineTable struct {
|
|
| 39 |
+ Data []byte |
|
| 40 |
+ PC uint64 |
|
| 41 |
+ Line int |
|
| 42 |
+ |
|
| 43 |
+ // Go 1.2 state |
|
| 44 |
+ mu sync.Mutex |
|
| 45 |
+ go12 int // is this in Go 1.2 format? -1 no, 0 unknown, 1 yes |
|
| 46 |
+ binary binary.ByteOrder |
|
| 47 |
+ quantum uint32 |
|
| 48 |
+ ptrsize uint32 |
|
| 49 |
+ functab []byte |
|
| 50 |
+ nfunctab uint32 |
|
| 51 |
+ filetab []byte |
|
| 52 |
+ nfiletab uint32 |
|
| 53 |
+ fileMap map[string]uint32 |
|
| 54 |
+} |
|
| 55 |
+ |
|
| 56 |
+// NOTE(rsc): This is wrong for GOARCH=arm, which uses a quantum of 4, |
|
| 57 |
+// but we have no idea whether we're using arm or not. This only |
|
| 58 |
+// matters in the old (pre-Go 1.2) symbol table format, so it's not worth |
|
| 59 |
+// fixing. |
|
| 60 |
+const oldQuantum = 1 |
|
| 61 |
+ |
|
| 62 |
+func (t *LineTable) parse(targetPC uint64, targetLine int) (b []byte, pc uint64, line int) {
|
|
| 63 |
+ // The PC/line table can be thought of as a sequence of |
|
| 64 |
+ // <pc update>* <line update> |
|
| 65 |
+ // batches. Each update batch results in a (pc, line) pair, |
|
| 66 |
+ // where line applies to every PC from pc up to but not |
|
| 67 |
+ // including the pc of the next pair. |
|
| 68 |
+ // |
|
| 69 |
+ // Here we process each update individually, which simplifies |
|
| 70 |
+ // the code, but makes the corner cases more confusing. |
|
| 71 |
+ b, pc, line = t.Data, t.PC, t.Line |
|
| 72 |
+ for pc <= targetPC && line != targetLine && len(b) > 0 {
|
|
| 73 |
+ code := b[0] |
|
| 74 |
+ b = b[1:] |
|
| 75 |
+ switch {
|
|
| 76 |
+ case code == 0: |
|
| 77 |
+ if len(b) < 4 {
|
|
| 78 |
+ b = b[0:0] |
|
| 79 |
+ break |
|
| 80 |
+ } |
|
| 81 |
+ val := binary.BigEndian.Uint32(b) |
|
| 82 |
+ b = b[4:] |
|
| 83 |
+ line += int(val) |
|
| 84 |
+ case code <= 64: |
|
| 85 |
+ line += int(code) |
|
| 86 |
+ case code <= 128: |
|
| 87 |
+ line -= int(code - 64) |
|
| 88 |
+ default: |
|
| 89 |
+ pc += oldQuantum * uint64(code-128) |
|
| 90 |
+ continue |
|
| 91 |
+ } |
|
| 92 |
+ pc += oldQuantum |
|
| 93 |
+ } |
|
| 94 |
+ return b, pc, line |
|
| 95 |
+} |
|
| 96 |
+ |
|
| 97 |
+func (t *LineTable) slice(pc uint64) *LineTable {
|
|
| 98 |
+ data, pc, line := t.parse(pc, -1) |
|
| 99 |
+ return &LineTable{Data: data, PC: pc, Line: line}
|
|
| 100 |
+} |
|
| 101 |
+ |
|
| 102 |
+// PCToLine returns the line number for the given program counter. |
|
| 103 |
+// Callers should use Table's PCToLine method instead. |
|
| 104 |
+func (t *LineTable) PCToLine(pc uint64) int {
|
|
| 105 |
+ if t.isGo12() {
|
|
| 106 |
+ return t.go12PCToLine(pc) |
|
| 107 |
+ } |
|
| 108 |
+ _, _, line := t.parse(pc, -1) |
|
| 109 |
+ return line |
|
| 110 |
+} |
|
| 111 |
+ |
|
| 112 |
+// LineToPC returns the program counter for the given line number, |
|
| 113 |
+// considering only program counters before maxpc. |
|
| 114 |
+// Callers should use Table's LineToPC method instead. |
|
| 115 |
+func (t *LineTable) LineToPC(line int, maxpc uint64) uint64 {
|
|
| 116 |
+ if t.isGo12() {
|
|
| 117 |
+ return 0 |
|
| 118 |
+ } |
|
| 119 |
+ _, pc, line1 := t.parse(maxpc, line) |
|
| 120 |
+ if line1 != line {
|
|
| 121 |
+ return 0 |
|
| 122 |
+ } |
|
| 123 |
+ // Subtract quantum from PC to account for post-line increment |
|
| 124 |
+ return pc - oldQuantum |
|
| 125 |
+} |
|
| 126 |
+ |
|
| 127 |
+// NewLineTable returns a new PC/line table |
|
| 128 |
+// corresponding to the encoded data. |
|
| 129 |
+// Text must be the start address of the |
|
| 130 |
+// corresponding text segment. |
|
| 131 |
+func NewLineTable(data []byte, text uint64) *LineTable {
|
|
| 132 |
+ return &LineTable{Data: data, PC: text, Line: 0}
|
|
| 133 |
+} |
|
| 134 |
+ |
|
| 135 |
+// Go 1.2 symbol table format. |
|
| 136 |
+// See golang.org/s/go12symtab. |
|
| 137 |
+// |
|
| 138 |
+// A general note about the methods here: rather than try to avoid |
|
| 139 |
+// index out of bounds errors, we trust Go to detect them, and then |
|
| 140 |
+// we recover from the panics and treat them as indicative of a malformed |
|
| 141 |
+// or incomplete table. |
|
| 142 |
+// |
|
| 143 |
+// The methods called by symtab.go, which begin with "go12" prefixes, |
|
| 144 |
+// are expected to have that recovery logic. |
|
| 145 |
+ |
|
| 146 |
+// isGo12 reports whether this is a Go 1.2 (or later) symbol table. |
|
| 147 |
+func (t *LineTable) isGo12() bool {
|
|
| 148 |
+ t.go12Init() |
|
| 149 |
+ return t.go12 == 1 |
|
| 150 |
+} |
|
| 151 |
+ |
|
| 152 |
+const go12magic = 0xfffffffb |
|
| 153 |
+ |
|
| 154 |
+// uintptr returns the pointer-sized value encoded at b. |
|
| 155 |
+// The pointer size is dictated by the table being read. |
|
| 156 |
+func (t *LineTable) uintptr(b []byte) uint64 {
|
|
| 157 |
+ if t.ptrsize == 4 {
|
|
| 158 |
+ return uint64(t.binary.Uint32(b)) |
|
| 159 |
+ } |
|
| 160 |
+ return t.binary.Uint64(b) |
|
| 161 |
+} |
|
| 162 |
+ |
|
| 163 |
+// go12init initializes the Go 1.2 metadata if t is a Go 1.2 symbol table. |
|
| 164 |
+func (t *LineTable) go12Init() {
|
|
| 165 |
+ t.mu.Lock() |
|
| 166 |
+ defer t.mu.Unlock() |
|
| 167 |
+ if t.go12 != 0 {
|
|
| 168 |
+ return |
|
| 169 |
+ } |
|
| 170 |
+ |
|
| 171 |
+ defer func() {
|
|
| 172 |
+ // If we panic parsing, assume it's not a Go 1.2 symbol table. |
|
| 173 |
+ recover() |
|
| 174 |
+ }() |
|
| 175 |
+ |
|
| 176 |
+ // Check header: 4-byte magic, two zeros, pc quantum, pointer size. |
|
| 177 |
+ t.go12 = -1 // not Go 1.2 until proven otherwise |
|
| 178 |
+ if len(t.Data) < 16 || t.Data[4] != 0 || t.Data[5] != 0 || |
|
| 179 |
+ (t.Data[6] != 1 && t.Data[6] != 4) || // pc quantum |
|
| 180 |
+ (t.Data[7] != 4 && t.Data[7] != 8) { // pointer size
|
|
| 181 |
+ return |
|
| 182 |
+ } |
|
| 183 |
+ |
|
| 184 |
+ switch uint32(go12magic) {
|
|
| 185 |
+ case binary.LittleEndian.Uint32(t.Data): |
|
| 186 |
+ t.binary = binary.LittleEndian |
|
| 187 |
+ case binary.BigEndian.Uint32(t.Data): |
|
| 188 |
+ t.binary = binary.BigEndian |
|
| 189 |
+ default: |
|
| 190 |
+ return |
|
| 191 |
+ } |
|
| 192 |
+ |
|
| 193 |
+ t.quantum = uint32(t.Data[6]) |
|
| 194 |
+ t.ptrsize = uint32(t.Data[7]) |
|
| 195 |
+ |
|
| 196 |
+ t.nfunctab = uint32(t.uintptr(t.Data[8:])) |
|
| 197 |
+ t.functab = t.Data[8+t.ptrsize:] |
|
| 198 |
+ functabsize := t.nfunctab*2*t.ptrsize + t.ptrsize |
|
| 199 |
+ fileoff := t.binary.Uint32(t.functab[functabsize:]) |
|
| 200 |
+ t.functab = t.functab[:functabsize] |
|
| 201 |
+ t.filetab = t.Data[fileoff:] |
|
| 202 |
+ t.nfiletab = t.binary.Uint32(t.filetab) |
|
| 203 |
+ t.filetab = t.filetab[:t.nfiletab*4] |
|
| 204 |
+ |
|
| 205 |
+ t.go12 = 1 // so far so good |
|
| 206 |
+} |
|
| 207 |
+ |
|
| 208 |
+// go12Funcs returns a slice of Funcs derived from the Go 1.2 pcln table. |
|
| 209 |
+func (t *LineTable) go12Funcs() []Func {
|
|
| 210 |
+ // Assume it is malformed and return nil on error. |
|
| 211 |
+ defer func() {
|
|
| 212 |
+ recover() |
|
| 213 |
+ }() |
|
| 214 |
+ |
|
| 215 |
+ n := len(t.functab) / int(t.ptrsize) / 2 |
|
| 216 |
+ funcs := make([]Func, n) |
|
| 217 |
+ for i := range funcs {
|
|
| 218 |
+ f := &funcs[i] |
|
| 219 |
+ f.Entry = uint64(t.uintptr(t.functab[2*i*int(t.ptrsize):])) |
|
| 220 |
+ f.End = uint64(t.uintptr(t.functab[(2*i+2)*int(t.ptrsize):])) |
|
| 221 |
+ info := t.Data[t.uintptr(t.functab[(2*i+1)*int(t.ptrsize):]):] |
|
| 222 |
+ f.LineTable = t |
|
| 223 |
+ f.FrameSize = int(t.binary.Uint32(info[t.ptrsize+2*4:])) |
|
| 224 |
+ f.Sym = &Sym{
|
|
| 225 |
+ Value: f.Entry, |
|
| 226 |
+ Type: 'T', |
|
| 227 |
+ Name: t.string(t.binary.Uint32(info[t.ptrsize:])), |
|
| 228 |
+ GoType: 0, |
|
| 229 |
+ Func: f, |
|
| 230 |
+ } |
|
| 231 |
+ } |
|
| 232 |
+ return funcs |
|
| 233 |
+} |
|
| 234 |
+ |
|
| 235 |
+// findFunc returns the func corresponding to the given program counter. |
|
| 236 |
+func (t *LineTable) findFunc(pc uint64) []byte {
|
|
| 237 |
+ if pc < t.uintptr(t.functab) || pc >= t.uintptr(t.functab[len(t.functab)-int(t.ptrsize):]) {
|
|
| 238 |
+ return nil |
|
| 239 |
+ } |
|
| 240 |
+ |
|
| 241 |
+ // The function table is a list of 2*nfunctab+1 uintptrs, |
|
| 242 |
+ // alternating program counters and offsets to func structures. |
|
| 243 |
+ f := t.functab |
|
| 244 |
+ nf := t.nfunctab |
|
| 245 |
+ for nf > 0 {
|
|
| 246 |
+ m := nf / 2 |
|
| 247 |
+ fm := f[2*t.ptrsize*m:] |
|
| 248 |
+ if t.uintptr(fm) <= pc && pc < t.uintptr(fm[2*t.ptrsize:]) {
|
|
| 249 |
+ return t.Data[t.uintptr(fm[t.ptrsize:]):] |
|
| 250 |
+ } else if pc < t.uintptr(fm) {
|
|
| 251 |
+ nf = m |
|
| 252 |
+ } else {
|
|
| 253 |
+ f = f[(m+1)*2*t.ptrsize:] |
|
| 254 |
+ nf -= m + 1 |
|
| 255 |
+ } |
|
| 256 |
+ } |
|
| 257 |
+ return nil |
|
| 258 |
+} |
|
| 259 |
+ |
|
| 260 |
+// readvarint reads, removes, and returns a varint from *pp. |
|
| 261 |
+func (t *LineTable) readvarint(pp *[]byte) uint32 {
|
|
| 262 |
+ var v, shift uint32 |
|
| 263 |
+ p := *pp |
|
| 264 |
+ for shift = 0; ; shift += 7 {
|
|
| 265 |
+ b := p[0] |
|
| 266 |
+ p = p[1:] |
|
| 267 |
+ v |= (uint32(b) & 0x7F) << shift |
|
| 268 |
+ if b&0x80 == 0 {
|
|
| 269 |
+ break |
|
| 270 |
+ } |
|
| 271 |
+ } |
|
| 272 |
+ *pp = p |
|
| 273 |
+ return v |
|
| 274 |
+} |
|
| 275 |
+ |
|
| 276 |
+// string returns a Go string found at off. |
|
| 277 |
+func (t *LineTable) string(off uint32) string {
|
|
| 278 |
+ for i := off; ; i++ {
|
|
| 279 |
+ if t.Data[i] == 0 {
|
|
| 280 |
+ return string(t.Data[off:i]) |
|
| 281 |
+ } |
|
| 282 |
+ } |
|
| 283 |
+} |
|
| 284 |
+ |
|
| 285 |
+// step advances to the next pc, value pair in the encoded table. |
|
| 286 |
+func (t *LineTable) step(p *[]byte, pc *uint64, val *int32, first bool) bool {
|
|
| 287 |
+ uvdelta := t.readvarint(p) |
|
| 288 |
+ if uvdelta == 0 && !first {
|
|
| 289 |
+ return false |
|
| 290 |
+ } |
|
| 291 |
+ if uvdelta&1 != 0 {
|
|
| 292 |
+ uvdelta = ^(uvdelta >> 1) |
|
| 293 |
+ } else {
|
|
| 294 |
+ uvdelta >>= 1 |
|
| 295 |
+ } |
|
| 296 |
+ vdelta := int32(uvdelta) |
|
| 297 |
+ pcdelta := t.readvarint(p) * t.quantum |
|
| 298 |
+ *pc += uint64(pcdelta) |
|
| 299 |
+ *val += vdelta |
|
| 300 |
+ return true |
|
| 301 |
+} |
|
| 302 |
+ |
|
| 303 |
+// pcvalue reports the value associated with the target pc. |
|
| 304 |
+// off is the offset to the beginning of the pc-value table, |
|
| 305 |
+// and entry is the start PC for the corresponding function. |
|
| 306 |
+func (t *LineTable) pcvalue(off uint32, entry, targetpc uint64) int32 {
|
|
| 307 |
+ if off == 0 {
|
|
| 308 |
+ return -1 |
|
| 309 |
+ } |
|
| 310 |
+ p := t.Data[off:] |
|
| 311 |
+ |
|
| 312 |
+ val := int32(-1) |
|
| 313 |
+ pc := entry |
|
| 314 |
+ for t.step(&p, &pc, &val, pc == entry) {
|
|
| 315 |
+ if targetpc < pc {
|
|
| 316 |
+ return val |
|
| 317 |
+ } |
|
| 318 |
+ } |
|
| 319 |
+ return -1 |
|
| 320 |
+} |
|
| 321 |
+ |
|
| 322 |
+// findFileLine scans one function in the binary looking for a |
|
| 323 |
+// program counter in the given file on the given line. |
|
| 324 |
+// It does so by running the pc-value tables mapping program counter |
|
| 325 |
+// to file number. Since most functions come from a single file, these |
|
| 326 |
+// are usually short and quick to scan. If a file match is found, then the |
|
| 327 |
+// code goes to the expense of looking for a simultaneous line number match. |
|
| 328 |
+func (t *LineTable) findFileLine(entry uint64, filetab, linetab uint32, filenum, line int32) uint64 {
|
|
| 329 |
+ if filetab == 0 || linetab == 0 {
|
|
| 330 |
+ return 0 |
|
| 331 |
+ } |
|
| 332 |
+ |
|
| 333 |
+ fp := t.Data[filetab:] |
|
| 334 |
+ fl := t.Data[linetab:] |
|
| 335 |
+ fileVal := int32(-1) |
|
| 336 |
+ filePC := entry |
|
| 337 |
+ lineVal := int32(-1) |
|
| 338 |
+ linePC := entry |
|
| 339 |
+ fileStartPC := filePC |
|
| 340 |
+ for t.step(&fp, &filePC, &fileVal, filePC == entry) {
|
|
| 341 |
+ if fileVal == filenum && fileStartPC < filePC {
|
|
| 342 |
+ // fileVal is in effect starting at fileStartPC up to |
|
| 343 |
+ // but not including filePC, and it's the file we want. |
|
| 344 |
+ // Run the PC table looking for a matching line number |
|
| 345 |
+ // or until we reach filePC. |
|
| 346 |
+ lineStartPC := linePC |
|
| 347 |
+ for linePC < filePC && t.step(&fl, &linePC, &lineVal, linePC == entry) {
|
|
| 348 |
+ // lineVal is in effect until linePC, and lineStartPC < filePC. |
|
| 349 |
+ if lineVal == line {
|
|
| 350 |
+ if fileStartPC <= lineStartPC {
|
|
| 351 |
+ return lineStartPC |
|
| 352 |
+ } |
|
| 353 |
+ if fileStartPC < linePC {
|
|
| 354 |
+ return fileStartPC |
|
| 355 |
+ } |
|
| 356 |
+ } |
|
| 357 |
+ lineStartPC = linePC |
|
| 358 |
+ } |
|
| 359 |
+ } |
|
| 360 |
+ fileStartPC = filePC |
|
| 361 |
+ } |
|
| 362 |
+ return 0 |
|
| 363 |
+} |
|
| 364 |
+ |
|
| 365 |
+// go12PCToLine maps program counter to line number for the Go 1.2 pcln table. |
|
| 366 |
+func (t *LineTable) go12PCToLine(pc uint64) (line int) {
|
|
| 367 |
+ return t.go12PCToVal(pc, t.ptrsize+5*4) |
|
| 368 |
+} |
|
| 369 |
+ |
|
| 370 |
+// go12PCToSPAdj maps program counter to Stack Pointer adjustment for the Go 1.2 pcln table. |
|
| 371 |
+func (t *LineTable) go12PCToSPAdj(pc uint64) (spadj int) {
|
|
| 372 |
+ return t.go12PCToVal(pc, t.ptrsize+3*4) |
|
| 373 |
+} |
|
| 374 |
+ |
|
| 375 |
+func (t *LineTable) go12PCToVal(pc uint64, fOffset uint32) (val int) {
|
|
| 376 |
+ defer func() {
|
|
| 377 |
+ if recover() != nil {
|
|
| 378 |
+ val = -1 |
|
| 379 |
+ } |
|
| 380 |
+ }() |
|
| 381 |
+ |
|
| 382 |
+ f := t.findFunc(pc) |
|
| 383 |
+ if f == nil {
|
|
| 384 |
+ return -1 |
|
| 385 |
+ } |
|
| 386 |
+ entry := t.uintptr(f) |
|
| 387 |
+ linetab := t.binary.Uint32(f[fOffset:]) |
|
| 388 |
+ return int(t.pcvalue(linetab, entry, pc)) |
|
| 389 |
+} |
|
| 390 |
+ |
|
| 391 |
+// go12PCToFile maps program counter to file name for the Go 1.2 pcln table. |
|
| 392 |
+func (t *LineTable) go12PCToFile(pc uint64) (file string) {
|
|
| 393 |
+ defer func() {
|
|
| 394 |
+ if recover() != nil {
|
|
| 395 |
+ file = "" |
|
| 396 |
+ } |
|
| 397 |
+ }() |
|
| 398 |
+ |
|
| 399 |
+ f := t.findFunc(pc) |
|
| 400 |
+ if f == nil {
|
|
| 401 |
+ return "" |
|
| 402 |
+ } |
|
| 403 |
+ entry := t.uintptr(f) |
|
| 404 |
+ filetab := t.binary.Uint32(f[t.ptrsize+4*4:]) |
|
| 405 |
+ fno := t.pcvalue(filetab, entry, pc) |
|
| 406 |
+ if fno <= 0 {
|
|
| 407 |
+ return "" |
|
| 408 |
+ } |
|
| 409 |
+ return t.string(t.binary.Uint32(t.filetab[4*fno:])) |
|
| 410 |
+} |
|
| 411 |
+ |
|
| 412 |
+// go12LineToPC maps a (file, line) pair to a program counter for the Go 1.2 pcln table. |
|
| 413 |
+func (t *LineTable) go12LineToPC(file string, line int) (pc uint64) {
|
|
| 414 |
+ defer func() {
|
|
| 415 |
+ if recover() != nil {
|
|
| 416 |
+ pc = 0 |
|
| 417 |
+ } |
|
| 418 |
+ }() |
|
| 419 |
+ |
|
| 420 |
+ t.initFileMap() |
|
| 421 |
+ filenum := t.fileMap[file] |
|
| 422 |
+ if filenum == 0 {
|
|
| 423 |
+ return 0 |
|
| 424 |
+ } |
|
| 425 |
+ |
|
| 426 |
+ // Scan all functions. |
|
| 427 |
+ // If this turns out to be a bottleneck, we could build a map[int32][]int32 |
|
| 428 |
+ // mapping file number to a list of functions with code from that file. |
|
| 429 |
+ for i := uint32(0); i < t.nfunctab; i++ {
|
|
| 430 |
+ f := t.Data[t.uintptr(t.functab[2*t.ptrsize*i+t.ptrsize:]):] |
|
| 431 |
+ entry := t.uintptr(f) |
|
| 432 |
+ filetab := t.binary.Uint32(f[t.ptrsize+4*4:]) |
|
| 433 |
+ linetab := t.binary.Uint32(f[t.ptrsize+5*4:]) |
|
| 434 |
+ pc := t.findFileLine(entry, filetab, linetab, int32(filenum), int32(line)) |
|
| 435 |
+ if pc != 0 {
|
|
| 436 |
+ return pc |
|
| 437 |
+ } |
|
| 438 |
+ } |
|
| 439 |
+ return 0 |
|
| 440 |
+} |
|
| 441 |
+ |
|
| 442 |
+// initFileMap initializes the map from file name to file number. |
|
| 443 |
+func (t *LineTable) initFileMap() {
|
|
| 444 |
+ t.mu.Lock() |
|
| 445 |
+ defer t.mu.Unlock() |
|
| 446 |
+ |
|
| 447 |
+ if t.fileMap != nil {
|
|
| 448 |
+ return |
|
| 449 |
+ } |
|
| 450 |
+ m := make(map[string]uint32) |
|
| 451 |
+ |
|
| 452 |
+ for i := uint32(1); i < t.nfiletab; i++ {
|
|
| 453 |
+ s := t.string(t.binary.Uint32(t.filetab[4*i:])) |
|
| 454 |
+ m[s] = i |
|
| 455 |
+ } |
|
| 456 |
+ t.fileMap = m |
|
| 457 |
+} |
|
| 458 |
+ |
|
| 459 |
+// go12MapFiles adds to m a key for every file in the Go 1.2 LineTable. |
|
| 460 |
+// Every key maps to obj. That's not a very interesting map, but it provides |
|
| 461 |
+// a way for callers to obtain the list of files in the program. |
|
| 462 |
+func (t *LineTable) go12MapFiles(m map[string]*Obj, obj *Obj) {
|
|
| 463 |
+ defer func() {
|
|
| 464 |
+ recover() |
|
| 465 |
+ }() |
|
| 466 |
+ |
|
| 467 |
+ t.initFileMap() |
|
| 468 |
+ for file := range t.fileMap {
|
|
| 469 |
+ m[file] = obj |
|
| 470 |
+ } |
|
| 471 |
+} |
| 0 | 472 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,731 @@ |
| 0 |
+// Copyright 2018 Google Inc. All Rights Reserved. |
|
| 1 |
+// |
|
| 2 |
+// Licensed under the Apache License, Version 2.0 (the "License"); |
|
| 3 |
+// you may not use this file except in compliance with the License. |
|
| 4 |
+// You may obtain a copy of the License at |
|
| 5 |
+// |
|
| 6 |
+// http://www.apache.org/licenses/LICENSE-2.0 |
|
| 7 |
+// |
|
| 8 |
+// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
+// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
+// See the License for the specific language governing permissions and |
|
| 12 |
+// limitations under the License. |
|
| 13 |
+ |
|
| 14 |
+// Package gosym implements access to the Go symbol |
|
| 15 |
+// and line number tables embedded in Go binaries generated |
|
| 16 |
+// by the gc compilers. |
|
| 17 |
+package gosym |
|
| 18 |
+ |
|
| 19 |
+// The table format is a variant of the format used in Plan 9's a.out |
|
| 20 |
+// format, documented at http://plan9.bell-labs.com/magic/man2html/6/a.out. |
|
| 21 |
+// The best reference for the differences between the Plan 9 format |
|
| 22 |
+// and the Go format is the runtime source, specifically ../../runtime/symtab.c. |
|
| 23 |
+ |
|
| 24 |
+import ( |
|
| 25 |
+ "bytes" |
|
| 26 |
+ "encoding/binary" |
|
| 27 |
+ "fmt" |
|
| 28 |
+ "strconv" |
|
| 29 |
+ "strings" |
|
| 30 |
+) |
|
| 31 |
+ |
|
| 32 |
+/* |
|
| 33 |
+ * Symbols |
|
| 34 |
+ */ |
|
| 35 |
+ |
|
| 36 |
+// A Sym represents a single symbol table entry. |
|
| 37 |
+type Sym struct {
|
|
| 38 |
+ Value uint64 |
|
| 39 |
+ Type byte |
|
| 40 |
+ Name string |
|
| 41 |
+ GoType uint64 |
|
| 42 |
+ // If this symbol if a function symbol, the corresponding Func |
|
| 43 |
+ Func *Func |
|
| 44 |
+} |
|
| 45 |
+ |
|
| 46 |
+// Static reports whether this symbol is static (not visible outside its file). |
|
| 47 |
+func (s *Sym) Static() bool { return s.Type >= 'a' }
|
|
| 48 |
+ |
|
| 49 |
+// PackageName returns the package part of the symbol name, |
|
| 50 |
+// or the empty string if there is none. |
|
| 51 |
+func (s *Sym) PackageName() string {
|
|
| 52 |
+ if i := strings.Index(s.Name, "."); i != -1 {
|
|
| 53 |
+ return s.Name[0:i] |
|
| 54 |
+ } |
|
| 55 |
+ return "" |
|
| 56 |
+} |
|
| 57 |
+ |
|
| 58 |
+// ReceiverName returns the receiver type name of this symbol, |
|
| 59 |
+// or the empty string if there is none. |
|
| 60 |
+func (s *Sym) ReceiverName() string {
|
|
| 61 |
+ l := strings.Index(s.Name, ".") |
|
| 62 |
+ r := strings.LastIndex(s.Name, ".") |
|
| 63 |
+ if l == -1 || r == -1 || l == r {
|
|
| 64 |
+ return "" |
|
| 65 |
+ } |
|
| 66 |
+ return s.Name[l+1 : r] |
|
| 67 |
+} |
|
| 68 |
+ |
|
| 69 |
+// BaseName returns the symbol name without the package or receiver name. |
|
| 70 |
+func (s *Sym) BaseName() string {
|
|
| 71 |
+ if i := strings.LastIndex(s.Name, "."); i != -1 {
|
|
| 72 |
+ return s.Name[i+1:] |
|
| 73 |
+ } |
|
| 74 |
+ return s.Name |
|
| 75 |
+} |
|
| 76 |
+ |
|
| 77 |
+// A Func collects information about a single function. |
|
| 78 |
+type Func struct {
|
|
| 79 |
+ Entry uint64 |
|
| 80 |
+ *Sym |
|
| 81 |
+ End uint64 |
|
| 82 |
+ Params []*Sym |
|
| 83 |
+ Locals []*Sym |
|
| 84 |
+ FrameSize int |
|
| 85 |
+ LineTable *LineTable |
|
| 86 |
+ Obj *Obj |
|
| 87 |
+} |
|
| 88 |
+ |
|
| 89 |
+// An Obj represents a collection of functions in a symbol table. |
|
| 90 |
+// |
|
| 91 |
+// The exact method of division of a binary into separate Objs is an internal detail |
|
| 92 |
+// of the symbol table format. |
|
| 93 |
+// |
|
| 94 |
+// In early versions of Go each source file became a different Obj. |
|
| 95 |
+// |
|
| 96 |
+// In Go 1 and Go 1.1, each package produced one Obj for all Go sources |
|
| 97 |
+// and one Obj per C source file. |
|
| 98 |
+// |
|
| 99 |
+// In Go 1.2, there is a single Obj for the entire program. |
|
| 100 |
+type Obj struct {
|
|
| 101 |
+ // Funcs is a list of functions in the Obj. |
|
| 102 |
+ Funcs []Func |
|
| 103 |
+ |
|
| 104 |
+ // In Go 1.1 and earlier, Paths is a list of symbols corresponding |
|
| 105 |
+ // to the source file names that produced the Obj. |
|
| 106 |
+ // In Go 1.2, Paths is nil. |
|
| 107 |
+ // Use the keys of Table.Files to obtain a list of source files. |
|
| 108 |
+ Paths []Sym // meta |
|
| 109 |
+} |
|
| 110 |
+ |
|
| 111 |
+/* |
|
| 112 |
+ * Symbol tables |
|
| 113 |
+ */ |
|
| 114 |
+ |
|
| 115 |
+// Table represents a Go symbol table. It stores all of the |
|
| 116 |
+// symbols decoded from the program and provides methods to translate |
|
| 117 |
+// between symbols, names, and addresses. |
|
| 118 |
+type Table struct {
|
|
| 119 |
+ Syms []Sym |
|
| 120 |
+ Funcs []Func |
|
| 121 |
+ Files map[string]*Obj // nil for Go 1.2 and later binaries |
|
| 122 |
+ Objs []Obj // nil for Go 1.2 and later binaries |
|
| 123 |
+ |
|
| 124 |
+ go12line *LineTable // Go 1.2 line number table |
|
| 125 |
+} |
|
| 126 |
+ |
|
| 127 |
+type sym struct {
|
|
| 128 |
+ value uint64 |
|
| 129 |
+ gotype uint64 |
|
| 130 |
+ typ byte |
|
| 131 |
+ name []byte |
|
| 132 |
+} |
|
| 133 |
+ |
|
| 134 |
+var ( |
|
| 135 |
+ littleEndianSymtab = []byte{0xFD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00}
|
|
| 136 |
+ bigEndianSymtab = []byte{0xFF, 0xFF, 0xFF, 0xFD, 0x00, 0x00, 0x00}
|
|
| 137 |
+ oldLittleEndianSymtab = []byte{0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00}
|
|
| 138 |
+) |
|
| 139 |
+ |
|
| 140 |
+func walksymtab(data []byte, fn func(sym) error) error {
|
|
| 141 |
+ if len(data) == 0 { // missing symtab is okay
|
|
| 142 |
+ return nil |
|
| 143 |
+ } |
|
| 144 |
+ var order binary.ByteOrder = binary.BigEndian |
|
| 145 |
+ newTable := false |
|
| 146 |
+ switch {
|
|
| 147 |
+ case bytes.HasPrefix(data, oldLittleEndianSymtab): |
|
| 148 |
+ // Same as Go 1.0, but little endian. |
|
| 149 |
+ // Format was used during interim development between Go 1.0 and Go 1.1. |
|
| 150 |
+ // Should not be widespread, but easy to support. |
|
| 151 |
+ data = data[6:] |
|
| 152 |
+ order = binary.LittleEndian |
|
| 153 |
+ case bytes.HasPrefix(data, bigEndianSymtab): |
|
| 154 |
+ newTable = true |
|
| 155 |
+ case bytes.HasPrefix(data, littleEndianSymtab): |
|
| 156 |
+ newTable = true |
|
| 157 |
+ order = binary.LittleEndian |
|
| 158 |
+ } |
|
| 159 |
+ var ptrsz int |
|
| 160 |
+ if newTable {
|
|
| 161 |
+ if len(data) < 8 {
|
|
| 162 |
+ return &DecodingError{len(data), "unexpected EOF", nil}
|
|
| 163 |
+ } |
|
| 164 |
+ ptrsz = int(data[7]) |
|
| 165 |
+ if ptrsz != 4 && ptrsz != 8 {
|
|
| 166 |
+ return &DecodingError{7, "invalid pointer size", ptrsz}
|
|
| 167 |
+ } |
|
| 168 |
+ data = data[8:] |
|
| 169 |
+ } |
|
| 170 |
+ var s sym |
|
| 171 |
+ p := data |
|
| 172 |
+ for len(p) >= 4 {
|
|
| 173 |
+ var typ byte |
|
| 174 |
+ if newTable {
|
|
| 175 |
+ // Symbol type, value, Go type. |
|
| 176 |
+ typ = p[0] & 0x3F |
|
| 177 |
+ wideValue := p[0]&0x40 != 0 |
|
| 178 |
+ goType := p[0]&0x80 != 0 |
|
| 179 |
+ if typ < 26 {
|
|
| 180 |
+ typ += 'A' |
|
| 181 |
+ } else {
|
|
| 182 |
+ typ += 'a' - 26 |
|
| 183 |
+ } |
|
| 184 |
+ s.typ = typ |
|
| 185 |
+ p = p[1:] |
|
| 186 |
+ if wideValue {
|
|
| 187 |
+ if len(p) < ptrsz {
|
|
| 188 |
+ return &DecodingError{len(data), "unexpected EOF", nil}
|
|
| 189 |
+ } |
|
| 190 |
+ // fixed-width value |
|
| 191 |
+ if ptrsz == 8 {
|
|
| 192 |
+ s.value = order.Uint64(p[0:8]) |
|
| 193 |
+ p = p[8:] |
|
| 194 |
+ } else {
|
|
| 195 |
+ s.value = uint64(order.Uint32(p[0:4])) |
|
| 196 |
+ p = p[4:] |
|
| 197 |
+ } |
|
| 198 |
+ } else {
|
|
| 199 |
+ // varint value |
|
| 200 |
+ s.value = 0 |
|
| 201 |
+ shift := uint(0) |
|
| 202 |
+ for len(p) > 0 && p[0]&0x80 != 0 {
|
|
| 203 |
+ s.value |= uint64(p[0]&0x7F) << shift |
|
| 204 |
+ shift += 7 |
|
| 205 |
+ p = p[1:] |
|
| 206 |
+ } |
|
| 207 |
+ if len(p) == 0 {
|
|
| 208 |
+ return &DecodingError{len(data), "unexpected EOF", nil}
|
|
| 209 |
+ } |
|
| 210 |
+ s.value |= uint64(p[0]) << shift |
|
| 211 |
+ p = p[1:] |
|
| 212 |
+ } |
|
| 213 |
+ if goType {
|
|
| 214 |
+ if len(p) < ptrsz {
|
|
| 215 |
+ return &DecodingError{len(data), "unexpected EOF", nil}
|
|
| 216 |
+ } |
|
| 217 |
+ // fixed-width go type |
|
| 218 |
+ if ptrsz == 8 {
|
|
| 219 |
+ s.gotype = order.Uint64(p[0:8]) |
|
| 220 |
+ p = p[8:] |
|
| 221 |
+ } else {
|
|
| 222 |
+ s.gotype = uint64(order.Uint32(p[0:4])) |
|
| 223 |
+ p = p[4:] |
|
| 224 |
+ } |
|
| 225 |
+ } |
|
| 226 |
+ } else {
|
|
| 227 |
+ // Value, symbol type. |
|
| 228 |
+ s.value = uint64(order.Uint32(p[0:4])) |
|
| 229 |
+ if len(p) < 5 {
|
|
| 230 |
+ return &DecodingError{len(data), "unexpected EOF", nil}
|
|
| 231 |
+ } |
|
| 232 |
+ typ = p[4] |
|
| 233 |
+ if typ&0x80 == 0 {
|
|
| 234 |
+ return &DecodingError{len(data) - len(p) + 4, "bad symbol type", typ}
|
|
| 235 |
+ } |
|
| 236 |
+ typ &^= 0x80 |
|
| 237 |
+ s.typ = typ |
|
| 238 |
+ p = p[5:] |
|
| 239 |
+ } |
|
| 240 |
+ |
|
| 241 |
+ // Name. |
|
| 242 |
+ var i int |
|
| 243 |
+ var nnul int |
|
| 244 |
+ for i = 0; i < len(p); i++ {
|
|
| 245 |
+ if p[i] == 0 {
|
|
| 246 |
+ nnul = 1 |
|
| 247 |
+ break |
|
| 248 |
+ } |
|
| 249 |
+ } |
|
| 250 |
+ switch typ {
|
|
| 251 |
+ case 'z', 'Z': |
|
| 252 |
+ p = p[i+nnul:] |
|
| 253 |
+ for i = 0; i+2 <= len(p); i += 2 {
|
|
| 254 |
+ if p[i] == 0 && p[i+1] == 0 {
|
|
| 255 |
+ nnul = 2 |
|
| 256 |
+ break |
|
| 257 |
+ } |
|
| 258 |
+ } |
|
| 259 |
+ } |
|
| 260 |
+ if len(p) < i+nnul {
|
|
| 261 |
+ return &DecodingError{len(data), "unexpected EOF", nil}
|
|
| 262 |
+ } |
|
| 263 |
+ s.name = p[0:i] |
|
| 264 |
+ i += nnul |
|
| 265 |
+ p = p[i:] |
|
| 266 |
+ |
|
| 267 |
+ if !newTable {
|
|
| 268 |
+ if len(p) < 4 {
|
|
| 269 |
+ return &DecodingError{len(data), "unexpected EOF", nil}
|
|
| 270 |
+ } |
|
| 271 |
+ // Go type. |
|
| 272 |
+ s.gotype = uint64(order.Uint32(p[:4])) |
|
| 273 |
+ p = p[4:] |
|
| 274 |
+ } |
|
| 275 |
+ fn(s) |
|
| 276 |
+ } |
|
| 277 |
+ return nil |
|
| 278 |
+} |
|
| 279 |
+ |
|
| 280 |
+// NewTable decodes the Go symbol table in data, |
|
| 281 |
+// returning an in-memory representation. |
|
| 282 |
+func NewTable(symtab []byte, pcln *LineTable) (*Table, error) {
|
|
| 283 |
+ var n int |
|
| 284 |
+ err := walksymtab(symtab, func(s sym) error {
|
|
| 285 |
+ n++ |
|
| 286 |
+ return nil |
|
| 287 |
+ }) |
|
| 288 |
+ if err != nil {
|
|
| 289 |
+ return nil, err |
|
| 290 |
+ } |
|
| 291 |
+ |
|
| 292 |
+ var t Table |
|
| 293 |
+ if pcln.isGo12() {
|
|
| 294 |
+ t.go12line = pcln |
|
| 295 |
+ } |
|
| 296 |
+ fname := make(map[uint16]string) |
|
| 297 |
+ t.Syms = make([]Sym, 0, n) |
|
| 298 |
+ nf := 0 |
|
| 299 |
+ nz := 0 |
|
| 300 |
+ lasttyp := uint8(0) |
|
| 301 |
+ err = walksymtab(symtab, func(s sym) error {
|
|
| 302 |
+ n := len(t.Syms) |
|
| 303 |
+ t.Syms = t.Syms[0 : n+1] |
|
| 304 |
+ ts := &t.Syms[n] |
|
| 305 |
+ ts.Type = s.typ |
|
| 306 |
+ ts.Value = uint64(s.value) |
|
| 307 |
+ ts.GoType = uint64(s.gotype) |
|
| 308 |
+ switch s.typ {
|
|
| 309 |
+ default: |
|
| 310 |
+ // rewrite name to use . instead of · (c2 b7) |
|
| 311 |
+ w := 0 |
|
| 312 |
+ b := s.name |
|
| 313 |
+ for i := 0; i < len(b); i++ {
|
|
| 314 |
+ if b[i] == 0xc2 && i+1 < len(b) && b[i+1] == 0xb7 {
|
|
| 315 |
+ i++ |
|
| 316 |
+ b[i] = '.' |
|
| 317 |
+ } |
|
| 318 |
+ b[w] = b[i] |
|
| 319 |
+ w++ |
|
| 320 |
+ } |
|
| 321 |
+ ts.Name = string(s.name[0:w]) |
|
| 322 |
+ case 'z', 'Z': |
|
| 323 |
+ if lasttyp != 'z' && lasttyp != 'Z' {
|
|
| 324 |
+ nz++ |
|
| 325 |
+ } |
|
| 326 |
+ for i := 0; i < len(s.name); i += 2 {
|
|
| 327 |
+ eltIdx := binary.BigEndian.Uint16(s.name[i : i+2]) |
|
| 328 |
+ elt, ok := fname[eltIdx] |
|
| 329 |
+ if !ok {
|
|
| 330 |
+ return &DecodingError{-1, "bad filename code", eltIdx}
|
|
| 331 |
+ } |
|
| 332 |
+ if n := len(ts.Name); n > 0 && ts.Name[n-1] != '/' {
|
|
| 333 |
+ ts.Name += "/" |
|
| 334 |
+ } |
|
| 335 |
+ ts.Name += elt |
|
| 336 |
+ } |
|
| 337 |
+ } |
|
| 338 |
+ switch s.typ {
|
|
| 339 |
+ case 'T', 't', 'L', 'l': |
|
| 340 |
+ nf++ |
|
| 341 |
+ case 'f': |
|
| 342 |
+ fname[uint16(s.value)] = ts.Name |
|
| 343 |
+ } |
|
| 344 |
+ lasttyp = s.typ |
|
| 345 |
+ return nil |
|
| 346 |
+ }) |
|
| 347 |
+ if err != nil {
|
|
| 348 |
+ return nil, err |
|
| 349 |
+ } |
|
| 350 |
+ |
|
| 351 |
+ t.Funcs = make([]Func, 0, nf) |
|
| 352 |
+ t.Files = make(map[string]*Obj) |
|
| 353 |
+ |
|
| 354 |
+ var obj *Obj |
|
| 355 |
+ if t.go12line != nil {
|
|
| 356 |
+ // Put all functions into one Obj. |
|
| 357 |
+ t.Objs = make([]Obj, 1) |
|
| 358 |
+ obj = &t.Objs[0] |
|
| 359 |
+ t.go12line.go12MapFiles(t.Files, obj) |
|
| 360 |
+ } else {
|
|
| 361 |
+ t.Objs = make([]Obj, 0, nz) |
|
| 362 |
+ } |
|
| 363 |
+ |
|
| 364 |
+ // Count text symbols and attach frame sizes, parameters, and |
|
| 365 |
+ // locals to them. Also, find object file boundaries. |
|
| 366 |
+ lastf := 0 |
|
| 367 |
+ for i := 0; i < len(t.Syms); i++ {
|
|
| 368 |
+ sym := &t.Syms[i] |
|
| 369 |
+ switch sym.Type {
|
|
| 370 |
+ case 'Z', 'z': // path symbol |
|
| 371 |
+ if t.go12line != nil {
|
|
| 372 |
+ // Go 1.2 binaries have the file information elsewhere. Ignore. |
|
| 373 |
+ break |
|
| 374 |
+ } |
|
| 375 |
+ // Finish the current object |
|
| 376 |
+ if obj != nil {
|
|
| 377 |
+ obj.Funcs = t.Funcs[lastf:] |
|
| 378 |
+ } |
|
| 379 |
+ lastf = len(t.Funcs) |
|
| 380 |
+ |
|
| 381 |
+ // Start new object |
|
| 382 |
+ n := len(t.Objs) |
|
| 383 |
+ t.Objs = t.Objs[0 : n+1] |
|
| 384 |
+ obj = &t.Objs[n] |
|
| 385 |
+ |
|
| 386 |
+ // Count & copy path symbols |
|
| 387 |
+ var end int |
|
| 388 |
+ for end = i + 1; end < len(t.Syms); end++ {
|
|
| 389 |
+ if c := t.Syms[end].Type; c != 'Z' && c != 'z' {
|
|
| 390 |
+ break |
|
| 391 |
+ } |
|
| 392 |
+ } |
|
| 393 |
+ obj.Paths = t.Syms[i:end] |
|
| 394 |
+ i = end - 1 // loop will i++ |
|
| 395 |
+ |
|
| 396 |
+ // Record file names |
|
| 397 |
+ depth := 0 |
|
| 398 |
+ for j := range obj.Paths {
|
|
| 399 |
+ s := &obj.Paths[j] |
|
| 400 |
+ if s.Name == "" {
|
|
| 401 |
+ depth-- |
|
| 402 |
+ } else {
|
|
| 403 |
+ if depth == 0 {
|
|
| 404 |
+ t.Files[s.Name] = obj |
|
| 405 |
+ } |
|
| 406 |
+ depth++ |
|
| 407 |
+ } |
|
| 408 |
+ } |
|
| 409 |
+ |
|
| 410 |
+ case 'T', 't', 'L', 'l': // text symbol |
|
| 411 |
+ if n := len(t.Funcs); n > 0 {
|
|
| 412 |
+ t.Funcs[n-1].End = sym.Value |
|
| 413 |
+ } |
|
| 414 |
+ if sym.Name == "etext" {
|
|
| 415 |
+ continue |
|
| 416 |
+ } |
|
| 417 |
+ |
|
| 418 |
+ // Count parameter and local (auto) syms |
|
| 419 |
+ var np, na int |
|
| 420 |
+ var end int |
|
| 421 |
+ countloop: |
|
| 422 |
+ for end = i + 1; end < len(t.Syms); end++ {
|
|
| 423 |
+ switch t.Syms[end].Type {
|
|
| 424 |
+ case 'T', 't', 'L', 'l', 'Z', 'z': |
|
| 425 |
+ break countloop |
|
| 426 |
+ case 'p': |
|
| 427 |
+ np++ |
|
| 428 |
+ case 'a': |
|
| 429 |
+ na++ |
|
| 430 |
+ } |
|
| 431 |
+ } |
|
| 432 |
+ |
|
| 433 |
+ // Fill in the function symbol |
|
| 434 |
+ n := len(t.Funcs) |
|
| 435 |
+ t.Funcs = t.Funcs[0 : n+1] |
|
| 436 |
+ fn := &t.Funcs[n] |
|
| 437 |
+ sym.Func = fn |
|
| 438 |
+ fn.Params = make([]*Sym, 0, np) |
|
| 439 |
+ fn.Locals = make([]*Sym, 0, na) |
|
| 440 |
+ fn.Sym = sym |
|
| 441 |
+ fn.Entry = sym.Value |
|
| 442 |
+ fn.Obj = obj |
|
| 443 |
+ if t.go12line != nil {
|
|
| 444 |
+ // All functions share the same line table. |
|
| 445 |
+ // It knows how to narrow down to a specific |
|
| 446 |
+ // function quickly. |
|
| 447 |
+ fn.LineTable = t.go12line |
|
| 448 |
+ } else if pcln != nil {
|
|
| 449 |
+ fn.LineTable = pcln.slice(fn.Entry) |
|
| 450 |
+ pcln = fn.LineTable |
|
| 451 |
+ } |
|
| 452 |
+ for j := i; j < end; j++ {
|
|
| 453 |
+ s := &t.Syms[j] |
|
| 454 |
+ switch s.Type {
|
|
| 455 |
+ case 'm': |
|
| 456 |
+ fn.FrameSize = int(s.Value) |
|
| 457 |
+ case 'p': |
|
| 458 |
+ n := len(fn.Params) |
|
| 459 |
+ fn.Params = fn.Params[0 : n+1] |
|
| 460 |
+ fn.Params[n] = s |
|
| 461 |
+ case 'a': |
|
| 462 |
+ n := len(fn.Locals) |
|
| 463 |
+ fn.Locals = fn.Locals[0 : n+1] |
|
| 464 |
+ fn.Locals[n] = s |
|
| 465 |
+ } |
|
| 466 |
+ } |
|
| 467 |
+ i = end - 1 // loop will i++ |
|
| 468 |
+ } |
|
| 469 |
+ } |
|
| 470 |
+ |
|
| 471 |
+ if t.go12line != nil && nf == 0 {
|
|
| 472 |
+ t.Funcs = t.go12line.go12Funcs() |
|
| 473 |
+ } |
|
| 474 |
+ if obj != nil {
|
|
| 475 |
+ obj.Funcs = t.Funcs[lastf:] |
|
| 476 |
+ } |
|
| 477 |
+ return &t, nil |
|
| 478 |
+} |
|
| 479 |
+ |
|
| 480 |
+// PCToFunc returns the function containing the program counter pc, |
|
| 481 |
+// or nil if there is no such function. |
|
| 482 |
+func (t *Table) PCToFunc(pc uint64) *Func {
|
|
| 483 |
+ funcs := t.Funcs |
|
| 484 |
+ for len(funcs) > 0 {
|
|
| 485 |
+ m := len(funcs) / 2 |
|
| 486 |
+ fn := &funcs[m] |
|
| 487 |
+ switch {
|
|
| 488 |
+ case pc < fn.Entry: |
|
| 489 |
+ funcs = funcs[0:m] |
|
| 490 |
+ case fn.Entry <= pc && pc < fn.End: |
|
| 491 |
+ return fn |
|
| 492 |
+ default: |
|
| 493 |
+ funcs = funcs[m+1:] |
|
| 494 |
+ } |
|
| 495 |
+ } |
|
| 496 |
+ return nil |
|
| 497 |
+} |
|
| 498 |
+ |
|
| 499 |
+// PCToLine looks up line number information for a program counter. |
|
| 500 |
+// If there is no information, it returns fn == nil. |
|
| 501 |
+func (t *Table) PCToLine(pc uint64) (file string, line int, fn *Func) {
|
|
| 502 |
+ if fn = t.PCToFunc(pc); fn == nil {
|
|
| 503 |
+ return |
|
| 504 |
+ } |
|
| 505 |
+ if t.go12line != nil {
|
|
| 506 |
+ file = t.go12line.go12PCToFile(pc) |
|
| 507 |
+ line = t.go12line.go12PCToLine(pc) |
|
| 508 |
+ } else {
|
|
| 509 |
+ file, line = fn.Obj.lineFromAline(fn.LineTable.PCToLine(pc)) |
|
| 510 |
+ } |
|
| 511 |
+ return |
|
| 512 |
+} |
|
| 513 |
+ |
|
| 514 |
+// PCToSPAdj returns the stack pointer adjustment for a program counter. |
|
| 515 |
+func (t *Table) PCToSPAdj(pc uint64) (spadj int) {
|
|
| 516 |
+ if fn := t.PCToFunc(pc); fn == nil {
|
|
| 517 |
+ return 0 |
|
| 518 |
+ } |
|
| 519 |
+ if t.go12line != nil {
|
|
| 520 |
+ return t.go12line.go12PCToSPAdj(pc) |
|
| 521 |
+ } |
|
| 522 |
+ return 0 |
|
| 523 |
+} |
|
| 524 |
+ |
|
| 525 |
+// LineToPC looks up the first program counter on the given line in |
|
| 526 |
+// the named file. It returns UnknownPathError or UnknownLineError if |
|
| 527 |
+// there is an error looking up this line. |
|
| 528 |
+func (t *Table) LineToPC(file string, line int) (pc uint64, fn *Func, err error) {
|
|
| 529 |
+ obj, ok := t.Files[file] |
|
| 530 |
+ if !ok {
|
|
| 531 |
+ return 0, nil, UnknownFileError(file) |
|
| 532 |
+ } |
|
| 533 |
+ |
|
| 534 |
+ if t.go12line != nil {
|
|
| 535 |
+ pc := t.go12line.go12LineToPC(file, line) |
|
| 536 |
+ if pc == 0 {
|
|
| 537 |
+ return 0, nil, &UnknownLineError{file, line}
|
|
| 538 |
+ } |
|
| 539 |
+ return pc, t.PCToFunc(pc), nil |
|
| 540 |
+ } |
|
| 541 |
+ |
|
| 542 |
+ abs, err := obj.alineFromLine(file, line) |
|
| 543 |
+ if err != nil {
|
|
| 544 |
+ return |
|
| 545 |
+ } |
|
| 546 |
+ for i := range obj.Funcs {
|
|
| 547 |
+ f := &obj.Funcs[i] |
|
| 548 |
+ pc := f.LineTable.LineToPC(abs, f.End) |
|
| 549 |
+ if pc != 0 {
|
|
| 550 |
+ return pc, f, nil |
|
| 551 |
+ } |
|
| 552 |
+ } |
|
| 553 |
+ return 0, nil, &UnknownLineError{file, line}
|
|
| 554 |
+} |
|
| 555 |
+ |
|
| 556 |
+// LookupSym returns the text, data, or bss symbol with the given name, |
|
| 557 |
+// or nil if no such symbol is found. |
|
| 558 |
+func (t *Table) LookupSym(name string) *Sym {
|
|
| 559 |
+ // TODO(austin) Maybe make a map |
|
| 560 |
+ for i := range t.Syms {
|
|
| 561 |
+ s := &t.Syms[i] |
|
| 562 |
+ switch s.Type {
|
|
| 563 |
+ case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b': |
|
| 564 |
+ if s.Name == name {
|
|
| 565 |
+ return s |
|
| 566 |
+ } |
|
| 567 |
+ } |
|
| 568 |
+ } |
|
| 569 |
+ return nil |
|
| 570 |
+} |
|
| 571 |
+ |
|
| 572 |
+// LookupFunc returns the text, data, or bss symbol with the given name, |
|
| 573 |
+// or nil if no such symbol is found. |
|
| 574 |
+func (t *Table) LookupFunc(name string) *Func {
|
|
| 575 |
+ for i := range t.Funcs {
|
|
| 576 |
+ f := &t.Funcs[i] |
|
| 577 |
+ if f.Sym.Name == name {
|
|
| 578 |
+ return f |
|
| 579 |
+ } |
|
| 580 |
+ } |
|
| 581 |
+ return nil |
|
| 582 |
+} |
|
| 583 |
+ |
|
| 584 |
+// SymByAddr returns the text, data, or bss symbol starting at the given address. |
|
| 585 |
+func (t *Table) SymByAddr(addr uint64) *Sym {
|
|
| 586 |
+ for i := range t.Syms {
|
|
| 587 |
+ s := &t.Syms[i] |
|
| 588 |
+ switch s.Type {
|
|
| 589 |
+ case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b': |
|
| 590 |
+ if s.Value == addr {
|
|
| 591 |
+ return s |
|
| 592 |
+ } |
|
| 593 |
+ } |
|
| 594 |
+ } |
|
| 595 |
+ return nil |
|
| 596 |
+} |
|
| 597 |
+ |
|
| 598 |
+/* |
|
| 599 |
+ * Object files |
|
| 600 |
+ */ |
|
| 601 |
+ |
|
| 602 |
+// This is legacy code for Go 1.1 and earlier, which used the |
|
| 603 |
+// Plan 9 format for pc-line tables. This code was never quite |
|
| 604 |
+// correct. It's probably very close, and it's usually correct, but |
|
| 605 |
+// we never quite found all the corner cases. |
|
| 606 |
+// |
|
| 607 |
+// Go 1.2 and later use a simpler format, documented at golang.org/s/go12symtab. |
|
| 608 |
+ |
|
| 609 |
+func (o *Obj) lineFromAline(aline int) (string, int) {
|
|
| 610 |
+ type stackEnt struct {
|
|
| 611 |
+ path string |
|
| 612 |
+ start int |
|
| 613 |
+ offset int |
|
| 614 |
+ prev *stackEnt |
|
| 615 |
+ } |
|
| 616 |
+ |
|
| 617 |
+ noPath := &stackEnt{"", 0, 0, nil}
|
|
| 618 |
+ tos := noPath |
|
| 619 |
+ |
|
| 620 |
+pathloop: |
|
| 621 |
+ for _, s := range o.Paths {
|
|
| 622 |
+ val := int(s.Value) |
|
| 623 |
+ switch {
|
|
| 624 |
+ case val > aline: |
|
| 625 |
+ break pathloop |
|
| 626 |
+ |
|
| 627 |
+ case val == 1: |
|
| 628 |
+ // Start a new stack |
|
| 629 |
+ tos = &stackEnt{s.Name, val, 0, noPath}
|
|
| 630 |
+ |
|
| 631 |
+ case s.Name == "": |
|
| 632 |
+ // Pop |
|
| 633 |
+ if tos == noPath {
|
|
| 634 |
+ return "<malformed symbol table>", 0 |
|
| 635 |
+ } |
|
| 636 |
+ tos.prev.offset += val - tos.start |
|
| 637 |
+ tos = tos.prev |
|
| 638 |
+ |
|
| 639 |
+ default: |
|
| 640 |
+ // Push |
|
| 641 |
+ tos = &stackEnt{s.Name, val, 0, tos}
|
|
| 642 |
+ } |
|
| 643 |
+ } |
|
| 644 |
+ |
|
| 645 |
+ if tos == noPath {
|
|
| 646 |
+ return "", 0 |
|
| 647 |
+ } |
|
| 648 |
+ return tos.path, aline - tos.start - tos.offset + 1 |
|
| 649 |
+} |
|
| 650 |
+ |
|
| 651 |
+func (o *Obj) alineFromLine(path string, line int) (int, error) {
|
|
| 652 |
+ if line < 1 {
|
|
| 653 |
+ return 0, &UnknownLineError{path, line}
|
|
| 654 |
+ } |
|
| 655 |
+ |
|
| 656 |
+ for i, s := range o.Paths {
|
|
| 657 |
+ // Find this path |
|
| 658 |
+ if s.Name != path {
|
|
| 659 |
+ continue |
|
| 660 |
+ } |
|
| 661 |
+ |
|
| 662 |
+ // Find this line at this stack level |
|
| 663 |
+ depth := 0 |
|
| 664 |
+ var incstart int |
|
| 665 |
+ line += int(s.Value) |
|
| 666 |
+ pathloop: |
|
| 667 |
+ for _, s := range o.Paths[i:] {
|
|
| 668 |
+ val := int(s.Value) |
|
| 669 |
+ switch {
|
|
| 670 |
+ case depth == 1 && val >= line: |
|
| 671 |
+ return line - 1, nil |
|
| 672 |
+ |
|
| 673 |
+ case s.Name == "": |
|
| 674 |
+ depth-- |
|
| 675 |
+ if depth == 0 {
|
|
| 676 |
+ break pathloop |
|
| 677 |
+ } else if depth == 1 {
|
|
| 678 |
+ line += val - incstart |
|
| 679 |
+ } |
|
| 680 |
+ |
|
| 681 |
+ default: |
|
| 682 |
+ if depth == 1 {
|
|
| 683 |
+ incstart = val |
|
| 684 |
+ } |
|
| 685 |
+ depth++ |
|
| 686 |
+ } |
|
| 687 |
+ } |
|
| 688 |
+ return 0, &UnknownLineError{path, line}
|
|
| 689 |
+ } |
|
| 690 |
+ return 0, UnknownFileError(path) |
|
| 691 |
+} |
|
| 692 |
+ |
|
| 693 |
+/* |
|
| 694 |
+ * Errors |
|
| 695 |
+ */ |
|
| 696 |
+ |
|
| 697 |
+// UnknownFileError represents a failure to find the specific file in |
|
| 698 |
+// the symbol table. |
|
| 699 |
+type UnknownFileError string |
|
| 700 |
+ |
|
| 701 |
+func (e UnknownFileError) Error() string { return "unknown file: " + string(e) }
|
|
| 702 |
+ |
|
| 703 |
+// UnknownLineError represents a failure to map a line to a program |
|
| 704 |
+// counter, either because the line is beyond the bounds of the file |
|
| 705 |
+// or because there is no code on the given line. |
|
| 706 |
+type UnknownLineError struct {
|
|
| 707 |
+ File string |
|
| 708 |
+ Line int |
|
| 709 |
+} |
|
| 710 |
+ |
|
| 711 |
+func (e *UnknownLineError) Error() string {
|
|
| 712 |
+ return "no code at " + e.File + ":" + strconv.Itoa(e.Line) |
|
| 713 |
+} |
|
| 714 |
+ |
|
| 715 |
+// DecodingError represents an error during the decoding of |
|
| 716 |
+// the symbol table. |
|
| 717 |
+type DecodingError struct {
|
|
| 718 |
+ off int |
|
| 719 |
+ msg string |
|
| 720 |
+ val interface{}
|
|
| 721 |
+} |
|
| 722 |
+ |
|
| 723 |
+func (e *DecodingError) Error() string {
|
|
| 724 |
+ msg := e.msg |
|
| 725 |
+ if e.val != nil {
|
|
| 726 |
+ msg += fmt.Sprintf(" '%v'", e.val)
|
|
| 727 |
+ } |
|
| 728 |
+ msg += fmt.Sprintf(" at byte %#x", e.off)
|
|
| 729 |
+ return msg |
|
| 730 |
+} |
| ... | ... |
@@ -1,4 +1,4 @@ |
| 1 |
-// Copyright 2014 Google Inc. All Rights Reserved. |
|
| 1 |
+// Copyright 2014 Google LLC |
|
| 2 | 2 |
// |
| 3 | 3 |
// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | 4 |
// you may not use this file except in compliance with the License. |
| ... | ... |
@@ -20,6 +20,7 @@ |
| 20 | 20 |
package metadata // import "cloud.google.com/go/compute/metadata" |
| 21 | 21 |
|
| 22 | 22 |
import ( |
| 23 |
+ "context" |
|
| 23 | 24 |
"encoding/json" |
| 24 | 25 |
"fmt" |
| 25 | 26 |
"io/ioutil" |
| ... | ... |
@@ -31,9 +32,6 @@ import ( |
| 31 | 31 |
"strings" |
| 32 | 32 |
"sync" |
| 33 | 33 |
"time" |
| 34 |
- |
|
| 35 |
- "golang.org/x/net/context" |
|
| 36 |
- "golang.org/x/net/context/ctxhttp" |
|
| 37 | 34 |
) |
| 38 | 35 |
|
| 39 | 36 |
const ( |
| ... | ... |
@@ -64,7 +62,7 @@ var ( |
| 64 | 64 |
) |
| 65 | 65 |
|
| 66 | 66 |
var ( |
| 67 |
- metaClient = &http.Client{
|
|
| 67 |
+ defaultClient = &Client{hc: &http.Client{
|
|
| 68 | 68 |
Transport: &http.Transport{
|
| 69 | 69 |
Dial: (&net.Dialer{
|
| 70 | 70 |
Timeout: 2 * time.Second, |
| ... | ... |
@@ -72,15 +70,15 @@ var ( |
| 72 | 72 |
}).Dial, |
| 73 | 73 |
ResponseHeaderTimeout: 2 * time.Second, |
| 74 | 74 |
}, |
| 75 |
- } |
|
| 76 |
- subscribeClient = &http.Client{
|
|
| 75 |
+ }} |
|
| 76 |
+ subscribeClient = &Client{hc: &http.Client{
|
|
| 77 | 77 |
Transport: &http.Transport{
|
| 78 | 78 |
Dial: (&net.Dialer{
|
| 79 | 79 |
Timeout: 2 * time.Second, |
| 80 | 80 |
KeepAlive: 30 * time.Second, |
| 81 | 81 |
}).Dial, |
| 82 | 82 |
}, |
| 83 |
- } |
|
| 83 |
+ }} |
|
| 84 | 84 |
) |
| 85 | 85 |
|
| 86 | 86 |
// NotDefinedError is returned when requested metadata is not defined. |
| ... | ... |
@@ -95,74 +93,16 @@ func (suffix NotDefinedError) Error() string {
|
| 95 | 95 |
return fmt.Sprintf("metadata: GCE metadata %q not defined", string(suffix))
|
| 96 | 96 |
} |
| 97 | 97 |
|
| 98 |
-// Get returns a value from the metadata service. |
|
| 99 |
-// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
|
|
| 100 |
-// |
|
| 101 |
-// If the GCE_METADATA_HOST environment variable is not defined, a default of |
|
| 102 |
-// 169.254.169.254 will be used instead. |
|
| 103 |
-// |
|
| 104 |
-// If the requested metadata is not defined, the returned error will |
|
| 105 |
-// be of type NotDefinedError. |
|
| 106 |
-func Get(suffix string) (string, error) {
|
|
| 107 |
- val, _, err := getETag(metaClient, suffix) |
|
| 108 |
- return val, err |
|
| 109 |
-} |
|
| 110 |
- |
|
| 111 |
-// getETag returns a value from the metadata service as well as the associated |
|
| 112 |
-// ETag using the provided client. This func is otherwise equivalent to Get. |
|
| 113 |
-func getETag(client *http.Client, suffix string) (value, etag string, err error) {
|
|
| 114 |
- // Using a fixed IP makes it very difficult to spoof the metadata service in |
|
| 115 |
- // a container, which is an important use-case for local testing of cloud |
|
| 116 |
- // deployments. To enable spoofing of the metadata service, the environment |
|
| 117 |
- // variable GCE_METADATA_HOST is first inspected to decide where metadata |
|
| 118 |
- // requests shall go. |
|
| 119 |
- host := os.Getenv(metadataHostEnv) |
|
| 120 |
- if host == "" {
|
|
| 121 |
- // Using 169.254.169.254 instead of "metadata" here because Go |
|
| 122 |
- // binaries built with the "netgo" tag and without cgo won't |
|
| 123 |
- // know the search suffix for "metadata" is |
|
| 124 |
- // ".google.internal", and this IP address is documented as |
|
| 125 |
- // being stable anyway. |
|
| 126 |
- host = metadataIP |
|
| 127 |
- } |
|
| 128 |
- url := "http://" + host + "/computeMetadata/v1/" + suffix |
|
| 129 |
- req, _ := http.NewRequest("GET", url, nil)
|
|
| 130 |
- req.Header.Set("Metadata-Flavor", "Google")
|
|
| 131 |
- req.Header.Set("User-Agent", userAgent)
|
|
| 132 |
- res, err := client.Do(req) |
|
| 133 |
- if err != nil {
|
|
| 134 |
- return "", "", err |
|
| 135 |
- } |
|
| 136 |
- defer res.Body.Close() |
|
| 137 |
- if res.StatusCode == http.StatusNotFound {
|
|
| 138 |
- return "", "", NotDefinedError(suffix) |
|
| 139 |
- } |
|
| 140 |
- if res.StatusCode != 200 {
|
|
| 141 |
- return "", "", fmt.Errorf("status code %d trying to fetch %s", res.StatusCode, url)
|
|
| 142 |
- } |
|
| 143 |
- all, err := ioutil.ReadAll(res.Body) |
|
| 144 |
- if err != nil {
|
|
| 145 |
- return "", "", err |
|
| 146 |
- } |
|
| 147 |
- return string(all), res.Header.Get("Etag"), nil
|
|
| 148 |
-} |
|
| 149 |
- |
|
| 150 |
-func getTrimmed(suffix string) (s string, err error) {
|
|
| 151 |
- s, err = Get(suffix) |
|
| 152 |
- s = strings.TrimSpace(s) |
|
| 153 |
- return |
|
| 154 |
-} |
|
| 155 |
- |
|
| 156 |
-func (c *cachedValue) get() (v string, err error) {
|
|
| 98 |
+func (c *cachedValue) get(cl *Client) (v string, err error) {
|
|
| 157 | 99 |
defer c.mu.Unlock() |
| 158 | 100 |
c.mu.Lock() |
| 159 | 101 |
if c.v != "" {
|
| 160 | 102 |
return c.v, nil |
| 161 | 103 |
} |
| 162 | 104 |
if c.trim {
|
| 163 |
- v, err = getTrimmed(c.k) |
|
| 105 |
+ v, err = cl.getTrimmed(c.k) |
|
| 164 | 106 |
} else {
|
| 165 |
- v, err = Get(c.k) |
|
| 107 |
+ v, err = cl.Get(c.k) |
|
| 166 | 108 |
} |
| 167 | 109 |
if err == nil {
|
| 168 | 110 |
c.v = v |
| ... | ... |
@@ -197,11 +137,11 @@ func testOnGCE() bool {
|
| 197 | 197 |
resc := make(chan bool, 2) |
| 198 | 198 |
|
| 199 | 199 |
// Try two strategies in parallel. |
| 200 |
- // See https://github.com/GoogleCloudPlatform/google-cloud-go/issues/194 |
|
| 200 |
+ // See https://github.com/googleapis/google-cloud-go/issues/194 |
|
| 201 | 201 |
go func() {
|
| 202 | 202 |
req, _ := http.NewRequest("GET", "http://"+metadataIP, nil)
|
| 203 | 203 |
req.Header.Set("User-Agent", userAgent)
|
| 204 |
- res, err := ctxhttp.Do(ctx, metaClient, req) |
|
| 204 |
+ res, err := defaultClient.hc.Do(req.WithContext(ctx)) |
|
| 205 | 205 |
if err != nil {
|
| 206 | 206 |
resc <- false |
| 207 | 207 |
return |
| ... | ... |
@@ -266,78 +206,183 @@ func systemInfoSuggestsGCE() bool {
|
| 266 | 266 |
return name == "Google" || name == "Google Compute Engine" |
| 267 | 267 |
} |
| 268 | 268 |
|
| 269 |
-// Subscribe subscribes to a value from the metadata service. |
|
| 270 |
-// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
|
|
| 271 |
-// The suffix may contain query parameters. |
|
| 272 |
-// |
|
| 273 |
-// Subscribe calls fn with the latest metadata value indicated by the provided |
|
| 274 |
-// suffix. If the metadata value is deleted, fn is called with the empty string |
|
| 275 |
-// and ok false. Subscribe blocks until fn returns a non-nil error or the value |
|
| 276 |
-// is deleted. Subscribe returns the error value returned from the last call to |
|
| 277 |
-// fn, which may be nil when ok == false. |
|
| 269 |
+// Subscribe calls Client.Subscribe on a client designed for subscribing (one with no |
|
| 270 |
+// ResponseHeaderTimeout). |
|
| 278 | 271 |
func Subscribe(suffix string, fn func(v string, ok bool) error) error {
|
| 279 |
- const failedSubscribeSleep = time.Second * 5 |
|
| 272 |
+ return subscribeClient.Subscribe(suffix, fn) |
|
| 273 |
+} |
|
| 280 | 274 |
|
| 281 |
- // First check to see if the metadata value exists at all. |
|
| 282 |
- val, lastETag, err := getETag(subscribeClient, suffix) |
|
| 283 |
- if err != nil {
|
|
| 284 |
- return err |
|
| 285 |
- } |
|
| 275 |
+// Get calls Client.Get on the default client. |
|
| 276 |
+func Get(suffix string) (string, error) { return defaultClient.Get(suffix) }
|
|
| 286 | 277 |
|
| 287 |
- if err := fn(val, true); err != nil {
|
|
| 288 |
- return err |
|
| 278 |
+// ProjectID returns the current instance's project ID string. |
|
| 279 |
+func ProjectID() (string, error) { return defaultClient.ProjectID() }
|
|
| 280 |
+ |
|
| 281 |
+// NumericProjectID returns the current instance's numeric project ID. |
|
| 282 |
+func NumericProjectID() (string, error) { return defaultClient.NumericProjectID() }
|
|
| 283 |
+ |
|
| 284 |
+// InternalIP returns the instance's primary internal IP address. |
|
| 285 |
+func InternalIP() (string, error) { return defaultClient.InternalIP() }
|
|
| 286 |
+ |
|
| 287 |
+// ExternalIP returns the instance's primary external (public) IP address. |
|
| 288 |
+func ExternalIP() (string, error) { return defaultClient.ExternalIP() }
|
|
| 289 |
+ |
|
| 290 |
+// Hostname returns the instance's hostname. This will be of the form |
|
| 291 |
+// "<instanceID>.c.<projID>.internal". |
|
| 292 |
+func Hostname() (string, error) { return defaultClient.Hostname() }
|
|
| 293 |
+ |
|
| 294 |
+// InstanceTags returns the list of user-defined instance tags, |
|
| 295 |
+// assigned when initially creating a GCE instance. |
|
| 296 |
+func InstanceTags() ([]string, error) { return defaultClient.InstanceTags() }
|
|
| 297 |
+ |
|
| 298 |
+// InstanceID returns the current VM's numeric instance ID. |
|
| 299 |
+func InstanceID() (string, error) { return defaultClient.InstanceID() }
|
|
| 300 |
+ |
|
| 301 |
+// InstanceName returns the current VM's instance ID string. |
|
| 302 |
+func InstanceName() (string, error) { return defaultClient.InstanceName() }
|
|
| 303 |
+ |
|
| 304 |
+// Zone returns the current VM's zone, such as "us-central1-b". |
|
| 305 |
+func Zone() (string, error) { return defaultClient.Zone() }
|
|
| 306 |
+ |
|
| 307 |
+// InstanceAttributes calls Client.InstanceAttributes on the default client. |
|
| 308 |
+func InstanceAttributes() ([]string, error) { return defaultClient.InstanceAttributes() }
|
|
| 309 |
+ |
|
| 310 |
+// ProjectAttributes calls Client.ProjectAttributes on the default client. |
|
| 311 |
+func ProjectAttributes() ([]string, error) { return defaultClient.ProjectAttributes() }
|
|
| 312 |
+ |
|
| 313 |
+// InstanceAttributeValue calls Client.InstanceAttributeValue on the default client. |
|
| 314 |
+func InstanceAttributeValue(attr string) (string, error) {
|
|
| 315 |
+ return defaultClient.InstanceAttributeValue(attr) |
|
| 316 |
+} |
|
| 317 |
+ |
|
| 318 |
+// ProjectAttributeValue calls Client.ProjectAttributeValue on the default client. |
|
| 319 |
+func ProjectAttributeValue(attr string) (string, error) {
|
|
| 320 |
+ return defaultClient.ProjectAttributeValue(attr) |
|
| 321 |
+} |
|
| 322 |
+ |
|
| 323 |
+// Scopes calls Client.Scopes on the default client. |
|
| 324 |
+func Scopes(serviceAccount string) ([]string, error) { return defaultClient.Scopes(serviceAccount) }
|
|
| 325 |
+ |
|
| 326 |
+func strsContains(ss []string, s string) bool {
|
|
| 327 |
+ for _, v := range ss {
|
|
| 328 |
+ if v == s {
|
|
| 329 |
+ return true |
|
| 330 |
+ } |
|
| 289 | 331 |
} |
| 332 |
+ return false |
|
| 333 |
+} |
|
| 290 | 334 |
|
| 291 |
- ok := true |
|
| 292 |
- if strings.ContainsRune(suffix, '?') {
|
|
| 293 |
- suffix += "&wait_for_change=true&last_etag=" |
|
| 294 |
- } else {
|
|
| 295 |
- suffix += "?wait_for_change=true&last_etag=" |
|
| 335 |
+// A Client provides metadata. |
|
| 336 |
+type Client struct {
|
|
| 337 |
+ hc *http.Client |
|
| 338 |
+} |
|
| 339 |
+ |
|
| 340 |
+// NewClient returns a Client that can be used to fetch metadata. All HTTP requests |
|
| 341 |
+// will use the given http.Client instead of the default client. |
|
| 342 |
+func NewClient(c *http.Client) *Client {
|
|
| 343 |
+ return &Client{hc: c}
|
|
| 344 |
+} |
|
| 345 |
+ |
|
| 346 |
+// getETag returns a value from the metadata service as well as the associated ETag. |
|
| 347 |
+// This func is otherwise equivalent to Get. |
|
| 348 |
+func (c *Client) getETag(suffix string) (value, etag string, err error) {
|
|
| 349 |
+ // Using a fixed IP makes it very difficult to spoof the metadata service in |
|
| 350 |
+ // a container, which is an important use-case for local testing of cloud |
|
| 351 |
+ // deployments. To enable spoofing of the metadata service, the environment |
|
| 352 |
+ // variable GCE_METADATA_HOST is first inspected to decide where metadata |
|
| 353 |
+ // requests shall go. |
|
| 354 |
+ host := os.Getenv(metadataHostEnv) |
|
| 355 |
+ if host == "" {
|
|
| 356 |
+ // Using 169.254.169.254 instead of "metadata" here because Go |
|
| 357 |
+ // binaries built with the "netgo" tag and without cgo won't |
|
| 358 |
+ // know the search suffix for "metadata" is |
|
| 359 |
+ // ".google.internal", and this IP address is documented as |
|
| 360 |
+ // being stable anyway. |
|
| 361 |
+ host = metadataIP |
|
| 296 | 362 |
} |
| 297 |
- for {
|
|
| 298 |
- val, etag, err := getETag(subscribeClient, suffix+url.QueryEscape(lastETag)) |
|
| 299 |
- if err != nil {
|
|
| 300 |
- if _, deleted := err.(NotDefinedError); !deleted {
|
|
| 301 |
- time.Sleep(failedSubscribeSleep) |
|
| 302 |
- continue // Retry on other errors. |
|
| 303 |
- } |
|
| 304 |
- ok = false |
|
| 305 |
- } |
|
| 306 |
- lastETag = etag |
|
| 363 |
+ u := "http://" + host + "/computeMetadata/v1/" + suffix |
|
| 364 |
+ req, _ := http.NewRequest("GET", u, nil)
|
|
| 365 |
+ req.Header.Set("Metadata-Flavor", "Google")
|
|
| 366 |
+ req.Header.Set("User-Agent", userAgent)
|
|
| 367 |
+ res, err := c.hc.Do(req) |
|
| 368 |
+ if err != nil {
|
|
| 369 |
+ return "", "", err |
|
| 370 |
+ } |
|
| 371 |
+ defer res.Body.Close() |
|
| 372 |
+ if res.StatusCode == http.StatusNotFound {
|
|
| 373 |
+ return "", "", NotDefinedError(suffix) |
|
| 374 |
+ } |
|
| 375 |
+ all, err := ioutil.ReadAll(res.Body) |
|
| 376 |
+ if err != nil {
|
|
| 377 |
+ return "", "", err |
|
| 378 |
+ } |
|
| 379 |
+ if res.StatusCode != 200 {
|
|
| 380 |
+ return "", "", &Error{Code: res.StatusCode, Message: string(all)}
|
|
| 381 |
+ } |
|
| 382 |
+ return string(all), res.Header.Get("Etag"), nil
|
|
| 383 |
+} |
|
| 307 | 384 |
|
| 308 |
- if err := fn(val, ok); err != nil || !ok {
|
|
| 309 |
- return err |
|
| 310 |
- } |
|
| 385 |
+// Get returns a value from the metadata service. |
|
| 386 |
+// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
|
|
| 387 |
+// |
|
| 388 |
+// If the GCE_METADATA_HOST environment variable is not defined, a default of |
|
| 389 |
+// 169.254.169.254 will be used instead. |
|
| 390 |
+// |
|
| 391 |
+// If the requested metadata is not defined, the returned error will |
|
| 392 |
+// be of type NotDefinedError. |
|
| 393 |
+func (c *Client) Get(suffix string) (string, error) {
|
|
| 394 |
+ val, _, err := c.getETag(suffix) |
|
| 395 |
+ return val, err |
|
| 396 |
+} |
|
| 397 |
+ |
|
| 398 |
+func (c *Client) getTrimmed(suffix string) (s string, err error) {
|
|
| 399 |
+ s, err = c.Get(suffix) |
|
| 400 |
+ s = strings.TrimSpace(s) |
|
| 401 |
+ return |
|
| 402 |
+} |
|
| 403 |
+ |
|
| 404 |
+func (c *Client) lines(suffix string) ([]string, error) {
|
|
| 405 |
+ j, err := c.Get(suffix) |
|
| 406 |
+ if err != nil {
|
|
| 407 |
+ return nil, err |
|
| 311 | 408 |
} |
| 409 |
+ s := strings.Split(strings.TrimSpace(j), "\n") |
|
| 410 |
+ for i := range s {
|
|
| 411 |
+ s[i] = strings.TrimSpace(s[i]) |
|
| 412 |
+ } |
|
| 413 |
+ return s, nil |
|
| 312 | 414 |
} |
| 313 | 415 |
|
| 314 | 416 |
// ProjectID returns the current instance's project ID string. |
| 315 |
-func ProjectID() (string, error) { return projID.get() }
|
|
| 417 |
+func (c *Client) ProjectID() (string, error) { return projID.get(c) }
|
|
| 316 | 418 |
|
| 317 | 419 |
// NumericProjectID returns the current instance's numeric project ID. |
| 318 |
-func NumericProjectID() (string, error) { return projNum.get() }
|
|
| 420 |
+func (c *Client) NumericProjectID() (string, error) { return projNum.get(c) }
|
|
| 421 |
+ |
|
| 422 |
+// InstanceID returns the current VM's numeric instance ID. |
|
| 423 |
+func (c *Client) InstanceID() (string, error) { return instID.get(c) }
|
|
| 319 | 424 |
|
| 320 | 425 |
// InternalIP returns the instance's primary internal IP address. |
| 321 |
-func InternalIP() (string, error) {
|
|
| 322 |
- return getTrimmed("instance/network-interfaces/0/ip")
|
|
| 426 |
+func (c *Client) InternalIP() (string, error) {
|
|
| 427 |
+ return c.getTrimmed("instance/network-interfaces/0/ip")
|
|
| 323 | 428 |
} |
| 324 | 429 |
|
| 325 | 430 |
// ExternalIP returns the instance's primary external (public) IP address. |
| 326 |
-func ExternalIP() (string, error) {
|
|
| 327 |
- return getTrimmed("instance/network-interfaces/0/access-configs/0/external-ip")
|
|
| 431 |
+func (c *Client) ExternalIP() (string, error) {
|
|
| 432 |
+ return c.getTrimmed("instance/network-interfaces/0/access-configs/0/external-ip")
|
|
| 328 | 433 |
} |
| 329 | 434 |
|
| 330 | 435 |
// Hostname returns the instance's hostname. This will be of the form |
| 331 | 436 |
// "<instanceID>.c.<projID>.internal". |
| 332 |
-func Hostname() (string, error) {
|
|
| 333 |
- return getTrimmed("instance/hostname")
|
|
| 437 |
+func (c *Client) Hostname() (string, error) {
|
|
| 438 |
+ return c.getTrimmed("instance/hostname")
|
|
| 334 | 439 |
} |
| 335 | 440 |
|
| 336 | 441 |
// InstanceTags returns the list of user-defined instance tags, |
| 337 | 442 |
// assigned when initially creating a GCE instance. |
| 338 |
-func InstanceTags() ([]string, error) {
|
|
| 443 |
+func (c *Client) InstanceTags() ([]string, error) {
|
|
| 339 | 444 |
var s []string |
| 340 |
- j, err := Get("instance/tags")
|
|
| 445 |
+ j, err := c.Get("instance/tags")
|
|
| 341 | 446 |
if err != nil {
|
| 342 | 447 |
return nil, err |
| 343 | 448 |
} |
| ... | ... |
@@ -347,14 +392,9 @@ func InstanceTags() ([]string, error) {
|
| 347 | 347 |
return s, nil |
| 348 | 348 |
} |
| 349 | 349 |
|
| 350 |
-// InstanceID returns the current VM's numeric instance ID. |
|
| 351 |
-func InstanceID() (string, error) {
|
|
| 352 |
- return instID.get() |
|
| 353 |
-} |
|
| 354 |
- |
|
| 355 | 350 |
// InstanceName returns the current VM's instance ID string. |
| 356 |
-func InstanceName() (string, error) {
|
|
| 357 |
- host, err := Hostname() |
|
| 351 |
+func (c *Client) InstanceName() (string, error) {
|
|
| 352 |
+ host, err := c.Hostname() |
|
| 358 | 353 |
if err != nil {
|
| 359 | 354 |
return "", err |
| 360 | 355 |
} |
| ... | ... |
@@ -362,8 +402,8 @@ func InstanceName() (string, error) {
|
| 362 | 362 |
} |
| 363 | 363 |
|
| 364 | 364 |
// Zone returns the current VM's zone, such as "us-central1-b". |
| 365 |
-func Zone() (string, error) {
|
|
| 366 |
- zone, err := getTrimmed("instance/zone")
|
|
| 365 |
+func (c *Client) Zone() (string, error) {
|
|
| 366 |
+ zone, err := c.getTrimmed("instance/zone")
|
|
| 367 | 367 |
// zone is of the form "projects/<projNum>/zones/<zoneName>". |
| 368 | 368 |
if err != nil {
|
| 369 | 369 |
return "", err |
| ... | ... |
@@ -374,24 +414,12 @@ func Zone() (string, error) {
|
| 374 | 374 |
// InstanceAttributes returns the list of user-defined attributes, |
| 375 | 375 |
// assigned when initially creating a GCE VM instance. The value of an |
| 376 | 376 |
// attribute can be obtained with InstanceAttributeValue. |
| 377 |
-func InstanceAttributes() ([]string, error) { return lines("instance/attributes/") }
|
|
| 377 |
+func (c *Client) InstanceAttributes() ([]string, error) { return c.lines("instance/attributes/") }
|
|
| 378 | 378 |
|
| 379 | 379 |
// ProjectAttributes returns the list of user-defined attributes |
| 380 | 380 |
// applying to the project as a whole, not just this VM. The value of |
| 381 | 381 |
// an attribute can be obtained with ProjectAttributeValue. |
| 382 |
-func ProjectAttributes() ([]string, error) { return lines("project/attributes/") }
|
|
| 383 |
- |
|
| 384 |
-func lines(suffix string) ([]string, error) {
|
|
| 385 |
- j, err := Get(suffix) |
|
| 386 |
- if err != nil {
|
|
| 387 |
- return nil, err |
|
| 388 |
- } |
|
| 389 |
- s := strings.Split(strings.TrimSpace(j), "\n") |
|
| 390 |
- for i := range s {
|
|
| 391 |
- s[i] = strings.TrimSpace(s[i]) |
|
| 392 |
- } |
|
| 393 |
- return s, nil |
|
| 394 |
-} |
|
| 382 |
+func (c *Client) ProjectAttributes() ([]string, error) { return c.lines("project/attributes/") }
|
|
| 395 | 383 |
|
| 396 | 384 |
// InstanceAttributeValue returns the value of the provided VM |
| 397 | 385 |
// instance attribute. |
| ... | ... |
@@ -401,8 +429,8 @@ func lines(suffix string) ([]string, error) {
|
| 401 | 401 |
// |
| 402 | 402 |
// InstanceAttributeValue may return ("", nil) if the attribute was
|
| 403 | 403 |
// defined to be the empty string. |
| 404 |
-func InstanceAttributeValue(attr string) (string, error) {
|
|
| 405 |
- return Get("instance/attributes/" + attr)
|
|
| 404 |
+func (c *Client) InstanceAttributeValue(attr string) (string, error) {
|
|
| 405 |
+ return c.Get("instance/attributes/" + attr)
|
|
| 406 | 406 |
} |
| 407 | 407 |
|
| 408 | 408 |
// ProjectAttributeValue returns the value of the provided |
| ... | ... |
@@ -413,25 +441,73 @@ func InstanceAttributeValue(attr string) (string, error) {
|
| 413 | 413 |
// |
| 414 | 414 |
// ProjectAttributeValue may return ("", nil) if the attribute was
|
| 415 | 415 |
// defined to be the empty string. |
| 416 |
-func ProjectAttributeValue(attr string) (string, error) {
|
|
| 417 |
- return Get("project/attributes/" + attr)
|
|
| 416 |
+func (c *Client) ProjectAttributeValue(attr string) (string, error) {
|
|
| 417 |
+ return c.Get("project/attributes/" + attr)
|
|
| 418 | 418 |
} |
| 419 | 419 |
|
| 420 | 420 |
// Scopes returns the service account scopes for the given account. |
| 421 | 421 |
// The account may be empty or the string "default" to use the instance's |
| 422 | 422 |
// main account. |
| 423 |
-func Scopes(serviceAccount string) ([]string, error) {
|
|
| 423 |
+func (c *Client) Scopes(serviceAccount string) ([]string, error) {
|
|
| 424 | 424 |
if serviceAccount == "" {
|
| 425 | 425 |
serviceAccount = "default" |
| 426 | 426 |
} |
| 427 |
- return lines("instance/service-accounts/" + serviceAccount + "/scopes")
|
|
| 427 |
+ return c.lines("instance/service-accounts/" + serviceAccount + "/scopes")
|
|
| 428 | 428 |
} |
| 429 | 429 |
|
| 430 |
-func strsContains(ss []string, s string) bool {
|
|
| 431 |
- for _, v := range ss {
|
|
| 432 |
- if v == s {
|
|
| 433 |
- return true |
|
| 430 |
+// Subscribe subscribes to a value from the metadata service. |
|
| 431 |
+// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
|
|
| 432 |
+// The suffix may contain query parameters. |
|
| 433 |
+// |
|
| 434 |
+// Subscribe calls fn with the latest metadata value indicated by the provided |
|
| 435 |
+// suffix. If the metadata value is deleted, fn is called with the empty string |
|
| 436 |
+// and ok false. Subscribe blocks until fn returns a non-nil error or the value |
|
| 437 |
+// is deleted. Subscribe returns the error value returned from the last call to |
|
| 438 |
+// fn, which may be nil when ok == false. |
|
| 439 |
+func (c *Client) Subscribe(suffix string, fn func(v string, ok bool) error) error {
|
|
| 440 |
+ const failedSubscribeSleep = time.Second * 5 |
|
| 441 |
+ |
|
| 442 |
+ // First check to see if the metadata value exists at all. |
|
| 443 |
+ val, lastETag, err := c.getETag(suffix) |
|
| 444 |
+ if err != nil {
|
|
| 445 |
+ return err |
|
| 446 |
+ } |
|
| 447 |
+ |
|
| 448 |
+ if err := fn(val, true); err != nil {
|
|
| 449 |
+ return err |
|
| 450 |
+ } |
|
| 451 |
+ |
|
| 452 |
+ ok := true |
|
| 453 |
+ if strings.ContainsRune(suffix, '?') {
|
|
| 454 |
+ suffix += "&wait_for_change=true&last_etag=" |
|
| 455 |
+ } else {
|
|
| 456 |
+ suffix += "?wait_for_change=true&last_etag=" |
|
| 457 |
+ } |
|
| 458 |
+ for {
|
|
| 459 |
+ val, etag, err := c.getETag(suffix + url.QueryEscape(lastETag)) |
|
| 460 |
+ if err != nil {
|
|
| 461 |
+ if _, deleted := err.(NotDefinedError); !deleted {
|
|
| 462 |
+ time.Sleep(failedSubscribeSleep) |
|
| 463 |
+ continue // Retry on other errors. |
|
| 464 |
+ } |
|
| 465 |
+ ok = false |
|
| 466 |
+ } |
|
| 467 |
+ lastETag = etag |
|
| 468 |
+ |
|
| 469 |
+ if err := fn(val, ok); err != nil || !ok {
|
|
| 470 |
+ return err |
|
| 434 | 471 |
} |
| 435 | 472 |
} |
| 436 |
- return false |
|
| 473 |
+} |
|
| 474 |
+ |
|
| 475 |
+// Error contains an error response from the server. |
|
| 476 |
+type Error struct {
|
|
| 477 |
+ // Code is the HTTP response status code. |
|
| 478 |
+ Code int |
|
| 479 |
+ // Message is the server response message. |
|
| 480 |
+ Message string |
|
| 481 |
+} |
|
| 482 |
+ |
|
| 483 |
+func (e *Error) Error() string {
|
|
| 484 |
+ return fmt.Sprintf("compute: Received %d `%s`", e.Code, e.Message)
|
|
| 437 | 485 |
} |
| 438 | 486 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,29 @@ |
| 0 |
+module cloud.google.com/go |
|
| 1 |
+ |
|
| 2 |
+go 1.9 |
|
| 3 |
+ |
|
| 4 |
+require ( |
|
| 5 |
+ cloud.google.com/go/datastore v1.0.0 |
|
| 6 |
+ github.com/golang/mock v1.3.1 |
|
| 7 |
+ github.com/golang/protobuf v1.3.2 |
|
| 8 |
+ github.com/google/btree v1.0.0 |
|
| 9 |
+ github.com/google/go-cmp v0.3.0 |
|
| 10 |
+ github.com/google/martian v2.1.0+incompatible |
|
| 11 |
+ github.com/google/pprof v0.0.0-20190515194954-54271f7e092f |
|
| 12 |
+ github.com/googleapis/gax-go/v2 v2.0.5 |
|
| 13 |
+ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024 |
|
| 14 |
+ go.opencensus.io v0.22.0 |
|
| 15 |
+ golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522 |
|
| 16 |
+ golang.org/x/lint v0.0.0-20190409202823-959b441ac422 |
|
| 17 |
+ golang.org/x/net v0.0.0-20190620200207-3b0461eec859 |
|
| 18 |
+ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 |
|
| 19 |
+ golang.org/x/sync v0.0.0-20190423024810-112230192c58 |
|
| 20 |
+ golang.org/x/text v0.3.2 |
|
| 21 |
+ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 |
|
| 22 |
+ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0 |
|
| 23 |
+ google.golang.org/api v0.8.0 |
|
| 24 |
+ google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64 |
|
| 25 |
+ google.golang.org/grpc v1.21.1 |
|
| 26 |
+ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a |
|
| 27 |
+ rsc.io/binaryregexp v0.2.0 |
|
| 28 |
+) |
| ... | ... |
@@ -1,4 +1,4 @@ |
| 1 |
-// Copyright 2016 Google Inc. All Rights Reserved. |
|
| 1 |
+// Copyright 2016 Google LLC |
|
| 2 | 2 |
// |
| 3 | 3 |
// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | 4 |
// you may not use this file except in compliance with the License. |
| ... | ... |
@@ -26,7 +26,7 @@ import ( |
| 26 | 26 |
|
| 27 | 27 |
// Repo is the current version of the client libraries in this |
| 28 | 28 |
// repo. It should be a date in YYYYMMDD format. |
| 29 |
-const Repo = "20180226" |
|
| 29 |
+const Repo = "20190802" |
|
| 30 | 30 |
|
| 31 | 31 |
// Go returns the Go runtime version. The returned string |
| 32 | 32 |
// has no whitespace. |
| ... | ... |
@@ -67,5 +67,5 @@ func goVer(s string) string {
|
| 67 | 67 |
} |
| 68 | 68 |
|
| 69 | 69 |
func notSemverRune(r rune) bool {
|
| 70 |
- return strings.IndexRune("0123456789.", r) < 0
|
|
| 70 |
+ return !strings.ContainsRune("0123456789.", r)
|
|
| 71 | 71 |
} |
| 72 | 72 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,35 @@ |
| 0 |
+## Stackdriver Logging [](https://godoc.org/cloud.google.com/go/logging) |
|
| 1 |
+ |
|
| 2 |
+- [About Stackdriver Logging](https://cloud.google.com/logging/) |
|
| 3 |
+- [API documentation](https://cloud.google.com/logging/docs) |
|
| 4 |
+- [Go client documentation](https://godoc.org/cloud.google.com/go/logging) |
|
| 5 |
+- [Complete sample programs](https://github.com/GoogleCloudPlatform/golang-samples/tree/master/logging) |
|
| 6 |
+ |
|
| 7 |
+### Example Usage |
|
| 8 |
+ |
|
| 9 |
+First create a `logging.Client` to use throughout your application: |
|
| 10 |
+[snip]:# (logging-1) |
|
| 11 |
+```go |
|
| 12 |
+ctx := context.Background() |
|
| 13 |
+client, err := logging.NewClient(ctx, "my-project") |
|
| 14 |
+if err != nil {
|
|
| 15 |
+ // TODO: Handle error. |
|
| 16 |
+} |
|
| 17 |
+``` |
|
| 18 |
+ |
|
| 19 |
+Usually, you'll want to add log entries to a buffer to be periodically flushed |
|
| 20 |
+(automatically and asynchronously) to the Stackdriver Logging service. |
|
| 21 |
+[snip]:# (logging-2) |
|
| 22 |
+```go |
|
| 23 |
+logger := client.Logger("my-log")
|
|
| 24 |
+logger.Log(logging.Entry{Payload: "something happened!"})
|
|
| 25 |
+``` |
|
| 26 |
+ |
|
| 27 |
+Close your client before your program exits, to flush any buffered log entries. |
|
| 28 |
+[snip]:# (logging-3) |
|
| 29 |
+```go |
|
| 30 |
+err = client.Close() |
|
| 31 |
+if err != nil {
|
|
| 32 |
+ // TODO: Handle error. |
|
| 33 |
+} |
|
| 34 |
+``` |
|
| 0 | 35 |
\ No newline at end of file |
| ... | ... |
@@ -1,4 +1,4 @@ |
| 1 |
-// Copyright 2018 Google LLC |
|
| 1 |
+// Copyright 2019 Google LLC |
|
| 2 | 2 |
// |
| 3 | 3 |
// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | 4 |
// you may not use this file except in compliance with the License. |
| ... | ... |
@@ -12,17 +12,19 @@ |
| 12 | 12 |
// See the License for the specific language governing permissions and |
| 13 | 13 |
// limitations under the License. |
| 14 | 14 |
|
| 15 |
-// AUTO-GENERATED CODE. DO NOT EDIT. |
|
| 15 |
+// Code generated by gapic-generator. DO NOT EDIT. |
|
| 16 | 16 |
|
| 17 | 17 |
package logging |
| 18 | 18 |
|
| 19 | 19 |
import ( |
| 20 |
+ "context" |
|
| 21 |
+ "fmt" |
|
| 20 | 22 |
"math" |
| 23 |
+ "net/url" |
|
| 21 | 24 |
"time" |
| 22 | 25 |
|
| 23 |
- "cloud.google.com/go/internal/version" |
|
| 24 |
- gax "github.com/googleapis/gax-go" |
|
| 25 |
- "golang.org/x/net/context" |
|
| 26 |
+ "github.com/golang/protobuf/proto" |
|
| 27 |
+ gax "github.com/googleapis/gax-go/v2" |
|
| 26 | 28 |
"google.golang.org/api/iterator" |
| 27 | 29 |
"google.golang.org/api/option" |
| 28 | 30 |
"google.golang.org/api/transport" |
| ... | ... |
@@ -63,8 +65,8 @@ func defaultConfigCallOptions() *ConfigCallOptions {
|
| 63 | 63 |
codes.Unavailable, |
| 64 | 64 |
}, gax.Backoff{
|
| 65 | 65 |
Initial: 100 * time.Millisecond, |
| 66 |
- Max: 1000 * time.Millisecond, |
|
| 67 |
- Multiplier: 1.2, |
|
| 66 |
+ Max: 60000 * time.Millisecond, |
|
| 67 |
+ Multiplier: 1.3, |
|
| 68 | 68 |
}) |
| 69 | 69 |
}), |
| 70 | 70 |
}, |
| ... | ... |
@@ -73,7 +75,7 @@ func defaultConfigCallOptions() *ConfigCallOptions {
|
| 73 | 73 |
ListSinks: retry[[2]string{"default", "idempotent"}],
|
| 74 | 74 |
GetSink: retry[[2]string{"default", "idempotent"}],
|
| 75 | 75 |
CreateSink: retry[[2]string{"default", "non_idempotent"}],
|
| 76 |
- UpdateSink: retry[[2]string{"default", "non_idempotent"}],
|
|
| 76 |
+ UpdateSink: retry[[2]string{"default", "idempotent"}],
|
|
| 77 | 77 |
DeleteSink: retry[[2]string{"default", "idempotent"}],
|
| 78 | 78 |
ListExclusions: retry[[2]string{"default", "idempotent"}],
|
| 79 | 79 |
GetExclusion: retry[[2]string{"default", "idempotent"}],
|
| ... | ... |
@@ -84,6 +86,8 @@ func defaultConfigCallOptions() *ConfigCallOptions {
|
| 84 | 84 |
} |
| 85 | 85 |
|
| 86 | 86 |
// ConfigClient is a client for interacting with Stackdriver Logging API. |
| 87 |
+// |
|
| 88 |
+// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. |
|
| 87 | 89 |
type ConfigClient struct {
|
| 88 | 90 |
// The connection to the service. |
| 89 | 91 |
conn *grpc.ClientConn |
| ... | ... |
@@ -100,8 +104,8 @@ type ConfigClient struct {
|
| 100 | 100 |
|
| 101 | 101 |
// NewConfigClient creates a new config service v2 client. |
| 102 | 102 |
// |
| 103 |
-// Service for configuring sinks used to export log entries outside of |
|
| 104 |
-// Stackdriver Logging. |
|
| 103 |
+// Service for configuring sinks used to export log entries out of |
|
| 104 |
+// Logging. |
|
| 105 | 105 |
func NewConfigClient(ctx context.Context, opts ...option.ClientOption) (*ConfigClient, error) {
|
| 106 | 106 |
conn, err := transport.DialGRPC(ctx, append(defaultConfigClientOptions(), opts...)...) |
| 107 | 107 |
if err != nil {
|
| ... | ... |
@@ -132,16 +136,18 @@ func (c *ConfigClient) Close() error {
|
| 132 | 132 |
// the `x-goog-api-client` header passed on each request. Intended for |
| 133 | 133 |
// use by Google-written clients. |
| 134 | 134 |
func (c *ConfigClient) SetGoogleClientInfo(keyval ...string) {
|
| 135 |
- kv := append([]string{"gl-go", version.Go()}, keyval...)
|
|
| 136 |
- kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) |
|
| 135 |
+ kv := append([]string{"gl-go", versionGo()}, keyval...)
|
|
| 136 |
+ kv = append(kv, "gapic", versionClient, "gax", gax.Version, "grpc", grpc.Version) |
|
| 137 | 137 |
c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...))
|
| 138 | 138 |
} |
| 139 | 139 |
|
| 140 | 140 |
// ListSinks lists sinks. |
| 141 | 141 |
func (c *ConfigClient) ListSinks(ctx context.Context, req *loggingpb.ListSinksRequest, opts ...gax.CallOption) *LogSinkIterator {
|
| 142 |
- ctx = insertMetadata(ctx, c.xGoogMetadata) |
|
| 142 |
+ md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "parent", url.QueryEscape(req.GetParent())))
|
|
| 143 |
+ ctx = insertMetadata(ctx, c.xGoogMetadata, md) |
|
| 143 | 144 |
opts = append(c.CallOptions.ListSinks[0:len(c.CallOptions.ListSinks):len(c.CallOptions.ListSinks)], opts...) |
| 144 | 145 |
it := &LogSinkIterator{}
|
| 146 |
+ req = proto.Clone(req).(*loggingpb.ListSinksRequest) |
|
| 145 | 147 |
it.InternalFetch = func(pageSize int, pageToken string) ([]*loggingpb.LogSink, string, error) {
|
| 146 | 148 |
var resp *loggingpb.ListSinksResponse |
| 147 | 149 |
req.PageToken = pageToken |
| ... | ... |
@@ -169,12 +175,15 @@ func (c *ConfigClient) ListSinks(ctx context.Context, req *loggingpb.ListSinksRe |
| 169 | 169 |
return nextPageToken, nil |
| 170 | 170 |
} |
| 171 | 171 |
it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf) |
| 172 |
+ it.pageInfo.MaxSize = int(req.PageSize) |
|
| 173 |
+ it.pageInfo.Token = req.PageToken |
|
| 172 | 174 |
return it |
| 173 | 175 |
} |
| 174 | 176 |
|
| 175 | 177 |
// GetSink gets a sink. |
| 176 | 178 |
func (c *ConfigClient) GetSink(ctx context.Context, req *loggingpb.GetSinkRequest, opts ...gax.CallOption) (*loggingpb.LogSink, error) {
|
| 177 |
- ctx = insertMetadata(ctx, c.xGoogMetadata) |
|
| 179 |
+ md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "sink_name", url.QueryEscape(req.GetSinkName())))
|
|
| 180 |
+ ctx = insertMetadata(ctx, c.xGoogMetadata, md) |
|
| 178 | 181 |
opts = append(c.CallOptions.GetSink[0:len(c.CallOptions.GetSink):len(c.CallOptions.GetSink)], opts...) |
| 179 | 182 |
var resp *loggingpb.LogSink |
| 180 | 183 |
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
|
| ... | ... |
@@ -193,7 +202,8 @@ func (c *ConfigClient) GetSink(ctx context.Context, req *loggingpb.GetSinkReques |
| 193 | 193 |
// writer_identity is not permitted to write to the destination. A sink can |
| 194 | 194 |
// export log entries only from the resource owning the sink. |
| 195 | 195 |
func (c *ConfigClient) CreateSink(ctx context.Context, req *loggingpb.CreateSinkRequest, opts ...gax.CallOption) (*loggingpb.LogSink, error) {
|
| 196 |
- ctx = insertMetadata(ctx, c.xGoogMetadata) |
|
| 196 |
+ md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "parent", url.QueryEscape(req.GetParent())))
|
|
| 197 |
+ ctx = insertMetadata(ctx, c.xGoogMetadata, md) |
|
| 197 | 198 |
opts = append(c.CallOptions.CreateSink[0:len(c.CallOptions.CreateSink):len(c.CallOptions.CreateSink)], opts...) |
| 198 | 199 |
var resp *loggingpb.LogSink |
| 199 | 200 |
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
|
| ... | ... |
@@ -212,7 +222,8 @@ func (c *ConfigClient) CreateSink(ctx context.Context, req *loggingpb.CreateSink |
| 212 | 212 |
// The updated sink might also have a new writer_identity; see the |
| 213 | 213 |
// unique_writer_identity field. |
| 214 | 214 |
func (c *ConfigClient) UpdateSink(ctx context.Context, req *loggingpb.UpdateSinkRequest, opts ...gax.CallOption) (*loggingpb.LogSink, error) {
|
| 215 |
- ctx = insertMetadata(ctx, c.xGoogMetadata) |
|
| 215 |
+ md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "sink_name", url.QueryEscape(req.GetSinkName())))
|
|
| 216 |
+ ctx = insertMetadata(ctx, c.xGoogMetadata, md) |
|
| 216 | 217 |
opts = append(c.CallOptions.UpdateSink[0:len(c.CallOptions.UpdateSink):len(c.CallOptions.UpdateSink)], opts...) |
| 217 | 218 |
var resp *loggingpb.LogSink |
| 218 | 219 |
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
|
| ... | ... |
@@ -229,7 +240,8 @@ func (c *ConfigClient) UpdateSink(ctx context.Context, req *loggingpb.UpdateSink |
| 229 | 229 |
// DeleteSink deletes a sink. If the sink has a unique writer_identity, then that |
| 230 | 230 |
// service account is also deleted. |
| 231 | 231 |
func (c *ConfigClient) DeleteSink(ctx context.Context, req *loggingpb.DeleteSinkRequest, opts ...gax.CallOption) error {
|
| 232 |
- ctx = insertMetadata(ctx, c.xGoogMetadata) |
|
| 232 |
+ md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "sink_name", url.QueryEscape(req.GetSinkName())))
|
|
| 233 |
+ ctx = insertMetadata(ctx, c.xGoogMetadata, md) |
|
| 233 | 234 |
opts = append(c.CallOptions.DeleteSink[0:len(c.CallOptions.DeleteSink):len(c.CallOptions.DeleteSink)], opts...) |
| 234 | 235 |
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
|
| 235 | 236 |
var err error |
| ... | ... |
@@ -241,9 +253,11 @@ func (c *ConfigClient) DeleteSink(ctx context.Context, req *loggingpb.DeleteSink |
| 241 | 241 |
|
| 242 | 242 |
// ListExclusions lists all the exclusions in a parent resource. |
| 243 | 243 |
func (c *ConfigClient) ListExclusions(ctx context.Context, req *loggingpb.ListExclusionsRequest, opts ...gax.CallOption) *LogExclusionIterator {
|
| 244 |
- ctx = insertMetadata(ctx, c.xGoogMetadata) |
|
| 244 |
+ md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "parent", url.QueryEscape(req.GetParent())))
|
|
| 245 |
+ ctx = insertMetadata(ctx, c.xGoogMetadata, md) |
|
| 245 | 246 |
opts = append(c.CallOptions.ListExclusions[0:len(c.CallOptions.ListExclusions):len(c.CallOptions.ListExclusions)], opts...) |
| 246 | 247 |
it := &LogExclusionIterator{}
|
| 248 |
+ req = proto.Clone(req).(*loggingpb.ListExclusionsRequest) |
|
| 247 | 249 |
it.InternalFetch = func(pageSize int, pageToken string) ([]*loggingpb.LogExclusion, string, error) {
|
| 248 | 250 |
var resp *loggingpb.ListExclusionsResponse |
| 249 | 251 |
req.PageToken = pageToken |
| ... | ... |
@@ -271,12 +285,15 @@ func (c *ConfigClient) ListExclusions(ctx context.Context, req *loggingpb.ListEx |
| 271 | 271 |
return nextPageToken, nil |
| 272 | 272 |
} |
| 273 | 273 |
it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf) |
| 274 |
+ it.pageInfo.MaxSize = int(req.PageSize) |
|
| 275 |
+ it.pageInfo.Token = req.PageToken |
|
| 274 | 276 |
return it |
| 275 | 277 |
} |
| 276 | 278 |
|
| 277 | 279 |
// GetExclusion gets the description of an exclusion. |
| 278 | 280 |
func (c *ConfigClient) GetExclusion(ctx context.Context, req *loggingpb.GetExclusionRequest, opts ...gax.CallOption) (*loggingpb.LogExclusion, error) {
|
| 279 |
- ctx = insertMetadata(ctx, c.xGoogMetadata) |
|
| 281 |
+ md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "name", url.QueryEscape(req.GetName())))
|
|
| 282 |
+ ctx = insertMetadata(ctx, c.xGoogMetadata, md) |
|
| 280 | 283 |
opts = append(c.CallOptions.GetExclusion[0:len(c.CallOptions.GetExclusion):len(c.CallOptions.GetExclusion)], opts...) |
| 281 | 284 |
var resp *loggingpb.LogExclusion |
| 282 | 285 |
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
|
| ... | ... |
@@ -294,7 +311,8 @@ func (c *ConfigClient) GetExclusion(ctx context.Context, req *loggingpb.GetExclu |
| 294 | 294 |
// Only log entries belonging to that resource can be excluded. |
| 295 | 295 |
// You can have up to 10 exclusions in a resource. |
| 296 | 296 |
func (c *ConfigClient) CreateExclusion(ctx context.Context, req *loggingpb.CreateExclusionRequest, opts ...gax.CallOption) (*loggingpb.LogExclusion, error) {
|
| 297 |
- ctx = insertMetadata(ctx, c.xGoogMetadata) |
|
| 297 |
+ md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "parent", url.QueryEscape(req.GetParent())))
|
|
| 298 |
+ ctx = insertMetadata(ctx, c.xGoogMetadata, md) |
|
| 298 | 299 |
opts = append(c.CallOptions.CreateExclusion[0:len(c.CallOptions.CreateExclusion):len(c.CallOptions.CreateExclusion)], opts...) |
| 299 | 300 |
var resp *loggingpb.LogExclusion |
| 300 | 301 |
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
|
| ... | ... |
@@ -310,7 +328,8 @@ func (c *ConfigClient) CreateExclusion(ctx context.Context, req *loggingpb.Creat |
| 310 | 310 |
|
| 311 | 311 |
// UpdateExclusion changes one or more properties of an existing exclusion. |
| 312 | 312 |
func (c *ConfigClient) UpdateExclusion(ctx context.Context, req *loggingpb.UpdateExclusionRequest, opts ...gax.CallOption) (*loggingpb.LogExclusion, error) {
|
| 313 |
- ctx = insertMetadata(ctx, c.xGoogMetadata) |
|
| 313 |
+ md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "name", url.QueryEscape(req.GetName())))
|
|
| 314 |
+ ctx = insertMetadata(ctx, c.xGoogMetadata, md) |
|
| 314 | 315 |
opts = append(c.CallOptions.UpdateExclusion[0:len(c.CallOptions.UpdateExclusion):len(c.CallOptions.UpdateExclusion)], opts...) |
| 315 | 316 |
var resp *loggingpb.LogExclusion |
| 316 | 317 |
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
|
| ... | ... |
@@ -326,7 +345,8 @@ func (c *ConfigClient) UpdateExclusion(ctx context.Context, req *loggingpb.Updat |
| 326 | 326 |
|
| 327 | 327 |
// DeleteExclusion deletes an exclusion. |
| 328 | 328 |
func (c *ConfigClient) DeleteExclusion(ctx context.Context, req *loggingpb.DeleteExclusionRequest, opts ...gax.CallOption) error {
|
| 329 |
- ctx = insertMetadata(ctx, c.xGoogMetadata) |
|
| 329 |
+ md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "name", url.QueryEscape(req.GetName())))
|
|
| 330 |
+ ctx = insertMetadata(ctx, c.xGoogMetadata, md) |
|
| 330 | 331 |
opts = append(c.CallOptions.DeleteExclusion[0:len(c.CallOptions.DeleteExclusion):len(c.CallOptions.DeleteExclusion)], opts...) |
| 331 | 332 |
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
|
| 332 | 333 |
var err error |
| ... | ... |
@@ -1,4 +1,4 @@ |
| 1 |
-// Copyright 2018 Google LLC |
|
| 1 |
+// Copyright 2019 Google LLC |
|
| 2 | 2 |
// |
| 3 | 3 |
// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | 4 |
// you may not use this file except in compliance with the License. |
| ... | ... |
@@ -12,20 +12,35 @@ |
| 12 | 12 |
// See the License for the specific language governing permissions and |
| 13 | 13 |
// limitations under the License. |
| 14 | 14 |
|
| 15 |
-// AUTO-GENERATED CODE. DO NOT EDIT. |
|
| 15 |
+// Code generated by gapic-generator. DO NOT EDIT. |
|
| 16 | 16 |
|
| 17 | 17 |
// Package logging is an auto-generated package for the |
| 18 | 18 |
// Stackdriver Logging API. |
| 19 | 19 |
// |
| 20 | 20 |
// NOTE: This package is in alpha. It is not stable, and is likely to change. |
| 21 | 21 |
// |
| 22 |
-// Writes log entries and manages your Stackdriver Logging configuration. |
|
| 22 |
+// Writes log entries and manages your Logging configuration. |
|
| 23 |
+// |
|
| 24 |
+// Use of Context |
|
| 25 |
+// |
|
| 26 |
+// The ctx passed to NewClient is used for authentication requests and |
|
| 27 |
+// for creating the underlying connection, but is not used for subsequent calls. |
|
| 28 |
+// Individual methods on the client use the ctx given to them. |
|
| 29 |
+// |
|
| 30 |
+// To close the open connection, use the Close() method. |
|
| 31 |
+// |
|
| 32 |
+// For information about setting deadlines, reusing contexts, and more |
|
| 33 |
+// please visit godoc.org/cloud.google.com/go. |
|
| 23 | 34 |
// |
| 24 | 35 |
// Use the client at cloud.google.com/go/logging in preference to this. |
| 25 | 36 |
package logging // import "cloud.google.com/go/logging/apiv2" |
| 26 | 37 |
|
| 27 | 38 |
import ( |
| 28 |
- "golang.org/x/net/context" |
|
| 39 |
+ "context" |
|
| 40 |
+ "runtime" |
|
| 41 |
+ "strings" |
|
| 42 |
+ "unicode" |
|
| 43 |
+ |
|
| 29 | 44 |
"google.golang.org/grpc/metadata" |
| 30 | 45 |
) |
| 31 | 46 |
|
| ... | ... |
@@ -50,3 +65,42 @@ func DefaultAuthScopes() []string {
|
| 50 | 50 |
"https://www.googleapis.com/auth/logging.write", |
| 51 | 51 |
} |
| 52 | 52 |
} |
| 53 |
+ |
|
| 54 |
+// versionGo returns the Go runtime version. The returned string |
|
| 55 |
+// has no whitespace, suitable for reporting in header. |
|
| 56 |
+func versionGo() string {
|
|
| 57 |
+ const develPrefix = "devel +" |
|
| 58 |
+ |
|
| 59 |
+ s := runtime.Version() |
|
| 60 |
+ if strings.HasPrefix(s, develPrefix) {
|
|
| 61 |
+ s = s[len(develPrefix):] |
|
| 62 |
+ if p := strings.IndexFunc(s, unicode.IsSpace); p >= 0 {
|
|
| 63 |
+ s = s[:p] |
|
| 64 |
+ } |
|
| 65 |
+ return s |
|
| 66 |
+ } |
|
| 67 |
+ |
|
| 68 |
+ notSemverRune := func(r rune) bool {
|
|
| 69 |
+ return strings.IndexRune("0123456789.", r) < 0
|
|
| 70 |
+ } |
|
| 71 |
+ |
|
| 72 |
+ if strings.HasPrefix(s, "go1") {
|
|
| 73 |
+ s = s[2:] |
|
| 74 |
+ var prerelease string |
|
| 75 |
+ if p := strings.IndexFunc(s, notSemverRune); p >= 0 {
|
|
| 76 |
+ s, prerelease = s[:p], s[p:] |
|
| 77 |
+ } |
|
| 78 |
+ if strings.HasSuffix(s, ".") {
|
|
| 79 |
+ s += "0" |
|
| 80 |
+ } else if strings.Count(s, ".") < 2 {
|
|
| 81 |
+ s += ".0" |
|
| 82 |
+ } |
|
| 83 |
+ if prerelease != "" {
|
|
| 84 |
+ s += "-" + prerelease |
|
| 85 |
+ } |
|
| 86 |
+ return s |
|
| 87 |
+ } |
|
| 88 |
+ return "UNKNOWN" |
|
| 89 |
+} |
|
| 90 |
+ |
|
| 91 |
+const versionClient = "20190801" |
| ... | ... |
@@ -1,4 +1,4 @@ |
| 1 |
-// Copyright 2018 Google LLC |
|
| 1 |
+// Copyright 2019 Google LLC |
|
| 2 | 2 |
// |
| 3 | 3 |
// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | 4 |
// you may not use this file except in compliance with the License. |
| ... | ... |
@@ -12,17 +12,19 @@ |
| 12 | 12 |
// See the License for the specific language governing permissions and |
| 13 | 13 |
// limitations under the License. |
| 14 | 14 |
|
| 15 |
-// AUTO-GENERATED CODE. DO NOT EDIT. |
|
| 15 |
+// Code generated by gapic-generator. DO NOT EDIT. |
|
| 16 | 16 |
|
| 17 | 17 |
package logging |
| 18 | 18 |
|
| 19 | 19 |
import ( |
| 20 |
+ "context" |
|
| 21 |
+ "fmt" |
|
| 20 | 22 |
"math" |
| 23 |
+ "net/url" |
|
| 21 | 24 |
"time" |
| 22 | 25 |
|
| 23 |
- "cloud.google.com/go/internal/version" |
|
| 24 |
- gax "github.com/googleapis/gax-go" |
|
| 25 |
- "golang.org/x/net/context" |
|
| 26 |
+ "github.com/golang/protobuf/proto" |
|
| 27 |
+ gax "github.com/googleapis/gax-go/v2" |
|
| 26 | 28 |
"google.golang.org/api/iterator" |
| 27 | 29 |
"google.golang.org/api/option" |
| 28 | 30 |
"google.golang.org/api/transport" |
| ... | ... |
@@ -59,35 +61,24 @@ func defaultCallOptions() *CallOptions {
|
| 59 | 59 |
codes.Unavailable, |
| 60 | 60 |
}, gax.Backoff{
|
| 61 | 61 |
Initial: 100 * time.Millisecond, |
| 62 |
- Max: 1000 * time.Millisecond, |
|
| 63 |
- Multiplier: 1.2, |
|
| 64 |
- }) |
|
| 65 |
- }), |
|
| 66 |
- }, |
|
| 67 |
- {"list", "idempotent"}: {
|
|
| 68 |
- gax.WithRetry(func() gax.Retryer {
|
|
| 69 |
- return gax.OnCodes([]codes.Code{
|
|
| 70 |
- codes.DeadlineExceeded, |
|
| 71 |
- codes.Internal, |
|
| 72 |
- codes.Unavailable, |
|
| 73 |
- }, gax.Backoff{
|
|
| 74 |
- Initial: 100 * time.Millisecond, |
|
| 75 |
- Max: 1000 * time.Millisecond, |
|
| 76 |
- Multiplier: 1.2, |
|
| 62 |
+ Max: 60000 * time.Millisecond, |
|
| 63 |
+ Multiplier: 1.3, |
|
| 77 | 64 |
}) |
| 78 | 65 |
}), |
| 79 | 66 |
}, |
| 80 | 67 |
} |
| 81 | 68 |
return &CallOptions{
|
| 82 | 69 |
DeleteLog: retry[[2]string{"default", "idempotent"}],
|
| 83 |
- WriteLogEntries: retry[[2]string{"default", "non_idempotent"}],
|
|
| 84 |
- ListLogEntries: retry[[2]string{"list", "idempotent"}],
|
|
| 70 |
+ WriteLogEntries: retry[[2]string{"default", "idempotent"}],
|
|
| 71 |
+ ListLogEntries: retry[[2]string{"default", "idempotent"}],
|
|
| 85 | 72 |
ListMonitoredResourceDescriptors: retry[[2]string{"default", "idempotent"}],
|
| 86 |
- ListLogs: retry[[2]string{"default", "idempotent"}],
|
|
| 73 |
+ ListLogs: retry[[2]string{"default", "idempotent"}],
|
|
| 87 | 74 |
} |
| 88 | 75 |
} |
| 89 | 76 |
|
| 90 | 77 |
// Client is a client for interacting with Stackdriver Logging API. |
| 78 |
+// |
|
| 79 |
+// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. |
|
| 91 | 80 |
type Client struct {
|
| 92 | 81 |
// The connection to the service. |
| 93 | 82 |
conn *grpc.ClientConn |
| ... | ... |
@@ -135,8 +126,8 @@ func (c *Client) Close() error {
|
| 135 | 135 |
// the `x-goog-api-client` header passed on each request. Intended for |
| 136 | 136 |
// use by Google-written clients. |
| 137 | 137 |
func (c *Client) SetGoogleClientInfo(keyval ...string) {
|
| 138 |
- kv := append([]string{"gl-go", version.Go()}, keyval...)
|
|
| 139 |
- kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) |
|
| 138 |
+ kv := append([]string{"gl-go", versionGo()}, keyval...)
|
|
| 139 |
+ kv = append(kv, "gapic", versionClient, "gax", gax.Version, "grpc", grpc.Version) |
|
| 140 | 140 |
c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...))
|
| 141 | 141 |
} |
| 142 | 142 |
|
| ... | ... |
@@ -145,7 +136,8 @@ func (c *Client) SetGoogleClientInfo(keyval ...string) {
|
| 145 | 145 |
// Log entries written shortly before the delete operation might not be |
| 146 | 146 |
// deleted. |
| 147 | 147 |
func (c *Client) DeleteLog(ctx context.Context, req *loggingpb.DeleteLogRequest, opts ...gax.CallOption) error {
|
| 148 |
- ctx = insertMetadata(ctx, c.xGoogMetadata) |
|
| 148 |
+ md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "log_name", url.QueryEscape(req.GetLogName())))
|
|
| 149 |
+ ctx = insertMetadata(ctx, c.xGoogMetadata, md) |
|
| 149 | 150 |
opts = append(c.CallOptions.DeleteLog[0:len(c.CallOptions.DeleteLog):len(c.CallOptions.DeleteLog)], opts...) |
| 150 | 151 |
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
|
| 151 | 152 |
var err error |
| ... | ... |
@@ -155,13 +147,13 @@ func (c *Client) DeleteLog(ctx context.Context, req *loggingpb.DeleteLogRequest, |
| 155 | 155 |
return err |
| 156 | 156 |
} |
| 157 | 157 |
|
| 158 |
-// WriteLogEntries ## Log entry resources |
|
| 159 |
-// |
|
| 160 |
-// Writes log entries to Stackdriver Logging. This API method is the |
|
| 161 |
-// only way to send log entries to Stackdriver Logging. This method |
|
| 162 |
-// is used, directly or indirectly, by the Stackdriver Logging agent |
|
| 163 |
-// (fluentd) and all logging libraries configured to use Stackdriver |
|
| 164 |
-// Logging. |
|
| 158 |
+// WriteLogEntries writes log entries to Logging. This API method is the |
|
| 159 |
+// only way to send log entries to Logging. This method |
|
| 160 |
+// is used, directly or indirectly, by the Logging agent |
|
| 161 |
+// (fluentd) and all logging libraries configured to use Logging. |
|
| 162 |
+// A single request may contain log entries for a maximum of 1000 |
|
| 163 |
+// different resources (projects, organizations, billing accounts or |
|
| 164 |
+// folders) |
|
| 165 | 165 |
func (c *Client) WriteLogEntries(ctx context.Context, req *loggingpb.WriteLogEntriesRequest, opts ...gax.CallOption) (*loggingpb.WriteLogEntriesResponse, error) {
|
| 166 | 166 |
ctx = insertMetadata(ctx, c.xGoogMetadata) |
| 167 | 167 |
opts = append(c.CallOptions.WriteLogEntries[0:len(c.CallOptions.WriteLogEntries):len(c.CallOptions.WriteLogEntries)], opts...) |
| ... | ... |
@@ -178,12 +170,13 @@ func (c *Client) WriteLogEntries(ctx context.Context, req *loggingpb.WriteLogEnt |
| 178 | 178 |
} |
| 179 | 179 |
|
| 180 | 180 |
// ListLogEntries lists log entries. Use this method to retrieve log entries from |
| 181 |
-// Stackdriver Logging. For ways to export log entries, see |
|
| 181 |
+// Logging. For ways to export log entries, see |
|
| 182 | 182 |
// Exporting Logs (at /logging/docs/export). |
| 183 | 183 |
func (c *Client) ListLogEntries(ctx context.Context, req *loggingpb.ListLogEntriesRequest, opts ...gax.CallOption) *LogEntryIterator {
|
| 184 | 184 |
ctx = insertMetadata(ctx, c.xGoogMetadata) |
| 185 | 185 |
opts = append(c.CallOptions.ListLogEntries[0:len(c.CallOptions.ListLogEntries):len(c.CallOptions.ListLogEntries)], opts...) |
| 186 | 186 |
it := &LogEntryIterator{}
|
| 187 |
+ req = proto.Clone(req).(*loggingpb.ListLogEntriesRequest) |
|
| 187 | 188 |
it.InternalFetch = func(pageSize int, pageToken string) ([]*loggingpb.LogEntry, string, error) {
|
| 188 | 189 |
var resp *loggingpb.ListLogEntriesResponse |
| 189 | 190 |
req.PageToken = pageToken |
| ... | ... |
@@ -211,15 +204,17 @@ func (c *Client) ListLogEntries(ctx context.Context, req *loggingpb.ListLogEntri |
| 211 | 211 |
return nextPageToken, nil |
| 212 | 212 |
} |
| 213 | 213 |
it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf) |
| 214 |
+ it.pageInfo.MaxSize = int(req.PageSize) |
|
| 215 |
+ it.pageInfo.Token = req.PageToken |
|
| 214 | 216 |
return it |
| 215 | 217 |
} |
| 216 | 218 |
|
| 217 |
-// ListMonitoredResourceDescriptors lists the descriptors for monitored resource types used by Stackdriver |
|
| 218 |
-// Logging. |
|
| 219 |
+// ListMonitoredResourceDescriptors lists the descriptors for monitored resource types used by Logging. |
|
| 219 | 220 |
func (c *Client) ListMonitoredResourceDescriptors(ctx context.Context, req *loggingpb.ListMonitoredResourceDescriptorsRequest, opts ...gax.CallOption) *MonitoredResourceDescriptorIterator {
|
| 220 | 221 |
ctx = insertMetadata(ctx, c.xGoogMetadata) |
| 221 | 222 |
opts = append(c.CallOptions.ListMonitoredResourceDescriptors[0:len(c.CallOptions.ListMonitoredResourceDescriptors):len(c.CallOptions.ListMonitoredResourceDescriptors)], opts...) |
| 222 | 223 |
it := &MonitoredResourceDescriptorIterator{}
|
| 224 |
+ req = proto.Clone(req).(*loggingpb.ListMonitoredResourceDescriptorsRequest) |
|
| 223 | 225 |
it.InternalFetch = func(pageSize int, pageToken string) ([]*monitoredrespb.MonitoredResourceDescriptor, string, error) {
|
| 224 | 226 |
var resp *loggingpb.ListMonitoredResourceDescriptorsResponse |
| 225 | 227 |
req.PageToken = pageToken |
| ... | ... |
@@ -247,15 +242,19 @@ func (c *Client) ListMonitoredResourceDescriptors(ctx context.Context, req *logg |
| 247 | 247 |
return nextPageToken, nil |
| 248 | 248 |
} |
| 249 | 249 |
it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf) |
| 250 |
+ it.pageInfo.MaxSize = int(req.PageSize) |
|
| 251 |
+ it.pageInfo.Token = req.PageToken |
|
| 250 | 252 |
return it |
| 251 | 253 |
} |
| 252 | 254 |
|
| 253 | 255 |
// ListLogs lists the logs in projects, organizations, folders, or billing accounts. |
| 254 | 256 |
// Only logs that have entries are listed. |
| 255 | 257 |
func (c *Client) ListLogs(ctx context.Context, req *loggingpb.ListLogsRequest, opts ...gax.CallOption) *StringIterator {
|
| 256 |
- ctx = insertMetadata(ctx, c.xGoogMetadata) |
|
| 258 |
+ md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "parent", url.QueryEscape(req.GetParent())))
|
|
| 259 |
+ ctx = insertMetadata(ctx, c.xGoogMetadata, md) |
|
| 257 | 260 |
opts = append(c.CallOptions.ListLogs[0:len(c.CallOptions.ListLogs):len(c.CallOptions.ListLogs)], opts...) |
| 258 | 261 |
it := &StringIterator{}
|
| 262 |
+ req = proto.Clone(req).(*loggingpb.ListLogsRequest) |
|
| 259 | 263 |
it.InternalFetch = func(pageSize int, pageToken string) ([]string, string, error) {
|
| 260 | 264 |
var resp *loggingpb.ListLogsResponse |
| 261 | 265 |
req.PageToken = pageToken |
| ... | ... |
@@ -283,6 +282,8 @@ func (c *Client) ListLogs(ctx context.Context, req *loggingpb.ListLogsRequest, o |
| 283 | 283 |
return nextPageToken, nil |
| 284 | 284 |
} |
| 285 | 285 |
it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf) |
| 286 |
+ it.pageInfo.MaxSize = int(req.PageSize) |
|
| 287 |
+ it.pageInfo.Token = req.PageToken |
|
| 286 | 288 |
return it |
| 287 | 289 |
} |
| 288 | 290 |
|
| ... | ... |
@@ -1,4 +1,4 @@ |
| 1 |
-// Copyright 2018 Google LLC |
|
| 1 |
+// Copyright 2019 Google LLC |
|
| 2 | 2 |
// |
| 3 | 3 |
// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | 4 |
// you may not use this file except in compliance with the License. |
| ... | ... |
@@ -12,17 +12,19 @@ |
| 12 | 12 |
// See the License for the specific language governing permissions and |
| 13 | 13 |
// limitations under the License. |
| 14 | 14 |
|
| 15 |
-// AUTO-GENERATED CODE. DO NOT EDIT. |
|
| 15 |
+// Code generated by gapic-generator. DO NOT EDIT. |
|
| 16 | 16 |
|
| 17 | 17 |
package logging |
| 18 | 18 |
|
| 19 | 19 |
import ( |
| 20 |
+ "context" |
|
| 21 |
+ "fmt" |
|
| 20 | 22 |
"math" |
| 23 |
+ "net/url" |
|
| 21 | 24 |
"time" |
| 22 | 25 |
|
| 23 |
- "cloud.google.com/go/internal/version" |
|
| 24 |
- gax "github.com/googleapis/gax-go" |
|
| 25 |
- "golang.org/x/net/context" |
|
| 26 |
+ "github.com/golang/protobuf/proto" |
|
| 27 |
+ gax "github.com/googleapis/gax-go/v2" |
|
| 26 | 28 |
"google.golang.org/api/iterator" |
| 27 | 29 |
"google.golang.org/api/option" |
| 28 | 30 |
"google.golang.org/api/transport" |
| ... | ... |
@@ -58,8 +60,8 @@ func defaultMetricsCallOptions() *MetricsCallOptions {
|
| 58 | 58 |
codes.Unavailable, |
| 59 | 59 |
}, gax.Backoff{
|
| 60 | 60 |
Initial: 100 * time.Millisecond, |
| 61 |
- Max: 1000 * time.Millisecond, |
|
| 62 |
- Multiplier: 1.2, |
|
| 61 |
+ Max: 60000 * time.Millisecond, |
|
| 62 |
+ Multiplier: 1.3, |
|
| 63 | 63 |
}) |
| 64 | 64 |
}), |
| 65 | 65 |
}, |
| ... | ... |
@@ -68,12 +70,14 @@ func defaultMetricsCallOptions() *MetricsCallOptions {
|
| 68 | 68 |
ListLogMetrics: retry[[2]string{"default", "idempotent"}],
|
| 69 | 69 |
GetLogMetric: retry[[2]string{"default", "idempotent"}],
|
| 70 | 70 |
CreateLogMetric: retry[[2]string{"default", "non_idempotent"}],
|
| 71 |
- UpdateLogMetric: retry[[2]string{"default", "non_idempotent"}],
|
|
| 71 |
+ UpdateLogMetric: retry[[2]string{"default", "idempotent"}],
|
|
| 72 | 72 |
DeleteLogMetric: retry[[2]string{"default", "idempotent"}],
|
| 73 | 73 |
} |
| 74 | 74 |
} |
| 75 | 75 |
|
| 76 | 76 |
// MetricsClient is a client for interacting with Stackdriver Logging API. |
| 77 |
+// |
|
| 78 |
+// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. |
|
| 77 | 79 |
type MetricsClient struct {
|
| 78 | 80 |
// The connection to the service. |
| 79 | 81 |
conn *grpc.ClientConn |
| ... | ... |
@@ -121,16 +125,18 @@ func (c *MetricsClient) Close() error {
|
| 121 | 121 |
// the `x-goog-api-client` header passed on each request. Intended for |
| 122 | 122 |
// use by Google-written clients. |
| 123 | 123 |
func (c *MetricsClient) SetGoogleClientInfo(keyval ...string) {
|
| 124 |
- kv := append([]string{"gl-go", version.Go()}, keyval...)
|
|
| 125 |
- kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) |
|
| 124 |
+ kv := append([]string{"gl-go", versionGo()}, keyval...)
|
|
| 125 |
+ kv = append(kv, "gapic", versionClient, "gax", gax.Version, "grpc", grpc.Version) |
|
| 126 | 126 |
c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...))
|
| 127 | 127 |
} |
| 128 | 128 |
|
| 129 | 129 |
// ListLogMetrics lists logs-based metrics. |
| 130 | 130 |
func (c *MetricsClient) ListLogMetrics(ctx context.Context, req *loggingpb.ListLogMetricsRequest, opts ...gax.CallOption) *LogMetricIterator {
|
| 131 |
- ctx = insertMetadata(ctx, c.xGoogMetadata) |
|
| 131 |
+ md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "parent", url.QueryEscape(req.GetParent())))
|
|
| 132 |
+ ctx = insertMetadata(ctx, c.xGoogMetadata, md) |
|
| 132 | 133 |
opts = append(c.CallOptions.ListLogMetrics[0:len(c.CallOptions.ListLogMetrics):len(c.CallOptions.ListLogMetrics)], opts...) |
| 133 | 134 |
it := &LogMetricIterator{}
|
| 135 |
+ req = proto.Clone(req).(*loggingpb.ListLogMetricsRequest) |
|
| 134 | 136 |
it.InternalFetch = func(pageSize int, pageToken string) ([]*loggingpb.LogMetric, string, error) {
|
| 135 | 137 |
var resp *loggingpb.ListLogMetricsResponse |
| 136 | 138 |
req.PageToken = pageToken |
| ... | ... |
@@ -158,12 +164,15 @@ func (c *MetricsClient) ListLogMetrics(ctx context.Context, req *loggingpb.ListL |
| 158 | 158 |
return nextPageToken, nil |
| 159 | 159 |
} |
| 160 | 160 |
it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf) |
| 161 |
+ it.pageInfo.MaxSize = int(req.PageSize) |
|
| 162 |
+ it.pageInfo.Token = req.PageToken |
|
| 161 | 163 |
return it |
| 162 | 164 |
} |
| 163 | 165 |
|
| 164 | 166 |
// GetLogMetric gets a logs-based metric. |
| 165 | 167 |
func (c *MetricsClient) GetLogMetric(ctx context.Context, req *loggingpb.GetLogMetricRequest, opts ...gax.CallOption) (*loggingpb.LogMetric, error) {
|
| 166 |
- ctx = insertMetadata(ctx, c.xGoogMetadata) |
|
| 168 |
+ md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "metric_name", url.QueryEscape(req.GetMetricName())))
|
|
| 169 |
+ ctx = insertMetadata(ctx, c.xGoogMetadata, md) |
|
| 167 | 170 |
opts = append(c.CallOptions.GetLogMetric[0:len(c.CallOptions.GetLogMetric):len(c.CallOptions.GetLogMetric)], opts...) |
| 168 | 171 |
var resp *loggingpb.LogMetric |
| 169 | 172 |
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
|
| ... | ... |
@@ -179,7 +188,8 @@ func (c *MetricsClient) GetLogMetric(ctx context.Context, req *loggingpb.GetLogM |
| 179 | 179 |
|
| 180 | 180 |
// CreateLogMetric creates a logs-based metric. |
| 181 | 181 |
func (c *MetricsClient) CreateLogMetric(ctx context.Context, req *loggingpb.CreateLogMetricRequest, opts ...gax.CallOption) (*loggingpb.LogMetric, error) {
|
| 182 |
- ctx = insertMetadata(ctx, c.xGoogMetadata) |
|
| 182 |
+ md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "parent", url.QueryEscape(req.GetParent())))
|
|
| 183 |
+ ctx = insertMetadata(ctx, c.xGoogMetadata, md) |
|
| 183 | 184 |
opts = append(c.CallOptions.CreateLogMetric[0:len(c.CallOptions.CreateLogMetric):len(c.CallOptions.CreateLogMetric)], opts...) |
| 184 | 185 |
var resp *loggingpb.LogMetric |
| 185 | 186 |
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
|
| ... | ... |
@@ -195,7 +205,8 @@ func (c *MetricsClient) CreateLogMetric(ctx context.Context, req *loggingpb.Crea |
| 195 | 195 |
|
| 196 | 196 |
// UpdateLogMetric creates or updates a logs-based metric. |
| 197 | 197 |
func (c *MetricsClient) UpdateLogMetric(ctx context.Context, req *loggingpb.UpdateLogMetricRequest, opts ...gax.CallOption) (*loggingpb.LogMetric, error) {
|
| 198 |
- ctx = insertMetadata(ctx, c.xGoogMetadata) |
|
| 198 |
+ md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "metric_name", url.QueryEscape(req.GetMetricName())))
|
|
| 199 |
+ ctx = insertMetadata(ctx, c.xGoogMetadata, md) |
|
| 199 | 200 |
opts = append(c.CallOptions.UpdateLogMetric[0:len(c.CallOptions.UpdateLogMetric):len(c.CallOptions.UpdateLogMetric)], opts...) |
| 200 | 201 |
var resp *loggingpb.LogMetric |
| 201 | 202 |
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
|
| ... | ... |
@@ -211,7 +222,8 @@ func (c *MetricsClient) UpdateLogMetric(ctx context.Context, req *loggingpb.Upda |
| 211 | 211 |
|
| 212 | 212 |
// DeleteLogMetric deletes a logs-based metric. |
| 213 | 213 |
func (c *MetricsClient) DeleteLogMetric(ctx context.Context, req *loggingpb.DeleteLogMetricRequest, opts ...gax.CallOption) error {
|
| 214 |
- ctx = insertMetadata(ctx, c.xGoogMetadata) |
|
| 214 |
+ md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "metric_name", url.QueryEscape(req.GetMetricName())))
|
|
| 215 |
+ ctx = insertMetadata(ctx, c.xGoogMetadata, md) |
|
| 215 | 216 |
opts = append(c.CallOptions.DeleteLogMetric[0:len(c.CallOptions.DeleteLogMetric):len(c.CallOptions.DeleteLogMetric)], opts...) |
| 216 | 217 |
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
|
| 217 | 218 |
var err error |
| ... | ... |
@@ -1,4 +1,4 @@ |
| 1 |
-// Copyright 2016 Google Inc. All Rights Reserved. |
|
| 1 |
+// Copyright 2016 Google LLC |
|
| 2 | 2 |
// |
| 3 | 3 |
// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | 4 |
// you may not use this file except in compliance with the License. |
| ... | ... |
@@ -21,9 +21,6 @@ This client uses Logging API v2. |
| 21 | 21 |
See https://cloud.google.com/logging/docs/api/v2/ for an introduction to the API. |
| 22 | 22 |
|
| 23 | 23 |
|
| 24 |
-Note: This package is in beta. Some backwards-incompatible changes may occur. |
|
| 25 |
- |
|
| 26 |
- |
|
| 27 | 24 |
Creating a Client |
| 28 | 25 |
|
| 29 | 26 |
Use a Client to interact with the Stackdriver Logging API. |
| ... | ... |
@@ -65,7 +62,10 @@ For critical errors, you may want to send your log entries immediately. |
| 65 | 65 |
LogSync is slow and will block until the log entry has been sent, so it is |
| 66 | 66 |
not recommended for normal use. |
| 67 | 67 |
|
| 68 |
- lg.LogSync(ctx, logging.Entry{Payload: "ALERT! Something critical happened!"})
|
|
| 68 |
+ err = lg.LogSync(ctx, logging.Entry{Payload: "ALERT! Something critical happened!"})
|
|
| 69 |
+ if err != nil {
|
|
| 70 |
+ // TODO: Handle error. |
|
| 71 |
+ } |
|
| 69 | 72 |
|
| 70 | 73 |
|
| 71 | 74 |
Payloads |
| ... | ... |
@@ -85,11 +85,11 @@ If you have a []byte of JSON, wrap it in json.RawMessage: |
| 85 | 85 |
lg.Log(logging.Entry{Payload: json.RawMessage(j)})
|
| 86 | 86 |
|
| 87 | 87 |
|
| 88 |
-The Standard Logger Interface |
|
| 88 |
+The Standard Logger |
|
| 89 | 89 |
|
| 90 | 90 |
You may want use a standard log.Logger in your program. |
| 91 | 91 |
|
| 92 |
- // stdlg implements log.Logger |
|
| 92 |
+ // stdlg is an instance of *log.Logger. |
|
| 93 | 93 |
stdlg := lg.StandardLogger(logging.Info) |
| 94 | 94 |
stdlg.Println("some info")
|
| 95 | 95 |
|
| ... | ... |
@@ -113,5 +113,22 @@ running from a Google Cloud Platform VM, select "GCE VM Instance". Otherwise, se |
| 113 | 113 |
accounts can be viewed on the command line with the "gcloud logging read" command. |
| 114 | 114 |
|
| 115 | 115 |
|
| 116 |
+Grouping Logs by Request |
|
| 117 |
+ |
|
| 118 |
+To group all the log entries written during a single HTTP request, create two |
|
| 119 |
+Loggers, a "parent" and a "child," with different log IDs. Both should be in the same |
|
| 120 |
+project, and have the same MonitoredResouce type and labels. |
|
| 121 |
+ |
|
| 122 |
+- Parent entries must have HTTPRequest.Request populated. (Strictly speaking, only the URL is necessary.) |
|
| 123 |
+ |
|
| 124 |
+- A child entry's timestamp must be within the time interval covered by the parent request (i.e., older |
|
| 125 |
+than parent.Timestamp, and newer than parent.Timestamp - parent.HTTPRequest.Latency, assuming the |
|
| 126 |
+parent timestamp marks the end of the request. |
|
| 127 |
+ |
|
| 128 |
+- The trace field must be populated in all of the entries and match exactly. |
|
| 129 |
+ |
|
| 130 |
+You should observe the child log entries grouped under the parent on the console. The |
|
| 131 |
+parent entry will not inherit the severity of its children; you must update the |
|
| 132 |
+parent severity yourself. |
|
| 116 | 133 |
*/ |
| 117 | 134 |
package logging // import "cloud.google.com/go/logging" |
| 118 | 135 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,15 @@ |
| 0 |
+module cloud.google.com/go/logging |
|
| 1 |
+ |
|
| 2 |
+go 1.9 |
|
| 3 |
+ |
|
| 4 |
+require ( |
|
| 5 |
+ cloud.google.com/go v0.43.0 |
|
| 6 |
+ github.com/golang/protobuf v1.3.1 |
|
| 7 |
+ github.com/google/go-cmp v0.3.0 |
|
| 8 |
+ github.com/googleapis/gax-go/v2 v2.0.5 |
|
| 9 |
+ go.opencensus.io v0.22.0 |
|
| 10 |
+ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 |
|
| 11 |
+ google.golang.org/api v0.7.0 |
|
| 12 |
+ google.golang.org/genproto v0.0.0-20190708153700-3bdd9d9f5532 |
|
| 13 |
+ google.golang.org/grpc v1.21.1 |
|
| 14 |
+) |
| 0 | 15 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,22 @@ |
| 0 |
+// Copyright 2019 Google LLC |
|
| 1 |
+// |
|
| 2 |
+// Licensed under the Apache License, Version 2.0 (the "License"); |
|
| 3 |
+// you may not use this file except in compliance with the License. |
|
| 4 |
+// You may obtain a copy of the License at |
|
| 5 |
+// |
|
| 6 |
+// http://www.apache.org/licenses/LICENSE-2.0 |
|
| 7 |
+// |
|
| 8 |
+// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
+// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
+// See the License for the specific language governing permissions and |
|
| 12 |
+// limitations under the License. |
|
| 13 |
+ |
|
| 14 |
+// This file, and the cloud.google.com/go import, won't actually become part of |
|
| 15 |
+// the resultant binary. |
|
| 16 |
+// +build modhack |
|
| 17 |
+ |
|
| 18 |
+package logging |
|
| 19 |
+ |
|
| 20 |
+// Necessary for safely adding multi-module repo. See: https://github.com/golang/go/wiki/Modules#is-it-possible-to-add-a-module-to-a-multi-module-repository |
|
| 21 |
+import _ "cloud.google.com/go" |
| ... | ... |
@@ -1,4 +1,4 @@ |
| 1 |
-// Copyright 2016 Google Inc. All Rights Reserved. |
|
| 1 |
+// Copyright 2016 Google LLC |
|
| 2 | 2 |
// |
| 3 | 3 |
// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | 4 |
// you may not use this file except in compliance with the License. |
| ... | ... |
@@ -20,15 +20,17 @@ import ( |
| 20 | 20 |
) |
| 21 | 21 |
|
| 22 | 22 |
const ( |
| 23 |
+ // ProdAddr is the production address. |
|
| 23 | 24 |
ProdAddr = "logging.googleapis.com:443" |
| 24 |
- Version = "0.2.0" |
|
| 25 | 25 |
) |
| 26 | 26 |
|
| 27 |
+// LogPath creates a formatted path from a parent and a logID. |
|
| 27 | 28 |
func LogPath(parent, logID string) string {
|
| 28 | 29 |
logID = strings.Replace(logID, "/", "%2F", -1) |
| 29 | 30 |
return fmt.Sprintf("%s/logs/%s", parent, logID)
|
| 30 | 31 |
} |
| 31 | 32 |
|
| 33 |
+// LogIDFromPath parses and returns the ID from a log path. |
|
| 32 | 34 |
func LogIDFromPath(parent, path string) string {
|
| 33 | 35 |
start := len(parent) + len("/logs/")
|
| 34 | 36 |
if len(path) < start {
|
| ... | ... |
@@ -1,4 +1,4 @@ |
| 1 |
-// Copyright 2016 Google Inc. All Rights Reserved. |
|
| 1 |
+// Copyright 2016 Google LLC |
|
| 2 | 2 |
// |
| 3 | 3 |
// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | 4 |
// you may not use this file except in compliance with the License. |
| ... | ... |
@@ -25,16 +25,19 @@ |
| 25 | 25 |
package logging |
| 26 | 26 |
|
| 27 | 27 |
import ( |
| 28 |
+ "bytes" |
|
| 29 |
+ "context" |
|
| 28 | 30 |
"encoding/json" |
| 29 | 31 |
"errors" |
| 30 | 32 |
"fmt" |
| 31 | 33 |
"log" |
| 32 |
- "math" |
|
| 33 | 34 |
"net/http" |
| 35 |
+ "regexp" |
|
| 34 | 36 |
"strconv" |
| 35 | 37 |
"strings" |
| 36 | 38 |
"sync" |
| 37 | 39 |
"time" |
| 40 |
+ "unicode/utf8" |
|
| 38 | 41 |
|
| 39 | 42 |
"cloud.google.com/go/compute/metadata" |
| 40 | 43 |
"cloud.google.com/go/internal/version" |
| ... | ... |
@@ -44,7 +47,6 @@ import ( |
| 44 | 44 |
"github.com/golang/protobuf/ptypes" |
| 45 | 45 |
structpb "github.com/golang/protobuf/ptypes/struct" |
| 46 | 46 |
tspb "github.com/golang/protobuf/ptypes/timestamp" |
| 47 |
- "golang.org/x/net/context" |
|
| 48 | 47 |
"google.golang.org/api/option" |
| 49 | 48 |
"google.golang.org/api/support/bundler" |
| 50 | 49 |
mrpb "google.golang.org/genproto/googleapis/api/monitoredres" |
| ... | ... |
@@ -53,13 +55,13 @@ import ( |
| 53 | 53 |
) |
| 54 | 54 |
|
| 55 | 55 |
const ( |
| 56 |
- // Scope for reading from the logging service. |
|
| 56 |
+ // ReadScope is the scope for reading from the logging service. |
|
| 57 | 57 |
ReadScope = "https://www.googleapis.com/auth/logging.read" |
| 58 | 58 |
|
| 59 |
- // Scope for writing to the logging service. |
|
| 59 |
+ // WriteScope is the scope for writing to the logging service. |
|
| 60 | 60 |
WriteScope = "https://www.googleapis.com/auth/logging.write" |
| 61 | 61 |
|
| 62 |
- // Scope for administrative actions on the logging service. |
|
| 62 |
+ // AdminScope is the scope for administrative actions on the logging service. |
|
| 63 | 63 |
AdminScope = "https://www.googleapis.com/auth/logging.admin" |
| 64 | 64 |
) |
| 65 | 65 |
|
| ... | ... |
@@ -234,7 +236,7 @@ type Logger struct {
|
| 234 | 234 |
// Options |
| 235 | 235 |
commonResource *mrpb.MonitoredResource |
| 236 | 236 |
commonLabels map[string]string |
| 237 |
- writeTimeout time.Duration |
|
| 237 |
+ ctxFunc func() (context.Context, func()) |
|
| 238 | 238 |
} |
| 239 | 239 |
|
| 240 | 240 |
// A LoggerOption is a configuration option for a Logger. |
| ... | ... |
@@ -274,12 +276,17 @@ func detectResource() *mrpb.MonitoredResource {
|
| 274 | 274 |
if err != nil {
|
| 275 | 275 |
return |
| 276 | 276 |
} |
| 277 |
+ name, err := metadata.InstanceName() |
|
| 278 |
+ if err != nil {
|
|
| 279 |
+ return |
|
| 280 |
+ } |
|
| 277 | 281 |
detectedResource.pb = &mrpb.MonitoredResource{
|
| 278 | 282 |
Type: "gce_instance", |
| 279 | 283 |
Labels: map[string]string{
|
| 280 |
- "project_id": projectID, |
|
| 281 |
- "instance_id": id, |
|
| 282 |
- "zone": zone, |
|
| 284 |
+ "project_id": projectID, |
|
| 285 |
+ "instance_id": id, |
|
| 286 |
+ "instance_name": name, |
|
| 287 |
+ "zone": zone, |
|
| 283 | 288 |
}, |
| 284 | 289 |
} |
| 285 | 290 |
}) |
| ... | ... |
@@ -398,6 +405,23 @@ type bufferedByteLimit int |
| 398 | 398 |
|
| 399 | 399 |
func (b bufferedByteLimit) set(l *Logger) { l.bundler.BufferedByteLimit = int(b) }
|
| 400 | 400 |
|
| 401 |
+// ContextFunc is a function that will be called to obtain a context.Context for the |
|
| 402 |
+// WriteLogEntries RPC executed in the background for calls to Logger.Log. The |
|
| 403 |
+// default is a function that always returns context.Background. The second return |
|
| 404 |
+// value of the function is a function to call after the RPC completes. |
|
| 405 |
+// |
|
| 406 |
+// The function is not used for calls to Logger.LogSync, since the caller can pass |
|
| 407 |
+// in the context directly. |
|
| 408 |
+// |
|
| 409 |
+// This option is EXPERIMENTAL. It may be changed or removed. |
|
| 410 |
+func ContextFunc(f func() (ctx context.Context, afterCall func())) LoggerOption {
|
|
| 411 |
+ return contextFunc(f) |
|
| 412 |
+} |
|
| 413 |
+ |
|
| 414 |
+type contextFunc func() (ctx context.Context, afterCall func()) |
|
| 415 |
+ |
|
| 416 |
+func (c contextFunc) set(l *Logger) { l.ctxFunc = c }
|
|
| 417 |
+ |
|
| 401 | 418 |
// Logger returns a Logger that will write entries with the given log ID, such as |
| 402 | 419 |
// "syslog". A log ID must be less than 512 characters long and can only |
| 403 | 420 |
// include the following characters: upper and lower case alphanumeric |
| ... | ... |
@@ -412,6 +436,7 @@ func (c *Client) Logger(logID string, opts ...LoggerOption) *Logger {
|
| 412 | 412 |
client: c, |
| 413 | 413 |
logName: internal.LogPath(c.parent, logID), |
| 414 | 414 |
commonResource: r, |
| 415 |
+ ctxFunc: func() (context.Context, func()) { return context.Background(), nil },
|
|
| 415 | 416 |
} |
| 416 | 417 |
l.bundler = bundler.NewBundler(&logpb.LogEntry{}, func(entries interface{}) {
|
| 417 | 418 |
l.writeLogEntries(entries.([]*logpb.LogEntry)) |
| ... | ... |
@@ -578,6 +603,17 @@ type Entry struct {
|
| 578 | 578 |
// if any. If it contains a relative resource name, the name is assumed to |
| 579 | 579 |
// be relative to //tracing.googleapis.com. |
| 580 | 580 |
Trace string |
| 581 |
+ |
|
| 582 |
+ // ID of the span within the trace associated with the log entry. |
|
| 583 |
+ // The ID is a 16-character hexadecimal encoding of an 8-byte array. |
|
| 584 |
+ SpanID string |
|
| 585 |
+ |
|
| 586 |
+ // If set, symbolizes that this request was sampled. |
|
| 587 |
+ TraceSampled bool |
|
| 588 |
+ |
|
| 589 |
+ // Optional. Source code location information associated with the log entry, |
|
| 590 |
+ // if any. |
|
| 591 |
+ SourceLocation *logpb.LogEntrySourceLocation |
|
| 581 | 592 |
} |
| 582 | 593 |
|
| 583 | 594 |
// HTTPRequest contains an http.Request as well as additional |
| ... | ... |
@@ -631,7 +667,7 @@ func fromHTTPRequest(r *HTTPRequest) *logtypepb.HttpRequest {
|
| 631 | 631 |
u.Fragment = "" |
| 632 | 632 |
pb := &logtypepb.HttpRequest{
|
| 633 | 633 |
RequestMethod: r.Request.Method, |
| 634 |
- RequestUrl: u.String(), |
|
| 634 |
+ RequestUrl: fixUTF8(u.String()), |
|
| 635 | 635 |
RequestSize: r.RequestSize, |
| 636 | 636 |
Status: int32(r.Status), |
| 637 | 637 |
ResponseSize: r.ResponseSize, |
| ... | ... |
@@ -648,6 +684,27 @@ func fromHTTPRequest(r *HTTPRequest) *logtypepb.HttpRequest {
|
| 648 | 648 |
return pb |
| 649 | 649 |
} |
| 650 | 650 |
|
| 651 |
+// fixUTF8 is a helper that fixes an invalid UTF-8 string by replacing |
|
| 652 |
+// invalid UTF-8 runes with the Unicode replacement character (U+FFFD). |
|
| 653 |
+// See Issue https://github.com/googleapis/google-cloud-go/issues/1383. |
|
| 654 |
+func fixUTF8(s string) string {
|
|
| 655 |
+ if utf8.ValidString(s) {
|
|
| 656 |
+ return s |
|
| 657 |
+ } |
|
| 658 |
+ |
|
| 659 |
+ // Otherwise time to build the sequence. |
|
| 660 |
+ buf := new(bytes.Buffer) |
|
| 661 |
+ buf.Grow(len(s)) |
|
| 662 |
+ for _, r := range s {
|
|
| 663 |
+ if utf8.ValidRune(r) {
|
|
| 664 |
+ buf.WriteRune(r) |
|
| 665 |
+ } else {
|
|
| 666 |
+ buf.WriteRune('\uFFFD')
|
|
| 667 |
+ } |
|
| 668 |
+ } |
|
| 669 |
+ return buf.String() |
|
| 670 |
+} |
|
| 671 |
+ |
|
| 651 | 672 |
// toProtoStruct converts v, which must marshal into a JSON object, |
| 652 | 673 |
// into a Google Struct proto. |
| 653 | 674 |
func toProtoStruct(v interface{}) (*structpb.Struct, error) {
|
| ... | ... |
@@ -713,7 +770,7 @@ func jsonValueToStructValue(v interface{}) *structpb.Value {
|
| 713 | 713 |
// Prefer Log for most uses. |
| 714 | 714 |
// TODO(jba): come up with a better name (LogNow?) or eliminate. |
| 715 | 715 |
func (l *Logger) LogSync(ctx context.Context, e Entry) error {
|
| 716 |
- ent, err := toLogEntry(e) |
|
| 716 |
+ ent, err := l.toLogEntry(e) |
|
| 717 | 717 |
if err != nil {
|
| 718 | 718 |
return err |
| 719 | 719 |
} |
| ... | ... |
@@ -728,7 +785,7 @@ func (l *Logger) LogSync(ctx context.Context, e Entry) error {
|
| 728 | 728 |
|
| 729 | 729 |
// Log buffers the Entry for output to the logging service. It never blocks. |
| 730 | 730 |
func (l *Logger) Log(e Entry) {
|
| 731 |
- ent, err := toLogEntry(e) |
|
| 731 |
+ ent, err := l.toLogEntry(e) |
|
| 732 | 732 |
if err != nil {
|
| 733 | 733 |
l.client.error(err) |
| 734 | 734 |
return |
| ... | ... |
@@ -756,12 +813,16 @@ func (l *Logger) writeLogEntries(entries []*logpb.LogEntry) {
|
| 756 | 756 |
Labels: l.commonLabels, |
| 757 | 757 |
Entries: entries, |
| 758 | 758 |
} |
| 759 |
- ctx, cancel := context.WithTimeout(context.Background(), defaultWriteTimeout) |
|
| 759 |
+ ctx, afterCall := l.ctxFunc() |
|
| 760 |
+ ctx, cancel := context.WithTimeout(ctx, defaultWriteTimeout) |
|
| 760 | 761 |
defer cancel() |
| 761 | 762 |
_, err := l.client.client.WriteLogEntries(ctx, req) |
| 762 | 763 |
if err != nil {
|
| 763 | 764 |
l.client.error(err) |
| 764 | 765 |
} |
| 766 |
+ if afterCall != nil {
|
|
| 767 |
+ afterCall() |
|
| 768 |
+ } |
|
| 765 | 769 |
} |
| 766 | 770 |
|
| 767 | 771 |
// StandardLogger returns a *log.Logger for the provided severity. |
| ... | ... |
@@ -771,14 +832,38 @@ func (l *Logger) writeLogEntries(entries []*logpb.LogEntry) {
|
| 771 | 771 |
// (for example by calling SetFlags or SetPrefix). |
| 772 | 772 |
func (l *Logger) StandardLogger(s Severity) *log.Logger { return l.stdLoggers[s] }
|
| 773 | 773 |
|
| 774 |
-func trunc32(i int) int32 {
|
|
| 775 |
- if i > math.MaxInt32 {
|
|
| 776 |
- i = math.MaxInt32 |
|
| 774 |
+var reCloudTraceContext = regexp.MustCompile(`([a-f\d]+)/([a-f\d]+);o=(\d)`) |
|
| 775 |
+ |
|
| 776 |
+func deconstructXCloudTraceContext(s string) (traceID, spanID string, traceSampled bool) {
|
|
| 777 |
+ // As per the format described at https://cloud.google.com/trace/docs/troubleshooting#force-trace |
|
| 778 |
+ // "X-Cloud-Trace-Context: TRACE_ID/SPAN_ID;o=TRACE_TRUE" |
|
| 779 |
+ // for example: |
|
| 780 |
+ // "X-Cloud-Trace-Context: 105445aa7843bc8bf206b120001000/0;o=1" |
|
| 781 |
+ // |
|
| 782 |
+ // We expect: |
|
| 783 |
+ // * traceID: "105445aa7843bc8bf206b120001000" |
|
| 784 |
+ // * spanID: "" |
|
| 785 |
+ // * traceSampled: true |
|
| 786 |
+ matches := reCloudTraceContext.FindAllStringSubmatch(s, -1) |
|
| 787 |
+ if len(matches) != 1 {
|
|
| 788 |
+ return |
|
| 789 |
+ } |
|
| 790 |
+ |
|
| 791 |
+ sub := matches[0] |
|
| 792 |
+ if len(sub) != 4 {
|
|
| 793 |
+ return |
|
| 777 | 794 |
} |
| 778 |
- return int32(i) |
|
| 795 |
+ |
|
| 796 |
+ traceID, spanID = sub[1], sub[2] |
|
| 797 |
+ if spanID == "0" {
|
|
| 798 |
+ spanID = "" |
|
| 799 |
+ } |
|
| 800 |
+ traceSampled = sub[3] == "1" |
|
| 801 |
+ |
|
| 802 |
+ return |
|
| 779 | 803 |
} |
| 780 | 804 |
|
| 781 |
-func toLogEntry(e Entry) (*logpb.LogEntry, error) {
|
|
| 805 |
+func (l *Logger) toLogEntry(e Entry) (*logpb.LogEntry, error) {
|
|
| 782 | 806 |
if e.LogName != "" {
|
| 783 | 807 |
return nil, errors.New("logging: Entry.LogName should be not be set when writing")
|
| 784 | 808 |
} |
| ... | ... |
@@ -790,15 +875,37 @@ func toLogEntry(e Entry) (*logpb.LogEntry, error) {
|
| 790 | 790 |
if err != nil {
|
| 791 | 791 |
return nil, err |
| 792 | 792 |
} |
| 793 |
+ if e.Trace == "" && e.HTTPRequest != nil && e.HTTPRequest.Request != nil {
|
|
| 794 |
+ traceHeader := e.HTTPRequest.Request.Header.Get("X-Cloud-Trace-Context")
|
|
| 795 |
+ if traceHeader != "" {
|
|
| 796 |
+ // Set to a relative resource name, as described at |
|
| 797 |
+ // https://cloud.google.com/appengine/docs/flexible/go/writing-application-logs. |
|
| 798 |
+ traceID, spanID, traceSampled := deconstructXCloudTraceContext(traceHeader) |
|
| 799 |
+ if traceID != "" {
|
|
| 800 |
+ e.Trace = fmt.Sprintf("%s/traces/%s", l.client.parent, traceID)
|
|
| 801 |
+ } |
|
| 802 |
+ if e.SpanID == "" {
|
|
| 803 |
+ e.SpanID = spanID |
|
| 804 |
+ } |
|
| 805 |
+ |
|
| 806 |
+ // If we previously hadn't set TraceSampled, let's retrieve it |
|
| 807 |
+ // from the HTTP request's header, as per: |
|
| 808 |
+ // https://cloud.google.com/trace/docs/troubleshooting#force-trace |
|
| 809 |
+ e.TraceSampled = e.TraceSampled || traceSampled |
|
| 810 |
+ } |
|
| 811 |
+ } |
|
| 793 | 812 |
ent := &logpb.LogEntry{
|
| 794 |
- Timestamp: ts, |
|
| 795 |
- Severity: logtypepb.LogSeverity(e.Severity), |
|
| 796 |
- InsertId: e.InsertID, |
|
| 797 |
- HttpRequest: fromHTTPRequest(e.HTTPRequest), |
|
| 798 |
- Operation: e.Operation, |
|
| 799 |
- Labels: e.Labels, |
|
| 800 |
- Trace: e.Trace, |
|
| 801 |
- Resource: e.Resource, |
|
| 813 |
+ Timestamp: ts, |
|
| 814 |
+ Severity: logtypepb.LogSeverity(e.Severity), |
|
| 815 |
+ InsertId: e.InsertID, |
|
| 816 |
+ HttpRequest: fromHTTPRequest(e.HTTPRequest), |
|
| 817 |
+ Operation: e.Operation, |
|
| 818 |
+ Labels: e.Labels, |
|
| 819 |
+ Trace: e.Trace, |
|
| 820 |
+ SpanId: e.SpanID, |
|
| 821 |
+ Resource: e.Resource, |
|
| 822 |
+ SourceLocation: e.SourceLocation, |
|
| 823 |
+ TraceSampled: e.TraceSampled, |
|
| 802 | 824 |
} |
| 803 | 825 |
switch p := e.Payload.(type) {
|
| 804 | 826 |
case string: |
| 805 | 827 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,33 @@ |
| 0 |
+// +build tools |
|
| 1 |
+ |
|
| 2 |
+// Copyright 2018 Google LLC |
|
| 3 |
+// |
|
| 4 |
+// Licensed under the Apache License, Version 2.0 (the "License"); |
|
| 5 |
+// you may not use this file except in compliance with the License. |
|
| 6 |
+// You may obtain a copy of the License at |
|
| 7 |
+// |
|
| 8 |
+// http://www.apache.org/licenses/LICENSE-2.0 |
|
| 9 |
+// |
|
| 10 |
+// Unless required by applicable law or agreed to in writing, software |
|
| 11 |
+// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 12 |
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 13 |
+// See the License for the specific language governing permissions and |
|
| 14 |
+// limitations under the License. |
|
| 15 |
+ |
|
| 16 |
+// This package exists to cause `go mod` and `go get` to believe these tools |
|
| 17 |
+// are dependencies, even though they are not runtime dependencies of any |
|
| 18 |
+// package (these are tools used by our CI builds). This means they will appear |
|
| 19 |
+// in our `go.mod` file, but will not be a part of the build. Also, since the |
|
| 20 |
+// build target is something non-existent, these should not be included in any |
|
| 21 |
+// binaries. |
|
| 22 |
+ |
|
| 23 |
+package cloud |
|
| 24 |
+ |
|
| 25 |
+import ( |
|
| 26 |
+ _ "github.com/golang/protobuf/protoc-gen-go" |
|
| 27 |
+ _ "github.com/jstemmer/go-junit-report" |
|
| 28 |
+ _ "golang.org/x/exp/cmd/apidiff" |
|
| 29 |
+ _ "golang.org/x/lint/golint" |
|
| 30 |
+ _ "golang.org/x/tools/cmd/goimports" |
|
| 31 |
+ _ "honnef.co/go/tools/cmd/staticcheck" |
|
| 32 |
+) |
| 0 | 33 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,2808 @@ |
| 0 |
+// Go support for Protocol Buffers - Google's data interchange format |
|
| 1 |
+// |
|
| 2 |
+// Copyright 2010 The Go Authors. All rights reserved. |
|
| 3 |
+// https://github.com/golang/protobuf |
|
| 4 |
+// |
|
| 5 |
+// Redistribution and use in source and binary forms, with or without |
|
| 6 |
+// modification, are permitted provided that the following conditions are |
|
| 7 |
+// met: |
|
| 8 |
+// |
|
| 9 |
+// * Redistributions of source code must retain the above copyright |
|
| 10 |
+// notice, this list of conditions and the following disclaimer. |
|
| 11 |
+// * Redistributions in binary form must reproduce the above |
|
| 12 |
+// copyright notice, this list of conditions and the following disclaimer |
|
| 13 |
+// in the documentation and/or other materials provided with the |
|
| 14 |
+// distribution. |
|
| 15 |
+// * Neither the name of Google Inc. nor the names of its |
|
| 16 |
+// contributors may be used to endorse or promote products derived from |
|
| 17 |
+// this software without specific prior written permission. |
|
| 18 |
+// |
|
| 19 |
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
| 20 |
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
| 21 |
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
| 22 |
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
| 23 |
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
| 24 |
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 25 |
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
| 26 |
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
| 27 |
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
| 28 |
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
| 29 |
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
| 30 |
+ |
|
| 31 |
+/* |
|
| 32 |
+ The code generator for the plugin for the Google protocol buffer compiler. |
|
| 33 |
+ It generates Go code from the protocol buffer description files read by the |
|
| 34 |
+ main routine. |
|
| 35 |
+*/ |
|
| 36 |
+package generator |
|
| 37 |
+ |
|
| 38 |
+import ( |
|
| 39 |
+ "bufio" |
|
| 40 |
+ "bytes" |
|
| 41 |
+ "compress/gzip" |
|
| 42 |
+ "crypto/sha256" |
|
| 43 |
+ "encoding/hex" |
|
| 44 |
+ "fmt" |
|
| 45 |
+ "go/ast" |
|
| 46 |
+ "go/build" |
|
| 47 |
+ "go/parser" |
|
| 48 |
+ "go/printer" |
|
| 49 |
+ "go/token" |
|
| 50 |
+ "log" |
|
| 51 |
+ "os" |
|
| 52 |
+ "path" |
|
| 53 |
+ "sort" |
|
| 54 |
+ "strconv" |
|
| 55 |
+ "strings" |
|
| 56 |
+ "unicode" |
|
| 57 |
+ "unicode/utf8" |
|
| 58 |
+ |
|
| 59 |
+ "github.com/golang/protobuf/proto" |
|
| 60 |
+ "github.com/golang/protobuf/protoc-gen-go/generator/internal/remap" |
|
| 61 |
+ |
|
| 62 |
+ "github.com/golang/protobuf/protoc-gen-go/descriptor" |
|
| 63 |
+ plugin "github.com/golang/protobuf/protoc-gen-go/plugin" |
|
| 64 |
+) |
|
| 65 |
+ |
|
| 66 |
+// generatedCodeVersion indicates a version of the generated code. |
|
| 67 |
+// It is incremented whenever an incompatibility between the generated code and |
|
| 68 |
+// proto package is introduced; the generated code references |
|
| 69 |
+// a constant, proto.ProtoPackageIsVersionN (where N is generatedCodeVersion). |
|
| 70 |
+const generatedCodeVersion = 3 |
|
| 71 |
+ |
|
| 72 |
+// A Plugin provides functionality to add to the output during Go code generation, |
|
| 73 |
+// such as to produce RPC stubs. |
|
| 74 |
+type Plugin interface {
|
|
| 75 |
+ // Name identifies the plugin. |
|
| 76 |
+ Name() string |
|
| 77 |
+ // Init is called once after data structures are built but before |
|
| 78 |
+ // code generation begins. |
|
| 79 |
+ Init(g *Generator) |
|
| 80 |
+ // Generate produces the code generated by the plugin for this file, |
|
| 81 |
+ // except for the imports, by calling the generator's methods P, In, and Out. |
|
| 82 |
+ Generate(file *FileDescriptor) |
|
| 83 |
+ // GenerateImports produces the import declarations for this file. |
|
| 84 |
+ // It is called after Generate. |
|
| 85 |
+ GenerateImports(file *FileDescriptor) |
|
| 86 |
+} |
|
| 87 |
+ |
|
| 88 |
+var plugins []Plugin |
|
| 89 |
+ |
|
| 90 |
+// RegisterPlugin installs a (second-order) plugin to be run when the Go output is generated. |
|
| 91 |
+// It is typically called during initialization. |
|
| 92 |
+func RegisterPlugin(p Plugin) {
|
|
| 93 |
+ plugins = append(plugins, p) |
|
| 94 |
+} |
|
| 95 |
+ |
|
| 96 |
+// A GoImportPath is the import path of a Go package. e.g., "google.golang.org/genproto/protobuf". |
|
| 97 |
+type GoImportPath string |
|
| 98 |
+ |
|
| 99 |
+func (p GoImportPath) String() string { return strconv.Quote(string(p)) }
|
|
| 100 |
+ |
|
| 101 |
+// A GoPackageName is the name of a Go package. e.g., "protobuf". |
|
| 102 |
+type GoPackageName string |
|
| 103 |
+ |
|
| 104 |
+// Each type we import as a protocol buffer (other than FileDescriptorProto) needs |
|
| 105 |
+// a pointer to the FileDescriptorProto that represents it. These types achieve that |
|
| 106 |
+// wrapping by placing each Proto inside a struct with the pointer to its File. The |
|
| 107 |
+// structs have the same names as their contents, with "Proto" removed. |
|
| 108 |
+// FileDescriptor is used to store the things that it points to. |
|
| 109 |
+ |
|
| 110 |
+// The file and package name method are common to messages and enums. |
|
| 111 |
+type common struct {
|
|
| 112 |
+ file *FileDescriptor // File this object comes from. |
|
| 113 |
+} |
|
| 114 |
+ |
|
| 115 |
+// GoImportPath is the import path of the Go package containing the type. |
|
| 116 |
+func (c *common) GoImportPath() GoImportPath {
|
|
| 117 |
+ return c.file.importPath |
|
| 118 |
+} |
|
| 119 |
+ |
|
| 120 |
+func (c *common) File() *FileDescriptor { return c.file }
|
|
| 121 |
+ |
|
| 122 |
+func fileIsProto3(file *descriptor.FileDescriptorProto) bool {
|
|
| 123 |
+ return file.GetSyntax() == "proto3" |
|
| 124 |
+} |
|
| 125 |
+ |
|
| 126 |
+func (c *common) proto3() bool { return fileIsProto3(c.file.FileDescriptorProto) }
|
|
| 127 |
+ |
|
| 128 |
+// Descriptor represents a protocol buffer message. |
|
| 129 |
+type Descriptor struct {
|
|
| 130 |
+ common |
|
| 131 |
+ *descriptor.DescriptorProto |
|
| 132 |
+ parent *Descriptor // The containing message, if any. |
|
| 133 |
+ nested []*Descriptor // Inner messages, if any. |
|
| 134 |
+ enums []*EnumDescriptor // Inner enums, if any. |
|
| 135 |
+ ext []*ExtensionDescriptor // Extensions, if any. |
|
| 136 |
+ typename []string // Cached typename vector. |
|
| 137 |
+ index int // The index into the container, whether the file or another message. |
|
| 138 |
+ path string // The SourceCodeInfo path as comma-separated integers. |
|
| 139 |
+ group bool |
|
| 140 |
+} |
|
| 141 |
+ |
|
| 142 |
+// TypeName returns the elements of the dotted type name. |
|
| 143 |
+// The package name is not part of this name. |
|
| 144 |
+func (d *Descriptor) TypeName() []string {
|
|
| 145 |
+ if d.typename != nil {
|
|
| 146 |
+ return d.typename |
|
| 147 |
+ } |
|
| 148 |
+ n := 0 |
|
| 149 |
+ for parent := d; parent != nil; parent = parent.parent {
|
|
| 150 |
+ n++ |
|
| 151 |
+ } |
|
| 152 |
+ s := make([]string, n) |
|
| 153 |
+ for parent := d; parent != nil; parent = parent.parent {
|
|
| 154 |
+ n-- |
|
| 155 |
+ s[n] = parent.GetName() |
|
| 156 |
+ } |
|
| 157 |
+ d.typename = s |
|
| 158 |
+ return s |
|
| 159 |
+} |
|
| 160 |
+ |
|
| 161 |
+// EnumDescriptor describes an enum. If it's at top level, its parent will be nil. |
|
| 162 |
+// Otherwise it will be the descriptor of the message in which it is defined. |
|
| 163 |
+type EnumDescriptor struct {
|
|
| 164 |
+ common |
|
| 165 |
+ *descriptor.EnumDescriptorProto |
|
| 166 |
+ parent *Descriptor // The containing message, if any. |
|
| 167 |
+ typename []string // Cached typename vector. |
|
| 168 |
+ index int // The index into the container, whether the file or a message. |
|
| 169 |
+ path string // The SourceCodeInfo path as comma-separated integers. |
|
| 170 |
+} |
|
| 171 |
+ |
|
| 172 |
+// TypeName returns the elements of the dotted type name. |
|
| 173 |
+// The package name is not part of this name. |
|
| 174 |
+func (e *EnumDescriptor) TypeName() (s []string) {
|
|
| 175 |
+ if e.typename != nil {
|
|
| 176 |
+ return e.typename |
|
| 177 |
+ } |
|
| 178 |
+ name := e.GetName() |
|
| 179 |
+ if e.parent == nil {
|
|
| 180 |
+ s = make([]string, 1) |
|
| 181 |
+ } else {
|
|
| 182 |
+ pname := e.parent.TypeName() |
|
| 183 |
+ s = make([]string, len(pname)+1) |
|
| 184 |
+ copy(s, pname) |
|
| 185 |
+ } |
|
| 186 |
+ s[len(s)-1] = name |
|
| 187 |
+ e.typename = s |
|
| 188 |
+ return s |
|
| 189 |
+} |
|
| 190 |
+ |
|
| 191 |
+// Everything but the last element of the full type name, CamelCased. |
|
| 192 |
+// The values of type Foo.Bar are call Foo_value1... not Foo_Bar_value1... . |
|
| 193 |
+func (e *EnumDescriptor) prefix() string {
|
|
| 194 |
+ if e.parent == nil {
|
|
| 195 |
+ // If the enum is not part of a message, the prefix is just the type name. |
|
| 196 |
+ return CamelCase(*e.Name) + "_" |
|
| 197 |
+ } |
|
| 198 |
+ typeName := e.TypeName() |
|
| 199 |
+ return CamelCaseSlice(typeName[0:len(typeName)-1]) + "_" |
|
| 200 |
+} |
|
| 201 |
+ |
|
| 202 |
+// The integer value of the named constant in this enumerated type. |
|
| 203 |
+func (e *EnumDescriptor) integerValueAsString(name string) string {
|
|
| 204 |
+ for _, c := range e.Value {
|
|
| 205 |
+ if c.GetName() == name {
|
|
| 206 |
+ return fmt.Sprint(c.GetNumber()) |
|
| 207 |
+ } |
|
| 208 |
+ } |
|
| 209 |
+ log.Fatal("cannot find value for enum constant")
|
|
| 210 |
+ return "" |
|
| 211 |
+} |
|
| 212 |
+ |
|
| 213 |
+// ExtensionDescriptor describes an extension. If it's at top level, its parent will be nil. |
|
| 214 |
+// Otherwise it will be the descriptor of the message in which it is defined. |
|
| 215 |
+type ExtensionDescriptor struct {
|
|
| 216 |
+ common |
|
| 217 |
+ *descriptor.FieldDescriptorProto |
|
| 218 |
+ parent *Descriptor // The containing message, if any. |
|
| 219 |
+} |
|
| 220 |
+ |
|
| 221 |
+// TypeName returns the elements of the dotted type name. |
|
| 222 |
+// The package name is not part of this name. |
|
| 223 |
+func (e *ExtensionDescriptor) TypeName() (s []string) {
|
|
| 224 |
+ name := e.GetName() |
|
| 225 |
+ if e.parent == nil {
|
|
| 226 |
+ // top-level extension |
|
| 227 |
+ s = make([]string, 1) |
|
| 228 |
+ } else {
|
|
| 229 |
+ pname := e.parent.TypeName() |
|
| 230 |
+ s = make([]string, len(pname)+1) |
|
| 231 |
+ copy(s, pname) |
|
| 232 |
+ } |
|
| 233 |
+ s[len(s)-1] = name |
|
| 234 |
+ return s |
|
| 235 |
+} |
|
| 236 |
+ |
|
| 237 |
+// DescName returns the variable name used for the generated descriptor. |
|
| 238 |
+func (e *ExtensionDescriptor) DescName() string {
|
|
| 239 |
+ // The full type name. |
|
| 240 |
+ typeName := e.TypeName() |
|
| 241 |
+ // Each scope of the extension is individually CamelCased, and all are joined with "_" with an "E_" prefix. |
|
| 242 |
+ for i, s := range typeName {
|
|
| 243 |
+ typeName[i] = CamelCase(s) |
|
| 244 |
+ } |
|
| 245 |
+ return "E_" + strings.Join(typeName, "_") |
|
| 246 |
+} |
|
| 247 |
+ |
|
| 248 |
+// ImportedDescriptor describes a type that has been publicly imported from another file. |
|
| 249 |
+type ImportedDescriptor struct {
|
|
| 250 |
+ common |
|
| 251 |
+ o Object |
|
| 252 |
+} |
|
| 253 |
+ |
|
| 254 |
+func (id *ImportedDescriptor) TypeName() []string { return id.o.TypeName() }
|
|
| 255 |
+ |
|
| 256 |
+// FileDescriptor describes an protocol buffer descriptor file (.proto). |
|
| 257 |
+// It includes slices of all the messages and enums defined within it. |
|
| 258 |
+// Those slices are constructed by WrapTypes. |
|
| 259 |
+type FileDescriptor struct {
|
|
| 260 |
+ *descriptor.FileDescriptorProto |
|
| 261 |
+ desc []*Descriptor // All the messages defined in this file. |
|
| 262 |
+ enum []*EnumDescriptor // All the enums defined in this file. |
|
| 263 |
+ ext []*ExtensionDescriptor // All the top-level extensions defined in this file. |
|
| 264 |
+ imp []*ImportedDescriptor // All types defined in files publicly imported by this file. |
|
| 265 |
+ |
|
| 266 |
+ // Comments, stored as a map of path (comma-separated integers) to the comment. |
|
| 267 |
+ comments map[string]*descriptor.SourceCodeInfo_Location |
|
| 268 |
+ |
|
| 269 |
+ // The full list of symbols that are exported, |
|
| 270 |
+ // as a map from the exported object to its symbols. |
|
| 271 |
+ // This is used for supporting public imports. |
|
| 272 |
+ exported map[Object][]symbol |
|
| 273 |
+ |
|
| 274 |
+ importPath GoImportPath // Import path of this file's package. |
|
| 275 |
+ packageName GoPackageName // Name of this file's Go package. |
|
| 276 |
+ |
|
| 277 |
+ proto3 bool // whether to generate proto3 code for this file |
|
| 278 |
+} |
|
| 279 |
+ |
|
| 280 |
+// VarName is the variable name we'll use in the generated code to refer |
|
| 281 |
+// to the compressed bytes of this descriptor. It is not exported, so |
|
| 282 |
+// it is only valid inside the generated package. |
|
| 283 |
+func (d *FileDescriptor) VarName() string {
|
|
| 284 |
+ h := sha256.Sum256([]byte(d.GetName())) |
|
| 285 |
+ return fmt.Sprintf("fileDescriptor_%s", hex.EncodeToString(h[:8]))
|
|
| 286 |
+} |
|
| 287 |
+ |
|
| 288 |
+// goPackageOption interprets the file's go_package option. |
|
| 289 |
+// If there is no go_package, it returns ("", "", false).
|
|
| 290 |
+// If there's a simple name, it returns ("", pkg, true).
|
|
| 291 |
+// If the option implies an import path, it returns (impPath, pkg, true). |
|
| 292 |
+func (d *FileDescriptor) goPackageOption() (impPath GoImportPath, pkg GoPackageName, ok bool) {
|
|
| 293 |
+ opt := d.GetOptions().GetGoPackage() |
|
| 294 |
+ if opt == "" {
|
|
| 295 |
+ return "", "", false |
|
| 296 |
+ } |
|
| 297 |
+ // A semicolon-delimited suffix delimits the import path and package name. |
|
| 298 |
+ sc := strings.Index(opt, ";") |
|
| 299 |
+ if sc >= 0 {
|
|
| 300 |
+ return GoImportPath(opt[:sc]), cleanPackageName(opt[sc+1:]), true |
|
| 301 |
+ } |
|
| 302 |
+ // The presence of a slash implies there's an import path. |
|
| 303 |
+ slash := strings.LastIndex(opt, "/") |
|
| 304 |
+ if slash >= 0 {
|
|
| 305 |
+ return GoImportPath(opt), cleanPackageName(opt[slash+1:]), true |
|
| 306 |
+ } |
|
| 307 |
+ return "", cleanPackageName(opt), true |
|
| 308 |
+} |
|
| 309 |
+ |
|
| 310 |
+// goFileName returns the output name for the generated Go file. |
|
| 311 |
+func (d *FileDescriptor) goFileName(pathType pathType) string {
|
|
| 312 |
+ name := *d.Name |
|
| 313 |
+ if ext := path.Ext(name); ext == ".proto" || ext == ".protodevel" {
|
|
| 314 |
+ name = name[:len(name)-len(ext)] |
|
| 315 |
+ } |
|
| 316 |
+ name += ".pb.go" |
|
| 317 |
+ |
|
| 318 |
+ if pathType == pathTypeSourceRelative {
|
|
| 319 |
+ return name |
|
| 320 |
+ } |
|
| 321 |
+ |
|
| 322 |
+ // Does the file have a "go_package" option? |
|
| 323 |
+ // If it does, it may override the filename. |
|
| 324 |
+ if impPath, _, ok := d.goPackageOption(); ok && impPath != "" {
|
|
| 325 |
+ // Replace the existing dirname with the declared import path. |
|
| 326 |
+ _, name = path.Split(name) |
|
| 327 |
+ name = path.Join(string(impPath), name) |
|
| 328 |
+ return name |
|
| 329 |
+ } |
|
| 330 |
+ |
|
| 331 |
+ return name |
|
| 332 |
+} |
|
| 333 |
+ |
|
| 334 |
+func (d *FileDescriptor) addExport(obj Object, sym symbol) {
|
|
| 335 |
+ d.exported[obj] = append(d.exported[obj], sym) |
|
| 336 |
+} |
|
| 337 |
+ |
|
| 338 |
+// symbol is an interface representing an exported Go symbol. |
|
| 339 |
+type symbol interface {
|
|
| 340 |
+ // GenerateAlias should generate an appropriate alias |
|
| 341 |
+ // for the symbol from the named package. |
|
| 342 |
+ GenerateAlias(g *Generator, filename string, pkg GoPackageName) |
|
| 343 |
+} |
|
| 344 |
+ |
|
| 345 |
+type messageSymbol struct {
|
|
| 346 |
+ sym string |
|
| 347 |
+ hasExtensions, isMessageSet bool |
|
| 348 |
+ oneofTypes []string |
|
| 349 |
+} |
|
| 350 |
+ |
|
| 351 |
+type getterSymbol struct {
|
|
| 352 |
+ name string |
|
| 353 |
+ typ string |
|
| 354 |
+ typeName string // canonical name in proto world; empty for proto.Message and similar |
|
| 355 |
+ genType bool // whether typ contains a generated type (message/group/enum) |
|
| 356 |
+} |
|
| 357 |
+ |
|
| 358 |
+func (ms *messageSymbol) GenerateAlias(g *Generator, filename string, pkg GoPackageName) {
|
|
| 359 |
+ g.P("// ", ms.sym, " from public import ", filename)
|
|
| 360 |
+ g.P("type ", ms.sym, " = ", pkg, ".", ms.sym)
|
|
| 361 |
+ for _, name := range ms.oneofTypes {
|
|
| 362 |
+ g.P("type ", name, " = ", pkg, ".", name)
|
|
| 363 |
+ } |
|
| 364 |
+} |
|
| 365 |
+ |
|
| 366 |
+type enumSymbol struct {
|
|
| 367 |
+ name string |
|
| 368 |
+ proto3 bool // Whether this came from a proto3 file. |
|
| 369 |
+} |
|
| 370 |
+ |
|
| 371 |
+func (es enumSymbol) GenerateAlias(g *Generator, filename string, pkg GoPackageName) {
|
|
| 372 |
+ s := es.name |
|
| 373 |
+ g.P("// ", s, " from public import ", filename)
|
|
| 374 |
+ g.P("type ", s, " = ", pkg, ".", s)
|
|
| 375 |
+ g.P("var ", s, "_name = ", pkg, ".", s, "_name")
|
|
| 376 |
+ g.P("var ", s, "_value = ", pkg, ".", s, "_value")
|
|
| 377 |
+} |
|
| 378 |
+ |
|
| 379 |
+type constOrVarSymbol struct {
|
|
| 380 |
+ sym string |
|
| 381 |
+ typ string // either "const" or "var" |
|
| 382 |
+ cast string // if non-empty, a type cast is required (used for enums) |
|
| 383 |
+} |
|
| 384 |
+ |
|
| 385 |
+func (cs constOrVarSymbol) GenerateAlias(g *Generator, filename string, pkg GoPackageName) {
|
|
| 386 |
+ v := string(pkg) + "." + cs.sym |
|
| 387 |
+ if cs.cast != "" {
|
|
| 388 |
+ v = cs.cast + "(" + v + ")"
|
|
| 389 |
+ } |
|
| 390 |
+ g.P(cs.typ, " ", cs.sym, " = ", v) |
|
| 391 |
+} |
|
| 392 |
+ |
|
| 393 |
+// Object is an interface abstracting the abilities shared by enums, messages, extensions and imported objects. |
|
| 394 |
+type Object interface {
|
|
| 395 |
+ GoImportPath() GoImportPath |
|
| 396 |
+ TypeName() []string |
|
| 397 |
+ File() *FileDescriptor |
|
| 398 |
+} |
|
| 399 |
+ |
|
| 400 |
+// Generator is the type whose methods generate the output, stored in the associated response structure. |
|
| 401 |
+type Generator struct {
|
|
| 402 |
+ *bytes.Buffer |
|
| 403 |
+ |
|
| 404 |
+ Request *plugin.CodeGeneratorRequest // The input. |
|
| 405 |
+ Response *plugin.CodeGeneratorResponse // The output. |
|
| 406 |
+ |
|
| 407 |
+ Param map[string]string // Command-line parameters. |
|
| 408 |
+ PackageImportPath string // Go import path of the package we're generating code for |
|
| 409 |
+ ImportPrefix string // String to prefix to imported package file names. |
|
| 410 |
+ ImportMap map[string]string // Mapping from .proto file name to import path |
|
| 411 |
+ |
|
| 412 |
+ Pkg map[string]string // The names under which we import support packages |
|
| 413 |
+ |
|
| 414 |
+ outputImportPath GoImportPath // Package we're generating code for. |
|
| 415 |
+ allFiles []*FileDescriptor // All files in the tree |
|
| 416 |
+ allFilesByName map[string]*FileDescriptor // All files by filename. |
|
| 417 |
+ genFiles []*FileDescriptor // Those files we will generate output for. |
|
| 418 |
+ file *FileDescriptor // The file we are compiling now. |
|
| 419 |
+ packageNames map[GoImportPath]GoPackageName // Imported package names in the current file. |
|
| 420 |
+ usedPackages map[GoImportPath]bool // Packages used in current file. |
|
| 421 |
+ usedPackageNames map[GoPackageName]bool // Package names used in the current file. |
|
| 422 |
+ addedImports map[GoImportPath]bool // Additional imports to emit. |
|
| 423 |
+ typeNameToObject map[string]Object // Key is a fully-qualified name in input syntax. |
|
| 424 |
+ init []string // Lines to emit in the init function. |
|
| 425 |
+ indent string |
|
| 426 |
+ pathType pathType // How to generate output filenames. |
|
| 427 |
+ writeOutput bool |
|
| 428 |
+ annotateCode bool // whether to store annotations |
|
| 429 |
+ annotations []*descriptor.GeneratedCodeInfo_Annotation // annotations to store |
|
| 430 |
+} |
|
| 431 |
+ |
|
| 432 |
+type pathType int |
|
| 433 |
+ |
|
| 434 |
+const ( |
|
| 435 |
+ pathTypeImport pathType = iota |
|
| 436 |
+ pathTypeSourceRelative |
|
| 437 |
+) |
|
| 438 |
+ |
|
| 439 |
+// New creates a new generator and allocates the request and response protobufs. |
|
| 440 |
+func New() *Generator {
|
|
| 441 |
+ g := new(Generator) |
|
| 442 |
+ g.Buffer = new(bytes.Buffer) |
|
| 443 |
+ g.Request = new(plugin.CodeGeneratorRequest) |
|
| 444 |
+ g.Response = new(plugin.CodeGeneratorResponse) |
|
| 445 |
+ return g |
|
| 446 |
+} |
|
| 447 |
+ |
|
| 448 |
+// Error reports a problem, including an error, and exits the program. |
|
| 449 |
+func (g *Generator) Error(err error, msgs ...string) {
|
|
| 450 |
+ s := strings.Join(msgs, " ") + ":" + err.Error() |
|
| 451 |
+ log.Print("protoc-gen-go: error:", s)
|
|
| 452 |
+ os.Exit(1) |
|
| 453 |
+} |
|
| 454 |
+ |
|
| 455 |
+// Fail reports a problem and exits the program. |
|
| 456 |
+func (g *Generator) Fail(msgs ...string) {
|
|
| 457 |
+ s := strings.Join(msgs, " ") |
|
| 458 |
+ log.Print("protoc-gen-go: error:", s)
|
|
| 459 |
+ os.Exit(1) |
|
| 460 |
+} |
|
| 461 |
+ |
|
| 462 |
+// CommandLineParameters breaks the comma-separated list of key=value pairs |
|
| 463 |
+// in the parameter (a member of the request protobuf) into a key/value map. |
|
| 464 |
+// It then sets file name mappings defined by those entries. |
|
| 465 |
+func (g *Generator) CommandLineParameters(parameter string) {
|
|
| 466 |
+ g.Param = make(map[string]string) |
|
| 467 |
+ for _, p := range strings.Split(parameter, ",") {
|
|
| 468 |
+ if i := strings.Index(p, "="); i < 0 {
|
|
| 469 |
+ g.Param[p] = "" |
|
| 470 |
+ } else {
|
|
| 471 |
+ g.Param[p[0:i]] = p[i+1:] |
|
| 472 |
+ } |
|
| 473 |
+ } |
|
| 474 |
+ |
|
| 475 |
+ g.ImportMap = make(map[string]string) |
|
| 476 |
+ pluginList := "none" // Default list of plugin names to enable (empty means all). |
|
| 477 |
+ for k, v := range g.Param {
|
|
| 478 |
+ switch k {
|
|
| 479 |
+ case "import_prefix": |
|
| 480 |
+ g.ImportPrefix = v |
|
| 481 |
+ case "import_path": |
|
| 482 |
+ g.PackageImportPath = v |
|
| 483 |
+ case "paths": |
|
| 484 |
+ switch v {
|
|
| 485 |
+ case "import": |
|
| 486 |
+ g.pathType = pathTypeImport |
|
| 487 |
+ case "source_relative": |
|
| 488 |
+ g.pathType = pathTypeSourceRelative |
|
| 489 |
+ default: |
|
| 490 |
+ g.Fail(fmt.Sprintf(`Unknown path type %q: want "import" or "source_relative".`, v)) |
|
| 491 |
+ } |
|
| 492 |
+ case "plugins": |
|
| 493 |
+ pluginList = v |
|
| 494 |
+ case "annotate_code": |
|
| 495 |
+ if v == "true" {
|
|
| 496 |
+ g.annotateCode = true |
|
| 497 |
+ } |
|
| 498 |
+ default: |
|
| 499 |
+ if len(k) > 0 && k[0] == 'M' {
|
|
| 500 |
+ g.ImportMap[k[1:]] = v |
|
| 501 |
+ } |
|
| 502 |
+ } |
|
| 503 |
+ } |
|
| 504 |
+ if pluginList != "" {
|
|
| 505 |
+ // Amend the set of plugins. |
|
| 506 |
+ enabled := make(map[string]bool) |
|
| 507 |
+ for _, name := range strings.Split(pluginList, "+") {
|
|
| 508 |
+ enabled[name] = true |
|
| 509 |
+ } |
|
| 510 |
+ var nplugins []Plugin |
|
| 511 |
+ for _, p := range plugins {
|
|
| 512 |
+ if enabled[p.Name()] {
|
|
| 513 |
+ nplugins = append(nplugins, p) |
|
| 514 |
+ } |
|
| 515 |
+ } |
|
| 516 |
+ plugins = nplugins |
|
| 517 |
+ } |
|
| 518 |
+} |
|
| 519 |
+ |
|
| 520 |
+// DefaultPackageName returns the package name printed for the object. |
|
| 521 |
+// If its file is in a different package, it returns the package name we're using for this file, plus ".". |
|
| 522 |
+// Otherwise it returns the empty string. |
|
| 523 |
+func (g *Generator) DefaultPackageName(obj Object) string {
|
|
| 524 |
+ importPath := obj.GoImportPath() |
|
| 525 |
+ if importPath == g.outputImportPath {
|
|
| 526 |
+ return "" |
|
| 527 |
+ } |
|
| 528 |
+ return string(g.GoPackageName(importPath)) + "." |
|
| 529 |
+} |
|
| 530 |
+ |
|
| 531 |
+// GoPackageName returns the name used for a package. |
|
| 532 |
+func (g *Generator) GoPackageName(importPath GoImportPath) GoPackageName {
|
|
| 533 |
+ if name, ok := g.packageNames[importPath]; ok {
|
|
| 534 |
+ return name |
|
| 535 |
+ } |
|
| 536 |
+ name := cleanPackageName(baseName(string(importPath))) |
|
| 537 |
+ for i, orig := 1, name; g.usedPackageNames[name] || isGoPredeclaredIdentifier[string(name)]; i++ {
|
|
| 538 |
+ name = orig + GoPackageName(strconv.Itoa(i)) |
|
| 539 |
+ } |
|
| 540 |
+ g.packageNames[importPath] = name |
|
| 541 |
+ g.usedPackageNames[name] = true |
|
| 542 |
+ return name |
|
| 543 |
+} |
|
| 544 |
+ |
|
| 545 |
+// AddImport adds a package to the generated file's import section. |
|
| 546 |
+// It returns the name used for the package. |
|
| 547 |
+func (g *Generator) AddImport(importPath GoImportPath) GoPackageName {
|
|
| 548 |
+ g.addedImports[importPath] = true |
|
| 549 |
+ return g.GoPackageName(importPath) |
|
| 550 |
+} |
|
| 551 |
+ |
|
| 552 |
+var globalPackageNames = map[GoPackageName]bool{
|
|
| 553 |
+ "fmt": true, |
|
| 554 |
+ "math": true, |
|
| 555 |
+ "proto": true, |
|
| 556 |
+} |
|
| 557 |
+ |
|
| 558 |
+// Create and remember a guaranteed unique package name. Pkg is the candidate name. |
|
| 559 |
+// The FileDescriptor parameter is unused. |
|
| 560 |
+func RegisterUniquePackageName(pkg string, f *FileDescriptor) string {
|
|
| 561 |
+ name := cleanPackageName(pkg) |
|
| 562 |
+ for i, orig := 1, name; globalPackageNames[name]; i++ {
|
|
| 563 |
+ name = orig + GoPackageName(strconv.Itoa(i)) |
|
| 564 |
+ } |
|
| 565 |
+ globalPackageNames[name] = true |
|
| 566 |
+ return string(name) |
|
| 567 |
+} |
|
| 568 |
+ |
|
| 569 |
+var isGoKeyword = map[string]bool{
|
|
| 570 |
+ "break": true, |
|
| 571 |
+ "case": true, |
|
| 572 |
+ "chan": true, |
|
| 573 |
+ "const": true, |
|
| 574 |
+ "continue": true, |
|
| 575 |
+ "default": true, |
|
| 576 |
+ "else": true, |
|
| 577 |
+ "defer": true, |
|
| 578 |
+ "fallthrough": true, |
|
| 579 |
+ "for": true, |
|
| 580 |
+ "func": true, |
|
| 581 |
+ "go": true, |
|
| 582 |
+ "goto": true, |
|
| 583 |
+ "if": true, |
|
| 584 |
+ "import": true, |
|
| 585 |
+ "interface": true, |
|
| 586 |
+ "map": true, |
|
| 587 |
+ "package": true, |
|
| 588 |
+ "range": true, |
|
| 589 |
+ "return": true, |
|
| 590 |
+ "select": true, |
|
| 591 |
+ "struct": true, |
|
| 592 |
+ "switch": true, |
|
| 593 |
+ "type": true, |
|
| 594 |
+ "var": true, |
|
| 595 |
+} |
|
| 596 |
+ |
|
| 597 |
+var isGoPredeclaredIdentifier = map[string]bool{
|
|
| 598 |
+ "append": true, |
|
| 599 |
+ "bool": true, |
|
| 600 |
+ "byte": true, |
|
| 601 |
+ "cap": true, |
|
| 602 |
+ "close": true, |
|
| 603 |
+ "complex": true, |
|
| 604 |
+ "complex128": true, |
|
| 605 |
+ "complex64": true, |
|
| 606 |
+ "copy": true, |
|
| 607 |
+ "delete": true, |
|
| 608 |
+ "error": true, |
|
| 609 |
+ "false": true, |
|
| 610 |
+ "float32": true, |
|
| 611 |
+ "float64": true, |
|
| 612 |
+ "imag": true, |
|
| 613 |
+ "int": true, |
|
| 614 |
+ "int16": true, |
|
| 615 |
+ "int32": true, |
|
| 616 |
+ "int64": true, |
|
| 617 |
+ "int8": true, |
|
| 618 |
+ "iota": true, |
|
| 619 |
+ "len": true, |
|
| 620 |
+ "make": true, |
|
| 621 |
+ "new": true, |
|
| 622 |
+ "nil": true, |
|
| 623 |
+ "panic": true, |
|
| 624 |
+ "print": true, |
|
| 625 |
+ "println": true, |
|
| 626 |
+ "real": true, |
|
| 627 |
+ "recover": true, |
|
| 628 |
+ "rune": true, |
|
| 629 |
+ "string": true, |
|
| 630 |
+ "true": true, |
|
| 631 |
+ "uint": true, |
|
| 632 |
+ "uint16": true, |
|
| 633 |
+ "uint32": true, |
|
| 634 |
+ "uint64": true, |
|
| 635 |
+ "uint8": true, |
|
| 636 |
+ "uintptr": true, |
|
| 637 |
+} |
|
| 638 |
+ |
|
| 639 |
+func cleanPackageName(name string) GoPackageName {
|
|
| 640 |
+ name = strings.Map(badToUnderscore, name) |
|
| 641 |
+ // Identifier must not be keyword or predeclared identifier: insert _. |
|
| 642 |
+ if isGoKeyword[name] {
|
|
| 643 |
+ name = "_" + name |
|
| 644 |
+ } |
|
| 645 |
+ // Identifier must not begin with digit: insert _. |
|
| 646 |
+ if r, _ := utf8.DecodeRuneInString(name); unicode.IsDigit(r) {
|
|
| 647 |
+ name = "_" + name |
|
| 648 |
+ } |
|
| 649 |
+ return GoPackageName(name) |
|
| 650 |
+} |
|
| 651 |
+ |
|
| 652 |
+// defaultGoPackage returns the package name to use, |
|
| 653 |
+// derived from the import path of the package we're building code for. |
|
| 654 |
+func (g *Generator) defaultGoPackage() GoPackageName {
|
|
| 655 |
+ p := g.PackageImportPath |
|
| 656 |
+ if i := strings.LastIndex(p, "/"); i >= 0 {
|
|
| 657 |
+ p = p[i+1:] |
|
| 658 |
+ } |
|
| 659 |
+ return cleanPackageName(p) |
|
| 660 |
+} |
|
| 661 |
+ |
|
| 662 |
+// SetPackageNames sets the package name for this run. |
|
| 663 |
+// The package name must agree across all files being generated. |
|
| 664 |
+// It also defines unique package names for all imported files. |
|
| 665 |
+func (g *Generator) SetPackageNames() {
|
|
| 666 |
+ g.outputImportPath = g.genFiles[0].importPath |
|
| 667 |
+ |
|
| 668 |
+ defaultPackageNames := make(map[GoImportPath]GoPackageName) |
|
| 669 |
+ for _, f := range g.genFiles {
|
|
| 670 |
+ if _, p, ok := f.goPackageOption(); ok {
|
|
| 671 |
+ defaultPackageNames[f.importPath] = p |
|
| 672 |
+ } |
|
| 673 |
+ } |
|
| 674 |
+ for _, f := range g.genFiles {
|
|
| 675 |
+ if _, p, ok := f.goPackageOption(); ok {
|
|
| 676 |
+ // Source file: option go_package = "quux/bar"; |
|
| 677 |
+ f.packageName = p |
|
| 678 |
+ } else if p, ok := defaultPackageNames[f.importPath]; ok {
|
|
| 679 |
+ // A go_package option in another file in the same package. |
|
| 680 |
+ // |
|
| 681 |
+ // This is a poor choice in general, since every source file should |
|
| 682 |
+ // contain a go_package option. Supported mainly for historical |
|
| 683 |
+ // compatibility. |
|
| 684 |
+ f.packageName = p |
|
| 685 |
+ } else if p := g.defaultGoPackage(); p != "" {
|
|
| 686 |
+ // Command-line: import_path=quux/bar. |
|
| 687 |
+ // |
|
| 688 |
+ // The import_path flag sets a package name for files which don't |
|
| 689 |
+ // contain a go_package option. |
|
| 690 |
+ f.packageName = p |
|
| 691 |
+ } else if p := f.GetPackage(); p != "" {
|
|
| 692 |
+ // Source file: package quux.bar; |
|
| 693 |
+ f.packageName = cleanPackageName(p) |
|
| 694 |
+ } else {
|
|
| 695 |
+ // Source filename. |
|
| 696 |
+ f.packageName = cleanPackageName(baseName(f.GetName())) |
|
| 697 |
+ } |
|
| 698 |
+ } |
|
| 699 |
+ |
|
| 700 |
+ // Check that all files have a consistent package name and import path. |
|
| 701 |
+ for _, f := range g.genFiles[1:] {
|
|
| 702 |
+ if a, b := g.genFiles[0].importPath, f.importPath; a != b {
|
|
| 703 |
+ g.Fail(fmt.Sprintf("inconsistent package import paths: %v, %v", a, b))
|
|
| 704 |
+ } |
|
| 705 |
+ if a, b := g.genFiles[0].packageName, f.packageName; a != b {
|
|
| 706 |
+ g.Fail(fmt.Sprintf("inconsistent package names: %v, %v", a, b))
|
|
| 707 |
+ } |
|
| 708 |
+ } |
|
| 709 |
+ |
|
| 710 |
+ // Names of support packages. These never vary (if there are conflicts, |
|
| 711 |
+ // we rename the conflicting package), so this could be removed someday. |
|
| 712 |
+ g.Pkg = map[string]string{
|
|
| 713 |
+ "fmt": "fmt", |
|
| 714 |
+ "math": "math", |
|
| 715 |
+ "proto": "proto", |
|
| 716 |
+ } |
|
| 717 |
+} |
|
| 718 |
+ |
|
| 719 |
+// WrapTypes walks the incoming data, wrapping DescriptorProtos, EnumDescriptorProtos |
|
| 720 |
+// and FileDescriptorProtos into file-referenced objects within the Generator. |
|
| 721 |
+// It also creates the list of files to generate and so should be called before GenerateAllFiles. |
|
| 722 |
+func (g *Generator) WrapTypes() {
|
|
| 723 |
+ g.allFiles = make([]*FileDescriptor, 0, len(g.Request.ProtoFile)) |
|
| 724 |
+ g.allFilesByName = make(map[string]*FileDescriptor, len(g.allFiles)) |
|
| 725 |
+ genFileNames := make(map[string]bool) |
|
| 726 |
+ for _, n := range g.Request.FileToGenerate {
|
|
| 727 |
+ genFileNames[n] = true |
|
| 728 |
+ } |
|
| 729 |
+ for _, f := range g.Request.ProtoFile {
|
|
| 730 |
+ fd := &FileDescriptor{
|
|
| 731 |
+ FileDescriptorProto: f, |
|
| 732 |
+ exported: make(map[Object][]symbol), |
|
| 733 |
+ proto3: fileIsProto3(f), |
|
| 734 |
+ } |
|
| 735 |
+ // The import path may be set in a number of ways. |
|
| 736 |
+ if substitution, ok := g.ImportMap[f.GetName()]; ok {
|
|
| 737 |
+ // Command-line: M=foo.proto=quux/bar. |
|
| 738 |
+ // |
|
| 739 |
+ // Explicit mapping of source file to import path. |
|
| 740 |
+ fd.importPath = GoImportPath(substitution) |
|
| 741 |
+ } else if genFileNames[f.GetName()] && g.PackageImportPath != "" {
|
|
| 742 |
+ // Command-line: import_path=quux/bar. |
|
| 743 |
+ // |
|
| 744 |
+ // The import_path flag sets the import path for every file that |
|
| 745 |
+ // we generate code for. |
|
| 746 |
+ fd.importPath = GoImportPath(g.PackageImportPath) |
|
| 747 |
+ } else if p, _, _ := fd.goPackageOption(); p != "" {
|
|
| 748 |
+ // Source file: option go_package = "quux/bar"; |
|
| 749 |
+ // |
|
| 750 |
+ // The go_package option sets the import path. Most users should use this. |
|
| 751 |
+ fd.importPath = p |
|
| 752 |
+ } else {
|
|
| 753 |
+ // Source filename. |
|
| 754 |
+ // |
|
| 755 |
+ // Last resort when nothing else is available. |
|
| 756 |
+ fd.importPath = GoImportPath(path.Dir(f.GetName())) |
|
| 757 |
+ } |
|
| 758 |
+ // We must wrap the descriptors before we wrap the enums |
|
| 759 |
+ fd.desc = wrapDescriptors(fd) |
|
| 760 |
+ g.buildNestedDescriptors(fd.desc) |
|
| 761 |
+ fd.enum = wrapEnumDescriptors(fd, fd.desc) |
|
| 762 |
+ g.buildNestedEnums(fd.desc, fd.enum) |
|
| 763 |
+ fd.ext = wrapExtensions(fd) |
|
| 764 |
+ extractComments(fd) |
|
| 765 |
+ g.allFiles = append(g.allFiles, fd) |
|
| 766 |
+ g.allFilesByName[f.GetName()] = fd |
|
| 767 |
+ } |
|
| 768 |
+ for _, fd := range g.allFiles {
|
|
| 769 |
+ fd.imp = wrapImported(fd, g) |
|
| 770 |
+ } |
|
| 771 |
+ |
|
| 772 |
+ g.genFiles = make([]*FileDescriptor, 0, len(g.Request.FileToGenerate)) |
|
| 773 |
+ for _, fileName := range g.Request.FileToGenerate {
|
|
| 774 |
+ fd := g.allFilesByName[fileName] |
|
| 775 |
+ if fd == nil {
|
|
| 776 |
+ g.Fail("could not find file named", fileName)
|
|
| 777 |
+ } |
|
| 778 |
+ g.genFiles = append(g.genFiles, fd) |
|
| 779 |
+ } |
|
| 780 |
+} |
|
| 781 |
+ |
|
| 782 |
+// Scan the descriptors in this file. For each one, build the slice of nested descriptors |
|
| 783 |
+func (g *Generator) buildNestedDescriptors(descs []*Descriptor) {
|
|
| 784 |
+ for _, desc := range descs {
|
|
| 785 |
+ if len(desc.NestedType) != 0 {
|
|
| 786 |
+ for _, nest := range descs {
|
|
| 787 |
+ if nest.parent == desc {
|
|
| 788 |
+ desc.nested = append(desc.nested, nest) |
|
| 789 |
+ } |
|
| 790 |
+ } |
|
| 791 |
+ if len(desc.nested) != len(desc.NestedType) {
|
|
| 792 |
+ g.Fail("internal error: nesting failure for", desc.GetName())
|
|
| 793 |
+ } |
|
| 794 |
+ } |
|
| 795 |
+ } |
|
| 796 |
+} |
|
| 797 |
+ |
|
| 798 |
+func (g *Generator) buildNestedEnums(descs []*Descriptor, enums []*EnumDescriptor) {
|
|
| 799 |
+ for _, desc := range descs {
|
|
| 800 |
+ if len(desc.EnumType) != 0 {
|
|
| 801 |
+ for _, enum := range enums {
|
|
| 802 |
+ if enum.parent == desc {
|
|
| 803 |
+ desc.enums = append(desc.enums, enum) |
|
| 804 |
+ } |
|
| 805 |
+ } |
|
| 806 |
+ if len(desc.enums) != len(desc.EnumType) {
|
|
| 807 |
+ g.Fail("internal error: enum nesting failure for", desc.GetName())
|
|
| 808 |
+ } |
|
| 809 |
+ } |
|
| 810 |
+ } |
|
| 811 |
+} |
|
| 812 |
+ |
|
| 813 |
+// Construct the Descriptor |
|
| 814 |
+func newDescriptor(desc *descriptor.DescriptorProto, parent *Descriptor, file *FileDescriptor, index int) *Descriptor {
|
|
| 815 |
+ d := &Descriptor{
|
|
| 816 |
+ common: common{file},
|
|
| 817 |
+ DescriptorProto: desc, |
|
| 818 |
+ parent: parent, |
|
| 819 |
+ index: index, |
|
| 820 |
+ } |
|
| 821 |
+ if parent == nil {
|
|
| 822 |
+ d.path = fmt.Sprintf("%d,%d", messagePath, index)
|
|
| 823 |
+ } else {
|
|
| 824 |
+ d.path = fmt.Sprintf("%s,%d,%d", parent.path, messageMessagePath, index)
|
|
| 825 |
+ } |
|
| 826 |
+ |
|
| 827 |
+ // The only way to distinguish a group from a message is whether |
|
| 828 |
+ // the containing message has a TYPE_GROUP field that matches. |
|
| 829 |
+ if parent != nil {
|
|
| 830 |
+ parts := d.TypeName() |
|
| 831 |
+ if file.Package != nil {
|
|
| 832 |
+ parts = append([]string{*file.Package}, parts...)
|
|
| 833 |
+ } |
|
| 834 |
+ exp := "." + strings.Join(parts, ".") |
|
| 835 |
+ for _, field := range parent.Field {
|
|
| 836 |
+ if field.GetType() == descriptor.FieldDescriptorProto_TYPE_GROUP && field.GetTypeName() == exp {
|
|
| 837 |
+ d.group = true |
|
| 838 |
+ break |
|
| 839 |
+ } |
|
| 840 |
+ } |
|
| 841 |
+ } |
|
| 842 |
+ |
|
| 843 |
+ for _, field := range desc.Extension {
|
|
| 844 |
+ d.ext = append(d.ext, &ExtensionDescriptor{common{file}, field, d})
|
|
| 845 |
+ } |
|
| 846 |
+ |
|
| 847 |
+ return d |
|
| 848 |
+} |
|
| 849 |
+ |
|
| 850 |
+// Return a slice of all the Descriptors defined within this file |
|
| 851 |
+func wrapDescriptors(file *FileDescriptor) []*Descriptor {
|
|
| 852 |
+ sl := make([]*Descriptor, 0, len(file.MessageType)+10) |
|
| 853 |
+ for i, desc := range file.MessageType {
|
|
| 854 |
+ sl = wrapThisDescriptor(sl, desc, nil, file, i) |
|
| 855 |
+ } |
|
| 856 |
+ return sl |
|
| 857 |
+} |
|
| 858 |
+ |
|
| 859 |
+// Wrap this Descriptor, recursively |
|
| 860 |
+func wrapThisDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *FileDescriptor, index int) []*Descriptor {
|
|
| 861 |
+ sl = append(sl, newDescriptor(desc, parent, file, index)) |
|
| 862 |
+ me := sl[len(sl)-1] |
|
| 863 |
+ for i, nested := range desc.NestedType {
|
|
| 864 |
+ sl = wrapThisDescriptor(sl, nested, me, file, i) |
|
| 865 |
+ } |
|
| 866 |
+ return sl |
|
| 867 |
+} |
|
| 868 |
+ |
|
| 869 |
+// Construct the EnumDescriptor |
|
| 870 |
+func newEnumDescriptor(desc *descriptor.EnumDescriptorProto, parent *Descriptor, file *FileDescriptor, index int) *EnumDescriptor {
|
|
| 871 |
+ ed := &EnumDescriptor{
|
|
| 872 |
+ common: common{file},
|
|
| 873 |
+ EnumDescriptorProto: desc, |
|
| 874 |
+ parent: parent, |
|
| 875 |
+ index: index, |
|
| 876 |
+ } |
|
| 877 |
+ if parent == nil {
|
|
| 878 |
+ ed.path = fmt.Sprintf("%d,%d", enumPath, index)
|
|
| 879 |
+ } else {
|
|
| 880 |
+ ed.path = fmt.Sprintf("%s,%d,%d", parent.path, messageEnumPath, index)
|
|
| 881 |
+ } |
|
| 882 |
+ return ed |
|
| 883 |
+} |
|
| 884 |
+ |
|
| 885 |
+// Return a slice of all the EnumDescriptors defined within this file |
|
| 886 |
+func wrapEnumDescriptors(file *FileDescriptor, descs []*Descriptor) []*EnumDescriptor {
|
|
| 887 |
+ sl := make([]*EnumDescriptor, 0, len(file.EnumType)+10) |
|
| 888 |
+ // Top-level enums. |
|
| 889 |
+ for i, enum := range file.EnumType {
|
|
| 890 |
+ sl = append(sl, newEnumDescriptor(enum, nil, file, i)) |
|
| 891 |
+ } |
|
| 892 |
+ // Enums within messages. Enums within embedded messages appear in the outer-most message. |
|
| 893 |
+ for _, nested := range descs {
|
|
| 894 |
+ for i, enum := range nested.EnumType {
|
|
| 895 |
+ sl = append(sl, newEnumDescriptor(enum, nested, file, i)) |
|
| 896 |
+ } |
|
| 897 |
+ } |
|
| 898 |
+ return sl |
|
| 899 |
+} |
|
| 900 |
+ |
|
| 901 |
+// Return a slice of all the top-level ExtensionDescriptors defined within this file. |
|
| 902 |
+func wrapExtensions(file *FileDescriptor) []*ExtensionDescriptor {
|
|
| 903 |
+ var sl []*ExtensionDescriptor |
|
| 904 |
+ for _, field := range file.Extension {
|
|
| 905 |
+ sl = append(sl, &ExtensionDescriptor{common{file}, field, nil})
|
|
| 906 |
+ } |
|
| 907 |
+ return sl |
|
| 908 |
+} |
|
| 909 |
+ |
|
| 910 |
+// Return a slice of all the types that are publicly imported into this file. |
|
| 911 |
+func wrapImported(file *FileDescriptor, g *Generator) (sl []*ImportedDescriptor) {
|
|
| 912 |
+ for _, index := range file.PublicDependency {
|
|
| 913 |
+ df := g.fileByName(file.Dependency[index]) |
|
| 914 |
+ for _, d := range df.desc {
|
|
| 915 |
+ if d.GetOptions().GetMapEntry() {
|
|
| 916 |
+ continue |
|
| 917 |
+ } |
|
| 918 |
+ sl = append(sl, &ImportedDescriptor{common{file}, d})
|
|
| 919 |
+ } |
|
| 920 |
+ for _, e := range df.enum {
|
|
| 921 |
+ sl = append(sl, &ImportedDescriptor{common{file}, e})
|
|
| 922 |
+ } |
|
| 923 |
+ for _, ext := range df.ext {
|
|
| 924 |
+ sl = append(sl, &ImportedDescriptor{common{file}, ext})
|
|
| 925 |
+ } |
|
| 926 |
+ } |
|
| 927 |
+ return |
|
| 928 |
+} |
|
| 929 |
+ |
|
| 930 |
+func extractComments(file *FileDescriptor) {
|
|
| 931 |
+ file.comments = make(map[string]*descriptor.SourceCodeInfo_Location) |
|
| 932 |
+ for _, loc := range file.GetSourceCodeInfo().GetLocation() {
|
|
| 933 |
+ if loc.LeadingComments == nil {
|
|
| 934 |
+ continue |
|
| 935 |
+ } |
|
| 936 |
+ var p []string |
|
| 937 |
+ for _, n := range loc.Path {
|
|
| 938 |
+ p = append(p, strconv.Itoa(int(n))) |
|
| 939 |
+ } |
|
| 940 |
+ file.comments[strings.Join(p, ",")] = loc |
|
| 941 |
+ } |
|
| 942 |
+} |
|
| 943 |
+ |
|
| 944 |
+// BuildTypeNameMap builds the map from fully qualified type names to objects. |
|
| 945 |
+// The key names for the map come from the input data, which puts a period at the beginning. |
|
| 946 |
+// It should be called after SetPackageNames and before GenerateAllFiles. |
|
| 947 |
+func (g *Generator) BuildTypeNameMap() {
|
|
| 948 |
+ g.typeNameToObject = make(map[string]Object) |
|
| 949 |
+ for _, f := range g.allFiles {
|
|
| 950 |
+ // The names in this loop are defined by the proto world, not us, so the |
|
| 951 |
+ // package name may be empty. If so, the dotted package name of X will |
|
| 952 |
+ // be ".X"; otherwise it will be ".pkg.X". |
|
| 953 |
+ dottedPkg := "." + f.GetPackage() |
|
| 954 |
+ if dottedPkg != "." {
|
|
| 955 |
+ dottedPkg += "." |
|
| 956 |
+ } |
|
| 957 |
+ for _, enum := range f.enum {
|
|
| 958 |
+ name := dottedPkg + dottedSlice(enum.TypeName()) |
|
| 959 |
+ g.typeNameToObject[name] = enum |
|
| 960 |
+ } |
|
| 961 |
+ for _, desc := range f.desc {
|
|
| 962 |
+ name := dottedPkg + dottedSlice(desc.TypeName()) |
|
| 963 |
+ g.typeNameToObject[name] = desc |
|
| 964 |
+ } |
|
| 965 |
+ } |
|
| 966 |
+} |
|
| 967 |
+ |
|
| 968 |
+// ObjectNamed, given a fully-qualified input type name as it appears in the input data, |
|
| 969 |
+// returns the descriptor for the message or enum with that name. |
|
| 970 |
+func (g *Generator) ObjectNamed(typeName string) Object {
|
|
| 971 |
+ o, ok := g.typeNameToObject[typeName] |
|
| 972 |
+ if !ok {
|
|
| 973 |
+ g.Fail("can't find object with type", typeName)
|
|
| 974 |
+ } |
|
| 975 |
+ return o |
|
| 976 |
+} |
|
| 977 |
+ |
|
| 978 |
+// AnnotatedAtoms is a list of atoms (as consumed by P) that records the file name and proto AST path from which they originated. |
|
| 979 |
+type AnnotatedAtoms struct {
|
|
| 980 |
+ source string |
|
| 981 |
+ path string |
|
| 982 |
+ atoms []interface{}
|
|
| 983 |
+} |
|
| 984 |
+ |
|
| 985 |
+// Annotate records the file name and proto AST path of a list of atoms |
|
| 986 |
+// so that a later call to P can emit a link from each atom to its origin. |
|
| 987 |
+func Annotate(file *FileDescriptor, path string, atoms ...interface{}) *AnnotatedAtoms {
|
|
| 988 |
+ return &AnnotatedAtoms{source: *file.Name, path: path, atoms: atoms}
|
|
| 989 |
+} |
|
| 990 |
+ |
|
| 991 |
+// printAtom prints the (atomic, non-annotation) argument to the generated output. |
|
| 992 |
+func (g *Generator) printAtom(v interface{}) {
|
|
| 993 |
+ switch v := v.(type) {
|
|
| 994 |
+ case string: |
|
| 995 |
+ g.WriteString(v) |
|
| 996 |
+ case *string: |
|
| 997 |
+ g.WriteString(*v) |
|
| 998 |
+ case bool: |
|
| 999 |
+ fmt.Fprint(g, v) |
|
| 1000 |
+ case *bool: |
|
| 1001 |
+ fmt.Fprint(g, *v) |
|
| 1002 |
+ case int: |
|
| 1003 |
+ fmt.Fprint(g, v) |
|
| 1004 |
+ case *int32: |
|
| 1005 |
+ fmt.Fprint(g, *v) |
|
| 1006 |
+ case *int64: |
|
| 1007 |
+ fmt.Fprint(g, *v) |
|
| 1008 |
+ case float64: |
|
| 1009 |
+ fmt.Fprint(g, v) |
|
| 1010 |
+ case *float64: |
|
| 1011 |
+ fmt.Fprint(g, *v) |
|
| 1012 |
+ case GoPackageName: |
|
| 1013 |
+ g.WriteString(string(v)) |
|
| 1014 |
+ case GoImportPath: |
|
| 1015 |
+ g.WriteString(strconv.Quote(string(v))) |
|
| 1016 |
+ default: |
|
| 1017 |
+ g.Fail(fmt.Sprintf("unknown type in printer: %T", v))
|
|
| 1018 |
+ } |
|
| 1019 |
+} |
|
| 1020 |
+ |
|
| 1021 |
+// P prints the arguments to the generated output. It handles strings and int32s, plus |
|
| 1022 |
+// handling indirections because they may be *string, etc. Any inputs of type AnnotatedAtoms may emit |
|
| 1023 |
+// annotations in a .meta file in addition to outputting the atoms themselves (if g.annotateCode |
|
| 1024 |
+// is true). |
|
| 1025 |
+func (g *Generator) P(str ...interface{}) {
|
|
| 1026 |
+ if !g.writeOutput {
|
|
| 1027 |
+ return |
|
| 1028 |
+ } |
|
| 1029 |
+ g.WriteString(g.indent) |
|
| 1030 |
+ for _, v := range str {
|
|
| 1031 |
+ switch v := v.(type) {
|
|
| 1032 |
+ case *AnnotatedAtoms: |
|
| 1033 |
+ begin := int32(g.Len()) |
|
| 1034 |
+ for _, v := range v.atoms {
|
|
| 1035 |
+ g.printAtom(v) |
|
| 1036 |
+ } |
|
| 1037 |
+ if g.annotateCode {
|
|
| 1038 |
+ end := int32(g.Len()) |
|
| 1039 |
+ var path []int32 |
|
| 1040 |
+ for _, token := range strings.Split(v.path, ",") {
|
|
| 1041 |
+ val, err := strconv.ParseInt(token, 10, 32) |
|
| 1042 |
+ if err != nil {
|
|
| 1043 |
+ g.Fail("could not parse proto AST path: ", err.Error())
|
|
| 1044 |
+ } |
|
| 1045 |
+ path = append(path, int32(val)) |
|
| 1046 |
+ } |
|
| 1047 |
+ g.annotations = append(g.annotations, &descriptor.GeneratedCodeInfo_Annotation{
|
|
| 1048 |
+ Path: path, |
|
| 1049 |
+ SourceFile: &v.source, |
|
| 1050 |
+ Begin: &begin, |
|
| 1051 |
+ End: &end, |
|
| 1052 |
+ }) |
|
| 1053 |
+ } |
|
| 1054 |
+ default: |
|
| 1055 |
+ g.printAtom(v) |
|
| 1056 |
+ } |
|
| 1057 |
+ } |
|
| 1058 |
+ g.WriteByte('\n')
|
|
| 1059 |
+} |
|
| 1060 |
+ |
|
| 1061 |
+// addInitf stores the given statement to be printed inside the file's init function. |
|
| 1062 |
+// The statement is given as a format specifier and arguments. |
|
| 1063 |
+func (g *Generator) addInitf(stmt string, a ...interface{}) {
|
|
| 1064 |
+ g.init = append(g.init, fmt.Sprintf(stmt, a...)) |
|
| 1065 |
+} |
|
| 1066 |
+ |
|
| 1067 |
+// In Indents the output one tab stop. |
|
| 1068 |
+func (g *Generator) In() { g.indent += "\t" }
|
|
| 1069 |
+ |
|
| 1070 |
+// Out unindents the output one tab stop. |
|
| 1071 |
+func (g *Generator) Out() {
|
|
| 1072 |
+ if len(g.indent) > 0 {
|
|
| 1073 |
+ g.indent = g.indent[1:] |
|
| 1074 |
+ } |
|
| 1075 |
+} |
|
| 1076 |
+ |
|
| 1077 |
+// GenerateAllFiles generates the output for all the files we're outputting. |
|
| 1078 |
+func (g *Generator) GenerateAllFiles() {
|
|
| 1079 |
+ // Initialize the plugins |
|
| 1080 |
+ for _, p := range plugins {
|
|
| 1081 |
+ p.Init(g) |
|
| 1082 |
+ } |
|
| 1083 |
+ // Generate the output. The generator runs for every file, even the files |
|
| 1084 |
+ // that we don't generate output for, so that we can collate the full list |
|
| 1085 |
+ // of exported symbols to support public imports. |
|
| 1086 |
+ genFileMap := make(map[*FileDescriptor]bool, len(g.genFiles)) |
|
| 1087 |
+ for _, file := range g.genFiles {
|
|
| 1088 |
+ genFileMap[file] = true |
|
| 1089 |
+ } |
|
| 1090 |
+ for _, file := range g.allFiles {
|
|
| 1091 |
+ g.Reset() |
|
| 1092 |
+ g.annotations = nil |
|
| 1093 |
+ g.writeOutput = genFileMap[file] |
|
| 1094 |
+ g.generate(file) |
|
| 1095 |
+ if !g.writeOutput {
|
|
| 1096 |
+ continue |
|
| 1097 |
+ } |
|
| 1098 |
+ fname := file.goFileName(g.pathType) |
|
| 1099 |
+ g.Response.File = append(g.Response.File, &plugin.CodeGeneratorResponse_File{
|
|
| 1100 |
+ Name: proto.String(fname), |
|
| 1101 |
+ Content: proto.String(g.String()), |
|
| 1102 |
+ }) |
|
| 1103 |
+ if g.annotateCode {
|
|
| 1104 |
+ // Store the generated code annotations in text, as the protoc plugin protocol requires that |
|
| 1105 |
+ // strings contain valid UTF-8. |
|
| 1106 |
+ g.Response.File = append(g.Response.File, &plugin.CodeGeneratorResponse_File{
|
|
| 1107 |
+ Name: proto.String(file.goFileName(g.pathType) + ".meta"), |
|
| 1108 |
+ Content: proto.String(proto.CompactTextString(&descriptor.GeneratedCodeInfo{Annotation: g.annotations})),
|
|
| 1109 |
+ }) |
|
| 1110 |
+ } |
|
| 1111 |
+ } |
|
| 1112 |
+} |
|
| 1113 |
+ |
|
| 1114 |
+// Run all the plugins associated with the file. |
|
| 1115 |
+func (g *Generator) runPlugins(file *FileDescriptor) {
|
|
| 1116 |
+ for _, p := range plugins {
|
|
| 1117 |
+ p.Generate(file) |
|
| 1118 |
+ } |
|
| 1119 |
+} |
|
| 1120 |
+ |
|
| 1121 |
+// Fill the response protocol buffer with the generated output for all the files we're |
|
| 1122 |
+// supposed to generate. |
|
| 1123 |
+func (g *Generator) generate(file *FileDescriptor) {
|
|
| 1124 |
+ g.file = file |
|
| 1125 |
+ g.usedPackages = make(map[GoImportPath]bool) |
|
| 1126 |
+ g.packageNames = make(map[GoImportPath]GoPackageName) |
|
| 1127 |
+ g.usedPackageNames = make(map[GoPackageName]bool) |
|
| 1128 |
+ g.addedImports = make(map[GoImportPath]bool) |
|
| 1129 |
+ for name := range globalPackageNames {
|
|
| 1130 |
+ g.usedPackageNames[name] = true |
|
| 1131 |
+ } |
|
| 1132 |
+ |
|
| 1133 |
+ g.P("// This is a compile-time assertion to ensure that this generated file")
|
|
| 1134 |
+ g.P("// is compatible with the proto package it is being compiled against.")
|
|
| 1135 |
+ g.P("// A compilation error at this line likely means your copy of the")
|
|
| 1136 |
+ g.P("// proto package needs to be updated.")
|
|
| 1137 |
+ g.P("const _ = ", g.Pkg["proto"], ".ProtoPackageIsVersion", generatedCodeVersion, " // please upgrade the proto package")
|
|
| 1138 |
+ g.P() |
|
| 1139 |
+ |
|
| 1140 |
+ for _, td := range g.file.imp {
|
|
| 1141 |
+ g.generateImported(td) |
|
| 1142 |
+ } |
|
| 1143 |
+ for _, enum := range g.file.enum {
|
|
| 1144 |
+ g.generateEnum(enum) |
|
| 1145 |
+ } |
|
| 1146 |
+ for _, desc := range g.file.desc {
|
|
| 1147 |
+ // Don't generate virtual messages for maps. |
|
| 1148 |
+ if desc.GetOptions().GetMapEntry() {
|
|
| 1149 |
+ continue |
|
| 1150 |
+ } |
|
| 1151 |
+ g.generateMessage(desc) |
|
| 1152 |
+ } |
|
| 1153 |
+ for _, ext := range g.file.ext {
|
|
| 1154 |
+ g.generateExtension(ext) |
|
| 1155 |
+ } |
|
| 1156 |
+ g.generateInitFunction() |
|
| 1157 |
+ g.generateFileDescriptor(file) |
|
| 1158 |
+ |
|
| 1159 |
+ // Run the plugins before the imports so we know which imports are necessary. |
|
| 1160 |
+ g.runPlugins(file) |
|
| 1161 |
+ |
|
| 1162 |
+ // Generate header and imports last, though they appear first in the output. |
|
| 1163 |
+ rem := g.Buffer |
|
| 1164 |
+ remAnno := g.annotations |
|
| 1165 |
+ g.Buffer = new(bytes.Buffer) |
|
| 1166 |
+ g.annotations = nil |
|
| 1167 |
+ g.generateHeader() |
|
| 1168 |
+ g.generateImports() |
|
| 1169 |
+ if !g.writeOutput {
|
|
| 1170 |
+ return |
|
| 1171 |
+ } |
|
| 1172 |
+ // Adjust the offsets for annotations displaced by the header and imports. |
|
| 1173 |
+ for _, anno := range remAnno {
|
|
| 1174 |
+ *anno.Begin += int32(g.Len()) |
|
| 1175 |
+ *anno.End += int32(g.Len()) |
|
| 1176 |
+ g.annotations = append(g.annotations, anno) |
|
| 1177 |
+ } |
|
| 1178 |
+ g.Write(rem.Bytes()) |
|
| 1179 |
+ |
|
| 1180 |
+ // Reformat generated code and patch annotation locations. |
|
| 1181 |
+ fset := token.NewFileSet() |
|
| 1182 |
+ original := g.Bytes() |
|
| 1183 |
+ if g.annotateCode {
|
|
| 1184 |
+ // make a copy independent of g; we'll need it after Reset. |
|
| 1185 |
+ original = append([]byte(nil), original...) |
|
| 1186 |
+ } |
|
| 1187 |
+ fileAST, err := parser.ParseFile(fset, "", original, parser.ParseComments) |
|
| 1188 |
+ if err != nil {
|
|
| 1189 |
+ // Print out the bad code with line numbers. |
|
| 1190 |
+ // This should never happen in practice, but it can while changing generated code, |
|
| 1191 |
+ // so consider this a debugging aid. |
|
| 1192 |
+ var src bytes.Buffer |
|
| 1193 |
+ s := bufio.NewScanner(bytes.NewReader(original)) |
|
| 1194 |
+ for line := 1; s.Scan(); line++ {
|
|
| 1195 |
+ fmt.Fprintf(&src, "%5d\t%s\n", line, s.Bytes()) |
|
| 1196 |
+ } |
|
| 1197 |
+ g.Fail("bad Go source code was generated:", err.Error(), "\n"+src.String())
|
|
| 1198 |
+ } |
|
| 1199 |
+ ast.SortImports(fset, fileAST) |
|
| 1200 |
+ g.Reset() |
|
| 1201 |
+ err = (&printer.Config{Mode: printer.TabIndent | printer.UseSpaces, Tabwidth: 8}).Fprint(g, fset, fileAST)
|
|
| 1202 |
+ if err != nil {
|
|
| 1203 |
+ g.Fail("generated Go source code could not be reformatted:", err.Error())
|
|
| 1204 |
+ } |
|
| 1205 |
+ if g.annotateCode {
|
|
| 1206 |
+ m, err := remap.Compute(original, g.Bytes()) |
|
| 1207 |
+ if err != nil {
|
|
| 1208 |
+ g.Fail("formatted generated Go source code could not be mapped back to the original code:", err.Error())
|
|
| 1209 |
+ } |
|
| 1210 |
+ for _, anno := range g.annotations {
|
|
| 1211 |
+ new, ok := m.Find(int(*anno.Begin), int(*anno.End)) |
|
| 1212 |
+ if !ok {
|
|
| 1213 |
+ g.Fail("span in formatted generated Go source code could not be mapped back to the original code")
|
|
| 1214 |
+ } |
|
| 1215 |
+ *anno.Begin = int32(new.Pos) |
|
| 1216 |
+ *anno.End = int32(new.End) |
|
| 1217 |
+ } |
|
| 1218 |
+ } |
|
| 1219 |
+} |
|
| 1220 |
+ |
|
| 1221 |
+// Generate the header, including package definition |
|
| 1222 |
+func (g *Generator) generateHeader() {
|
|
| 1223 |
+ g.P("// Code generated by protoc-gen-go. DO NOT EDIT.")
|
|
| 1224 |
+ if g.file.GetOptions().GetDeprecated() {
|
|
| 1225 |
+ g.P("// ", g.file.Name, " is a deprecated file.")
|
|
| 1226 |
+ } else {
|
|
| 1227 |
+ g.P("// source: ", g.file.Name)
|
|
| 1228 |
+ } |
|
| 1229 |
+ g.P() |
|
| 1230 |
+ g.PrintComments(strconv.Itoa(packagePath)) |
|
| 1231 |
+ g.P() |
|
| 1232 |
+ g.P("package ", g.file.packageName)
|
|
| 1233 |
+ g.P() |
|
| 1234 |
+} |
|
| 1235 |
+ |
|
| 1236 |
+// deprecationComment is the standard comment added to deprecated |
|
| 1237 |
+// messages, fields, enums, and enum values. |
|
| 1238 |
+var deprecationComment = "// Deprecated: Do not use." |
|
| 1239 |
+ |
|
| 1240 |
+// PrintComments prints any comments from the source .proto file. |
|
| 1241 |
+// The path is a comma-separated list of integers. |
|
| 1242 |
+// It returns an indication of whether any comments were printed. |
|
| 1243 |
+// See descriptor.proto for its format. |
|
| 1244 |
+func (g *Generator) PrintComments(path string) bool {
|
|
| 1245 |
+ if !g.writeOutput {
|
|
| 1246 |
+ return false |
|
| 1247 |
+ } |
|
| 1248 |
+ if c, ok := g.makeComments(path); ok {
|
|
| 1249 |
+ g.P(c) |
|
| 1250 |
+ return true |
|
| 1251 |
+ } |
|
| 1252 |
+ return false |
|
| 1253 |
+} |
|
| 1254 |
+ |
|
| 1255 |
+// makeComments generates the comment string for the field, no "\n" at the end |
|
| 1256 |
+func (g *Generator) makeComments(path string) (string, bool) {
|
|
| 1257 |
+ loc, ok := g.file.comments[path] |
|
| 1258 |
+ if !ok {
|
|
| 1259 |
+ return "", false |
|
| 1260 |
+ } |
|
| 1261 |
+ w := new(bytes.Buffer) |
|
| 1262 |
+ nl := "" |
|
| 1263 |
+ for _, line := range strings.Split(strings.TrimSuffix(loc.GetLeadingComments(), "\n"), "\n") {
|
|
| 1264 |
+ fmt.Fprintf(w, "%s//%s", nl, line) |
|
| 1265 |
+ nl = "\n" |
|
| 1266 |
+ } |
|
| 1267 |
+ return w.String(), true |
|
| 1268 |
+} |
|
| 1269 |
+ |
|
| 1270 |
+func (g *Generator) fileByName(filename string) *FileDescriptor {
|
|
| 1271 |
+ return g.allFilesByName[filename] |
|
| 1272 |
+} |
|
| 1273 |
+ |
|
| 1274 |
+// weak returns whether the ith import of the current file is a weak import. |
|
| 1275 |
+func (g *Generator) weak(i int32) bool {
|
|
| 1276 |
+ for _, j := range g.file.WeakDependency {
|
|
| 1277 |
+ if j == i {
|
|
| 1278 |
+ return true |
|
| 1279 |
+ } |
|
| 1280 |
+ } |
|
| 1281 |
+ return false |
|
| 1282 |
+} |
|
| 1283 |
+ |
|
| 1284 |
+// Generate the imports |
|
| 1285 |
+func (g *Generator) generateImports() {
|
|
| 1286 |
+ imports := make(map[GoImportPath]GoPackageName) |
|
| 1287 |
+ for i, s := range g.file.Dependency {
|
|
| 1288 |
+ fd := g.fileByName(s) |
|
| 1289 |
+ importPath := fd.importPath |
|
| 1290 |
+ // Do not import our own package. |
|
| 1291 |
+ if importPath == g.file.importPath {
|
|
| 1292 |
+ continue |
|
| 1293 |
+ } |
|
| 1294 |
+ // Do not import weak imports. |
|
| 1295 |
+ if g.weak(int32(i)) {
|
|
| 1296 |
+ continue |
|
| 1297 |
+ } |
|
| 1298 |
+ // Do not import a package twice. |
|
| 1299 |
+ if _, ok := imports[importPath]; ok {
|
|
| 1300 |
+ continue |
|
| 1301 |
+ } |
|
| 1302 |
+ // We need to import all the dependencies, even if we don't reference them, |
|
| 1303 |
+ // because other code and tools depend on having the full transitive closure |
|
| 1304 |
+ // of protocol buffer types in the binary. |
|
| 1305 |
+ packageName := g.GoPackageName(importPath) |
|
| 1306 |
+ if _, ok := g.usedPackages[importPath]; !ok {
|
|
| 1307 |
+ packageName = "_" |
|
| 1308 |
+ } |
|
| 1309 |
+ imports[importPath] = packageName |
|
| 1310 |
+ } |
|
| 1311 |
+ for importPath := range g.addedImports {
|
|
| 1312 |
+ imports[importPath] = g.GoPackageName(importPath) |
|
| 1313 |
+ } |
|
| 1314 |
+ // We almost always need a proto import. Rather than computing when we |
|
| 1315 |
+ // do, which is tricky when there's a plugin, just import it and |
|
| 1316 |
+ // reference it later. The same argument applies to the fmt and math packages. |
|
| 1317 |
+ g.P("import (")
|
|
| 1318 |
+ g.P(g.Pkg["fmt"] + ` "fmt"`) |
|
| 1319 |
+ g.P(g.Pkg["math"] + ` "math"`) |
|
| 1320 |
+ g.P(g.Pkg["proto"]+" ", GoImportPath(g.ImportPrefix)+"github.com/golang/protobuf/proto") |
|
| 1321 |
+ for importPath, packageName := range imports {
|
|
| 1322 |
+ g.P(packageName, " ", GoImportPath(g.ImportPrefix)+importPath) |
|
| 1323 |
+ } |
|
| 1324 |
+ g.P(")")
|
|
| 1325 |
+ g.P() |
|
| 1326 |
+ // TODO: may need to worry about uniqueness across plugins |
|
| 1327 |
+ for _, p := range plugins {
|
|
| 1328 |
+ p.GenerateImports(g.file) |
|
| 1329 |
+ g.P() |
|
| 1330 |
+ } |
|
| 1331 |
+ g.P("// Reference imports to suppress errors if they are not otherwise used.")
|
|
| 1332 |
+ g.P("var _ = ", g.Pkg["proto"], ".Marshal")
|
|
| 1333 |
+ g.P("var _ = ", g.Pkg["fmt"], ".Errorf")
|
|
| 1334 |
+ g.P("var _ = ", g.Pkg["math"], ".Inf")
|
|
| 1335 |
+ g.P() |
|
| 1336 |
+} |
|
| 1337 |
+ |
|
| 1338 |
+func (g *Generator) generateImported(id *ImportedDescriptor) {
|
|
| 1339 |
+ df := id.o.File() |
|
| 1340 |
+ filename := *df.Name |
|
| 1341 |
+ if df.importPath == g.file.importPath {
|
|
| 1342 |
+ // Don't generate type aliases for files in the same Go package as this one. |
|
| 1343 |
+ return |
|
| 1344 |
+ } |
|
| 1345 |
+ if !supportTypeAliases {
|
|
| 1346 |
+ g.Fail(fmt.Sprintf("%s: public imports require at least go1.9", filename))
|
|
| 1347 |
+ } |
|
| 1348 |
+ g.usedPackages[df.importPath] = true |
|
| 1349 |
+ |
|
| 1350 |
+ for _, sym := range df.exported[id.o] {
|
|
| 1351 |
+ sym.GenerateAlias(g, filename, g.GoPackageName(df.importPath)) |
|
| 1352 |
+ } |
|
| 1353 |
+ |
|
| 1354 |
+ g.P() |
|
| 1355 |
+} |
|
| 1356 |
+ |
|
| 1357 |
+// Generate the enum definitions for this EnumDescriptor. |
|
| 1358 |
+func (g *Generator) generateEnum(enum *EnumDescriptor) {
|
|
| 1359 |
+ // The full type name |
|
| 1360 |
+ typeName := enum.TypeName() |
|
| 1361 |
+ // The full type name, CamelCased. |
|
| 1362 |
+ ccTypeName := CamelCaseSlice(typeName) |
|
| 1363 |
+ ccPrefix := enum.prefix() |
|
| 1364 |
+ |
|
| 1365 |
+ deprecatedEnum := "" |
|
| 1366 |
+ if enum.GetOptions().GetDeprecated() {
|
|
| 1367 |
+ deprecatedEnum = deprecationComment |
|
| 1368 |
+ } |
|
| 1369 |
+ g.PrintComments(enum.path) |
|
| 1370 |
+ g.P("type ", Annotate(enum.file, enum.path, ccTypeName), " int32", deprecatedEnum)
|
|
| 1371 |
+ g.file.addExport(enum, enumSymbol{ccTypeName, enum.proto3()})
|
|
| 1372 |
+ g.P("const (")
|
|
| 1373 |
+ for i, e := range enum.Value {
|
|
| 1374 |
+ etorPath := fmt.Sprintf("%s,%d,%d", enum.path, enumValuePath, i)
|
|
| 1375 |
+ g.PrintComments(etorPath) |
|
| 1376 |
+ |
|
| 1377 |
+ deprecatedValue := "" |
|
| 1378 |
+ if e.GetOptions().GetDeprecated() {
|
|
| 1379 |
+ deprecatedValue = deprecationComment |
|
| 1380 |
+ } |
|
| 1381 |
+ |
|
| 1382 |
+ name := ccPrefix + *e.Name |
|
| 1383 |
+ g.P(Annotate(enum.file, etorPath, name), " ", ccTypeName, " = ", e.Number, " ", deprecatedValue) |
|
| 1384 |
+ g.file.addExport(enum, constOrVarSymbol{name, "const", ccTypeName})
|
|
| 1385 |
+ } |
|
| 1386 |
+ g.P(")")
|
|
| 1387 |
+ g.P() |
|
| 1388 |
+ g.P("var ", ccTypeName, "_name = map[int32]string{")
|
|
| 1389 |
+ generated := make(map[int32]bool) // avoid duplicate values |
|
| 1390 |
+ for _, e := range enum.Value {
|
|
| 1391 |
+ duplicate := "" |
|
| 1392 |
+ if _, present := generated[*e.Number]; present {
|
|
| 1393 |
+ duplicate = "// Duplicate value: " |
|
| 1394 |
+ } |
|
| 1395 |
+ g.P(duplicate, e.Number, ": ", strconv.Quote(*e.Name), ",") |
|
| 1396 |
+ generated[*e.Number] = true |
|
| 1397 |
+ } |
|
| 1398 |
+ g.P("}")
|
|
| 1399 |
+ g.P() |
|
| 1400 |
+ g.P("var ", ccTypeName, "_value = map[string]int32{")
|
|
| 1401 |
+ for _, e := range enum.Value {
|
|
| 1402 |
+ g.P(strconv.Quote(*e.Name), ": ", e.Number, ",") |
|
| 1403 |
+ } |
|
| 1404 |
+ g.P("}")
|
|
| 1405 |
+ g.P() |
|
| 1406 |
+ |
|
| 1407 |
+ if !enum.proto3() {
|
|
| 1408 |
+ g.P("func (x ", ccTypeName, ") Enum() *", ccTypeName, " {")
|
|
| 1409 |
+ g.P("p := new(", ccTypeName, ")")
|
|
| 1410 |
+ g.P("*p = x")
|
|
| 1411 |
+ g.P("return p")
|
|
| 1412 |
+ g.P("}")
|
|
| 1413 |
+ g.P() |
|
| 1414 |
+ } |
|
| 1415 |
+ |
|
| 1416 |
+ g.P("func (x ", ccTypeName, ") String() string {")
|
|
| 1417 |
+ g.P("return ", g.Pkg["proto"], ".EnumName(", ccTypeName, "_name, int32(x))")
|
|
| 1418 |
+ g.P("}")
|
|
| 1419 |
+ g.P() |
|
| 1420 |
+ |
|
| 1421 |
+ if !enum.proto3() {
|
|
| 1422 |
+ g.P("func (x *", ccTypeName, ") UnmarshalJSON(data []byte) error {")
|
|
| 1423 |
+ g.P("value, err := ", g.Pkg["proto"], ".UnmarshalJSONEnum(", ccTypeName, `_value, data, "`, ccTypeName, `")`)
|
|
| 1424 |
+ g.P("if err != nil {")
|
|
| 1425 |
+ g.P("return err")
|
|
| 1426 |
+ g.P("}")
|
|
| 1427 |
+ g.P("*x = ", ccTypeName, "(value)")
|
|
| 1428 |
+ g.P("return nil")
|
|
| 1429 |
+ g.P("}")
|
|
| 1430 |
+ g.P() |
|
| 1431 |
+ } |
|
| 1432 |
+ |
|
| 1433 |
+ var indexes []string |
|
| 1434 |
+ for m := enum.parent; m != nil; m = m.parent {
|
|
| 1435 |
+ // XXX: skip groups? |
|
| 1436 |
+ indexes = append([]string{strconv.Itoa(m.index)}, indexes...)
|
|
| 1437 |
+ } |
|
| 1438 |
+ indexes = append(indexes, strconv.Itoa(enum.index)) |
|
| 1439 |
+ g.P("func (", ccTypeName, ") EnumDescriptor() ([]byte, []int) {")
|
|
| 1440 |
+ g.P("return ", g.file.VarName(), ", []int{", strings.Join(indexes, ", "), "}")
|
|
| 1441 |
+ g.P("}")
|
|
| 1442 |
+ g.P() |
|
| 1443 |
+ if enum.file.GetPackage() == "google.protobuf" && enum.GetName() == "NullValue" {
|
|
| 1444 |
+ g.P("func (", ccTypeName, `) XXX_WellKnownType() string { return "`, enum.GetName(), `" }`)
|
|
| 1445 |
+ g.P() |
|
| 1446 |
+ } |
|
| 1447 |
+ |
|
| 1448 |
+ g.generateEnumRegistration(enum) |
|
| 1449 |
+} |
|
| 1450 |
+ |
|
| 1451 |
+// The tag is a string like "varint,2,opt,name=fieldname,def=7" that |
|
| 1452 |
+// identifies details of the field for the protocol buffer marshaling and unmarshaling |
|
| 1453 |
+// code. The fields are: |
|
| 1454 |
+// wire encoding |
|
| 1455 |
+// protocol tag number |
|
| 1456 |
+// opt,req,rep for optional, required, or repeated |
|
| 1457 |
+// packed whether the encoding is "packed" (optional; repeated primitives only) |
|
| 1458 |
+// name= the original declared name |
|
| 1459 |
+// enum= the name of the enum type if it is an enum-typed field. |
|
| 1460 |
+// proto3 if this field is in a proto3 message |
|
| 1461 |
+// def= string representation of the default value, if any. |
|
| 1462 |
+// The default value must be in a representation that can be used at run-time |
|
| 1463 |
+// to generate the default value. Thus bools become 0 and 1, for instance. |
|
| 1464 |
+func (g *Generator) goTag(message *Descriptor, field *descriptor.FieldDescriptorProto, wiretype string) string {
|
|
| 1465 |
+ optrepreq := "" |
|
| 1466 |
+ switch {
|
|
| 1467 |
+ case isOptional(field): |
|
| 1468 |
+ optrepreq = "opt" |
|
| 1469 |
+ case isRequired(field): |
|
| 1470 |
+ optrepreq = "req" |
|
| 1471 |
+ case isRepeated(field): |
|
| 1472 |
+ optrepreq = "rep" |
|
| 1473 |
+ } |
|
| 1474 |
+ var defaultValue string |
|
| 1475 |
+ if dv := field.DefaultValue; dv != nil { // set means an explicit default
|
|
| 1476 |
+ defaultValue = *dv |
|
| 1477 |
+ // Some types need tweaking. |
|
| 1478 |
+ switch *field.Type {
|
|
| 1479 |
+ case descriptor.FieldDescriptorProto_TYPE_BOOL: |
|
| 1480 |
+ if defaultValue == "true" {
|
|
| 1481 |
+ defaultValue = "1" |
|
| 1482 |
+ } else {
|
|
| 1483 |
+ defaultValue = "0" |
|
| 1484 |
+ } |
|
| 1485 |
+ case descriptor.FieldDescriptorProto_TYPE_STRING, |
|
| 1486 |
+ descriptor.FieldDescriptorProto_TYPE_BYTES: |
|
| 1487 |
+ // Nothing to do. Quoting is done for the whole tag. |
|
| 1488 |
+ case descriptor.FieldDescriptorProto_TYPE_ENUM: |
|
| 1489 |
+ // For enums we need to provide the integer constant. |
|
| 1490 |
+ obj := g.ObjectNamed(field.GetTypeName()) |
|
| 1491 |
+ if id, ok := obj.(*ImportedDescriptor); ok {
|
|
| 1492 |
+ // It is an enum that was publicly imported. |
|
| 1493 |
+ // We need the underlying type. |
|
| 1494 |
+ obj = id.o |
|
| 1495 |
+ } |
|
| 1496 |
+ enum, ok := obj.(*EnumDescriptor) |
|
| 1497 |
+ if !ok {
|
|
| 1498 |
+ log.Printf("obj is a %T", obj)
|
|
| 1499 |
+ if id, ok := obj.(*ImportedDescriptor); ok {
|
|
| 1500 |
+ log.Printf("id.o is a %T", id.o)
|
|
| 1501 |
+ } |
|
| 1502 |
+ g.Fail("unknown enum type", CamelCaseSlice(obj.TypeName()))
|
|
| 1503 |
+ } |
|
| 1504 |
+ defaultValue = enum.integerValueAsString(defaultValue) |
|
| 1505 |
+ case descriptor.FieldDescriptorProto_TYPE_FLOAT: |
|
| 1506 |
+ if def := defaultValue; def != "inf" && def != "-inf" && def != "nan" {
|
|
| 1507 |
+ if f, err := strconv.ParseFloat(defaultValue, 32); err == nil {
|
|
| 1508 |
+ defaultValue = fmt.Sprint(float32(f)) |
|
| 1509 |
+ } |
|
| 1510 |
+ } |
|
| 1511 |
+ case descriptor.FieldDescriptorProto_TYPE_DOUBLE: |
|
| 1512 |
+ if def := defaultValue; def != "inf" && def != "-inf" && def != "nan" {
|
|
| 1513 |
+ if f, err := strconv.ParseFloat(defaultValue, 64); err == nil {
|
|
| 1514 |
+ defaultValue = fmt.Sprint(f) |
|
| 1515 |
+ } |
|
| 1516 |
+ } |
|
| 1517 |
+ } |
|
| 1518 |
+ defaultValue = ",def=" + defaultValue |
|
| 1519 |
+ } |
|
| 1520 |
+ enum := "" |
|
| 1521 |
+ if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
|
|
| 1522 |
+ // We avoid using obj.GoPackageName(), because we want to use the |
|
| 1523 |
+ // original (proto-world) package name. |
|
| 1524 |
+ obj := g.ObjectNamed(field.GetTypeName()) |
|
| 1525 |
+ if id, ok := obj.(*ImportedDescriptor); ok {
|
|
| 1526 |
+ obj = id.o |
|
| 1527 |
+ } |
|
| 1528 |
+ enum = ",enum=" |
|
| 1529 |
+ if pkg := obj.File().GetPackage(); pkg != "" {
|
|
| 1530 |
+ enum += pkg + "." |
|
| 1531 |
+ } |
|
| 1532 |
+ enum += CamelCaseSlice(obj.TypeName()) |
|
| 1533 |
+ } |
|
| 1534 |
+ packed := "" |
|
| 1535 |
+ if (field.Options != nil && field.Options.GetPacked()) || |
|
| 1536 |
+ // Per https://developers.google.com/protocol-buffers/docs/proto3#simple: |
|
| 1537 |
+ // "In proto3, repeated fields of scalar numeric types use packed encoding by default." |
|
| 1538 |
+ (message.proto3() && (field.Options == nil || field.Options.Packed == nil) && |
|
| 1539 |
+ isRepeated(field) && isScalar(field)) {
|
|
| 1540 |
+ packed = ",packed" |
|
| 1541 |
+ } |
|
| 1542 |
+ fieldName := field.GetName() |
|
| 1543 |
+ name := fieldName |
|
| 1544 |
+ if *field.Type == descriptor.FieldDescriptorProto_TYPE_GROUP {
|
|
| 1545 |
+ // We must use the type name for groups instead of |
|
| 1546 |
+ // the field name to preserve capitalization. |
|
| 1547 |
+ // type_name in FieldDescriptorProto is fully-qualified, |
|
| 1548 |
+ // but we only want the local part. |
|
| 1549 |
+ name = *field.TypeName |
|
| 1550 |
+ if i := strings.LastIndex(name, "."); i >= 0 {
|
|
| 1551 |
+ name = name[i+1:] |
|
| 1552 |
+ } |
|
| 1553 |
+ } |
|
| 1554 |
+ if json := field.GetJsonName(); field.Extendee == nil && json != "" && json != name {
|
|
| 1555 |
+ // TODO: escaping might be needed, in which case |
|
| 1556 |
+ // perhaps this should be in its own "json" tag. |
|
| 1557 |
+ name += ",json=" + json |
|
| 1558 |
+ } |
|
| 1559 |
+ name = ",name=" + name |
|
| 1560 |
+ if message.proto3() {
|
|
| 1561 |
+ name += ",proto3" |
|
| 1562 |
+ } |
|
| 1563 |
+ oneof := "" |
|
| 1564 |
+ if field.OneofIndex != nil {
|
|
| 1565 |
+ oneof = ",oneof" |
|
| 1566 |
+ } |
|
| 1567 |
+ return strconv.Quote(fmt.Sprintf("%s,%d,%s%s%s%s%s%s",
|
|
| 1568 |
+ wiretype, |
|
| 1569 |
+ field.GetNumber(), |
|
| 1570 |
+ optrepreq, |
|
| 1571 |
+ packed, |
|
| 1572 |
+ name, |
|
| 1573 |
+ enum, |
|
| 1574 |
+ oneof, |
|
| 1575 |
+ defaultValue)) |
|
| 1576 |
+} |
|
| 1577 |
+ |
|
| 1578 |
+func needsStar(typ descriptor.FieldDescriptorProto_Type) bool {
|
|
| 1579 |
+ switch typ {
|
|
| 1580 |
+ case descriptor.FieldDescriptorProto_TYPE_GROUP: |
|
| 1581 |
+ return false |
|
| 1582 |
+ case descriptor.FieldDescriptorProto_TYPE_MESSAGE: |
|
| 1583 |
+ return false |
|
| 1584 |
+ case descriptor.FieldDescriptorProto_TYPE_BYTES: |
|
| 1585 |
+ return false |
|
| 1586 |
+ } |
|
| 1587 |
+ return true |
|
| 1588 |
+} |
|
| 1589 |
+ |
|
| 1590 |
+// TypeName is the printed name appropriate for an item. If the object is in the current file, |
|
| 1591 |
+// TypeName drops the package name and underscores the rest. |
|
| 1592 |
+// Otherwise the object is from another package; and the result is the underscored |
|
| 1593 |
+// package name followed by the item name. |
|
| 1594 |
+// The result always has an initial capital. |
|
| 1595 |
+func (g *Generator) TypeName(obj Object) string {
|
|
| 1596 |
+ return g.DefaultPackageName(obj) + CamelCaseSlice(obj.TypeName()) |
|
| 1597 |
+} |
|
| 1598 |
+ |
|
| 1599 |
+// GoType returns a string representing the type name, and the wire type |
|
| 1600 |
+func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) {
|
|
| 1601 |
+ // TODO: Options. |
|
| 1602 |
+ switch *field.Type {
|
|
| 1603 |
+ case descriptor.FieldDescriptorProto_TYPE_DOUBLE: |
|
| 1604 |
+ typ, wire = "float64", "fixed64" |
|
| 1605 |
+ case descriptor.FieldDescriptorProto_TYPE_FLOAT: |
|
| 1606 |
+ typ, wire = "float32", "fixed32" |
|
| 1607 |
+ case descriptor.FieldDescriptorProto_TYPE_INT64: |
|
| 1608 |
+ typ, wire = "int64", "varint" |
|
| 1609 |
+ case descriptor.FieldDescriptorProto_TYPE_UINT64: |
|
| 1610 |
+ typ, wire = "uint64", "varint" |
|
| 1611 |
+ case descriptor.FieldDescriptorProto_TYPE_INT32: |
|
| 1612 |
+ typ, wire = "int32", "varint" |
|
| 1613 |
+ case descriptor.FieldDescriptorProto_TYPE_UINT32: |
|
| 1614 |
+ typ, wire = "uint32", "varint" |
|
| 1615 |
+ case descriptor.FieldDescriptorProto_TYPE_FIXED64: |
|
| 1616 |
+ typ, wire = "uint64", "fixed64" |
|
| 1617 |
+ case descriptor.FieldDescriptorProto_TYPE_FIXED32: |
|
| 1618 |
+ typ, wire = "uint32", "fixed32" |
|
| 1619 |
+ case descriptor.FieldDescriptorProto_TYPE_BOOL: |
|
| 1620 |
+ typ, wire = "bool", "varint" |
|
| 1621 |
+ case descriptor.FieldDescriptorProto_TYPE_STRING: |
|
| 1622 |
+ typ, wire = "string", "bytes" |
|
| 1623 |
+ case descriptor.FieldDescriptorProto_TYPE_GROUP: |
|
| 1624 |
+ desc := g.ObjectNamed(field.GetTypeName()) |
|
| 1625 |
+ typ, wire = "*"+g.TypeName(desc), "group" |
|
| 1626 |
+ case descriptor.FieldDescriptorProto_TYPE_MESSAGE: |
|
| 1627 |
+ desc := g.ObjectNamed(field.GetTypeName()) |
|
| 1628 |
+ typ, wire = "*"+g.TypeName(desc), "bytes" |
|
| 1629 |
+ case descriptor.FieldDescriptorProto_TYPE_BYTES: |
|
| 1630 |
+ typ, wire = "[]byte", "bytes" |
|
| 1631 |
+ case descriptor.FieldDescriptorProto_TYPE_ENUM: |
|
| 1632 |
+ desc := g.ObjectNamed(field.GetTypeName()) |
|
| 1633 |
+ typ, wire = g.TypeName(desc), "varint" |
|
| 1634 |
+ case descriptor.FieldDescriptorProto_TYPE_SFIXED32: |
|
| 1635 |
+ typ, wire = "int32", "fixed32" |
|
| 1636 |
+ case descriptor.FieldDescriptorProto_TYPE_SFIXED64: |
|
| 1637 |
+ typ, wire = "int64", "fixed64" |
|
| 1638 |
+ case descriptor.FieldDescriptorProto_TYPE_SINT32: |
|
| 1639 |
+ typ, wire = "int32", "zigzag32" |
|
| 1640 |
+ case descriptor.FieldDescriptorProto_TYPE_SINT64: |
|
| 1641 |
+ typ, wire = "int64", "zigzag64" |
|
| 1642 |
+ default: |
|
| 1643 |
+ g.Fail("unknown type for", field.GetName())
|
|
| 1644 |
+ } |
|
| 1645 |
+ if isRepeated(field) {
|
|
| 1646 |
+ typ = "[]" + typ |
|
| 1647 |
+ } else if message != nil && message.proto3() {
|
|
| 1648 |
+ return |
|
| 1649 |
+ } else if field.OneofIndex != nil && message != nil {
|
|
| 1650 |
+ return |
|
| 1651 |
+ } else if needsStar(*field.Type) {
|
|
| 1652 |
+ typ = "*" + typ |
|
| 1653 |
+ } |
|
| 1654 |
+ return |
|
| 1655 |
+} |
|
| 1656 |
+ |
|
| 1657 |
+func (g *Generator) RecordTypeUse(t string) {
|
|
| 1658 |
+ if _, ok := g.typeNameToObject[t]; !ok {
|
|
| 1659 |
+ return |
|
| 1660 |
+ } |
|
| 1661 |
+ importPath := g.ObjectNamed(t).GoImportPath() |
|
| 1662 |
+ if importPath == g.outputImportPath {
|
|
| 1663 |
+ // Don't record use of objects in our package. |
|
| 1664 |
+ return |
|
| 1665 |
+ } |
|
| 1666 |
+ g.AddImport(importPath) |
|
| 1667 |
+ g.usedPackages[importPath] = true |
|
| 1668 |
+} |
|
| 1669 |
+ |
|
| 1670 |
+// Method names that may be generated. Fields with these names get an |
|
| 1671 |
+// underscore appended. Any change to this set is a potential incompatible |
|
| 1672 |
+// API change because it changes generated field names. |
|
| 1673 |
+var methodNames = [...]string{
|
|
| 1674 |
+ "Reset", |
|
| 1675 |
+ "String", |
|
| 1676 |
+ "ProtoMessage", |
|
| 1677 |
+ "Marshal", |
|
| 1678 |
+ "Unmarshal", |
|
| 1679 |
+ "ExtensionRangeArray", |
|
| 1680 |
+ "ExtensionMap", |
|
| 1681 |
+ "Descriptor", |
|
| 1682 |
+} |
|
| 1683 |
+ |
|
| 1684 |
+// Names of messages in the `google.protobuf` package for which |
|
| 1685 |
+// we will generate XXX_WellKnownType methods. |
|
| 1686 |
+var wellKnownTypes = map[string]bool{
|
|
| 1687 |
+ "Any": true, |
|
| 1688 |
+ "Duration": true, |
|
| 1689 |
+ "Empty": true, |
|
| 1690 |
+ "Struct": true, |
|
| 1691 |
+ "Timestamp": true, |
|
| 1692 |
+ |
|
| 1693 |
+ "Value": true, |
|
| 1694 |
+ "ListValue": true, |
|
| 1695 |
+ "DoubleValue": true, |
|
| 1696 |
+ "FloatValue": true, |
|
| 1697 |
+ "Int64Value": true, |
|
| 1698 |
+ "UInt64Value": true, |
|
| 1699 |
+ "Int32Value": true, |
|
| 1700 |
+ "UInt32Value": true, |
|
| 1701 |
+ "BoolValue": true, |
|
| 1702 |
+ "StringValue": true, |
|
| 1703 |
+ "BytesValue": true, |
|
| 1704 |
+} |
|
| 1705 |
+ |
|
| 1706 |
+// getterDefault finds the default value for the field to return from a getter, |
|
| 1707 |
+// regardless of if it's a built in default or explicit from the source. Returns e.g. "nil", `""`, "Default_MessageType_FieldName" |
|
| 1708 |
+func (g *Generator) getterDefault(field *descriptor.FieldDescriptorProto, goMessageType string) string {
|
|
| 1709 |
+ if isRepeated(field) {
|
|
| 1710 |
+ return "nil" |
|
| 1711 |
+ } |
|
| 1712 |
+ if def := field.GetDefaultValue(); def != "" {
|
|
| 1713 |
+ defaultConstant := g.defaultConstantName(goMessageType, field.GetName()) |
|
| 1714 |
+ if *field.Type != descriptor.FieldDescriptorProto_TYPE_BYTES {
|
|
| 1715 |
+ return defaultConstant |
|
| 1716 |
+ } |
|
| 1717 |
+ return "append([]byte(nil), " + defaultConstant + "...)" |
|
| 1718 |
+ } |
|
| 1719 |
+ switch *field.Type {
|
|
| 1720 |
+ case descriptor.FieldDescriptorProto_TYPE_BOOL: |
|
| 1721 |
+ return "false" |
|
| 1722 |
+ case descriptor.FieldDescriptorProto_TYPE_STRING: |
|
| 1723 |
+ return `""` |
|
| 1724 |
+ case descriptor.FieldDescriptorProto_TYPE_GROUP, descriptor.FieldDescriptorProto_TYPE_MESSAGE, descriptor.FieldDescriptorProto_TYPE_BYTES: |
|
| 1725 |
+ return "nil" |
|
| 1726 |
+ case descriptor.FieldDescriptorProto_TYPE_ENUM: |
|
| 1727 |
+ obj := g.ObjectNamed(field.GetTypeName()) |
|
| 1728 |
+ var enum *EnumDescriptor |
|
| 1729 |
+ if id, ok := obj.(*ImportedDescriptor); ok {
|
|
| 1730 |
+ // The enum type has been publicly imported. |
|
| 1731 |
+ enum, _ = id.o.(*EnumDescriptor) |
|
| 1732 |
+ } else {
|
|
| 1733 |
+ enum, _ = obj.(*EnumDescriptor) |
|
| 1734 |
+ } |
|
| 1735 |
+ if enum == nil {
|
|
| 1736 |
+ log.Printf("don't know how to generate getter for %s", field.GetName())
|
|
| 1737 |
+ return "nil" |
|
| 1738 |
+ } |
|
| 1739 |
+ if len(enum.Value) == 0 {
|
|
| 1740 |
+ return "0 // empty enum" |
|
| 1741 |
+ } |
|
| 1742 |
+ first := enum.Value[0].GetName() |
|
| 1743 |
+ return g.DefaultPackageName(obj) + enum.prefix() + first |
|
| 1744 |
+ default: |
|
| 1745 |
+ return "0" |
|
| 1746 |
+ } |
|
| 1747 |
+} |
|
| 1748 |
+ |
|
| 1749 |
+// defaultConstantName builds the name of the default constant from the message |
|
| 1750 |
+// type name and the untouched field name, e.g. "Default_MessageType_FieldName" |
|
| 1751 |
+func (g *Generator) defaultConstantName(goMessageType, protoFieldName string) string {
|
|
| 1752 |
+ return "Default_" + goMessageType + "_" + CamelCase(protoFieldName) |
|
| 1753 |
+} |
|
| 1754 |
+ |
|
| 1755 |
+// The different types of fields in a message and how to actually print them |
|
| 1756 |
+// Most of the logic for generateMessage is in the methods of these types. |
|
| 1757 |
+// |
|
| 1758 |
+// Note that the content of the field is irrelevant, a simpleField can contain |
|
| 1759 |
+// anything from a scalar to a group (which is just a message). |
|
| 1760 |
+// |
|
| 1761 |
+// Extension fields (and message sets) are however handled separately. |
|
| 1762 |
+// |
|
| 1763 |
+// simpleField - a field that is neiter weak nor oneof, possibly repeated |
|
| 1764 |
+// oneofField - field containing list of subfields: |
|
| 1765 |
+// - oneofSubField - a field within the oneof |
|
| 1766 |
+ |
|
| 1767 |
+// msgCtx contains the context for the generator functions. |
|
| 1768 |
+type msgCtx struct {
|
|
| 1769 |
+ goName string // Go struct name of the message, e.g. MessageName |
|
| 1770 |
+ message *Descriptor // The descriptor for the message |
|
| 1771 |
+} |
|
| 1772 |
+ |
|
| 1773 |
+// fieldCommon contains data common to all types of fields. |
|
| 1774 |
+type fieldCommon struct {
|
|
| 1775 |
+ goName string // Go name of field, e.g. "FieldName" or "Descriptor_" |
|
| 1776 |
+ protoName string // Name of field in proto language, e.g. "field_name" or "descriptor" |
|
| 1777 |
+ getterName string // Name of the getter, e.g. "GetFieldName" or "GetDescriptor_" |
|
| 1778 |
+ goType string // The Go type as a string, e.g. "*int32" or "*OtherMessage" |
|
| 1779 |
+ tags string // The tag string/annotation for the type, e.g. `protobuf:"varint,8,opt,name=region_id,json=regionId"` |
|
| 1780 |
+ fullPath string // The full path of the field as used by Annotate etc, e.g. "4,0,2,0" |
|
| 1781 |
+} |
|
| 1782 |
+ |
|
| 1783 |
+// getProtoName gets the proto name of a field, e.g. "field_name" or "descriptor". |
|
| 1784 |
+func (f *fieldCommon) getProtoName() string {
|
|
| 1785 |
+ return f.protoName |
|
| 1786 |
+} |
|
| 1787 |
+ |
|
| 1788 |
+// getGoType returns the go type of the field as a string, e.g. "*int32". |
|
| 1789 |
+func (f *fieldCommon) getGoType() string {
|
|
| 1790 |
+ return f.goType |
|
| 1791 |
+} |
|
| 1792 |
+ |
|
| 1793 |
+// simpleField is not weak, not a oneof, not an extension. Can be required, optional or repeated. |
|
| 1794 |
+type simpleField struct {
|
|
| 1795 |
+ fieldCommon |
|
| 1796 |
+ protoTypeName string // Proto type name, empty if primitive, e.g. ".google.protobuf.Duration" |
|
| 1797 |
+ protoType descriptor.FieldDescriptorProto_Type // Actual type enum value, e.g. descriptor.FieldDescriptorProto_TYPE_FIXED64 |
|
| 1798 |
+ deprecated string // Deprecation comment, if any, e.g. "// Deprecated: Do not use." |
|
| 1799 |
+ getterDef string // Default for getters, e.g. "nil", `""` or "Default_MessageType_FieldName" |
|
| 1800 |
+ protoDef string // Default value as defined in the proto file, e.g "yoshi" or "5" |
|
| 1801 |
+ comment string // The full comment for the field, e.g. "// Useful information" |
|
| 1802 |
+} |
|
| 1803 |
+ |
|
| 1804 |
+// decl prints the declaration of the field in the struct (if any). |
|
| 1805 |
+func (f *simpleField) decl(g *Generator, mc *msgCtx) {
|
|
| 1806 |
+ g.P(f.comment, Annotate(mc.message.file, f.fullPath, f.goName), "\t", f.goType, "\t`", f.tags, "`", f.deprecated) |
|
| 1807 |
+} |
|
| 1808 |
+ |
|
| 1809 |
+// getter prints the getter for the field. |
|
| 1810 |
+func (f *simpleField) getter(g *Generator, mc *msgCtx) {
|
|
| 1811 |
+ star := "" |
|
| 1812 |
+ tname := f.goType |
|
| 1813 |
+ if needsStar(f.protoType) && tname[0] == '*' {
|
|
| 1814 |
+ tname = tname[1:] |
|
| 1815 |
+ star = "*" |
|
| 1816 |
+ } |
|
| 1817 |
+ if f.deprecated != "" {
|
|
| 1818 |
+ g.P(f.deprecated) |
|
| 1819 |
+ } |
|
| 1820 |
+ g.P("func (m *", mc.goName, ") ", Annotate(mc.message.file, f.fullPath, f.getterName), "() "+tname+" {")
|
|
| 1821 |
+ if f.getterDef == "nil" { // Simpler getter
|
|
| 1822 |
+ g.P("if m != nil {")
|
|
| 1823 |
+ g.P("return m." + f.goName)
|
|
| 1824 |
+ g.P("}")
|
|
| 1825 |
+ g.P("return nil")
|
|
| 1826 |
+ g.P("}")
|
|
| 1827 |
+ g.P() |
|
| 1828 |
+ return |
|
| 1829 |
+ } |
|
| 1830 |
+ if mc.message.proto3() {
|
|
| 1831 |
+ g.P("if m != nil {")
|
|
| 1832 |
+ } else {
|
|
| 1833 |
+ g.P("if m != nil && m." + f.goName + " != nil {")
|
|
| 1834 |
+ } |
|
| 1835 |
+ g.P("return " + star + "m." + f.goName)
|
|
| 1836 |
+ g.P("}")
|
|
| 1837 |
+ g.P("return ", f.getterDef)
|
|
| 1838 |
+ g.P("}")
|
|
| 1839 |
+ g.P() |
|
| 1840 |
+} |
|
| 1841 |
+ |
|
| 1842 |
+// setter prints the setter method of the field. |
|
| 1843 |
+func (f *simpleField) setter(g *Generator, mc *msgCtx) {
|
|
| 1844 |
+ // No setter for regular fields yet |
|
| 1845 |
+} |
|
| 1846 |
+ |
|
| 1847 |
+// getProtoDef returns the default value explicitly stated in the proto file, e.g "yoshi" or "5". |
|
| 1848 |
+func (f *simpleField) getProtoDef() string {
|
|
| 1849 |
+ return f.protoDef |
|
| 1850 |
+} |
|
| 1851 |
+ |
|
| 1852 |
+// getProtoTypeName returns the protobuf type name for the field as returned by field.GetTypeName(), e.g. ".google.protobuf.Duration". |
|
| 1853 |
+func (f *simpleField) getProtoTypeName() string {
|
|
| 1854 |
+ return f.protoTypeName |
|
| 1855 |
+} |
|
| 1856 |
+ |
|
| 1857 |
+// getProtoType returns the *field.Type value, e.g. descriptor.FieldDescriptorProto_TYPE_FIXED64. |
|
| 1858 |
+func (f *simpleField) getProtoType() descriptor.FieldDescriptorProto_Type {
|
|
| 1859 |
+ return f.protoType |
|
| 1860 |
+} |
|
| 1861 |
+ |
|
| 1862 |
+// oneofSubFields are kept slize held by each oneofField. They do not appear in the top level slize of fields for the message. |
|
| 1863 |
+type oneofSubField struct {
|
|
| 1864 |
+ fieldCommon |
|
| 1865 |
+ protoTypeName string // Proto type name, empty if primitive, e.g. ".google.protobuf.Duration" |
|
| 1866 |
+ protoType descriptor.FieldDescriptorProto_Type // Actual type enum value, e.g. descriptor.FieldDescriptorProto_TYPE_FIXED64 |
|
| 1867 |
+ oneofTypeName string // Type name of the enclosing struct, e.g. "MessageName_FieldName" |
|
| 1868 |
+ fieldNumber int // Actual field number, as defined in proto, e.g. 12 |
|
| 1869 |
+ getterDef string // Default for getters, e.g. "nil", `""` or "Default_MessageType_FieldName" |
|
| 1870 |
+ protoDef string // Default value as defined in the proto file, e.g "yoshi" or "5" |
|
| 1871 |
+ deprecated string // Deprecation comment, if any. |
|
| 1872 |
+} |
|
| 1873 |
+ |
|
| 1874 |
+// typedNil prints a nil casted to the pointer to this field. |
|
| 1875 |
+// - for XXX_OneofWrappers |
|
| 1876 |
+func (f *oneofSubField) typedNil(g *Generator) {
|
|
| 1877 |
+ g.P("(*", f.oneofTypeName, ")(nil),")
|
|
| 1878 |
+} |
|
| 1879 |
+ |
|
| 1880 |
+// getProtoDef returns the default value explicitly stated in the proto file, e.g "yoshi" or "5". |
|
| 1881 |
+func (f *oneofSubField) getProtoDef() string {
|
|
| 1882 |
+ return f.protoDef |
|
| 1883 |
+} |
|
| 1884 |
+ |
|
| 1885 |
+// getProtoTypeName returns the protobuf type name for the field as returned by field.GetTypeName(), e.g. ".google.protobuf.Duration". |
|
| 1886 |
+func (f *oneofSubField) getProtoTypeName() string {
|
|
| 1887 |
+ return f.protoTypeName |
|
| 1888 |
+} |
|
| 1889 |
+ |
|
| 1890 |
+// getProtoType returns the *field.Type value, e.g. descriptor.FieldDescriptorProto_TYPE_FIXED64. |
|
| 1891 |
+func (f *oneofSubField) getProtoType() descriptor.FieldDescriptorProto_Type {
|
|
| 1892 |
+ return f.protoType |
|
| 1893 |
+} |
|
| 1894 |
+ |
|
| 1895 |
+// oneofField represents the oneof on top level. |
|
| 1896 |
+// The alternative fields within the oneof are represented by oneofSubField. |
|
| 1897 |
+type oneofField struct {
|
|
| 1898 |
+ fieldCommon |
|
| 1899 |
+ subFields []*oneofSubField // All the possible oneof fields |
|
| 1900 |
+ comment string // The full comment for the field, e.g. "// Types that are valid to be assigned to MyOneof:\n\\" |
|
| 1901 |
+} |
|
| 1902 |
+ |
|
| 1903 |
+// decl prints the declaration of the field in the struct (if any). |
|
| 1904 |
+func (f *oneofField) decl(g *Generator, mc *msgCtx) {
|
|
| 1905 |
+ comment := f.comment |
|
| 1906 |
+ for _, sf := range f.subFields {
|
|
| 1907 |
+ comment += "//\t*" + sf.oneofTypeName + "\n" |
|
| 1908 |
+ } |
|
| 1909 |
+ g.P(comment, Annotate(mc.message.file, f.fullPath, f.goName), " ", f.goType, " `", f.tags, "`") |
|
| 1910 |
+} |
|
| 1911 |
+ |
|
| 1912 |
+// getter for a oneof field will print additional discriminators and interfaces for the oneof, |
|
| 1913 |
+// also it prints all the getters for the sub fields. |
|
| 1914 |
+func (f *oneofField) getter(g *Generator, mc *msgCtx) {
|
|
| 1915 |
+ // The discriminator type |
|
| 1916 |
+ g.P("type ", f.goType, " interface {")
|
|
| 1917 |
+ g.P(f.goType, "()") |
|
| 1918 |
+ g.P("}")
|
|
| 1919 |
+ g.P() |
|
| 1920 |
+ // The subField types, fulfilling the discriminator type contract |
|
| 1921 |
+ for _, sf := range f.subFields {
|
|
| 1922 |
+ g.P("type ", Annotate(mc.message.file, sf.fullPath, sf.oneofTypeName), " struct {")
|
|
| 1923 |
+ g.P(Annotate(mc.message.file, sf.fullPath, sf.goName), " ", sf.goType, " `", sf.tags, "`") |
|
| 1924 |
+ g.P("}")
|
|
| 1925 |
+ g.P() |
|
| 1926 |
+ } |
|
| 1927 |
+ for _, sf := range f.subFields {
|
|
| 1928 |
+ g.P("func (*", sf.oneofTypeName, ") ", f.goType, "() {}")
|
|
| 1929 |
+ g.P() |
|
| 1930 |
+ } |
|
| 1931 |
+ // Getter for the oneof field |
|
| 1932 |
+ g.P("func (m *", mc.goName, ") ", Annotate(mc.message.file, f.fullPath, f.getterName), "() ", f.goType, " {")
|
|
| 1933 |
+ g.P("if m != nil { return m.", f.goName, " }")
|
|
| 1934 |
+ g.P("return nil")
|
|
| 1935 |
+ g.P("}")
|
|
| 1936 |
+ g.P() |
|
| 1937 |
+ // Getters for each oneof |
|
| 1938 |
+ for _, sf := range f.subFields {
|
|
| 1939 |
+ if sf.deprecated != "" {
|
|
| 1940 |
+ g.P(sf.deprecated) |
|
| 1941 |
+ } |
|
| 1942 |
+ g.P("func (m *", mc.goName, ") ", Annotate(mc.message.file, sf.fullPath, sf.getterName), "() "+sf.goType+" {")
|
|
| 1943 |
+ g.P("if x, ok := m.", f.getterName, "().(*", sf.oneofTypeName, "); ok {")
|
|
| 1944 |
+ g.P("return x.", sf.goName)
|
|
| 1945 |
+ g.P("}")
|
|
| 1946 |
+ g.P("return ", sf.getterDef)
|
|
| 1947 |
+ g.P("}")
|
|
| 1948 |
+ g.P() |
|
| 1949 |
+ } |
|
| 1950 |
+} |
|
| 1951 |
+ |
|
| 1952 |
+// setter prints the setter method of the field. |
|
| 1953 |
+func (f *oneofField) setter(g *Generator, mc *msgCtx) {
|
|
| 1954 |
+ // No setters for oneof yet |
|
| 1955 |
+} |
|
| 1956 |
+ |
|
| 1957 |
+// topLevelField interface implemented by all types of fields on the top level (not oneofSubField). |
|
| 1958 |
+type topLevelField interface {
|
|
| 1959 |
+ decl(g *Generator, mc *msgCtx) // print declaration within the struct |
|
| 1960 |
+ getter(g *Generator, mc *msgCtx) // print getter |
|
| 1961 |
+ setter(g *Generator, mc *msgCtx) // print setter if applicable |
|
| 1962 |
+} |
|
| 1963 |
+ |
|
| 1964 |
+// defField interface implemented by all types of fields that can have defaults (not oneofField, but instead oneofSubField). |
|
| 1965 |
+type defField interface {
|
|
| 1966 |
+ getProtoDef() string // default value explicitly stated in the proto file, e.g "yoshi" or "5" |
|
| 1967 |
+ getProtoName() string // proto name of a field, e.g. "field_name" or "descriptor" |
|
| 1968 |
+ getGoType() string // go type of the field as a string, e.g. "*int32" |
|
| 1969 |
+ getProtoTypeName() string // protobuf type name for the field, e.g. ".google.protobuf.Duration" |
|
| 1970 |
+ getProtoType() descriptor.FieldDescriptorProto_Type // *field.Type value, e.g. descriptor.FieldDescriptorProto_TYPE_FIXED64 |
|
| 1971 |
+} |
|
| 1972 |
+ |
|
| 1973 |
+// generateDefaultConstants adds constants for default values if needed, which is only if the default value is. |
|
| 1974 |
+// explicit in the proto. |
|
| 1975 |
+func (g *Generator) generateDefaultConstants(mc *msgCtx, topLevelFields []topLevelField) {
|
|
| 1976 |
+ // Collect fields that can have defaults |
|
| 1977 |
+ dFields := []defField{}
|
|
| 1978 |
+ for _, pf := range topLevelFields {
|
|
| 1979 |
+ if f, ok := pf.(*oneofField); ok {
|
|
| 1980 |
+ for _, osf := range f.subFields {
|
|
| 1981 |
+ dFields = append(dFields, osf) |
|
| 1982 |
+ } |
|
| 1983 |
+ continue |
|
| 1984 |
+ } |
|
| 1985 |
+ dFields = append(dFields, pf.(defField)) |
|
| 1986 |
+ } |
|
| 1987 |
+ for _, df := range dFields {
|
|
| 1988 |
+ def := df.getProtoDef() |
|
| 1989 |
+ if def == "" {
|
|
| 1990 |
+ continue |
|
| 1991 |
+ } |
|
| 1992 |
+ fieldname := g.defaultConstantName(mc.goName, df.getProtoName()) |
|
| 1993 |
+ typename := df.getGoType() |
|
| 1994 |
+ if typename[0] == '*' {
|
|
| 1995 |
+ typename = typename[1:] |
|
| 1996 |
+ } |
|
| 1997 |
+ kind := "const " |
|
| 1998 |
+ switch {
|
|
| 1999 |
+ case typename == "bool": |
|
| 2000 |
+ case typename == "string": |
|
| 2001 |
+ def = strconv.Quote(def) |
|
| 2002 |
+ case typename == "[]byte": |
|
| 2003 |
+ def = "[]byte(" + strconv.Quote(unescape(def)) + ")"
|
|
| 2004 |
+ kind = "var " |
|
| 2005 |
+ case def == "inf", def == "-inf", def == "nan": |
|
| 2006 |
+ // These names are known to, and defined by, the protocol language. |
|
| 2007 |
+ switch def {
|
|
| 2008 |
+ case "inf": |
|
| 2009 |
+ def = "math.Inf(1)" |
|
| 2010 |
+ case "-inf": |
|
| 2011 |
+ def = "math.Inf(-1)" |
|
| 2012 |
+ case "nan": |
|
| 2013 |
+ def = "math.NaN()" |
|
| 2014 |
+ } |
|
| 2015 |
+ if df.getProtoType() == descriptor.FieldDescriptorProto_TYPE_FLOAT {
|
|
| 2016 |
+ def = "float32(" + def + ")"
|
|
| 2017 |
+ } |
|
| 2018 |
+ kind = "var " |
|
| 2019 |
+ case df.getProtoType() == descriptor.FieldDescriptorProto_TYPE_FLOAT: |
|
| 2020 |
+ if f, err := strconv.ParseFloat(def, 32); err == nil {
|
|
| 2021 |
+ def = fmt.Sprint(float32(f)) |
|
| 2022 |
+ } |
|
| 2023 |
+ case df.getProtoType() == descriptor.FieldDescriptorProto_TYPE_DOUBLE: |
|
| 2024 |
+ if f, err := strconv.ParseFloat(def, 64); err == nil {
|
|
| 2025 |
+ def = fmt.Sprint(f) |
|
| 2026 |
+ } |
|
| 2027 |
+ case df.getProtoType() == descriptor.FieldDescriptorProto_TYPE_ENUM: |
|
| 2028 |
+ // Must be an enum. Need to construct the prefixed name. |
|
| 2029 |
+ obj := g.ObjectNamed(df.getProtoTypeName()) |
|
| 2030 |
+ var enum *EnumDescriptor |
|
| 2031 |
+ if id, ok := obj.(*ImportedDescriptor); ok {
|
|
| 2032 |
+ // The enum type has been publicly imported. |
|
| 2033 |
+ enum, _ = id.o.(*EnumDescriptor) |
|
| 2034 |
+ } else {
|
|
| 2035 |
+ enum, _ = obj.(*EnumDescriptor) |
|
| 2036 |
+ } |
|
| 2037 |
+ if enum == nil {
|
|
| 2038 |
+ log.Printf("don't know how to generate constant for %s", fieldname)
|
|
| 2039 |
+ continue |
|
| 2040 |
+ } |
|
| 2041 |
+ def = g.DefaultPackageName(obj) + enum.prefix() + def |
|
| 2042 |
+ } |
|
| 2043 |
+ g.P(kind, fieldname, " ", typename, " = ", def) |
|
| 2044 |
+ g.file.addExport(mc.message, constOrVarSymbol{fieldname, kind, ""})
|
|
| 2045 |
+ } |
|
| 2046 |
+ g.P() |
|
| 2047 |
+} |
|
| 2048 |
+ |
|
| 2049 |
+// generateInternalStructFields just adds the XXX_<something> fields to the message struct. |
|
| 2050 |
+func (g *Generator) generateInternalStructFields(mc *msgCtx, topLevelFields []topLevelField) {
|
|
| 2051 |
+ g.P("XXX_NoUnkeyedLiteral\tstruct{} `json:\"-\"`") // prevent unkeyed struct literals
|
|
| 2052 |
+ if len(mc.message.ExtensionRange) > 0 {
|
|
| 2053 |
+ messageset := "" |
|
| 2054 |
+ if opts := mc.message.Options; opts != nil && opts.GetMessageSetWireFormat() {
|
|
| 2055 |
+ messageset = "protobuf_messageset:\"1\" " |
|
| 2056 |
+ } |
|
| 2057 |
+ g.P(g.Pkg["proto"], ".XXX_InternalExtensions `", messageset, "json:\"-\"`") |
|
| 2058 |
+ } |
|
| 2059 |
+ g.P("XXX_unrecognized\t[]byte `json:\"-\"`")
|
|
| 2060 |
+ g.P("XXX_sizecache\tint32 `json:\"-\"`")
|
|
| 2061 |
+ |
|
| 2062 |
+} |
|
| 2063 |
+ |
|
| 2064 |
+// generateOneofFuncs adds all the utility functions for oneof, including marshaling, unmarshaling and sizer. |
|
| 2065 |
+func (g *Generator) generateOneofFuncs(mc *msgCtx, topLevelFields []topLevelField) {
|
|
| 2066 |
+ ofields := []*oneofField{}
|
|
| 2067 |
+ for _, f := range topLevelFields {
|
|
| 2068 |
+ if o, ok := f.(*oneofField); ok {
|
|
| 2069 |
+ ofields = append(ofields, o) |
|
| 2070 |
+ } |
|
| 2071 |
+ } |
|
| 2072 |
+ if len(ofields) == 0 {
|
|
| 2073 |
+ return |
|
| 2074 |
+ } |
|
| 2075 |
+ |
|
| 2076 |
+ // OneofFuncs |
|
| 2077 |
+ g.P("// XXX_OneofWrappers is for the internal use of the proto package.")
|
|
| 2078 |
+ g.P("func (*", mc.goName, ") XXX_OneofWrappers() []interface{} {")
|
|
| 2079 |
+ g.P("return []interface{}{")
|
|
| 2080 |
+ for _, of := range ofields {
|
|
| 2081 |
+ for _, sf := range of.subFields {
|
|
| 2082 |
+ sf.typedNil(g) |
|
| 2083 |
+ } |
|
| 2084 |
+ } |
|
| 2085 |
+ g.P("}")
|
|
| 2086 |
+ g.P("}")
|
|
| 2087 |
+ g.P() |
|
| 2088 |
+} |
|
| 2089 |
+ |
|
| 2090 |
+// generateMessageStruct adds the actual struct with it's members (but not methods) to the output. |
|
| 2091 |
+func (g *Generator) generateMessageStruct(mc *msgCtx, topLevelFields []topLevelField) {
|
|
| 2092 |
+ comments := g.PrintComments(mc.message.path) |
|
| 2093 |
+ |
|
| 2094 |
+ // Guarantee deprecation comments appear after user-provided comments. |
|
| 2095 |
+ if mc.message.GetOptions().GetDeprecated() {
|
|
| 2096 |
+ if comments {
|
|
| 2097 |
+ // Convention: Separate deprecation comments from original |
|
| 2098 |
+ // comments with an empty line. |
|
| 2099 |
+ g.P("//")
|
|
| 2100 |
+ } |
|
| 2101 |
+ g.P(deprecationComment) |
|
| 2102 |
+ } |
|
| 2103 |
+ |
|
| 2104 |
+ g.P("type ", Annotate(mc.message.file, mc.message.path, mc.goName), " struct {")
|
|
| 2105 |
+ for _, pf := range topLevelFields {
|
|
| 2106 |
+ pf.decl(g, mc) |
|
| 2107 |
+ } |
|
| 2108 |
+ g.generateInternalStructFields(mc, topLevelFields) |
|
| 2109 |
+ g.P("}")
|
|
| 2110 |
+} |
|
| 2111 |
+ |
|
| 2112 |
+// generateGetters adds getters for all fields, including oneofs and weak fields when applicable. |
|
| 2113 |
+func (g *Generator) generateGetters(mc *msgCtx, topLevelFields []topLevelField) {
|
|
| 2114 |
+ for _, pf := range topLevelFields {
|
|
| 2115 |
+ pf.getter(g, mc) |
|
| 2116 |
+ } |
|
| 2117 |
+} |
|
| 2118 |
+ |
|
| 2119 |
+// generateSetters add setters for all fields, including oneofs and weak fields when applicable. |
|
| 2120 |
+func (g *Generator) generateSetters(mc *msgCtx, topLevelFields []topLevelField) {
|
|
| 2121 |
+ for _, pf := range topLevelFields {
|
|
| 2122 |
+ pf.setter(g, mc) |
|
| 2123 |
+ } |
|
| 2124 |
+} |
|
| 2125 |
+ |
|
| 2126 |
+// generateCommonMethods adds methods to the message that are not on a per field basis. |
|
| 2127 |
+func (g *Generator) generateCommonMethods(mc *msgCtx) {
|
|
| 2128 |
+ // Reset, String and ProtoMessage methods. |
|
| 2129 |
+ g.P("func (m *", mc.goName, ") Reset() { *m = ", mc.goName, "{} }")
|
|
| 2130 |
+ g.P("func (m *", mc.goName, ") String() string { return ", g.Pkg["proto"], ".CompactTextString(m) }")
|
|
| 2131 |
+ g.P("func (*", mc.goName, ") ProtoMessage() {}")
|
|
| 2132 |
+ var indexes []string |
|
| 2133 |
+ for m := mc.message; m != nil; m = m.parent {
|
|
| 2134 |
+ indexes = append([]string{strconv.Itoa(m.index)}, indexes...)
|
|
| 2135 |
+ } |
|
| 2136 |
+ g.P("func (*", mc.goName, ") Descriptor() ([]byte, []int) {")
|
|
| 2137 |
+ g.P("return ", g.file.VarName(), ", []int{", strings.Join(indexes, ", "), "}")
|
|
| 2138 |
+ g.P("}")
|
|
| 2139 |
+ g.P() |
|
| 2140 |
+ // TODO: Revisit the decision to use a XXX_WellKnownType method |
|
| 2141 |
+ // if we change proto.MessageName to work with multiple equivalents. |
|
| 2142 |
+ if mc.message.file.GetPackage() == "google.protobuf" && wellKnownTypes[mc.message.GetName()] {
|
|
| 2143 |
+ g.P("func (*", mc.goName, `) XXX_WellKnownType() string { return "`, mc.message.GetName(), `" }`)
|
|
| 2144 |
+ g.P() |
|
| 2145 |
+ } |
|
| 2146 |
+ |
|
| 2147 |
+ // Extension support methods |
|
| 2148 |
+ if len(mc.message.ExtensionRange) > 0 {
|
|
| 2149 |
+ g.P() |
|
| 2150 |
+ g.P("var extRange_", mc.goName, " = []", g.Pkg["proto"], ".ExtensionRange{")
|
|
| 2151 |
+ for _, r := range mc.message.ExtensionRange {
|
|
| 2152 |
+ end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends |
|
| 2153 |
+ g.P("{Start: ", r.Start, ", End: ", end, "},")
|
|
| 2154 |
+ } |
|
| 2155 |
+ g.P("}")
|
|
| 2156 |
+ g.P("func (*", mc.goName, ") ExtensionRangeArray() []", g.Pkg["proto"], ".ExtensionRange {")
|
|
| 2157 |
+ g.P("return extRange_", mc.goName)
|
|
| 2158 |
+ g.P("}")
|
|
| 2159 |
+ g.P() |
|
| 2160 |
+ } |
|
| 2161 |
+ |
|
| 2162 |
+ // TODO: It does not scale to keep adding another method for every |
|
| 2163 |
+ // operation on protos that we want to switch over to using the |
|
| 2164 |
+ // table-driven approach. Instead, we should only add a single method |
|
| 2165 |
+ // that allows getting access to the *InternalMessageInfo struct and then |
|
| 2166 |
+ // calling Unmarshal, Marshal, Merge, Size, and Discard directly on that. |
|
| 2167 |
+ |
|
| 2168 |
+ // Wrapper for table-driven marshaling and unmarshaling. |
|
| 2169 |
+ g.P("func (m *", mc.goName, ") XXX_Unmarshal(b []byte) error {")
|
|
| 2170 |
+ g.P("return xxx_messageInfo_", mc.goName, ".Unmarshal(m, b)")
|
|
| 2171 |
+ g.P("}")
|
|
| 2172 |
+ |
|
| 2173 |
+ g.P("func (m *", mc.goName, ") XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {")
|
|
| 2174 |
+ g.P("return xxx_messageInfo_", mc.goName, ".Marshal(b, m, deterministic)")
|
|
| 2175 |
+ g.P("}")
|
|
| 2176 |
+ |
|
| 2177 |
+ g.P("func (m *", mc.goName, ") XXX_Merge(src ", g.Pkg["proto"], ".Message) {")
|
|
| 2178 |
+ g.P("xxx_messageInfo_", mc.goName, ".Merge(m, src)")
|
|
| 2179 |
+ g.P("}")
|
|
| 2180 |
+ |
|
| 2181 |
+ g.P("func (m *", mc.goName, ") XXX_Size() int {") // avoid name clash with "Size" field in some message
|
|
| 2182 |
+ g.P("return xxx_messageInfo_", mc.goName, ".Size(m)")
|
|
| 2183 |
+ g.P("}")
|
|
| 2184 |
+ |
|
| 2185 |
+ g.P("func (m *", mc.goName, ") XXX_DiscardUnknown() {")
|
|
| 2186 |
+ g.P("xxx_messageInfo_", mc.goName, ".DiscardUnknown(m)")
|
|
| 2187 |
+ g.P("}")
|
|
| 2188 |
+ |
|
| 2189 |
+ g.P("var xxx_messageInfo_", mc.goName, " ", g.Pkg["proto"], ".InternalMessageInfo")
|
|
| 2190 |
+ g.P() |
|
| 2191 |
+} |
|
| 2192 |
+ |
|
| 2193 |
+// Generate the type, methods and default constant definitions for this Descriptor. |
|
| 2194 |
+func (g *Generator) generateMessage(message *Descriptor) {
|
|
| 2195 |
+ topLevelFields := []topLevelField{}
|
|
| 2196 |
+ oFields := make(map[int32]*oneofField) |
|
| 2197 |
+ // The full type name |
|
| 2198 |
+ typeName := message.TypeName() |
|
| 2199 |
+ // The full type name, CamelCased. |
|
| 2200 |
+ goTypeName := CamelCaseSlice(typeName) |
|
| 2201 |
+ |
|
| 2202 |
+ usedNames := make(map[string]bool) |
|
| 2203 |
+ for _, n := range methodNames {
|
|
| 2204 |
+ usedNames[n] = true |
|
| 2205 |
+ } |
|
| 2206 |
+ |
|
| 2207 |
+ // allocNames finds a conflict-free variation of the given strings, |
|
| 2208 |
+ // consistently mutating their suffixes. |
|
| 2209 |
+ // It returns the same number of strings. |
|
| 2210 |
+ allocNames := func(ns ...string) []string {
|
|
| 2211 |
+ Loop: |
|
| 2212 |
+ for {
|
|
| 2213 |
+ for _, n := range ns {
|
|
| 2214 |
+ if usedNames[n] {
|
|
| 2215 |
+ for i := range ns {
|
|
| 2216 |
+ ns[i] += "_" |
|
| 2217 |
+ } |
|
| 2218 |
+ continue Loop |
|
| 2219 |
+ } |
|
| 2220 |
+ } |
|
| 2221 |
+ for _, n := range ns {
|
|
| 2222 |
+ usedNames[n] = true |
|
| 2223 |
+ } |
|
| 2224 |
+ return ns |
|
| 2225 |
+ } |
|
| 2226 |
+ } |
|
| 2227 |
+ |
|
| 2228 |
+ mapFieldTypes := make(map[*descriptor.FieldDescriptorProto]string) // keep track of the map fields to be added later |
|
| 2229 |
+ |
|
| 2230 |
+ // Build a structure more suitable for generating the text in one pass |
|
| 2231 |
+ for i, field := range message.Field {
|
|
| 2232 |
+ // Allocate the getter and the field at the same time so name |
|
| 2233 |
+ // collisions create field/method consistent names. |
|
| 2234 |
+ // TODO: This allocation occurs based on the order of the fields |
|
| 2235 |
+ // in the proto file, meaning that a change in the field |
|
| 2236 |
+ // ordering can change generated Method/Field names. |
|
| 2237 |
+ base := CamelCase(*field.Name) |
|
| 2238 |
+ ns := allocNames(base, "Get"+base) |
|
| 2239 |
+ fieldName, fieldGetterName := ns[0], ns[1] |
|
| 2240 |
+ typename, wiretype := g.GoType(message, field) |
|
| 2241 |
+ jsonName := *field.Name |
|
| 2242 |
+ tag := fmt.Sprintf("protobuf:%s json:%q", g.goTag(message, field, wiretype), jsonName+",omitempty")
|
|
| 2243 |
+ |
|
| 2244 |
+ oneof := field.OneofIndex != nil |
|
| 2245 |
+ if oneof && oFields[*field.OneofIndex] == nil {
|
|
| 2246 |
+ odp := message.OneofDecl[int(*field.OneofIndex)] |
|
| 2247 |
+ base := CamelCase(odp.GetName()) |
|
| 2248 |
+ fname := allocNames(base)[0] |
|
| 2249 |
+ |
|
| 2250 |
+ // This is the first field of a oneof we haven't seen before. |
|
| 2251 |
+ // Generate the union field. |
|
| 2252 |
+ oneofFullPath := fmt.Sprintf("%s,%d,%d", message.path, messageOneofPath, *field.OneofIndex)
|
|
| 2253 |
+ c, ok := g.makeComments(oneofFullPath) |
|
| 2254 |
+ if ok {
|
|
| 2255 |
+ c += "\n//\n" |
|
| 2256 |
+ } |
|
| 2257 |
+ c += "// Types that are valid to be assigned to " + fname + ":\n" |
|
| 2258 |
+ // Generate the rest of this comment later, |
|
| 2259 |
+ // when we've computed any disambiguation. |
|
| 2260 |
+ |
|
| 2261 |
+ dname := "is" + goTypeName + "_" + fname |
|
| 2262 |
+ tag := `protobuf_oneof:"` + odp.GetName() + `"` |
|
| 2263 |
+ of := oneofField{
|
|
| 2264 |
+ fieldCommon: fieldCommon{
|
|
| 2265 |
+ goName: fname, |
|
| 2266 |
+ getterName: "Get" + fname, |
|
| 2267 |
+ goType: dname, |
|
| 2268 |
+ tags: tag, |
|
| 2269 |
+ protoName: odp.GetName(), |
|
| 2270 |
+ fullPath: oneofFullPath, |
|
| 2271 |
+ }, |
|
| 2272 |
+ comment: c, |
|
| 2273 |
+ } |
|
| 2274 |
+ topLevelFields = append(topLevelFields, &of) |
|
| 2275 |
+ oFields[*field.OneofIndex] = &of |
|
| 2276 |
+ } |
|
| 2277 |
+ |
|
| 2278 |
+ if *field.Type == descriptor.FieldDescriptorProto_TYPE_MESSAGE {
|
|
| 2279 |
+ desc := g.ObjectNamed(field.GetTypeName()) |
|
| 2280 |
+ if d, ok := desc.(*Descriptor); ok && d.GetOptions().GetMapEntry() {
|
|
| 2281 |
+ // Figure out the Go types and tags for the key and value types. |
|
| 2282 |
+ keyField, valField := d.Field[0], d.Field[1] |
|
| 2283 |
+ keyType, keyWire := g.GoType(d, keyField) |
|
| 2284 |
+ valType, valWire := g.GoType(d, valField) |
|
| 2285 |
+ keyTag, valTag := g.goTag(d, keyField, keyWire), g.goTag(d, valField, valWire) |
|
| 2286 |
+ |
|
| 2287 |
+ // We don't use stars, except for message-typed values. |
|
| 2288 |
+ // Message and enum types are the only two possibly foreign types used in maps, |
|
| 2289 |
+ // so record their use. They are not permitted as map keys. |
|
| 2290 |
+ keyType = strings.TrimPrefix(keyType, "*") |
|
| 2291 |
+ switch *valField.Type {
|
|
| 2292 |
+ case descriptor.FieldDescriptorProto_TYPE_ENUM: |
|
| 2293 |
+ valType = strings.TrimPrefix(valType, "*") |
|
| 2294 |
+ g.RecordTypeUse(valField.GetTypeName()) |
|
| 2295 |
+ case descriptor.FieldDescriptorProto_TYPE_MESSAGE: |
|
| 2296 |
+ g.RecordTypeUse(valField.GetTypeName()) |
|
| 2297 |
+ default: |
|
| 2298 |
+ valType = strings.TrimPrefix(valType, "*") |
|
| 2299 |
+ } |
|
| 2300 |
+ |
|
| 2301 |
+ typename = fmt.Sprintf("map[%s]%s", keyType, valType)
|
|
| 2302 |
+ mapFieldTypes[field] = typename // record for the getter generation |
|
| 2303 |
+ |
|
| 2304 |
+ tag += fmt.Sprintf(" protobuf_key:%s protobuf_val:%s", keyTag, valTag)
|
|
| 2305 |
+ } |
|
| 2306 |
+ } |
|
| 2307 |
+ |
|
| 2308 |
+ fieldDeprecated := "" |
|
| 2309 |
+ if field.GetOptions().GetDeprecated() {
|
|
| 2310 |
+ fieldDeprecated = deprecationComment |
|
| 2311 |
+ } |
|
| 2312 |
+ |
|
| 2313 |
+ dvalue := g.getterDefault(field, goTypeName) |
|
| 2314 |
+ if oneof {
|
|
| 2315 |
+ tname := goTypeName + "_" + fieldName |
|
| 2316 |
+ // It is possible for this to collide with a message or enum |
|
| 2317 |
+ // nested in this message. Check for collisions. |
|
| 2318 |
+ for {
|
|
| 2319 |
+ ok := true |
|
| 2320 |
+ for _, desc := range message.nested {
|
|
| 2321 |
+ if CamelCaseSlice(desc.TypeName()) == tname {
|
|
| 2322 |
+ ok = false |
|
| 2323 |
+ break |
|
| 2324 |
+ } |
|
| 2325 |
+ } |
|
| 2326 |
+ for _, enum := range message.enums {
|
|
| 2327 |
+ if CamelCaseSlice(enum.TypeName()) == tname {
|
|
| 2328 |
+ ok = false |
|
| 2329 |
+ break |
|
| 2330 |
+ } |
|
| 2331 |
+ } |
|
| 2332 |
+ if !ok {
|
|
| 2333 |
+ tname += "_" |
|
| 2334 |
+ continue |
|
| 2335 |
+ } |
|
| 2336 |
+ break |
|
| 2337 |
+ } |
|
| 2338 |
+ |
|
| 2339 |
+ oneofField := oFields[*field.OneofIndex] |
|
| 2340 |
+ tag := "protobuf:" + g.goTag(message, field, wiretype) |
|
| 2341 |
+ sf := oneofSubField{
|
|
| 2342 |
+ fieldCommon: fieldCommon{
|
|
| 2343 |
+ goName: fieldName, |
|
| 2344 |
+ getterName: fieldGetterName, |
|
| 2345 |
+ goType: typename, |
|
| 2346 |
+ tags: tag, |
|
| 2347 |
+ protoName: field.GetName(), |
|
| 2348 |
+ fullPath: fmt.Sprintf("%s,%d,%d", message.path, messageFieldPath, i),
|
|
| 2349 |
+ }, |
|
| 2350 |
+ protoTypeName: field.GetTypeName(), |
|
| 2351 |
+ fieldNumber: int(*field.Number), |
|
| 2352 |
+ protoType: *field.Type, |
|
| 2353 |
+ getterDef: dvalue, |
|
| 2354 |
+ protoDef: field.GetDefaultValue(), |
|
| 2355 |
+ oneofTypeName: tname, |
|
| 2356 |
+ deprecated: fieldDeprecated, |
|
| 2357 |
+ } |
|
| 2358 |
+ oneofField.subFields = append(oneofField.subFields, &sf) |
|
| 2359 |
+ g.RecordTypeUse(field.GetTypeName()) |
|
| 2360 |
+ continue |
|
| 2361 |
+ } |
|
| 2362 |
+ |
|
| 2363 |
+ fieldFullPath := fmt.Sprintf("%s,%d,%d", message.path, messageFieldPath, i)
|
|
| 2364 |
+ c, ok := g.makeComments(fieldFullPath) |
|
| 2365 |
+ if ok {
|
|
| 2366 |
+ c += "\n" |
|
| 2367 |
+ } |
|
| 2368 |
+ rf := simpleField{
|
|
| 2369 |
+ fieldCommon: fieldCommon{
|
|
| 2370 |
+ goName: fieldName, |
|
| 2371 |
+ getterName: fieldGetterName, |
|
| 2372 |
+ goType: typename, |
|
| 2373 |
+ tags: tag, |
|
| 2374 |
+ protoName: field.GetName(), |
|
| 2375 |
+ fullPath: fieldFullPath, |
|
| 2376 |
+ }, |
|
| 2377 |
+ protoTypeName: field.GetTypeName(), |
|
| 2378 |
+ protoType: *field.Type, |
|
| 2379 |
+ deprecated: fieldDeprecated, |
|
| 2380 |
+ getterDef: dvalue, |
|
| 2381 |
+ protoDef: field.GetDefaultValue(), |
|
| 2382 |
+ comment: c, |
|
| 2383 |
+ } |
|
| 2384 |
+ var pf topLevelField = &rf |
|
| 2385 |
+ |
|
| 2386 |
+ topLevelFields = append(topLevelFields, pf) |
|
| 2387 |
+ g.RecordTypeUse(field.GetTypeName()) |
|
| 2388 |
+ } |
|
| 2389 |
+ |
|
| 2390 |
+ mc := &msgCtx{
|
|
| 2391 |
+ goName: goTypeName, |
|
| 2392 |
+ message: message, |
|
| 2393 |
+ } |
|
| 2394 |
+ |
|
| 2395 |
+ g.generateMessageStruct(mc, topLevelFields) |
|
| 2396 |
+ g.P() |
|
| 2397 |
+ g.generateCommonMethods(mc) |
|
| 2398 |
+ g.P() |
|
| 2399 |
+ g.generateDefaultConstants(mc, topLevelFields) |
|
| 2400 |
+ g.P() |
|
| 2401 |
+ g.generateGetters(mc, topLevelFields) |
|
| 2402 |
+ g.P() |
|
| 2403 |
+ g.generateSetters(mc, topLevelFields) |
|
| 2404 |
+ g.P() |
|
| 2405 |
+ g.generateOneofFuncs(mc, topLevelFields) |
|
| 2406 |
+ g.P() |
|
| 2407 |
+ |
|
| 2408 |
+ var oneofTypes []string |
|
| 2409 |
+ for _, f := range topLevelFields {
|
|
| 2410 |
+ if of, ok := f.(*oneofField); ok {
|
|
| 2411 |
+ for _, osf := range of.subFields {
|
|
| 2412 |
+ oneofTypes = append(oneofTypes, osf.oneofTypeName) |
|
| 2413 |
+ } |
|
| 2414 |
+ } |
|
| 2415 |
+ } |
|
| 2416 |
+ |
|
| 2417 |
+ opts := message.Options |
|
| 2418 |
+ ms := &messageSymbol{
|
|
| 2419 |
+ sym: goTypeName, |
|
| 2420 |
+ hasExtensions: len(message.ExtensionRange) > 0, |
|
| 2421 |
+ isMessageSet: opts != nil && opts.GetMessageSetWireFormat(), |
|
| 2422 |
+ oneofTypes: oneofTypes, |
|
| 2423 |
+ } |
|
| 2424 |
+ g.file.addExport(message, ms) |
|
| 2425 |
+ |
|
| 2426 |
+ for _, ext := range message.ext {
|
|
| 2427 |
+ g.generateExtension(ext) |
|
| 2428 |
+ } |
|
| 2429 |
+ |
|
| 2430 |
+ fullName := strings.Join(message.TypeName(), ".") |
|
| 2431 |
+ if g.file.Package != nil {
|
|
| 2432 |
+ fullName = *g.file.Package + "." + fullName |
|
| 2433 |
+ } |
|
| 2434 |
+ |
|
| 2435 |
+ g.addInitf("%s.RegisterType((*%s)(nil), %q)", g.Pkg["proto"], goTypeName, fullName)
|
|
| 2436 |
+ // Register types for native map types. |
|
| 2437 |
+ for _, k := range mapFieldKeys(mapFieldTypes) {
|
|
| 2438 |
+ fullName := strings.TrimPrefix(*k.TypeName, ".") |
|
| 2439 |
+ g.addInitf("%s.RegisterMapType((%s)(nil), %q)", g.Pkg["proto"], mapFieldTypes[k], fullName)
|
|
| 2440 |
+ } |
|
| 2441 |
+ |
|
| 2442 |
+} |
|
| 2443 |
+ |
|
| 2444 |
+type byTypeName []*descriptor.FieldDescriptorProto |
|
| 2445 |
+ |
|
| 2446 |
+func (a byTypeName) Len() int { return len(a) }
|
|
| 2447 |
+func (a byTypeName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
| 2448 |
+func (a byTypeName) Less(i, j int) bool { return *a[i].TypeName < *a[j].TypeName }
|
|
| 2449 |
+ |
|
| 2450 |
+// mapFieldKeys returns the keys of m in a consistent order. |
|
| 2451 |
+func mapFieldKeys(m map[*descriptor.FieldDescriptorProto]string) []*descriptor.FieldDescriptorProto {
|
|
| 2452 |
+ keys := make([]*descriptor.FieldDescriptorProto, 0, len(m)) |
|
| 2453 |
+ for k := range m {
|
|
| 2454 |
+ keys = append(keys, k) |
|
| 2455 |
+ } |
|
| 2456 |
+ sort.Sort(byTypeName(keys)) |
|
| 2457 |
+ return keys |
|
| 2458 |
+} |
|
| 2459 |
+ |
|
| 2460 |
+var escapeChars = [256]byte{
|
|
| 2461 |
+ 'a': '\a', 'b': '\b', 'f': '\f', 'n': '\n', 'r': '\r', 't': '\t', 'v': '\v', '\\': '\\', '"': '"', '\'': '\'', '?': '?', |
|
| 2462 |
+} |
|
| 2463 |
+ |
|
| 2464 |
+// unescape reverses the "C" escaping that protoc does for default values of bytes fields. |
|
| 2465 |
+// It is best effort in that it effectively ignores malformed input. Seemingly invalid escape |
|
| 2466 |
+// sequences are conveyed, unmodified, into the decoded result. |
|
| 2467 |
+func unescape(s string) string {
|
|
| 2468 |
+ // NB: Sadly, we can't use strconv.Unquote because protoc will escape both |
|
| 2469 |
+ // single and double quotes, but strconv.Unquote only allows one or the |
|
| 2470 |
+ // other (based on actual surrounding quotes of its input argument). |
|
| 2471 |
+ |
|
| 2472 |
+ var out []byte |
|
| 2473 |
+ for len(s) > 0 {
|
|
| 2474 |
+ // regular character, or too short to be valid escape |
|
| 2475 |
+ if s[0] != '\\' || len(s) < 2 {
|
|
| 2476 |
+ out = append(out, s[0]) |
|
| 2477 |
+ s = s[1:] |
|
| 2478 |
+ } else if c := escapeChars[s[1]]; c != 0 {
|
|
| 2479 |
+ // escape sequence |
|
| 2480 |
+ out = append(out, c) |
|
| 2481 |
+ s = s[2:] |
|
| 2482 |
+ } else if s[1] == 'x' || s[1] == 'X' {
|
|
| 2483 |
+ // hex escape, e.g. "\x80 |
|
| 2484 |
+ if len(s) < 4 {
|
|
| 2485 |
+ // too short to be valid |
|
| 2486 |
+ out = append(out, s[:2]...) |
|
| 2487 |
+ s = s[2:] |
|
| 2488 |
+ continue |
|
| 2489 |
+ } |
|
| 2490 |
+ v, err := strconv.ParseUint(s[2:4], 16, 8) |
|
| 2491 |
+ if err != nil {
|
|
| 2492 |
+ out = append(out, s[:4]...) |
|
| 2493 |
+ } else {
|
|
| 2494 |
+ out = append(out, byte(v)) |
|
| 2495 |
+ } |
|
| 2496 |
+ s = s[4:] |
|
| 2497 |
+ } else if '0' <= s[1] && s[1] <= '7' {
|
|
| 2498 |
+ // octal escape, can vary from 1 to 3 octal digits; e.g., "\0" "\40" or "\164" |
|
| 2499 |
+ // so consume up to 2 more bytes or up to end-of-string |
|
| 2500 |
+ n := len(s[1:]) - len(strings.TrimLeft(s[1:], "01234567")) |
|
| 2501 |
+ if n > 3 {
|
|
| 2502 |
+ n = 3 |
|
| 2503 |
+ } |
|
| 2504 |
+ v, err := strconv.ParseUint(s[1:1+n], 8, 8) |
|
| 2505 |
+ if err != nil {
|
|
| 2506 |
+ out = append(out, s[:1+n]...) |
|
| 2507 |
+ } else {
|
|
| 2508 |
+ out = append(out, byte(v)) |
|
| 2509 |
+ } |
|
| 2510 |
+ s = s[1+n:] |
|
| 2511 |
+ } else {
|
|
| 2512 |
+ // bad escape, just propagate the slash as-is |
|
| 2513 |
+ out = append(out, s[0]) |
|
| 2514 |
+ s = s[1:] |
|
| 2515 |
+ } |
|
| 2516 |
+ } |
|
| 2517 |
+ |
|
| 2518 |
+ return string(out) |
|
| 2519 |
+} |
|
| 2520 |
+ |
|
| 2521 |
+func (g *Generator) generateExtension(ext *ExtensionDescriptor) {
|
|
| 2522 |
+ ccTypeName := ext.DescName() |
|
| 2523 |
+ |
|
| 2524 |
+ extObj := g.ObjectNamed(*ext.Extendee) |
|
| 2525 |
+ var extDesc *Descriptor |
|
| 2526 |
+ if id, ok := extObj.(*ImportedDescriptor); ok {
|
|
| 2527 |
+ // This is extending a publicly imported message. |
|
| 2528 |
+ // We need the underlying type for goTag. |
|
| 2529 |
+ extDesc = id.o.(*Descriptor) |
|
| 2530 |
+ } else {
|
|
| 2531 |
+ extDesc = extObj.(*Descriptor) |
|
| 2532 |
+ } |
|
| 2533 |
+ extendedType := "*" + g.TypeName(extObj) // always use the original |
|
| 2534 |
+ field := ext.FieldDescriptorProto |
|
| 2535 |
+ fieldType, wireType := g.GoType(ext.parent, field) |
|
| 2536 |
+ tag := g.goTag(extDesc, field, wireType) |
|
| 2537 |
+ g.RecordTypeUse(*ext.Extendee) |
|
| 2538 |
+ if n := ext.FieldDescriptorProto.TypeName; n != nil {
|
|
| 2539 |
+ // foreign extension type |
|
| 2540 |
+ g.RecordTypeUse(*n) |
|
| 2541 |
+ } |
|
| 2542 |
+ |
|
| 2543 |
+ typeName := ext.TypeName() |
|
| 2544 |
+ |
|
| 2545 |
+ // Special case for proto2 message sets: If this extension is extending |
|
| 2546 |
+ // proto2.bridge.MessageSet, and its final name component is "message_set_extension", |
|
| 2547 |
+ // then drop that last component. |
|
| 2548 |
+ // |
|
| 2549 |
+ // TODO: This should be implemented in the text formatter rather than the generator. |
|
| 2550 |
+ // In addition, the situation for when to apply this special case is implemented |
|
| 2551 |
+ // differently in other languages: |
|
| 2552 |
+ // https://github.com/google/protobuf/blob/aff10976/src/google/protobuf/text_format.cc#L1560 |
|
| 2553 |
+ if extDesc.GetOptions().GetMessageSetWireFormat() && typeName[len(typeName)-1] == "message_set_extension" {
|
|
| 2554 |
+ typeName = typeName[:len(typeName)-1] |
|
| 2555 |
+ } |
|
| 2556 |
+ |
|
| 2557 |
+ // For text formatting, the package must be exactly what the .proto file declares, |
|
| 2558 |
+ // ignoring overrides such as the go_package option, and with no dot/underscore mapping. |
|
| 2559 |
+ extName := strings.Join(typeName, ".") |
|
| 2560 |
+ if g.file.Package != nil {
|
|
| 2561 |
+ extName = *g.file.Package + "." + extName |
|
| 2562 |
+ } |
|
| 2563 |
+ |
|
| 2564 |
+ g.P("var ", ccTypeName, " = &", g.Pkg["proto"], ".ExtensionDesc{")
|
|
| 2565 |
+ g.P("ExtendedType: (", extendedType, ")(nil),")
|
|
| 2566 |
+ g.P("ExtensionType: (", fieldType, ")(nil),")
|
|
| 2567 |
+ g.P("Field: ", field.Number, ",")
|
|
| 2568 |
+ g.P(`Name: "`, extName, `",`) |
|
| 2569 |
+ g.P("Tag: ", tag, ",")
|
|
| 2570 |
+ g.P(`Filename: "`, g.file.GetName(), `",`) |
|
| 2571 |
+ |
|
| 2572 |
+ g.P("}")
|
|
| 2573 |
+ g.P() |
|
| 2574 |
+ |
|
| 2575 |
+ g.addInitf("%s.RegisterExtension(%s)", g.Pkg["proto"], ext.DescName())
|
|
| 2576 |
+ |
|
| 2577 |
+ g.file.addExport(ext, constOrVarSymbol{ccTypeName, "var", ""})
|
|
| 2578 |
+} |
|
| 2579 |
+ |
|
| 2580 |
+func (g *Generator) generateInitFunction() {
|
|
| 2581 |
+ if len(g.init) == 0 {
|
|
| 2582 |
+ return |
|
| 2583 |
+ } |
|
| 2584 |
+ g.P("func init() {")
|
|
| 2585 |
+ for _, l := range g.init {
|
|
| 2586 |
+ g.P(l) |
|
| 2587 |
+ } |
|
| 2588 |
+ g.P("}")
|
|
| 2589 |
+ g.init = nil |
|
| 2590 |
+} |
|
| 2591 |
+ |
|
| 2592 |
+func (g *Generator) generateFileDescriptor(file *FileDescriptor) {
|
|
| 2593 |
+ // Make a copy and trim source_code_info data. |
|
| 2594 |
+ // TODO: Trim this more when we know exactly what we need. |
|
| 2595 |
+ pb := proto.Clone(file.FileDescriptorProto).(*descriptor.FileDescriptorProto) |
|
| 2596 |
+ pb.SourceCodeInfo = nil |
|
| 2597 |
+ |
|
| 2598 |
+ b, err := proto.Marshal(pb) |
|
| 2599 |
+ if err != nil {
|
|
| 2600 |
+ g.Fail(err.Error()) |
|
| 2601 |
+ } |
|
| 2602 |
+ |
|
| 2603 |
+ var buf bytes.Buffer |
|
| 2604 |
+ w, _ := gzip.NewWriterLevel(&buf, gzip.BestCompression) |
|
| 2605 |
+ w.Write(b) |
|
| 2606 |
+ w.Close() |
|
| 2607 |
+ b = buf.Bytes() |
|
| 2608 |
+ |
|
| 2609 |
+ v := file.VarName() |
|
| 2610 |
+ g.P() |
|
| 2611 |
+ g.P("func init() {")
|
|
| 2612 |
+ g.P(g.Pkg["proto"], ".RegisterFile(", strconv.Quote(*file.Name), ", ", v, ")")
|
|
| 2613 |
+ g.P("}")
|
|
| 2614 |
+ g.P("var ", v, " = []byte{")
|
|
| 2615 |
+ g.P("// ", len(b), " bytes of a gzipped FileDescriptorProto")
|
|
| 2616 |
+ for len(b) > 0 {
|
|
| 2617 |
+ n := 16 |
|
| 2618 |
+ if n > len(b) {
|
|
| 2619 |
+ n = len(b) |
|
| 2620 |
+ } |
|
| 2621 |
+ |
|
| 2622 |
+ s := "" |
|
| 2623 |
+ for _, c := range b[:n] {
|
|
| 2624 |
+ s += fmt.Sprintf("0x%02x,", c)
|
|
| 2625 |
+ } |
|
| 2626 |
+ g.P(s) |
|
| 2627 |
+ |
|
| 2628 |
+ b = b[n:] |
|
| 2629 |
+ } |
|
| 2630 |
+ g.P("}")
|
|
| 2631 |
+} |
|
| 2632 |
+ |
|
| 2633 |
+func (g *Generator) generateEnumRegistration(enum *EnumDescriptor) {
|
|
| 2634 |
+ // // We always print the full (proto-world) package name here. |
|
| 2635 |
+ pkg := enum.File().GetPackage() |
|
| 2636 |
+ if pkg != "" {
|
|
| 2637 |
+ pkg += "." |
|
| 2638 |
+ } |
|
| 2639 |
+ // The full type name |
|
| 2640 |
+ typeName := enum.TypeName() |
|
| 2641 |
+ // The full type name, CamelCased. |
|
| 2642 |
+ ccTypeName := CamelCaseSlice(typeName) |
|
| 2643 |
+ g.addInitf("%s.RegisterEnum(%q, %[3]s_name, %[3]s_value)", g.Pkg["proto"], pkg+ccTypeName, ccTypeName)
|
|
| 2644 |
+} |
|
| 2645 |
+ |
|
| 2646 |
+// And now lots of helper functions. |
|
| 2647 |
+ |
|
| 2648 |
+// Is c an ASCII lower-case letter? |
|
| 2649 |
+func isASCIILower(c byte) bool {
|
|
| 2650 |
+ return 'a' <= c && c <= 'z' |
|
| 2651 |
+} |
|
| 2652 |
+ |
|
| 2653 |
+// Is c an ASCII digit? |
|
| 2654 |
+func isASCIIDigit(c byte) bool {
|
|
| 2655 |
+ return '0' <= c && c <= '9' |
|
| 2656 |
+} |
|
| 2657 |
+ |
|
| 2658 |
+// CamelCase returns the CamelCased name. |
|
| 2659 |
+// If there is an interior underscore followed by a lower case letter, |
|
| 2660 |
+// drop the underscore and convert the letter to upper case. |
|
| 2661 |
+// There is a remote possibility of this rewrite causing a name collision, |
|
| 2662 |
+// but it's so remote we're prepared to pretend it's nonexistent - since the |
|
| 2663 |
+// C++ generator lowercases names, it's extremely unlikely to have two fields |
|
| 2664 |
+// with different capitalizations. |
|
| 2665 |
+// In short, _my_field_name_2 becomes XMyFieldName_2. |
|
| 2666 |
+func CamelCase(s string) string {
|
|
| 2667 |
+ if s == "" {
|
|
| 2668 |
+ return "" |
|
| 2669 |
+ } |
|
| 2670 |
+ t := make([]byte, 0, 32) |
|
| 2671 |
+ i := 0 |
|
| 2672 |
+ if s[0] == '_' {
|
|
| 2673 |
+ // Need a capital letter; drop the '_'. |
|
| 2674 |
+ t = append(t, 'X') |
|
| 2675 |
+ i++ |
|
| 2676 |
+ } |
|
| 2677 |
+ // Invariant: if the next letter is lower case, it must be converted |
|
| 2678 |
+ // to upper case. |
|
| 2679 |
+ // That is, we process a word at a time, where words are marked by _ or |
|
| 2680 |
+ // upper case letter. Digits are treated as words. |
|
| 2681 |
+ for ; i < len(s); i++ {
|
|
| 2682 |
+ c := s[i] |
|
| 2683 |
+ if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
|
|
| 2684 |
+ continue // Skip the underscore in s. |
|
| 2685 |
+ } |
|
| 2686 |
+ if isASCIIDigit(c) {
|
|
| 2687 |
+ t = append(t, c) |
|
| 2688 |
+ continue |
|
| 2689 |
+ } |
|
| 2690 |
+ // Assume we have a letter now - if not, it's a bogus identifier. |
|
| 2691 |
+ // The next word is a sequence of characters that must start upper case. |
|
| 2692 |
+ if isASCIILower(c) {
|
|
| 2693 |
+ c ^= ' ' // Make it a capital letter. |
|
| 2694 |
+ } |
|
| 2695 |
+ t = append(t, c) // Guaranteed not lower case. |
|
| 2696 |
+ // Accept lower case sequence that follows. |
|
| 2697 |
+ for i+1 < len(s) && isASCIILower(s[i+1]) {
|
|
| 2698 |
+ i++ |
|
| 2699 |
+ t = append(t, s[i]) |
|
| 2700 |
+ } |
|
| 2701 |
+ } |
|
| 2702 |
+ return string(t) |
|
| 2703 |
+} |
|
| 2704 |
+ |
|
| 2705 |
+// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to |
|
| 2706 |
+// be joined with "_". |
|
| 2707 |
+func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
|
|
| 2708 |
+ |
|
| 2709 |
+// dottedSlice turns a sliced name into a dotted name. |
|
| 2710 |
+func dottedSlice(elem []string) string { return strings.Join(elem, ".") }
|
|
| 2711 |
+ |
|
| 2712 |
+// Is this field optional? |
|
| 2713 |
+func isOptional(field *descriptor.FieldDescriptorProto) bool {
|
|
| 2714 |
+ return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL |
|
| 2715 |
+} |
|
| 2716 |
+ |
|
| 2717 |
+// Is this field required? |
|
| 2718 |
+func isRequired(field *descriptor.FieldDescriptorProto) bool {
|
|
| 2719 |
+ return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED |
|
| 2720 |
+} |
|
| 2721 |
+ |
|
| 2722 |
+// Is this field repeated? |
|
| 2723 |
+func isRepeated(field *descriptor.FieldDescriptorProto) bool {
|
|
| 2724 |
+ return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED |
|
| 2725 |
+} |
|
| 2726 |
+ |
|
| 2727 |
+// Is this field a scalar numeric type? |
|
| 2728 |
+func isScalar(field *descriptor.FieldDescriptorProto) bool {
|
|
| 2729 |
+ if field.Type == nil {
|
|
| 2730 |
+ return false |
|
| 2731 |
+ } |
|
| 2732 |
+ switch *field.Type {
|
|
| 2733 |
+ case descriptor.FieldDescriptorProto_TYPE_DOUBLE, |
|
| 2734 |
+ descriptor.FieldDescriptorProto_TYPE_FLOAT, |
|
| 2735 |
+ descriptor.FieldDescriptorProto_TYPE_INT64, |
|
| 2736 |
+ descriptor.FieldDescriptorProto_TYPE_UINT64, |
|
| 2737 |
+ descriptor.FieldDescriptorProto_TYPE_INT32, |
|
| 2738 |
+ descriptor.FieldDescriptorProto_TYPE_FIXED64, |
|
| 2739 |
+ descriptor.FieldDescriptorProto_TYPE_FIXED32, |
|
| 2740 |
+ descriptor.FieldDescriptorProto_TYPE_BOOL, |
|
| 2741 |
+ descriptor.FieldDescriptorProto_TYPE_UINT32, |
|
| 2742 |
+ descriptor.FieldDescriptorProto_TYPE_ENUM, |
|
| 2743 |
+ descriptor.FieldDescriptorProto_TYPE_SFIXED32, |
|
| 2744 |
+ descriptor.FieldDescriptorProto_TYPE_SFIXED64, |
|
| 2745 |
+ descriptor.FieldDescriptorProto_TYPE_SINT32, |
|
| 2746 |
+ descriptor.FieldDescriptorProto_TYPE_SINT64: |
|
| 2747 |
+ return true |
|
| 2748 |
+ default: |
|
| 2749 |
+ return false |
|
| 2750 |
+ } |
|
| 2751 |
+} |
|
| 2752 |
+ |
|
| 2753 |
+// badToUnderscore is the mapping function used to generate Go names from package names, |
|
| 2754 |
+// which can be dotted in the input .proto file. It replaces non-identifier characters such as |
|
| 2755 |
+// dot or dash with underscore. |
|
| 2756 |
+func badToUnderscore(r rune) rune {
|
|
| 2757 |
+ if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' {
|
|
| 2758 |
+ return r |
|
| 2759 |
+ } |
|
| 2760 |
+ return '_' |
|
| 2761 |
+} |
|
| 2762 |
+ |
|
| 2763 |
+// baseName returns the last path element of the name, with the last dotted suffix removed. |
|
| 2764 |
+func baseName(name string) string {
|
|
| 2765 |
+ // First, find the last element |
|
| 2766 |
+ if i := strings.LastIndex(name, "/"); i >= 0 {
|
|
| 2767 |
+ name = name[i+1:] |
|
| 2768 |
+ } |
|
| 2769 |
+ // Now drop the suffix |
|
| 2770 |
+ if i := strings.LastIndex(name, "."); i >= 0 {
|
|
| 2771 |
+ name = name[0:i] |
|
| 2772 |
+ } |
|
| 2773 |
+ return name |
|
| 2774 |
+} |
|
| 2775 |
+ |
|
| 2776 |
+// The SourceCodeInfo message describes the location of elements of a parsed |
|
| 2777 |
+// .proto file by way of a "path", which is a sequence of integers that |
|
| 2778 |
+// describe the route from a FileDescriptorProto to the relevant submessage. |
|
| 2779 |
+// The path alternates between a field number of a repeated field, and an index |
|
| 2780 |
+// into that repeated field. The constants below define the field numbers that |
|
| 2781 |
+// are used. |
|
| 2782 |
+// |
|
| 2783 |
+// See descriptor.proto for more information about this. |
|
| 2784 |
+const ( |
|
| 2785 |
+ // tag numbers in FileDescriptorProto |
|
| 2786 |
+ packagePath = 2 // package |
|
| 2787 |
+ messagePath = 4 // message_type |
|
| 2788 |
+ enumPath = 5 // enum_type |
|
| 2789 |
+ // tag numbers in DescriptorProto |
|
| 2790 |
+ messageFieldPath = 2 // field |
|
| 2791 |
+ messageMessagePath = 3 // nested_type |
|
| 2792 |
+ messageEnumPath = 4 // enum_type |
|
| 2793 |
+ messageOneofPath = 8 // oneof_decl |
|
| 2794 |
+ // tag numbers in EnumDescriptorProto |
|
| 2795 |
+ enumValuePath = 2 // value |
|
| 2796 |
+) |
|
| 2797 |
+ |
|
| 2798 |
+var supportTypeAliases bool |
|
| 2799 |
+ |
|
| 2800 |
+func init() {
|
|
| 2801 |
+ for _, tag := range build.Default.ReleaseTags {
|
|
| 2802 |
+ if tag == "go1.9" {
|
|
| 2803 |
+ supportTypeAliases = true |
|
| 2804 |
+ return |
|
| 2805 |
+ } |
|
| 2806 |
+ } |
|
| 2807 |
+} |
| 0 | 2808 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,117 @@ |
| 0 |
+// Go support for Protocol Buffers - Google's data interchange format |
|
| 1 |
+// |
|
| 2 |
+// Copyright 2017 The Go Authors. All rights reserved. |
|
| 3 |
+// https://github.com/golang/protobuf |
|
| 4 |
+// |
|
| 5 |
+// Redistribution and use in source and binary forms, with or without |
|
| 6 |
+// modification, are permitted provided that the following conditions are |
|
| 7 |
+// met: |
|
| 8 |
+// |
|
| 9 |
+// * Redistributions of source code must retain the above copyright |
|
| 10 |
+// notice, this list of conditions and the following disclaimer. |
|
| 11 |
+// * Redistributions in binary form must reproduce the above |
|
| 12 |
+// copyright notice, this list of conditions and the following disclaimer |
|
| 13 |
+// in the documentation and/or other materials provided with the |
|
| 14 |
+// distribution. |
|
| 15 |
+// * Neither the name of Google Inc. nor the names of its |
|
| 16 |
+// contributors may be used to endorse or promote products derived from |
|
| 17 |
+// this software without specific prior written permission. |
|
| 18 |
+// |
|
| 19 |
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
| 20 |
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
| 21 |
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
| 22 |
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
| 23 |
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
| 24 |
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 25 |
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
| 26 |
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
| 27 |
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
| 28 |
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
| 29 |
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
| 30 |
+ |
|
| 31 |
+/* |
|
| 32 |
+Package remap handles tracking the locations of Go tokens in a source text |
|
| 33 |
+across a rewrite by the Go formatter. |
|
| 34 |
+*/ |
|
| 35 |
+package remap |
|
| 36 |
+ |
|
| 37 |
+import ( |
|
| 38 |
+ "fmt" |
|
| 39 |
+ "go/scanner" |
|
| 40 |
+ "go/token" |
|
| 41 |
+) |
|
| 42 |
+ |
|
| 43 |
+// A Location represents a span of byte offsets in the source text. |
|
| 44 |
+type Location struct {
|
|
| 45 |
+ Pos, End int // End is exclusive |
|
| 46 |
+} |
|
| 47 |
+ |
|
| 48 |
+// A Map represents a mapping between token locations in an input source text |
|
| 49 |
+// and locations in the corresponding output text. |
|
| 50 |
+type Map map[Location]Location |
|
| 51 |
+ |
|
| 52 |
+// Find reports whether the specified span is recorded by m, and if so returns |
|
| 53 |
+// the new location it was mapped to. If the input span was not found, the |
|
| 54 |
+// returned location is the same as the input. |
|
| 55 |
+func (m Map) Find(pos, end int) (Location, bool) {
|
|
| 56 |
+ key := Location{
|
|
| 57 |
+ Pos: pos, |
|
| 58 |
+ End: end, |
|
| 59 |
+ } |
|
| 60 |
+ if loc, ok := m[key]; ok {
|
|
| 61 |
+ return loc, true |
|
| 62 |
+ } |
|
| 63 |
+ return key, false |
|
| 64 |
+} |
|
| 65 |
+ |
|
| 66 |
+func (m Map) add(opos, oend, npos, nend int) {
|
|
| 67 |
+ m[Location{Pos: opos, End: oend}] = Location{Pos: npos, End: nend}
|
|
| 68 |
+} |
|
| 69 |
+ |
|
| 70 |
+// Compute constructs a location mapping from input to output. An error is |
|
| 71 |
+// reported if any of the tokens of output cannot be mapped. |
|
| 72 |
+func Compute(input, output []byte) (Map, error) {
|
|
| 73 |
+ itok := tokenize(input) |
|
| 74 |
+ otok := tokenize(output) |
|
| 75 |
+ if len(itok) != len(otok) {
|
|
| 76 |
+ return nil, fmt.Errorf("wrong number of tokens, %d ≠%d", len(itok), len(otok))
|
|
| 77 |
+ } |
|
| 78 |
+ m := make(Map) |
|
| 79 |
+ for i, ti := range itok {
|
|
| 80 |
+ to := otok[i] |
|
| 81 |
+ if ti.Token != to.Token {
|
|
| 82 |
+ return nil, fmt.Errorf("token %d type mismatch: %s ≠%s", i+1, ti, to)
|
|
| 83 |
+ } |
|
| 84 |
+ m.add(ti.pos, ti.end, to.pos, to.end) |
|
| 85 |
+ } |
|
| 86 |
+ return m, nil |
|
| 87 |
+} |
|
| 88 |
+ |
|
| 89 |
+// tokinfo records the span and type of a source token. |
|
| 90 |
+type tokinfo struct {
|
|
| 91 |
+ pos, end int |
|
| 92 |
+ token.Token |
|
| 93 |
+} |
|
| 94 |
+ |
|
| 95 |
+func tokenize(src []byte) []tokinfo {
|
|
| 96 |
+ fs := token.NewFileSet() |
|
| 97 |
+ var s scanner.Scanner |
|
| 98 |
+ s.Init(fs.AddFile("src", fs.Base(), len(src)), src, nil, scanner.ScanComments)
|
|
| 99 |
+ var info []tokinfo |
|
| 100 |
+ for {
|
|
| 101 |
+ pos, next, lit := s.Scan() |
|
| 102 |
+ switch next {
|
|
| 103 |
+ case token.SEMICOLON: |
|
| 104 |
+ continue |
|
| 105 |
+ } |
|
| 106 |
+ info = append(info, tokinfo{
|
|
| 107 |
+ pos: int(pos - 1), |
|
| 108 |
+ end: int(pos + token.Pos(len(lit)) - 1), |
|
| 109 |
+ Token: next, |
|
| 110 |
+ }) |
|
| 111 |
+ if next == token.EOF {
|
|
| 112 |
+ break |
|
| 113 |
+ } |
|
| 114 |
+ } |
|
| 115 |
+ return info |
|
| 116 |
+} |
| 0 | 117 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,545 @@ |
| 0 |
+// Go support for Protocol Buffers - Google's data interchange format |
|
| 1 |
+// |
|
| 2 |
+// Copyright 2015 The Go Authors. All rights reserved. |
|
| 3 |
+// https://github.com/golang/protobuf |
|
| 4 |
+// |
|
| 5 |
+// Redistribution and use in source and binary forms, with or without |
|
| 6 |
+// modification, are permitted provided that the following conditions are |
|
| 7 |
+// met: |
|
| 8 |
+// |
|
| 9 |
+// * Redistributions of source code must retain the above copyright |
|
| 10 |
+// notice, this list of conditions and the following disclaimer. |
|
| 11 |
+// * Redistributions in binary form must reproduce the above |
|
| 12 |
+// copyright notice, this list of conditions and the following disclaimer |
|
| 13 |
+// in the documentation and/or other materials provided with the |
|
| 14 |
+// distribution. |
|
| 15 |
+// * Neither the name of Google Inc. nor the names of its |
|
| 16 |
+// contributors may be used to endorse or promote products derived from |
|
| 17 |
+// this software without specific prior written permission. |
|
| 18 |
+// |
|
| 19 |
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
| 20 |
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
| 21 |
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
| 22 |
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
| 23 |
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
| 24 |
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 25 |
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
| 26 |
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
| 27 |
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
| 28 |
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
| 29 |
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
| 30 |
+ |
|
| 31 |
+// Package grpc outputs gRPC service descriptions in Go code. |
|
| 32 |
+// It runs as a plugin for the Go protocol buffer compiler plugin. |
|
| 33 |
+// It is linked in to protoc-gen-go. |
|
| 34 |
+package grpc |
|
| 35 |
+ |
|
| 36 |
+import ( |
|
| 37 |
+ "fmt" |
|
| 38 |
+ "strconv" |
|
| 39 |
+ "strings" |
|
| 40 |
+ |
|
| 41 |
+ pb "github.com/golang/protobuf/protoc-gen-go/descriptor" |
|
| 42 |
+ "github.com/golang/protobuf/protoc-gen-go/generator" |
|
| 43 |
+) |
|
| 44 |
+ |
|
| 45 |
+// generatedCodeVersion indicates a version of the generated code. |
|
| 46 |
+// It is incremented whenever an incompatibility between the generated code and |
|
| 47 |
+// the grpc package is introduced; the generated code references |
|
| 48 |
+// a constant, grpc.SupportPackageIsVersionN (where N is generatedCodeVersion). |
|
| 49 |
+const generatedCodeVersion = 6 |
|
| 50 |
+ |
|
| 51 |
+// Paths for packages used by code generated in this file, |
|
| 52 |
+// relative to the import_prefix of the generator.Generator. |
|
| 53 |
+const ( |
|
| 54 |
+ contextPkgPath = "context" |
|
| 55 |
+ grpcPkgPath = "google.golang.org/grpc" |
|
| 56 |
+ codePkgPath = "google.golang.org/grpc/codes" |
|
| 57 |
+ statusPkgPath = "google.golang.org/grpc/status" |
|
| 58 |
+) |
|
| 59 |
+ |
|
| 60 |
+func init() {
|
|
| 61 |
+ generator.RegisterPlugin(new(grpc)) |
|
| 62 |
+} |
|
| 63 |
+ |
|
| 64 |
+// grpc is an implementation of the Go protocol buffer compiler's |
|
| 65 |
+// plugin architecture. It generates bindings for gRPC support. |
|
| 66 |
+type grpc struct {
|
|
| 67 |
+ gen *generator.Generator |
|
| 68 |
+} |
|
| 69 |
+ |
|
| 70 |
+// Name returns the name of this plugin, "grpc". |
|
| 71 |
+func (g *grpc) Name() string {
|
|
| 72 |
+ return "grpc" |
|
| 73 |
+} |
|
| 74 |
+ |
|
| 75 |
+// The names for packages imported in the generated code. |
|
| 76 |
+// They may vary from the final path component of the import path |
|
| 77 |
+// if the name is used by other packages. |
|
| 78 |
+var ( |
|
| 79 |
+ contextPkg string |
|
| 80 |
+ grpcPkg string |
|
| 81 |
+) |
|
| 82 |
+ |
|
| 83 |
+// Init initializes the plugin. |
|
| 84 |
+func (g *grpc) Init(gen *generator.Generator) {
|
|
| 85 |
+ g.gen = gen |
|
| 86 |
+} |
|
| 87 |
+ |
|
| 88 |
+// Given a type name defined in a .proto, return its object. |
|
| 89 |
+// Also record that we're using it, to guarantee the associated import. |
|
| 90 |
+func (g *grpc) objectNamed(name string) generator.Object {
|
|
| 91 |
+ g.gen.RecordTypeUse(name) |
|
| 92 |
+ return g.gen.ObjectNamed(name) |
|
| 93 |
+} |
|
| 94 |
+ |
|
| 95 |
+// Given a type name defined in a .proto, return its name as we will print it. |
|
| 96 |
+func (g *grpc) typeName(str string) string {
|
|
| 97 |
+ return g.gen.TypeName(g.objectNamed(str)) |
|
| 98 |
+} |
|
| 99 |
+ |
|
| 100 |
+// P forwards to g.gen.P. |
|
| 101 |
+func (g *grpc) P(args ...interface{}) { g.gen.P(args...) }
|
|
| 102 |
+ |
|
| 103 |
+// Generate generates code for the services in the given file. |
|
| 104 |
+func (g *grpc) Generate(file *generator.FileDescriptor) {
|
|
| 105 |
+ if len(file.FileDescriptorProto.Service) == 0 {
|
|
| 106 |
+ return |
|
| 107 |
+ } |
|
| 108 |
+ |
|
| 109 |
+ contextPkg = string(g.gen.AddImport(contextPkgPath)) |
|
| 110 |
+ grpcPkg = string(g.gen.AddImport(grpcPkgPath)) |
|
| 111 |
+ |
|
| 112 |
+ g.P("// Reference imports to suppress errors if they are not otherwise used.")
|
|
| 113 |
+ g.P("var _ ", contextPkg, ".Context")
|
|
| 114 |
+ g.P("var _ ", grpcPkg, ".ClientConnInterface")
|
|
| 115 |
+ g.P() |
|
| 116 |
+ |
|
| 117 |
+ // Assert version compatibility. |
|
| 118 |
+ g.P("// This is a compile-time assertion to ensure that this generated file")
|
|
| 119 |
+ g.P("// is compatible with the grpc package it is being compiled against.")
|
|
| 120 |
+ g.P("const _ = ", grpcPkg, ".SupportPackageIsVersion", generatedCodeVersion)
|
|
| 121 |
+ g.P() |
|
| 122 |
+ |
|
| 123 |
+ for i, service := range file.FileDescriptorProto.Service {
|
|
| 124 |
+ g.generateService(file, service, i) |
|
| 125 |
+ } |
|
| 126 |
+} |
|
| 127 |
+ |
|
| 128 |
+// GenerateImports generates the import declaration for this file. |
|
| 129 |
+func (g *grpc) GenerateImports(file *generator.FileDescriptor) {
|
|
| 130 |
+} |
|
| 131 |
+ |
|
| 132 |
+// reservedClientName records whether a client name is reserved on the client side. |
|
| 133 |
+var reservedClientName = map[string]bool{
|
|
| 134 |
+ // TODO: do we need any in gRPC? |
|
| 135 |
+} |
|
| 136 |
+ |
|
| 137 |
+func unexport(s string) string { return strings.ToLower(s[:1]) + s[1:] }
|
|
| 138 |
+ |
|
| 139 |
+// deprecationComment is the standard comment added to deprecated |
|
| 140 |
+// messages, fields, enums, and enum values. |
|
| 141 |
+var deprecationComment = "// Deprecated: Do not use." |
|
| 142 |
+ |
|
| 143 |
+// generateService generates all the code for the named service. |
|
| 144 |
+func (g *grpc) generateService(file *generator.FileDescriptor, service *pb.ServiceDescriptorProto, index int) {
|
|
| 145 |
+ path := fmt.Sprintf("6,%d", index) // 6 means service.
|
|
| 146 |
+ |
|
| 147 |
+ origServName := service.GetName() |
|
| 148 |
+ fullServName := origServName |
|
| 149 |
+ if pkg := file.GetPackage(); pkg != "" {
|
|
| 150 |
+ fullServName = pkg + "." + fullServName |
|
| 151 |
+ } |
|
| 152 |
+ servName := generator.CamelCase(origServName) |
|
| 153 |
+ deprecated := service.GetOptions().GetDeprecated() |
|
| 154 |
+ |
|
| 155 |
+ g.P() |
|
| 156 |
+ g.P(fmt.Sprintf(`// %sClient is the client API for %s service. |
|
| 157 |
+// |
|
| 158 |
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.`, servName, servName)) |
|
| 159 |
+ |
|
| 160 |
+ // Client interface. |
|
| 161 |
+ if deprecated {
|
|
| 162 |
+ g.P("//")
|
|
| 163 |
+ g.P(deprecationComment) |
|
| 164 |
+ } |
|
| 165 |
+ g.P("type ", servName, "Client interface {")
|
|
| 166 |
+ for i, method := range service.Method {
|
|
| 167 |
+ g.gen.PrintComments(fmt.Sprintf("%s,2,%d", path, i)) // 2 means method in a service.
|
|
| 168 |
+ if method.GetOptions().GetDeprecated() {
|
|
| 169 |
+ g.P("//")
|
|
| 170 |
+ g.P(deprecationComment) |
|
| 171 |
+ } |
|
| 172 |
+ g.P(g.generateClientSignature(servName, method)) |
|
| 173 |
+ } |
|
| 174 |
+ g.P("}")
|
|
| 175 |
+ g.P() |
|
| 176 |
+ |
|
| 177 |
+ // Client structure. |
|
| 178 |
+ g.P("type ", unexport(servName), "Client struct {")
|
|
| 179 |
+ g.P("cc ", grpcPkg, ".ClientConnInterface")
|
|
| 180 |
+ g.P("}")
|
|
| 181 |
+ g.P() |
|
| 182 |
+ |
|
| 183 |
+ // NewClient factory. |
|
| 184 |
+ if deprecated {
|
|
| 185 |
+ g.P(deprecationComment) |
|
| 186 |
+ } |
|
| 187 |
+ g.P("func New", servName, "Client (cc ", grpcPkg, ".ClientConnInterface) ", servName, "Client {")
|
|
| 188 |
+ g.P("return &", unexport(servName), "Client{cc}")
|
|
| 189 |
+ g.P("}")
|
|
| 190 |
+ g.P() |
|
| 191 |
+ |
|
| 192 |
+ var methodIndex, streamIndex int |
|
| 193 |
+ serviceDescVar := "_" + servName + "_serviceDesc" |
|
| 194 |
+ // Client method implementations. |
|
| 195 |
+ for _, method := range service.Method {
|
|
| 196 |
+ var descExpr string |
|
| 197 |
+ if !method.GetServerStreaming() && !method.GetClientStreaming() {
|
|
| 198 |
+ // Unary RPC method |
|
| 199 |
+ descExpr = fmt.Sprintf("&%s.Methods[%d]", serviceDescVar, methodIndex)
|
|
| 200 |
+ methodIndex++ |
|
| 201 |
+ } else {
|
|
| 202 |
+ // Streaming RPC method |
|
| 203 |
+ descExpr = fmt.Sprintf("&%s.Streams[%d]", serviceDescVar, streamIndex)
|
|
| 204 |
+ streamIndex++ |
|
| 205 |
+ } |
|
| 206 |
+ g.generateClientMethod(servName, fullServName, serviceDescVar, method, descExpr) |
|
| 207 |
+ } |
|
| 208 |
+ |
|
| 209 |
+ // Server interface. |
|
| 210 |
+ serverType := servName + "Server" |
|
| 211 |
+ g.P("// ", serverType, " is the server API for ", servName, " service.")
|
|
| 212 |
+ if deprecated {
|
|
| 213 |
+ g.P("//")
|
|
| 214 |
+ g.P(deprecationComment) |
|
| 215 |
+ } |
|
| 216 |
+ g.P("type ", serverType, " interface {")
|
|
| 217 |
+ for i, method := range service.Method {
|
|
| 218 |
+ g.gen.PrintComments(fmt.Sprintf("%s,2,%d", path, i)) // 2 means method in a service.
|
|
| 219 |
+ if method.GetOptions().GetDeprecated() {
|
|
| 220 |
+ g.P("//")
|
|
| 221 |
+ g.P(deprecationComment) |
|
| 222 |
+ } |
|
| 223 |
+ g.P(g.generateServerSignature(servName, method)) |
|
| 224 |
+ } |
|
| 225 |
+ g.P("}")
|
|
| 226 |
+ g.P() |
|
| 227 |
+ |
|
| 228 |
+ // Server Unimplemented struct for forward compatibility. |
|
| 229 |
+ if deprecated {
|
|
| 230 |
+ g.P(deprecationComment) |
|
| 231 |
+ } |
|
| 232 |
+ g.generateUnimplementedServer(servName, service) |
|
| 233 |
+ |
|
| 234 |
+ // Server registration. |
|
| 235 |
+ if deprecated {
|
|
| 236 |
+ g.P(deprecationComment) |
|
| 237 |
+ } |
|
| 238 |
+ g.P("func Register", servName, "Server(s *", grpcPkg, ".Server, srv ", serverType, ") {")
|
|
| 239 |
+ g.P("s.RegisterService(&", serviceDescVar, `, srv)`)
|
|
| 240 |
+ g.P("}")
|
|
| 241 |
+ g.P() |
|
| 242 |
+ |
|
| 243 |
+ // Server handler implementations. |
|
| 244 |
+ var handlerNames []string |
|
| 245 |
+ for _, method := range service.Method {
|
|
| 246 |
+ hname := g.generateServerMethod(servName, fullServName, method) |
|
| 247 |
+ handlerNames = append(handlerNames, hname) |
|
| 248 |
+ } |
|
| 249 |
+ |
|
| 250 |
+ // Service descriptor. |
|
| 251 |
+ g.P("var ", serviceDescVar, " = ", grpcPkg, ".ServiceDesc {")
|
|
| 252 |
+ g.P("ServiceName: ", strconv.Quote(fullServName), ",")
|
|
| 253 |
+ g.P("HandlerType: (*", serverType, ")(nil),")
|
|
| 254 |
+ g.P("Methods: []", grpcPkg, ".MethodDesc{")
|
|
| 255 |
+ for i, method := range service.Method {
|
|
| 256 |
+ if method.GetServerStreaming() || method.GetClientStreaming() {
|
|
| 257 |
+ continue |
|
| 258 |
+ } |
|
| 259 |
+ g.P("{")
|
|
| 260 |
+ g.P("MethodName: ", strconv.Quote(method.GetName()), ",")
|
|
| 261 |
+ g.P("Handler: ", handlerNames[i], ",")
|
|
| 262 |
+ g.P("},")
|
|
| 263 |
+ } |
|
| 264 |
+ g.P("},")
|
|
| 265 |
+ g.P("Streams: []", grpcPkg, ".StreamDesc{")
|
|
| 266 |
+ for i, method := range service.Method {
|
|
| 267 |
+ if !method.GetServerStreaming() && !method.GetClientStreaming() {
|
|
| 268 |
+ continue |
|
| 269 |
+ } |
|
| 270 |
+ g.P("{")
|
|
| 271 |
+ g.P("StreamName: ", strconv.Quote(method.GetName()), ",")
|
|
| 272 |
+ g.P("Handler: ", handlerNames[i], ",")
|
|
| 273 |
+ if method.GetServerStreaming() {
|
|
| 274 |
+ g.P("ServerStreams: true,")
|
|
| 275 |
+ } |
|
| 276 |
+ if method.GetClientStreaming() {
|
|
| 277 |
+ g.P("ClientStreams: true,")
|
|
| 278 |
+ } |
|
| 279 |
+ g.P("},")
|
|
| 280 |
+ } |
|
| 281 |
+ g.P("},")
|
|
| 282 |
+ g.P("Metadata: \"", file.GetName(), "\",")
|
|
| 283 |
+ g.P("}")
|
|
| 284 |
+ g.P() |
|
| 285 |
+} |
|
| 286 |
+ |
|
| 287 |
+// generateUnimplementedServer creates the unimplemented server struct |
|
| 288 |
+func (g *grpc) generateUnimplementedServer(servName string, service *pb.ServiceDescriptorProto) {
|
|
| 289 |
+ serverType := servName + "Server" |
|
| 290 |
+ g.P("// Unimplemented", serverType, " can be embedded to have forward compatible implementations.")
|
|
| 291 |
+ g.P("type Unimplemented", serverType, " struct {")
|
|
| 292 |
+ g.P("}")
|
|
| 293 |
+ g.P() |
|
| 294 |
+ // Unimplemented<service_name>Server's concrete methods |
|
| 295 |
+ for _, method := range service.Method {
|
|
| 296 |
+ g.generateServerMethodConcrete(servName, method) |
|
| 297 |
+ } |
|
| 298 |
+ g.P() |
|
| 299 |
+} |
|
| 300 |
+ |
|
| 301 |
+// generateServerMethodConcrete returns unimplemented methods which ensure forward compatibility |
|
| 302 |
+func (g *grpc) generateServerMethodConcrete(servName string, method *pb.MethodDescriptorProto) {
|
|
| 303 |
+ header := g.generateServerSignatureWithParamNames(servName, method) |
|
| 304 |
+ g.P("func (*Unimplemented", servName, "Server) ", header, " {")
|
|
| 305 |
+ var nilArg string |
|
| 306 |
+ if !method.GetServerStreaming() && !method.GetClientStreaming() {
|
|
| 307 |
+ nilArg = "nil, " |
|
| 308 |
+ } |
|
| 309 |
+ methName := generator.CamelCase(method.GetName()) |
|
| 310 |
+ statusPkg := string(g.gen.AddImport(statusPkgPath)) |
|
| 311 |
+ codePkg := string(g.gen.AddImport(codePkgPath)) |
|
| 312 |
+ g.P("return ", nilArg, statusPkg, `.Errorf(`, codePkg, `.Unimplemented, "method `, methName, ` not implemented")`)
|
|
| 313 |
+ g.P("}")
|
|
| 314 |
+} |
|
| 315 |
+ |
|
| 316 |
+// generateClientSignature returns the client-side signature for a method. |
|
| 317 |
+func (g *grpc) generateClientSignature(servName string, method *pb.MethodDescriptorProto) string {
|
|
| 318 |
+ origMethName := method.GetName() |
|
| 319 |
+ methName := generator.CamelCase(origMethName) |
|
| 320 |
+ if reservedClientName[methName] {
|
|
| 321 |
+ methName += "_" |
|
| 322 |
+ } |
|
| 323 |
+ reqArg := ", in *" + g.typeName(method.GetInputType()) |
|
| 324 |
+ if method.GetClientStreaming() {
|
|
| 325 |
+ reqArg = "" |
|
| 326 |
+ } |
|
| 327 |
+ respName := "*" + g.typeName(method.GetOutputType()) |
|
| 328 |
+ if method.GetServerStreaming() || method.GetClientStreaming() {
|
|
| 329 |
+ respName = servName + "_" + generator.CamelCase(origMethName) + "Client" |
|
| 330 |
+ } |
|
| 331 |
+ return fmt.Sprintf("%s(ctx %s.Context%s, opts ...%s.CallOption) (%s, error)", methName, contextPkg, reqArg, grpcPkg, respName)
|
|
| 332 |
+} |
|
| 333 |
+ |
|
| 334 |
+func (g *grpc) generateClientMethod(servName, fullServName, serviceDescVar string, method *pb.MethodDescriptorProto, descExpr string) {
|
|
| 335 |
+ sname := fmt.Sprintf("/%s/%s", fullServName, method.GetName())
|
|
| 336 |
+ methName := generator.CamelCase(method.GetName()) |
|
| 337 |
+ inType := g.typeName(method.GetInputType()) |
|
| 338 |
+ outType := g.typeName(method.GetOutputType()) |
|
| 339 |
+ |
|
| 340 |
+ if method.GetOptions().GetDeprecated() {
|
|
| 341 |
+ g.P(deprecationComment) |
|
| 342 |
+ } |
|
| 343 |
+ g.P("func (c *", unexport(servName), "Client) ", g.generateClientSignature(servName, method), "{")
|
|
| 344 |
+ if !method.GetServerStreaming() && !method.GetClientStreaming() {
|
|
| 345 |
+ g.P("out := new(", outType, ")")
|
|
| 346 |
+ // TODO: Pass descExpr to Invoke. |
|
| 347 |
+ g.P(`err := c.cc.Invoke(ctx, "`, sname, `", in, out, opts...)`) |
|
| 348 |
+ g.P("if err != nil { return nil, err }")
|
|
| 349 |
+ g.P("return out, nil")
|
|
| 350 |
+ g.P("}")
|
|
| 351 |
+ g.P() |
|
| 352 |
+ return |
|
| 353 |
+ } |
|
| 354 |
+ streamType := unexport(servName) + methName + "Client" |
|
| 355 |
+ g.P("stream, err := c.cc.NewStream(ctx, ", descExpr, `, "`, sname, `", opts...)`)
|
|
| 356 |
+ g.P("if err != nil { return nil, err }")
|
|
| 357 |
+ g.P("x := &", streamType, "{stream}")
|
|
| 358 |
+ if !method.GetClientStreaming() {
|
|
| 359 |
+ g.P("if err := x.ClientStream.SendMsg(in); err != nil { return nil, err }")
|
|
| 360 |
+ g.P("if err := x.ClientStream.CloseSend(); err != nil { return nil, err }")
|
|
| 361 |
+ } |
|
| 362 |
+ g.P("return x, nil")
|
|
| 363 |
+ g.P("}")
|
|
| 364 |
+ g.P() |
|
| 365 |
+ |
|
| 366 |
+ genSend := method.GetClientStreaming() |
|
| 367 |
+ genRecv := method.GetServerStreaming() |
|
| 368 |
+ genCloseAndRecv := !method.GetServerStreaming() |
|
| 369 |
+ |
|
| 370 |
+ // Stream auxiliary types and methods. |
|
| 371 |
+ g.P("type ", servName, "_", methName, "Client interface {")
|
|
| 372 |
+ if genSend {
|
|
| 373 |
+ g.P("Send(*", inType, ") error")
|
|
| 374 |
+ } |
|
| 375 |
+ if genRecv {
|
|
| 376 |
+ g.P("Recv() (*", outType, ", error)")
|
|
| 377 |
+ } |
|
| 378 |
+ if genCloseAndRecv {
|
|
| 379 |
+ g.P("CloseAndRecv() (*", outType, ", error)")
|
|
| 380 |
+ } |
|
| 381 |
+ g.P(grpcPkg, ".ClientStream") |
|
| 382 |
+ g.P("}")
|
|
| 383 |
+ g.P() |
|
| 384 |
+ |
|
| 385 |
+ g.P("type ", streamType, " struct {")
|
|
| 386 |
+ g.P(grpcPkg, ".ClientStream") |
|
| 387 |
+ g.P("}")
|
|
| 388 |
+ g.P() |
|
| 389 |
+ |
|
| 390 |
+ if genSend {
|
|
| 391 |
+ g.P("func (x *", streamType, ") Send(m *", inType, ") error {")
|
|
| 392 |
+ g.P("return x.ClientStream.SendMsg(m)")
|
|
| 393 |
+ g.P("}")
|
|
| 394 |
+ g.P() |
|
| 395 |
+ } |
|
| 396 |
+ if genRecv {
|
|
| 397 |
+ g.P("func (x *", streamType, ") Recv() (*", outType, ", error) {")
|
|
| 398 |
+ g.P("m := new(", outType, ")")
|
|
| 399 |
+ g.P("if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err }")
|
|
| 400 |
+ g.P("return m, nil")
|
|
| 401 |
+ g.P("}")
|
|
| 402 |
+ g.P() |
|
| 403 |
+ } |
|
| 404 |
+ if genCloseAndRecv {
|
|
| 405 |
+ g.P("func (x *", streamType, ") CloseAndRecv() (*", outType, ", error) {")
|
|
| 406 |
+ g.P("if err := x.ClientStream.CloseSend(); err != nil { return nil, err }")
|
|
| 407 |
+ g.P("m := new(", outType, ")")
|
|
| 408 |
+ g.P("if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err }")
|
|
| 409 |
+ g.P("return m, nil")
|
|
| 410 |
+ g.P("}")
|
|
| 411 |
+ g.P() |
|
| 412 |
+ } |
|
| 413 |
+} |
|
| 414 |
+ |
|
| 415 |
+// generateServerSignatureWithParamNames returns the server-side signature for a method with parameter names. |
|
| 416 |
+func (g *grpc) generateServerSignatureWithParamNames(servName string, method *pb.MethodDescriptorProto) string {
|
|
| 417 |
+ origMethName := method.GetName() |
|
| 418 |
+ methName := generator.CamelCase(origMethName) |
|
| 419 |
+ if reservedClientName[methName] {
|
|
| 420 |
+ methName += "_" |
|
| 421 |
+ } |
|
| 422 |
+ |
|
| 423 |
+ var reqArgs []string |
|
| 424 |
+ ret := "error" |
|
| 425 |
+ if !method.GetServerStreaming() && !method.GetClientStreaming() {
|
|
| 426 |
+ reqArgs = append(reqArgs, "ctx "+contextPkg+".Context") |
|
| 427 |
+ ret = "(*" + g.typeName(method.GetOutputType()) + ", error)" |
|
| 428 |
+ } |
|
| 429 |
+ if !method.GetClientStreaming() {
|
|
| 430 |
+ reqArgs = append(reqArgs, "req *"+g.typeName(method.GetInputType())) |
|
| 431 |
+ } |
|
| 432 |
+ if method.GetServerStreaming() || method.GetClientStreaming() {
|
|
| 433 |
+ reqArgs = append(reqArgs, "srv "+servName+"_"+generator.CamelCase(origMethName)+"Server") |
|
| 434 |
+ } |
|
| 435 |
+ |
|
| 436 |
+ return methName + "(" + strings.Join(reqArgs, ", ") + ") " + ret
|
|
| 437 |
+} |
|
| 438 |
+ |
|
| 439 |
+// generateServerSignature returns the server-side signature for a method. |
|
| 440 |
+func (g *grpc) generateServerSignature(servName string, method *pb.MethodDescriptorProto) string {
|
|
| 441 |
+ origMethName := method.GetName() |
|
| 442 |
+ methName := generator.CamelCase(origMethName) |
|
| 443 |
+ if reservedClientName[methName] {
|
|
| 444 |
+ methName += "_" |
|
| 445 |
+ } |
|
| 446 |
+ |
|
| 447 |
+ var reqArgs []string |
|
| 448 |
+ ret := "error" |
|
| 449 |
+ if !method.GetServerStreaming() && !method.GetClientStreaming() {
|
|
| 450 |
+ reqArgs = append(reqArgs, contextPkg+".Context") |
|
| 451 |
+ ret = "(*" + g.typeName(method.GetOutputType()) + ", error)" |
|
| 452 |
+ } |
|
| 453 |
+ if !method.GetClientStreaming() {
|
|
| 454 |
+ reqArgs = append(reqArgs, "*"+g.typeName(method.GetInputType())) |
|
| 455 |
+ } |
|
| 456 |
+ if method.GetServerStreaming() || method.GetClientStreaming() {
|
|
| 457 |
+ reqArgs = append(reqArgs, servName+"_"+generator.CamelCase(origMethName)+"Server") |
|
| 458 |
+ } |
|
| 459 |
+ |
|
| 460 |
+ return methName + "(" + strings.Join(reqArgs, ", ") + ") " + ret
|
|
| 461 |
+} |
|
| 462 |
+ |
|
| 463 |
+func (g *grpc) generateServerMethod(servName, fullServName string, method *pb.MethodDescriptorProto) string {
|
|
| 464 |
+ methName := generator.CamelCase(method.GetName()) |
|
| 465 |
+ hname := fmt.Sprintf("_%s_%s_Handler", servName, methName)
|
|
| 466 |
+ inType := g.typeName(method.GetInputType()) |
|
| 467 |
+ outType := g.typeName(method.GetOutputType()) |
|
| 468 |
+ |
|
| 469 |
+ if !method.GetServerStreaming() && !method.GetClientStreaming() {
|
|
| 470 |
+ g.P("func ", hname, "(srv interface{}, ctx ", contextPkg, ".Context, dec func(interface{}) error, interceptor ", grpcPkg, ".UnaryServerInterceptor) (interface{}, error) {")
|
|
| 471 |
+ g.P("in := new(", inType, ")")
|
|
| 472 |
+ g.P("if err := dec(in); err != nil { return nil, err }")
|
|
| 473 |
+ g.P("if interceptor == nil { return srv.(", servName, "Server).", methName, "(ctx, in) }")
|
|
| 474 |
+ g.P("info := &", grpcPkg, ".UnaryServerInfo{")
|
|
| 475 |
+ g.P("Server: srv,")
|
|
| 476 |
+ g.P("FullMethod: ", strconv.Quote(fmt.Sprintf("/%s/%s", fullServName, methName)), ",")
|
|
| 477 |
+ g.P("}")
|
|
| 478 |
+ g.P("handler := func(ctx ", contextPkg, ".Context, req interface{}) (interface{}, error) {")
|
|
| 479 |
+ g.P("return srv.(", servName, "Server).", methName, "(ctx, req.(*", inType, "))")
|
|
| 480 |
+ g.P("}")
|
|
| 481 |
+ g.P("return interceptor(ctx, in, info, handler)")
|
|
| 482 |
+ g.P("}")
|
|
| 483 |
+ g.P() |
|
| 484 |
+ return hname |
|
| 485 |
+ } |
|
| 486 |
+ streamType := unexport(servName) + methName + "Server" |
|
| 487 |
+ g.P("func ", hname, "(srv interface{}, stream ", grpcPkg, ".ServerStream) error {")
|
|
| 488 |
+ if !method.GetClientStreaming() {
|
|
| 489 |
+ g.P("m := new(", inType, ")")
|
|
| 490 |
+ g.P("if err := stream.RecvMsg(m); err != nil { return err }")
|
|
| 491 |
+ g.P("return srv.(", servName, "Server).", methName, "(m, &", streamType, "{stream})")
|
|
| 492 |
+ } else {
|
|
| 493 |
+ g.P("return srv.(", servName, "Server).", methName, "(&", streamType, "{stream})")
|
|
| 494 |
+ } |
|
| 495 |
+ g.P("}")
|
|
| 496 |
+ g.P() |
|
| 497 |
+ |
|
| 498 |
+ genSend := method.GetServerStreaming() |
|
| 499 |
+ genSendAndClose := !method.GetServerStreaming() |
|
| 500 |
+ genRecv := method.GetClientStreaming() |
|
| 501 |
+ |
|
| 502 |
+ // Stream auxiliary types and methods. |
|
| 503 |
+ g.P("type ", servName, "_", methName, "Server interface {")
|
|
| 504 |
+ if genSend {
|
|
| 505 |
+ g.P("Send(*", outType, ") error")
|
|
| 506 |
+ } |
|
| 507 |
+ if genSendAndClose {
|
|
| 508 |
+ g.P("SendAndClose(*", outType, ") error")
|
|
| 509 |
+ } |
|
| 510 |
+ if genRecv {
|
|
| 511 |
+ g.P("Recv() (*", inType, ", error)")
|
|
| 512 |
+ } |
|
| 513 |
+ g.P(grpcPkg, ".ServerStream") |
|
| 514 |
+ g.P("}")
|
|
| 515 |
+ g.P() |
|
| 516 |
+ |
|
| 517 |
+ g.P("type ", streamType, " struct {")
|
|
| 518 |
+ g.P(grpcPkg, ".ServerStream") |
|
| 519 |
+ g.P("}")
|
|
| 520 |
+ g.P() |
|
| 521 |
+ |
|
| 522 |
+ if genSend {
|
|
| 523 |
+ g.P("func (x *", streamType, ") Send(m *", outType, ") error {")
|
|
| 524 |
+ g.P("return x.ServerStream.SendMsg(m)")
|
|
| 525 |
+ g.P("}")
|
|
| 526 |
+ g.P() |
|
| 527 |
+ } |
|
| 528 |
+ if genSendAndClose {
|
|
| 529 |
+ g.P("func (x *", streamType, ") SendAndClose(m *", outType, ") error {")
|
|
| 530 |
+ g.P("return x.ServerStream.SendMsg(m)")
|
|
| 531 |
+ g.P("}")
|
|
| 532 |
+ g.P() |
|
| 533 |
+ } |
|
| 534 |
+ if genRecv {
|
|
| 535 |
+ g.P("func (x *", streamType, ") Recv() (*", inType, ", error) {")
|
|
| 536 |
+ g.P("m := new(", inType, ")")
|
|
| 537 |
+ g.P("if err := x.ServerStream.RecvMsg(m); err != nil { return nil, err }")
|
|
| 538 |
+ g.P("return m, nil")
|
|
| 539 |
+ g.P("}")
|
|
| 540 |
+ g.P() |
|
| 541 |
+ } |
|
| 542 |
+ |
|
| 543 |
+ return hname |
|
| 544 |
+} |
| 0 | 545 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,34 @@ |
| 0 |
+// Go support for Protocol Buffers - Google's data interchange format |
|
| 1 |
+// |
|
| 2 |
+// Copyright 2015 The Go Authors. All rights reserved. |
|
| 3 |
+// https://github.com/golang/protobuf |
|
| 4 |
+// |
|
| 5 |
+// Redistribution and use in source and binary forms, with or without |
|
| 6 |
+// modification, are permitted provided that the following conditions are |
|
| 7 |
+// met: |
|
| 8 |
+// |
|
| 9 |
+// * Redistributions of source code must retain the above copyright |
|
| 10 |
+// notice, this list of conditions and the following disclaimer. |
|
| 11 |
+// * Redistributions in binary form must reproduce the above |
|
| 12 |
+// copyright notice, this list of conditions and the following disclaimer |
|
| 13 |
+// in the documentation and/or other materials provided with the |
|
| 14 |
+// distribution. |
|
| 15 |
+// * Neither the name of Google Inc. nor the names of its |
|
| 16 |
+// contributors may be used to endorse or promote products derived from |
|
| 17 |
+// this software without specific prior written permission. |
|
| 18 |
+// |
|
| 19 |
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
| 20 |
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
| 21 |
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
| 22 |
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
| 23 |
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
| 24 |
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 25 |
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
| 26 |
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
| 27 |
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
| 28 |
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
| 29 |
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
| 30 |
+ |
|
| 31 |
+package main |
|
| 32 |
+ |
|
| 33 |
+import _ "github.com/golang/protobuf/protoc-gen-go/grpc" |
| 0 | 34 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,98 @@ |
| 0 |
+// Go support for Protocol Buffers - Google's data interchange format |
|
| 1 |
+// |
|
| 2 |
+// Copyright 2010 The Go Authors. All rights reserved. |
|
| 3 |
+// https://github.com/golang/protobuf |
|
| 4 |
+// |
|
| 5 |
+// Redistribution and use in source and binary forms, with or without |
|
| 6 |
+// modification, are permitted provided that the following conditions are |
|
| 7 |
+// met: |
|
| 8 |
+// |
|
| 9 |
+// * Redistributions of source code must retain the above copyright |
|
| 10 |
+// notice, this list of conditions and the following disclaimer. |
|
| 11 |
+// * Redistributions in binary form must reproduce the above |
|
| 12 |
+// copyright notice, this list of conditions and the following disclaimer |
|
| 13 |
+// in the documentation and/or other materials provided with the |
|
| 14 |
+// distribution. |
|
| 15 |
+// * Neither the name of Google Inc. nor the names of its |
|
| 16 |
+// contributors may be used to endorse or promote products derived from |
|
| 17 |
+// this software without specific prior written permission. |
|
| 18 |
+// |
|
| 19 |
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
| 20 |
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
| 21 |
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
| 22 |
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
| 23 |
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
| 24 |
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 25 |
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
| 26 |
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
| 27 |
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
| 28 |
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
| 29 |
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
| 30 |
+ |
|
| 31 |
+// protoc-gen-go is a plugin for the Google protocol buffer compiler to generate |
|
| 32 |
+// Go code. Run it by building this program and putting it in your path with |
|
| 33 |
+// the name |
|
| 34 |
+// protoc-gen-go |
|
| 35 |
+// That word 'go' at the end becomes part of the option string set for the |
|
| 36 |
+// protocol compiler, so once the protocol compiler (protoc) is installed |
|
| 37 |
+// you can run |
|
| 38 |
+// protoc --go_out=output_directory input_directory/file.proto |
|
| 39 |
+// to generate Go bindings for the protocol defined by file.proto. |
|
| 40 |
+// With that input, the output will be written to |
|
| 41 |
+// output_directory/file.pb.go |
|
| 42 |
+// |
|
| 43 |
+// The generated code is documented in the package comment for |
|
| 44 |
+// the library. |
|
| 45 |
+// |
|
| 46 |
+// See the README and documentation for protocol buffers to learn more: |
|
| 47 |
+// https://developers.google.com/protocol-buffers/ |
|
| 48 |
+package main |
|
| 49 |
+ |
|
| 50 |
+import ( |
|
| 51 |
+ "io/ioutil" |
|
| 52 |
+ "os" |
|
| 53 |
+ |
|
| 54 |
+ "github.com/golang/protobuf/proto" |
|
| 55 |
+ "github.com/golang/protobuf/protoc-gen-go/generator" |
|
| 56 |
+) |
|
| 57 |
+ |
|
| 58 |
+func main() {
|
|
| 59 |
+ // Begin by allocating a generator. The request and response structures are stored there |
|
| 60 |
+ // so we can do error handling easily - the response structure contains the field to |
|
| 61 |
+ // report failure. |
|
| 62 |
+ g := generator.New() |
|
| 63 |
+ |
|
| 64 |
+ data, err := ioutil.ReadAll(os.Stdin) |
|
| 65 |
+ if err != nil {
|
|
| 66 |
+ g.Error(err, "reading input") |
|
| 67 |
+ } |
|
| 68 |
+ |
|
| 69 |
+ if err := proto.Unmarshal(data, g.Request); err != nil {
|
|
| 70 |
+ g.Error(err, "parsing input proto") |
|
| 71 |
+ } |
|
| 72 |
+ |
|
| 73 |
+ if len(g.Request.FileToGenerate) == 0 {
|
|
| 74 |
+ g.Fail("no files to generate")
|
|
| 75 |
+ } |
|
| 76 |
+ |
|
| 77 |
+ g.CommandLineParameters(g.Request.GetParameter()) |
|
| 78 |
+ |
|
| 79 |
+ // Create a wrapped version of the Descriptors and EnumDescriptors that |
|
| 80 |
+ // point to the file that defines them. |
|
| 81 |
+ g.WrapTypes() |
|
| 82 |
+ |
|
| 83 |
+ g.SetPackageNames() |
|
| 84 |
+ g.BuildTypeNameMap() |
|
| 85 |
+ |
|
| 86 |
+ g.GenerateAllFiles() |
|
| 87 |
+ |
|
| 88 |
+ // Send back the results. |
|
| 89 |
+ data, err = proto.Marshal(g.Response) |
|
| 90 |
+ if err != nil {
|
|
| 91 |
+ g.Error(err, "failed to marshal output proto") |
|
| 92 |
+ } |
|
| 93 |
+ _, err = os.Stdout.Write(data) |
|
| 94 |
+ if err != nil {
|
|
| 95 |
+ g.Error(err, "failed to write output proto") |
|
| 96 |
+ } |
|
| 97 |
+} |
| 0 | 98 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,369 @@ |
| 0 |
+// Code generated by protoc-gen-go. DO NOT EDIT. |
|
| 1 |
+// source: google/protobuf/compiler/plugin.proto |
|
| 2 |
+ |
|
| 3 |
+/* |
|
| 4 |
+Package plugin_go is a generated protocol buffer package. |
|
| 5 |
+ |
|
| 6 |
+It is generated from these files: |
|
| 7 |
+ google/protobuf/compiler/plugin.proto |
|
| 8 |
+ |
|
| 9 |
+It has these top-level messages: |
|
| 10 |
+ Version |
|
| 11 |
+ CodeGeneratorRequest |
|
| 12 |
+ CodeGeneratorResponse |
|
| 13 |
+*/ |
|
| 14 |
+package plugin_go |
|
| 15 |
+ |
|
| 16 |
+import proto "github.com/golang/protobuf/proto" |
|
| 17 |
+import fmt "fmt" |
|
| 18 |
+import math "math" |
|
| 19 |
+import google_protobuf "github.com/golang/protobuf/protoc-gen-go/descriptor" |
|
| 20 |
+ |
|
| 21 |
+// Reference imports to suppress errors if they are not otherwise used. |
|
| 22 |
+var _ = proto.Marshal |
|
| 23 |
+var _ = fmt.Errorf |
|
| 24 |
+var _ = math.Inf |
|
| 25 |
+ |
|
| 26 |
+// This is a compile-time assertion to ensure that this generated file |
|
| 27 |
+// is compatible with the proto package it is being compiled against. |
|
| 28 |
+// A compilation error at this line likely means your copy of the |
|
| 29 |
+// proto package needs to be updated. |
|
| 30 |
+const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package |
|
| 31 |
+ |
|
| 32 |
+// The version number of protocol compiler. |
|
| 33 |
+type Version struct {
|
|
| 34 |
+ Major *int32 `protobuf:"varint,1,opt,name=major" json:"major,omitempty"` |
|
| 35 |
+ Minor *int32 `protobuf:"varint,2,opt,name=minor" json:"minor,omitempty"` |
|
| 36 |
+ Patch *int32 `protobuf:"varint,3,opt,name=patch" json:"patch,omitempty"` |
|
| 37 |
+ // A suffix for alpha, beta or rc release, e.g., "alpha-1", "rc2". It should |
|
| 38 |
+ // be empty for mainline stable releases. |
|
| 39 |
+ Suffix *string `protobuf:"bytes,4,opt,name=suffix" json:"suffix,omitempty"` |
|
| 40 |
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
| 41 |
+ XXX_unrecognized []byte `json:"-"` |
|
| 42 |
+ XXX_sizecache int32 `json:"-"` |
|
| 43 |
+} |
|
| 44 |
+ |
|
| 45 |
+func (m *Version) Reset() { *m = Version{} }
|
|
| 46 |
+func (m *Version) String() string { return proto.CompactTextString(m) }
|
|
| 47 |
+func (*Version) ProtoMessage() {}
|
|
| 48 |
+func (*Version) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
|
| 49 |
+func (m *Version) Unmarshal(b []byte) error {
|
|
| 50 |
+ return xxx_messageInfo_Version.Unmarshal(m, b) |
|
| 51 |
+} |
|
| 52 |
+func (m *Version) Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
| 53 |
+ return xxx_messageInfo_Version.Marshal(b, m, deterministic) |
|
| 54 |
+} |
|
| 55 |
+func (dst *Version) XXX_Merge(src proto.Message) {
|
|
| 56 |
+ xxx_messageInfo_Version.Merge(dst, src) |
|
| 57 |
+} |
|
| 58 |
+func (m *Version) XXX_Size() int {
|
|
| 59 |
+ return xxx_messageInfo_Version.Size(m) |
|
| 60 |
+} |
|
| 61 |
+func (m *Version) XXX_DiscardUnknown() {
|
|
| 62 |
+ xxx_messageInfo_Version.DiscardUnknown(m) |
|
| 63 |
+} |
|
| 64 |
+ |
|
| 65 |
+var xxx_messageInfo_Version proto.InternalMessageInfo |
|
| 66 |
+ |
|
| 67 |
+func (m *Version) GetMajor() int32 {
|
|
| 68 |
+ if m != nil && m.Major != nil {
|
|
| 69 |
+ return *m.Major |
|
| 70 |
+ } |
|
| 71 |
+ return 0 |
|
| 72 |
+} |
|
| 73 |
+ |
|
| 74 |
+func (m *Version) GetMinor() int32 {
|
|
| 75 |
+ if m != nil && m.Minor != nil {
|
|
| 76 |
+ return *m.Minor |
|
| 77 |
+ } |
|
| 78 |
+ return 0 |
|
| 79 |
+} |
|
| 80 |
+ |
|
| 81 |
+func (m *Version) GetPatch() int32 {
|
|
| 82 |
+ if m != nil && m.Patch != nil {
|
|
| 83 |
+ return *m.Patch |
|
| 84 |
+ } |
|
| 85 |
+ return 0 |
|
| 86 |
+} |
|
| 87 |
+ |
|
| 88 |
+func (m *Version) GetSuffix() string {
|
|
| 89 |
+ if m != nil && m.Suffix != nil {
|
|
| 90 |
+ return *m.Suffix |
|
| 91 |
+ } |
|
| 92 |
+ return "" |
|
| 93 |
+} |
|
| 94 |
+ |
|
| 95 |
+// An encoded CodeGeneratorRequest is written to the plugin's stdin. |
|
| 96 |
+type CodeGeneratorRequest struct {
|
|
| 97 |
+ // The .proto files that were explicitly listed on the command-line. The |
|
| 98 |
+ // code generator should generate code only for these files. Each file's |
|
| 99 |
+ // descriptor will be included in proto_file, below. |
|
| 100 |
+ FileToGenerate []string `protobuf:"bytes,1,rep,name=file_to_generate,json=fileToGenerate" json:"file_to_generate,omitempty"` |
|
| 101 |
+ // The generator parameter passed on the command-line. |
|
| 102 |
+ Parameter *string `protobuf:"bytes,2,opt,name=parameter" json:"parameter,omitempty"` |
|
| 103 |
+ // FileDescriptorProtos for all files in files_to_generate and everything |
|
| 104 |
+ // they import. The files will appear in topological order, so each file |
|
| 105 |
+ // appears before any file that imports it. |
|
| 106 |
+ // |
|
| 107 |
+ // protoc guarantees that all proto_files will be written after |
|
| 108 |
+ // the fields above, even though this is not technically guaranteed by the |
|
| 109 |
+ // protobuf wire format. This theoretically could allow a plugin to stream |
|
| 110 |
+ // in the FileDescriptorProtos and handle them one by one rather than read |
|
| 111 |
+ // the entire set into memory at once. However, as of this writing, this |
|
| 112 |
+ // is not similarly optimized on protoc's end -- it will store all fields in |
|
| 113 |
+ // memory at once before sending them to the plugin. |
|
| 114 |
+ // |
|
| 115 |
+ // Type names of fields and extensions in the FileDescriptorProto are always |
|
| 116 |
+ // fully qualified. |
|
| 117 |
+ ProtoFile []*google_protobuf.FileDescriptorProto `protobuf:"bytes,15,rep,name=proto_file,json=protoFile" json:"proto_file,omitempty"` |
|
| 118 |
+ // The version number of protocol compiler. |
|
| 119 |
+ CompilerVersion *Version `protobuf:"bytes,3,opt,name=compiler_version,json=compilerVersion" json:"compiler_version,omitempty"` |
|
| 120 |
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
| 121 |
+ XXX_unrecognized []byte `json:"-"` |
|
| 122 |
+ XXX_sizecache int32 `json:"-"` |
|
| 123 |
+} |
|
| 124 |
+ |
|
| 125 |
+func (m *CodeGeneratorRequest) Reset() { *m = CodeGeneratorRequest{} }
|
|
| 126 |
+func (m *CodeGeneratorRequest) String() string { return proto.CompactTextString(m) }
|
|
| 127 |
+func (*CodeGeneratorRequest) ProtoMessage() {}
|
|
| 128 |
+func (*CodeGeneratorRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
|
| 129 |
+func (m *CodeGeneratorRequest) Unmarshal(b []byte) error {
|
|
| 130 |
+ return xxx_messageInfo_CodeGeneratorRequest.Unmarshal(m, b) |
|
| 131 |
+} |
|
| 132 |
+func (m *CodeGeneratorRequest) Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
| 133 |
+ return xxx_messageInfo_CodeGeneratorRequest.Marshal(b, m, deterministic) |
|
| 134 |
+} |
|
| 135 |
+func (dst *CodeGeneratorRequest) XXX_Merge(src proto.Message) {
|
|
| 136 |
+ xxx_messageInfo_CodeGeneratorRequest.Merge(dst, src) |
|
| 137 |
+} |
|
| 138 |
+func (m *CodeGeneratorRequest) XXX_Size() int {
|
|
| 139 |
+ return xxx_messageInfo_CodeGeneratorRequest.Size(m) |
|
| 140 |
+} |
|
| 141 |
+func (m *CodeGeneratorRequest) XXX_DiscardUnknown() {
|
|
| 142 |
+ xxx_messageInfo_CodeGeneratorRequest.DiscardUnknown(m) |
|
| 143 |
+} |
|
| 144 |
+ |
|
| 145 |
+var xxx_messageInfo_CodeGeneratorRequest proto.InternalMessageInfo |
|
| 146 |
+ |
|
| 147 |
+func (m *CodeGeneratorRequest) GetFileToGenerate() []string {
|
|
| 148 |
+ if m != nil {
|
|
| 149 |
+ return m.FileToGenerate |
|
| 150 |
+ } |
|
| 151 |
+ return nil |
|
| 152 |
+} |
|
| 153 |
+ |
|
| 154 |
+func (m *CodeGeneratorRequest) GetParameter() string {
|
|
| 155 |
+ if m != nil && m.Parameter != nil {
|
|
| 156 |
+ return *m.Parameter |
|
| 157 |
+ } |
|
| 158 |
+ return "" |
|
| 159 |
+} |
|
| 160 |
+ |
|
| 161 |
+func (m *CodeGeneratorRequest) GetProtoFile() []*google_protobuf.FileDescriptorProto {
|
|
| 162 |
+ if m != nil {
|
|
| 163 |
+ return m.ProtoFile |
|
| 164 |
+ } |
|
| 165 |
+ return nil |
|
| 166 |
+} |
|
| 167 |
+ |
|
| 168 |
+func (m *CodeGeneratorRequest) GetCompilerVersion() *Version {
|
|
| 169 |
+ if m != nil {
|
|
| 170 |
+ return m.CompilerVersion |
|
| 171 |
+ } |
|
| 172 |
+ return nil |
|
| 173 |
+} |
|
| 174 |
+ |
|
| 175 |
+// The plugin writes an encoded CodeGeneratorResponse to stdout. |
|
| 176 |
+type CodeGeneratorResponse struct {
|
|
| 177 |
+ // Error message. If non-empty, code generation failed. The plugin process |
|
| 178 |
+ // should exit with status code zero even if it reports an error in this way. |
|
| 179 |
+ // |
|
| 180 |
+ // This should be used to indicate errors in .proto files which prevent the |
|
| 181 |
+ // code generator from generating correct code. Errors which indicate a |
|
| 182 |
+ // problem in protoc itself -- such as the input CodeGeneratorRequest being |
|
| 183 |
+ // unparseable -- should be reported by writing a message to stderr and |
|
| 184 |
+ // exiting with a non-zero status code. |
|
| 185 |
+ Error *string `protobuf:"bytes,1,opt,name=error" json:"error,omitempty"` |
|
| 186 |
+ File []*CodeGeneratorResponse_File `protobuf:"bytes,15,rep,name=file" json:"file,omitempty"` |
|
| 187 |
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
| 188 |
+ XXX_unrecognized []byte `json:"-"` |
|
| 189 |
+ XXX_sizecache int32 `json:"-"` |
|
| 190 |
+} |
|
| 191 |
+ |
|
| 192 |
+func (m *CodeGeneratorResponse) Reset() { *m = CodeGeneratorResponse{} }
|
|
| 193 |
+func (m *CodeGeneratorResponse) String() string { return proto.CompactTextString(m) }
|
|
| 194 |
+func (*CodeGeneratorResponse) ProtoMessage() {}
|
|
| 195 |
+func (*CodeGeneratorResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
|
|
| 196 |
+func (m *CodeGeneratorResponse) Unmarshal(b []byte) error {
|
|
| 197 |
+ return xxx_messageInfo_CodeGeneratorResponse.Unmarshal(m, b) |
|
| 198 |
+} |
|
| 199 |
+func (m *CodeGeneratorResponse) Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
| 200 |
+ return xxx_messageInfo_CodeGeneratorResponse.Marshal(b, m, deterministic) |
|
| 201 |
+} |
|
| 202 |
+func (dst *CodeGeneratorResponse) XXX_Merge(src proto.Message) {
|
|
| 203 |
+ xxx_messageInfo_CodeGeneratorResponse.Merge(dst, src) |
|
| 204 |
+} |
|
| 205 |
+func (m *CodeGeneratorResponse) XXX_Size() int {
|
|
| 206 |
+ return xxx_messageInfo_CodeGeneratorResponse.Size(m) |
|
| 207 |
+} |
|
| 208 |
+func (m *CodeGeneratorResponse) XXX_DiscardUnknown() {
|
|
| 209 |
+ xxx_messageInfo_CodeGeneratorResponse.DiscardUnknown(m) |
|
| 210 |
+} |
|
| 211 |
+ |
|
| 212 |
+var xxx_messageInfo_CodeGeneratorResponse proto.InternalMessageInfo |
|
| 213 |
+ |
|
| 214 |
+func (m *CodeGeneratorResponse) GetError() string {
|
|
| 215 |
+ if m != nil && m.Error != nil {
|
|
| 216 |
+ return *m.Error |
|
| 217 |
+ } |
|
| 218 |
+ return "" |
|
| 219 |
+} |
|
| 220 |
+ |
|
| 221 |
+func (m *CodeGeneratorResponse) GetFile() []*CodeGeneratorResponse_File {
|
|
| 222 |
+ if m != nil {
|
|
| 223 |
+ return m.File |
|
| 224 |
+ } |
|
| 225 |
+ return nil |
|
| 226 |
+} |
|
| 227 |
+ |
|
| 228 |
+// Represents a single generated file. |
|
| 229 |
+type CodeGeneratorResponse_File struct {
|
|
| 230 |
+ // The file name, relative to the output directory. The name must not |
|
| 231 |
+ // contain "." or ".." components and must be relative, not be absolute (so, |
|
| 232 |
+ // the file cannot lie outside the output directory). "/" must be used as |
|
| 233 |
+ // the path separator, not "\". |
|
| 234 |
+ // |
|
| 235 |
+ // If the name is omitted, the content will be appended to the previous |
|
| 236 |
+ // file. This allows the generator to break large files into small chunks, |
|
| 237 |
+ // and allows the generated text to be streamed back to protoc so that large |
|
| 238 |
+ // files need not reside completely in memory at one time. Note that as of |
|
| 239 |
+ // this writing protoc does not optimize for this -- it will read the entire |
|
| 240 |
+ // CodeGeneratorResponse before writing files to disk. |
|
| 241 |
+ Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` |
|
| 242 |
+ // If non-empty, indicates that the named file should already exist, and the |
|
| 243 |
+ // content here is to be inserted into that file at a defined insertion |
|
| 244 |
+ // point. This feature allows a code generator to extend the output |
|
| 245 |
+ // produced by another code generator. The original generator may provide |
|
| 246 |
+ // insertion points by placing special annotations in the file that look |
|
| 247 |
+ // like: |
|
| 248 |
+ // @@protoc_insertion_point(NAME) |
|
| 249 |
+ // The annotation can have arbitrary text before and after it on the line, |
|
| 250 |
+ // which allows it to be placed in a comment. NAME should be replaced with |
|
| 251 |
+ // an identifier naming the point -- this is what other generators will use |
|
| 252 |
+ // as the insertion_point. Code inserted at this point will be placed |
|
| 253 |
+ // immediately above the line containing the insertion point (thus multiple |
|
| 254 |
+ // insertions to the same point will come out in the order they were added). |
|
| 255 |
+ // The double-@ is intended to make it unlikely that the generated code |
|
| 256 |
+ // could contain things that look like insertion points by accident. |
|
| 257 |
+ // |
|
| 258 |
+ // For example, the C++ code generator places the following line in the |
|
| 259 |
+ // .pb.h files that it generates: |
|
| 260 |
+ // // @@protoc_insertion_point(namespace_scope) |
|
| 261 |
+ // This line appears within the scope of the file's package namespace, but |
|
| 262 |
+ // outside of any particular class. Another plugin can then specify the |
|
| 263 |
+ // insertion_point "namespace_scope" to generate additional classes or |
|
| 264 |
+ // other declarations that should be placed in this scope. |
|
| 265 |
+ // |
|
| 266 |
+ // Note that if the line containing the insertion point begins with |
|
| 267 |
+ // whitespace, the same whitespace will be added to every line of the |
|
| 268 |
+ // inserted text. This is useful for languages like Python, where |
|
| 269 |
+ // indentation matters. In these languages, the insertion point comment |
|
| 270 |
+ // should be indented the same amount as any inserted code will need to be |
|
| 271 |
+ // in order to work correctly in that context. |
|
| 272 |
+ // |
|
| 273 |
+ // The code generator that generates the initial file and the one which |
|
| 274 |
+ // inserts into it must both run as part of a single invocation of protoc. |
|
| 275 |
+ // Code generators are executed in the order in which they appear on the |
|
| 276 |
+ // command line. |
|
| 277 |
+ // |
|
| 278 |
+ // If |insertion_point| is present, |name| must also be present. |
|
| 279 |
+ InsertionPoint *string `protobuf:"bytes,2,opt,name=insertion_point,json=insertionPoint" json:"insertion_point,omitempty"` |
|
| 280 |
+ // The file contents. |
|
| 281 |
+ Content *string `protobuf:"bytes,15,opt,name=content" json:"content,omitempty"` |
|
| 282 |
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
| 283 |
+ XXX_unrecognized []byte `json:"-"` |
|
| 284 |
+ XXX_sizecache int32 `json:"-"` |
|
| 285 |
+} |
|
| 286 |
+ |
|
| 287 |
+func (m *CodeGeneratorResponse_File) Reset() { *m = CodeGeneratorResponse_File{} }
|
|
| 288 |
+func (m *CodeGeneratorResponse_File) String() string { return proto.CompactTextString(m) }
|
|
| 289 |
+func (*CodeGeneratorResponse_File) ProtoMessage() {}
|
|
| 290 |
+func (*CodeGeneratorResponse_File) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2, 0} }
|
|
| 291 |
+func (m *CodeGeneratorResponse_File) Unmarshal(b []byte) error {
|
|
| 292 |
+ return xxx_messageInfo_CodeGeneratorResponse_File.Unmarshal(m, b) |
|
| 293 |
+} |
|
| 294 |
+func (m *CodeGeneratorResponse_File) Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
| 295 |
+ return xxx_messageInfo_CodeGeneratorResponse_File.Marshal(b, m, deterministic) |
|
| 296 |
+} |
|
| 297 |
+func (dst *CodeGeneratorResponse_File) XXX_Merge(src proto.Message) {
|
|
| 298 |
+ xxx_messageInfo_CodeGeneratorResponse_File.Merge(dst, src) |
|
| 299 |
+} |
|
| 300 |
+func (m *CodeGeneratorResponse_File) XXX_Size() int {
|
|
| 301 |
+ return xxx_messageInfo_CodeGeneratorResponse_File.Size(m) |
|
| 302 |
+} |
|
| 303 |
+func (m *CodeGeneratorResponse_File) XXX_DiscardUnknown() {
|
|
| 304 |
+ xxx_messageInfo_CodeGeneratorResponse_File.DiscardUnknown(m) |
|
| 305 |
+} |
|
| 306 |
+ |
|
| 307 |
+var xxx_messageInfo_CodeGeneratorResponse_File proto.InternalMessageInfo |
|
| 308 |
+ |
|
| 309 |
+func (m *CodeGeneratorResponse_File) GetName() string {
|
|
| 310 |
+ if m != nil && m.Name != nil {
|
|
| 311 |
+ return *m.Name |
|
| 312 |
+ } |
|
| 313 |
+ return "" |
|
| 314 |
+} |
|
| 315 |
+ |
|
| 316 |
+func (m *CodeGeneratorResponse_File) GetInsertionPoint() string {
|
|
| 317 |
+ if m != nil && m.InsertionPoint != nil {
|
|
| 318 |
+ return *m.InsertionPoint |
|
| 319 |
+ } |
|
| 320 |
+ return "" |
|
| 321 |
+} |
|
| 322 |
+ |
|
| 323 |
+func (m *CodeGeneratorResponse_File) GetContent() string {
|
|
| 324 |
+ if m != nil && m.Content != nil {
|
|
| 325 |
+ return *m.Content |
|
| 326 |
+ } |
|
| 327 |
+ return "" |
|
| 328 |
+} |
|
| 329 |
+ |
|
| 330 |
+func init() {
|
|
| 331 |
+ proto.RegisterType((*Version)(nil), "google.protobuf.compiler.Version") |
|
| 332 |
+ proto.RegisterType((*CodeGeneratorRequest)(nil), "google.protobuf.compiler.CodeGeneratorRequest") |
|
| 333 |
+ proto.RegisterType((*CodeGeneratorResponse)(nil), "google.protobuf.compiler.CodeGeneratorResponse") |
|
| 334 |
+ proto.RegisterType((*CodeGeneratorResponse_File)(nil), "google.protobuf.compiler.CodeGeneratorResponse.File") |
|
| 335 |
+} |
|
| 336 |
+ |
|
| 337 |
+func init() { proto.RegisterFile("google/protobuf/compiler/plugin.proto", fileDescriptor0) }
|
|
| 338 |
+ |
|
| 339 |
+var fileDescriptor0 = []byte{
|
|
| 340 |
+ // 417 bytes of a gzipped FileDescriptorProto |
|
| 341 |
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xcf, 0x6a, 0x14, 0x41, |
|
| 342 |
+ 0x10, 0xc6, 0x19, 0x77, 0x63, 0x98, 0x8a, 0x64, 0x43, 0x13, 0xa5, 0x09, 0x39, 0x8c, 0x8b, 0xe2, |
|
| 343 |
+ 0x5c, 0x32, 0x0b, 0xc1, 0x8b, 0x78, 0x4b, 0x44, 0x3d, 0x78, 0x58, 0x1a, 0xf1, 0x20, 0xc8, 0x30, |
|
| 344 |
+ 0x99, 0xd4, 0x74, 0x5a, 0x66, 0xba, 0xc6, 0xee, 0x1e, 0xf1, 0x49, 0x7d, 0x0f, 0xdf, 0x40, 0xfa, |
|
| 345 |
+ 0xcf, 0x24, 0xb2, 0xb8, 0xa7, 0xee, 0xef, 0x57, 0xd5, 0xd5, 0x55, 0x1f, 0x05, 0x2f, 0x25, 0x91, |
|
| 346 |
+ 0xec, 0x71, 0x33, 0x1a, 0x72, 0x74, 0x33, 0x75, 0x9b, 0x96, 0x86, 0x51, 0xf5, 0x68, 0x36, 0x63, |
|
| 347 |
+ 0x3f, 0x49, 0xa5, 0xab, 0x10, 0x60, 0x3c, 0xa6, 0x55, 0x73, 0x5a, 0x35, 0xa7, 0x9d, 0x15, 0xbb, |
|
| 348 |
+ 0x05, 0x6e, 0xd1, 0xb6, 0x46, 0x8d, 0x8e, 0x4c, 0xcc, 0x5e, 0xb7, 0x70, 0xf8, 0x05, 0x8d, 0x55, |
|
| 349 |
+ 0xa4, 0xd9, 0x29, 0x1c, 0x0c, 0xcd, 0x77, 0x32, 0x3c, 0x2b, 0xb2, 0xf2, 0x40, 0x44, 0x11, 0xa8, |
|
| 350 |
+ 0xd2, 0x64, 0xf8, 0xa3, 0x44, 0xbd, 0xf0, 0x74, 0x6c, 0x5c, 0x7b, 0xc7, 0x17, 0x91, 0x06, 0xc1, |
|
| 351 |
+ 0x9e, 0xc1, 0x63, 0x3b, 0x75, 0x9d, 0xfa, 0xc5, 0x97, 0x45, 0x56, 0xe6, 0x22, 0xa9, 0xf5, 0x9f, |
|
| 352 |
+ 0x0c, 0x4e, 0xaf, 0xe9, 0x16, 0x3f, 0xa0, 0x46, 0xd3, 0x38, 0x32, 0x02, 0x7f, 0x4c, 0x68, 0x1d, |
|
| 353 |
+ 0x2b, 0xe1, 0xa4, 0x53, 0x3d, 0xd6, 0x8e, 0x6a, 0x19, 0x63, 0xc8, 0xb3, 0x62, 0x51, 0xe6, 0xe2, |
|
| 354 |
+ 0xd8, 0xf3, 0xcf, 0x94, 0x5e, 0x20, 0x3b, 0x87, 0x7c, 0x6c, 0x4c, 0x33, 0xa0, 0xc3, 0xd8, 0x4a, |
|
| 355 |
+ 0x2e, 0x1e, 0x00, 0xbb, 0x06, 0x08, 0xe3, 0xd4, 0xfe, 0x15, 0x5f, 0x15, 0x8b, 0xf2, 0xe8, 0xf2, |
|
| 356 |
+ 0x45, 0xb5, 0x6b, 0xcb, 0x7b, 0xd5, 0xe3, 0xbb, 0x7b, 0x03, 0xb6, 0x1e, 0x8b, 0x3c, 0x44, 0x7d, |
|
| 357 |
+ 0x84, 0x7d, 0x82, 0x93, 0xd9, 0xb8, 0xfa, 0x67, 0xf4, 0x24, 0x8c, 0x77, 0x74, 0xf9, 0xbc, 0xda, |
|
| 358 |
+ 0xe7, 0x70, 0x95, 0xcc, 0x13, 0xab, 0x99, 0x24, 0xb0, 0xfe, 0x9d, 0xc1, 0xd3, 0x9d, 0x99, 0xed, |
|
| 359 |
+ 0x48, 0xda, 0xa2, 0xf7, 0x0e, 0x8d, 0x49, 0x3e, 0xe7, 0x22, 0x0a, 0xf6, 0x11, 0x96, 0xff, 0x34, |
|
| 360 |
+ 0xff, 0x7a, 0xff, 0x8f, 0xff, 0x2d, 0x1a, 0x66, 0x13, 0xa1, 0xc2, 0xd9, 0x37, 0x58, 0x86, 0x79, |
|
| 361 |
+ 0x18, 0x2c, 0x75, 0x33, 0x60, 0xfa, 0x26, 0xdc, 0xd9, 0x2b, 0x58, 0x29, 0x6d, 0xd1, 0x38, 0x45, |
|
| 362 |
+ 0xba, 0x1e, 0x49, 0x69, 0x97, 0xcc, 0x3c, 0xbe, 0xc7, 0x5b, 0x4f, 0x19, 0x87, 0xc3, 0x96, 0xb4, |
|
| 363 |
+ 0x43, 0xed, 0xf8, 0x2a, 0x24, 0xcc, 0xf2, 0x4a, 0xc2, 0x79, 0x4b, 0xc3, 0xde, 0xfe, 0xae, 0x9e, |
|
| 364 |
+ 0x6c, 0xc3, 0x6e, 0x06, 0x7b, 0xed, 0xd7, 0x37, 0x52, 0xb9, 0xbb, 0xe9, 0xc6, 0x87, 0x37, 0x92, |
|
| 365 |
+ 0xfa, 0x46, 0xcb, 0x87, 0x65, 0x0c, 0x97, 0xf6, 0x42, 0xa2, 0xbe, 0x90, 0x94, 0x56, 0xfa, 0x6d, |
|
| 366 |
+ 0x3c, 0x6a, 0x49, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xf7, 0x15, 0x40, 0xc5, 0xfe, 0x02, 0x00, |
|
| 367 |
+ 0x00, |
|
| 368 |
+} |
| 0 | 369 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,167 @@ |
| 0 |
+// Protocol Buffers - Google's data interchange format |
|
| 1 |
+// Copyright 2008 Google Inc. All rights reserved. |
|
| 2 |
+// https://developers.google.com/protocol-buffers/ |
|
| 3 |
+// |
|
| 4 |
+// Redistribution and use in source and binary forms, with or without |
|
| 5 |
+// modification, are permitted provided that the following conditions are |
|
| 6 |
+// met: |
|
| 7 |
+// |
|
| 8 |
+// * Redistributions of source code must retain the above copyright |
|
| 9 |
+// notice, this list of conditions and the following disclaimer. |
|
| 10 |
+// * Redistributions in binary form must reproduce the above |
|
| 11 |
+// copyright notice, this list of conditions and the following disclaimer |
|
| 12 |
+// in the documentation and/or other materials provided with the |
|
| 13 |
+// distribution. |
|
| 14 |
+// * Neither the name of Google Inc. nor the names of its |
|
| 15 |
+// contributors may be used to endorse or promote products derived from |
|
| 16 |
+// this software without specific prior written permission. |
|
| 17 |
+// |
|
| 18 |
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
| 19 |
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
| 20 |
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
| 21 |
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
| 22 |
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
| 23 |
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 24 |
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
| 25 |
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
| 26 |
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
| 27 |
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
| 28 |
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
| 29 |
+ |
|
| 30 |
+// Author: kenton@google.com (Kenton Varda) |
|
| 31 |
+// |
|
| 32 |
+// WARNING: The plugin interface is currently EXPERIMENTAL and is subject to |
|
| 33 |
+// change. |
|
| 34 |
+// |
|
| 35 |
+// protoc (aka the Protocol Compiler) can be extended via plugins. A plugin is |
|
| 36 |
+// just a program that reads a CodeGeneratorRequest from stdin and writes a |
|
| 37 |
+// CodeGeneratorResponse to stdout. |
|
| 38 |
+// |
|
| 39 |
+// Plugins written using C++ can use google/protobuf/compiler/plugin.h instead |
|
| 40 |
+// of dealing with the raw protocol defined here. |
|
| 41 |
+// |
|
| 42 |
+// A plugin executable needs only to be placed somewhere in the path. The |
|
| 43 |
+// plugin should be named "protoc-gen-$NAME", and will then be used when the |
|
| 44 |
+// flag "--${NAME}_out" is passed to protoc.
|
|
| 45 |
+ |
|
| 46 |
+syntax = "proto2"; |
|
| 47 |
+package google.protobuf.compiler; |
|
| 48 |
+option java_package = "com.google.protobuf.compiler"; |
|
| 49 |
+option java_outer_classname = "PluginProtos"; |
|
| 50 |
+ |
|
| 51 |
+option go_package = "github.com/golang/protobuf/protoc-gen-go/plugin;plugin_go"; |
|
| 52 |
+ |
|
| 53 |
+import "google/protobuf/descriptor.proto"; |
|
| 54 |
+ |
|
| 55 |
+// The version number of protocol compiler. |
|
| 56 |
+message Version {
|
|
| 57 |
+ optional int32 major = 1; |
|
| 58 |
+ optional int32 minor = 2; |
|
| 59 |
+ optional int32 patch = 3; |
|
| 60 |
+ // A suffix for alpha, beta or rc release, e.g., "alpha-1", "rc2". It should |
|
| 61 |
+ // be empty for mainline stable releases. |
|
| 62 |
+ optional string suffix = 4; |
|
| 63 |
+} |
|
| 64 |
+ |
|
| 65 |
+// An encoded CodeGeneratorRequest is written to the plugin's stdin. |
|
| 66 |
+message CodeGeneratorRequest {
|
|
| 67 |
+ // The .proto files that were explicitly listed on the command-line. The |
|
| 68 |
+ // code generator should generate code only for these files. Each file's |
|
| 69 |
+ // descriptor will be included in proto_file, below. |
|
| 70 |
+ repeated string file_to_generate = 1; |
|
| 71 |
+ |
|
| 72 |
+ // The generator parameter passed on the command-line. |
|
| 73 |
+ optional string parameter = 2; |
|
| 74 |
+ |
|
| 75 |
+ // FileDescriptorProtos for all files in files_to_generate and everything |
|
| 76 |
+ // they import. The files will appear in topological order, so each file |
|
| 77 |
+ // appears before any file that imports it. |
|
| 78 |
+ // |
|
| 79 |
+ // protoc guarantees that all proto_files will be written after |
|
| 80 |
+ // the fields above, even though this is not technically guaranteed by the |
|
| 81 |
+ // protobuf wire format. This theoretically could allow a plugin to stream |
|
| 82 |
+ // in the FileDescriptorProtos and handle them one by one rather than read |
|
| 83 |
+ // the entire set into memory at once. However, as of this writing, this |
|
| 84 |
+ // is not similarly optimized on protoc's end -- it will store all fields in |
|
| 85 |
+ // memory at once before sending them to the plugin. |
|
| 86 |
+ // |
|
| 87 |
+ // Type names of fields and extensions in the FileDescriptorProto are always |
|
| 88 |
+ // fully qualified. |
|
| 89 |
+ repeated FileDescriptorProto proto_file = 15; |
|
| 90 |
+ |
|
| 91 |
+ // The version number of protocol compiler. |
|
| 92 |
+ optional Version compiler_version = 3; |
|
| 93 |
+ |
|
| 94 |
+} |
|
| 95 |
+ |
|
| 96 |
+// The plugin writes an encoded CodeGeneratorResponse to stdout. |
|
| 97 |
+message CodeGeneratorResponse {
|
|
| 98 |
+ // Error message. If non-empty, code generation failed. The plugin process |
|
| 99 |
+ // should exit with status code zero even if it reports an error in this way. |
|
| 100 |
+ // |
|
| 101 |
+ // This should be used to indicate errors in .proto files which prevent the |
|
| 102 |
+ // code generator from generating correct code. Errors which indicate a |
|
| 103 |
+ // problem in protoc itself -- such as the input CodeGeneratorRequest being |
|
| 104 |
+ // unparseable -- should be reported by writing a message to stderr and |
|
| 105 |
+ // exiting with a non-zero status code. |
|
| 106 |
+ optional string error = 1; |
|
| 107 |
+ |
|
| 108 |
+ // Represents a single generated file. |
|
| 109 |
+ message File {
|
|
| 110 |
+ // The file name, relative to the output directory. The name must not |
|
| 111 |
+ // contain "." or ".." components and must be relative, not be absolute (so, |
|
| 112 |
+ // the file cannot lie outside the output directory). "/" must be used as |
|
| 113 |
+ // the path separator, not "\". |
|
| 114 |
+ // |
|
| 115 |
+ // If the name is omitted, the content will be appended to the previous |
|
| 116 |
+ // file. This allows the generator to break large files into small chunks, |
|
| 117 |
+ // and allows the generated text to be streamed back to protoc so that large |
|
| 118 |
+ // files need not reside completely in memory at one time. Note that as of |
|
| 119 |
+ // this writing protoc does not optimize for this -- it will read the entire |
|
| 120 |
+ // CodeGeneratorResponse before writing files to disk. |
|
| 121 |
+ optional string name = 1; |
|
| 122 |
+ |
|
| 123 |
+ // If non-empty, indicates that the named file should already exist, and the |
|
| 124 |
+ // content here is to be inserted into that file at a defined insertion |
|
| 125 |
+ // point. This feature allows a code generator to extend the output |
|
| 126 |
+ // produced by another code generator. The original generator may provide |
|
| 127 |
+ // insertion points by placing special annotations in the file that look |
|
| 128 |
+ // like: |
|
| 129 |
+ // @@protoc_insertion_point(NAME) |
|
| 130 |
+ // The annotation can have arbitrary text before and after it on the line, |
|
| 131 |
+ // which allows it to be placed in a comment. NAME should be replaced with |
|
| 132 |
+ // an identifier naming the point -- this is what other generators will use |
|
| 133 |
+ // as the insertion_point. Code inserted at this point will be placed |
|
| 134 |
+ // immediately above the line containing the insertion point (thus multiple |
|
| 135 |
+ // insertions to the same point will come out in the order they were added). |
|
| 136 |
+ // The double-@ is intended to make it unlikely that the generated code |
|
| 137 |
+ // could contain things that look like insertion points by accident. |
|
| 138 |
+ // |
|
| 139 |
+ // For example, the C++ code generator places the following line in the |
|
| 140 |
+ // .pb.h files that it generates: |
|
| 141 |
+ // // @@protoc_insertion_point(namespace_scope) |
|
| 142 |
+ // This line appears within the scope of the file's package namespace, but |
|
| 143 |
+ // outside of any particular class. Another plugin can then specify the |
|
| 144 |
+ // insertion_point "namespace_scope" to generate additional classes or |
|
| 145 |
+ // other declarations that should be placed in this scope. |
|
| 146 |
+ // |
|
| 147 |
+ // Note that if the line containing the insertion point begins with |
|
| 148 |
+ // whitespace, the same whitespace will be added to every line of the |
|
| 149 |
+ // inserted text. This is useful for languages like Python, where |
|
| 150 |
+ // indentation matters. In these languages, the insertion point comment |
|
| 151 |
+ // should be indented the same amount as any inserted code will need to be |
|
| 152 |
+ // in order to work correctly in that context. |
|
| 153 |
+ // |
|
| 154 |
+ // The code generator that generates the initial file and the one which |
|
| 155 |
+ // inserts into it must both run as part of a single invocation of protoc. |
|
| 156 |
+ // Code generators are executed in the order in which they appear on the |
|
| 157 |
+ // command line. |
|
| 158 |
+ // |
|
| 159 |
+ // If |insertion_point| is present, |name| must also be present. |
|
| 160 |
+ optional string insertion_point = 2; |
|
| 161 |
+ |
|
| 162 |
+ // The file contents. |
|
| 163 |
+ optional string content = 15; |
|
| 164 |
+ } |
|
| 165 |
+ repeated File file = 15; |
|
| 166 |
+} |
| ... | ... |
@@ -1,19 +1,22 @@ |
| 1 | 1 |
Google API Extensions for Go |
| 2 | 2 |
============================ |
| 3 | 3 |
|
| 4 |
-[](https://travis-ci.org/googleapis/gax-go) |
|
| 5 |
-[](https://codecov.io/github/googleapis/gax-go) |
|
| 4 |
+[](https://godoc.org/github.com/googleapis/gax-go) |
|
| 6 | 5 |
|
| 7 | 6 |
Google API Extensions for Go (gax-go) is a set of modules which aids the |
| 8 | 7 |
development of APIs for clients and servers based on `gRPC` and Google API |
| 9 | 8 |
conventions. |
| 10 | 9 |
|
| 11 |
-Application code will rarely need to use this library directly, |
|
| 10 |
+To install the API extensions, use: |
|
| 11 |
+ |
|
| 12 |
+``` |
|
| 13 |
+go get -u github.com/googleapis/gax-go |
|
| 14 |
+``` |
|
| 15 |
+ |
|
| 16 |
+**Note:** Application code will rarely need to use this library directly, |
|
| 12 | 17 |
but the code generated automatically from API definition files can use it |
| 13 | 18 |
to simplify code generation and to provide more convenient and idiomatic API surface. |
| 14 | 19 |
|
| 15 |
-**This project is currently experimental and not supported.** |
|
| 16 |
- |
|
| 17 | 20 |
Go Versions |
| 18 | 21 |
=========== |
| 19 | 22 |
This library requires Go 1.6 or above. |
| 20 | 23 |
deleted file mode 100644 |
| ... | ... |
@@ -1,157 +0,0 @@ |
| 1 |
-// Copyright 2016, Google Inc. |
|
| 2 |
-// All rights reserved. |
|
| 3 |
-// |
|
| 4 |
-// Redistribution and use in source and binary forms, with or without |
|
| 5 |
-// modification, are permitted provided that the following conditions are |
|
| 6 |
-// met: |
|
| 7 |
-// |
|
| 8 |
-// * Redistributions of source code must retain the above copyright |
|
| 9 |
-// notice, this list of conditions and the following disclaimer. |
|
| 10 |
-// * Redistributions in binary form must reproduce the above |
|
| 11 |
-// copyright notice, this list of conditions and the following disclaimer |
|
| 12 |
-// in the documentation and/or other materials provided with the |
|
| 13 |
-// distribution. |
|
| 14 |
-// * Neither the name of Google Inc. nor the names of its |
|
| 15 |
-// contributors may be used to endorse or promote products derived from |
|
| 16 |
-// this software without specific prior written permission. |
|
| 17 |
-// |
|
| 18 |
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
| 19 |
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
| 20 |
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
| 21 |
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
| 22 |
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
| 23 |
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 24 |
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
| 25 |
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
| 26 |
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
| 27 |
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
| 28 |
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
| 29 |
- |
|
| 30 |
-package gax |
|
| 31 |
- |
|
| 32 |
-import ( |
|
| 33 |
- "math/rand" |
|
| 34 |
- "time" |
|
| 35 |
- |
|
| 36 |
- "google.golang.org/grpc" |
|
| 37 |
- "google.golang.org/grpc/codes" |
|
| 38 |
- "google.golang.org/grpc/status" |
|
| 39 |
-) |
|
| 40 |
- |
|
| 41 |
-// CallOption is an option used by Invoke to control behaviors of RPC calls. |
|
| 42 |
-// CallOption works by modifying relevant fields of CallSettings. |
|
| 43 |
-type CallOption interface {
|
|
| 44 |
- // Resolve applies the option by modifying cs. |
|
| 45 |
- Resolve(cs *CallSettings) |
|
| 46 |
-} |
|
| 47 |
- |
|
| 48 |
-// Retryer is used by Invoke to determine retry behavior. |
|
| 49 |
-type Retryer interface {
|
|
| 50 |
- // Retry reports whether a request should be retriedand how long to pause before retrying |
|
| 51 |
- // if the previous attempt returned with err. Invoke never calls Retry with nil error. |
|
| 52 |
- Retry(err error) (pause time.Duration, shouldRetry bool) |
|
| 53 |
-} |
|
| 54 |
- |
|
| 55 |
-type retryerOption func() Retryer |
|
| 56 |
- |
|
| 57 |
-func (o retryerOption) Resolve(s *CallSettings) {
|
|
| 58 |
- s.Retry = o |
|
| 59 |
-} |
|
| 60 |
- |
|
| 61 |
-// WithRetry sets CallSettings.Retry to fn. |
|
| 62 |
-func WithRetry(fn func() Retryer) CallOption {
|
|
| 63 |
- return retryerOption(fn) |
|
| 64 |
-} |
|
| 65 |
- |
|
| 66 |
-// OnCodes returns a Retryer that retries if and only if |
|
| 67 |
-// the previous attempt returns a GRPC error whose error code is stored in cc. |
|
| 68 |
-// Pause times between retries are specified by bo. |
|
| 69 |
-// |
|
| 70 |
-// bo is only used for its parameters; each Retryer has its own copy. |
|
| 71 |
-func OnCodes(cc []codes.Code, bo Backoff) Retryer {
|
|
| 72 |
- return &boRetryer{
|
|
| 73 |
- backoff: bo, |
|
| 74 |
- codes: append([]codes.Code(nil), cc...), |
|
| 75 |
- } |
|
| 76 |
-} |
|
| 77 |
- |
|
| 78 |
-type boRetryer struct {
|
|
| 79 |
- backoff Backoff |
|
| 80 |
- codes []codes.Code |
|
| 81 |
-} |
|
| 82 |
- |
|
| 83 |
-func (r *boRetryer) Retry(err error) (time.Duration, bool) {
|
|
| 84 |
- st, ok := status.FromError(err) |
|
| 85 |
- if !ok {
|
|
| 86 |
- return 0, false |
|
| 87 |
- } |
|
| 88 |
- c := st.Code() |
|
| 89 |
- for _, rc := range r.codes {
|
|
| 90 |
- if c == rc {
|
|
| 91 |
- return r.backoff.Pause(), true |
|
| 92 |
- } |
|
| 93 |
- } |
|
| 94 |
- return 0, false |
|
| 95 |
-} |
|
| 96 |
- |
|
| 97 |
-// Backoff implements exponential backoff. |
|
| 98 |
-// The wait time between retries is a random value between 0 and the "retry envelope". |
|
| 99 |
-// The envelope starts at Initial and increases by the factor of Multiplier every retry, |
|
| 100 |
-// but is capped at Max. |
|
| 101 |
-type Backoff struct {
|
|
| 102 |
- // Initial is the initial value of the retry envelope, defaults to 1 second. |
|
| 103 |
- Initial time.Duration |
|
| 104 |
- |
|
| 105 |
- // Max is the maximum value of the retry envelope, defaults to 30 seconds. |
|
| 106 |
- Max time.Duration |
|
| 107 |
- |
|
| 108 |
- // Multiplier is the factor by which the retry envelope increases. |
|
| 109 |
- // It should be greater than 1 and defaults to 2. |
|
| 110 |
- Multiplier float64 |
|
| 111 |
- |
|
| 112 |
- // cur is the current retry envelope |
|
| 113 |
- cur time.Duration |
|
| 114 |
-} |
|
| 115 |
- |
|
| 116 |
-func (bo *Backoff) Pause() time.Duration {
|
|
| 117 |
- if bo.Initial == 0 {
|
|
| 118 |
- bo.Initial = time.Second |
|
| 119 |
- } |
|
| 120 |
- if bo.cur == 0 {
|
|
| 121 |
- bo.cur = bo.Initial |
|
| 122 |
- } |
|
| 123 |
- if bo.Max == 0 {
|
|
| 124 |
- bo.Max = 30 * time.Second |
|
| 125 |
- } |
|
| 126 |
- if bo.Multiplier < 1 {
|
|
| 127 |
- bo.Multiplier = 2 |
|
| 128 |
- } |
|
| 129 |
- // Select a duration between zero and the current max. It might seem counterintuitive to |
|
| 130 |
- // have so much jitter, but https://www.awsarchitectureblog.com/2015/03/backoff.html |
|
| 131 |
- // argues that that is the best strategy. |
|
| 132 |
- d := time.Duration(rand.Int63n(int64(bo.cur))) |
|
| 133 |
- bo.cur = time.Duration(float64(bo.cur) * bo.Multiplier) |
|
| 134 |
- if bo.cur > bo.Max {
|
|
| 135 |
- bo.cur = bo.Max |
|
| 136 |
- } |
|
| 137 |
- return d |
|
| 138 |
-} |
|
| 139 |
- |
|
| 140 |
-type grpcOpt []grpc.CallOption |
|
| 141 |
- |
|
| 142 |
-func (o grpcOpt) Resolve(s *CallSettings) {
|
|
| 143 |
- s.GRPC = o |
|
| 144 |
-} |
|
| 145 |
- |
|
| 146 |
-func WithGRPCOptions(opt ...grpc.CallOption) CallOption {
|
|
| 147 |
- return grpcOpt(append([]grpc.CallOption(nil), opt...)) |
|
| 148 |
-} |
|
| 149 |
- |
|
| 150 |
-type CallSettings struct {
|
|
| 151 |
- // Retry returns a Retryer to be used to control retry logic of a method call. |
|
| 152 |
- // If Retry is nil or the returned Retryer is nil, the call will not be retried. |
|
| 153 |
- Retry func() Retryer |
|
| 154 |
- |
|
| 155 |
- // CallOptions to be forwarded to GRPC. |
|
| 156 |
- GRPC []grpc.CallOption |
|
| 157 |
-} |
| 158 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,40 +0,0 @@ |
| 1 |
-// Copyright 2016, Google Inc. |
|
| 2 |
-// All rights reserved. |
|
| 3 |
-// |
|
| 4 |
-// Redistribution and use in source and binary forms, with or without |
|
| 5 |
-// modification, are permitted provided that the following conditions are |
|
| 6 |
-// met: |
|
| 7 |
-// |
|
| 8 |
-// * Redistributions of source code must retain the above copyright |
|
| 9 |
-// notice, this list of conditions and the following disclaimer. |
|
| 10 |
-// * Redistributions in binary form must reproduce the above |
|
| 11 |
-// copyright notice, this list of conditions and the following disclaimer |
|
| 12 |
-// in the documentation and/or other materials provided with the |
|
| 13 |
-// distribution. |
|
| 14 |
-// * Neither the name of Google Inc. nor the names of its |
|
| 15 |
-// contributors may be used to endorse or promote products derived from |
|
| 16 |
-// this software without specific prior written permission. |
|
| 17 |
-// |
|
| 18 |
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
| 19 |
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
| 20 |
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
| 21 |
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
| 22 |
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
| 23 |
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 24 |
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
| 25 |
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
| 26 |
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
| 27 |
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
| 28 |
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
| 29 |
- |
|
| 30 |
-// Package gax contains a set of modules which aid the development of APIs |
|
| 31 |
-// for clients and servers based on gRPC and Google API conventions. |
|
| 32 |
-// |
|
| 33 |
-// Application code will rarely need to use this library directly. |
|
| 34 |
-// However, code generated automatically from API definition files can use it |
|
| 35 |
-// to simplify code generation and to provide more convenient and idiomatic API surfaces. |
|
| 36 |
-// |
|
| 37 |
-// This project is currently experimental and not supported. |
|
| 38 |
-package gax |
|
| 39 |
- |
|
| 40 |
-const Version = "0.1.0" |
| 41 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,11 @@ |
| 0 |
+module github.com/googleapis/gax-go |
|
| 1 |
+ |
|
| 2 |
+require ( |
|
| 3 |
+ github.com/golang/protobuf v1.3.1 |
|
| 4 |
+ github.com/googleapis/gax-go/v2 v2.0.2 |
|
| 5 |
+ golang.org/x/exp v0.0.0-20190221220918-438050ddec5e |
|
| 6 |
+ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3 |
|
| 7 |
+ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b |
|
| 8 |
+ google.golang.org/grpc v1.19.0 |
|
| 9 |
+ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099 |
|
| 10 |
+) |
| 0 | 11 |
deleted file mode 100644 |
| ... | ... |
@@ -1,24 +0,0 @@ |
| 1 |
-package gax |
|
| 2 |
- |
|
| 3 |
-import "bytes" |
|
| 4 |
- |
|
| 5 |
-// XGoogHeader is for use by the Google Cloud Libraries only. |
|
| 6 |
-// |
|
| 7 |
-// XGoogHeader formats key-value pairs. |
|
| 8 |
-// The resulting string is suitable for x-goog-api-client header. |
|
| 9 |
-func XGoogHeader(keyval ...string) string {
|
|
| 10 |
- if len(keyval) == 0 {
|
|
| 11 |
- return "" |
|
| 12 |
- } |
|
| 13 |
- if len(keyval)%2 != 0 {
|
|
| 14 |
- panic("gax.Header: odd argument count")
|
|
| 15 |
- } |
|
| 16 |
- var buf bytes.Buffer |
|
| 17 |
- for i := 0; i < len(keyval); i += 2 {
|
|
| 18 |
- buf.WriteByte(' ')
|
|
| 19 |
- buf.WriteString(keyval[i]) |
|
| 20 |
- buf.WriteByte('/')
|
|
| 21 |
- buf.WriteString(keyval[i+1]) |
|
| 22 |
- } |
|
| 23 |
- return buf.String()[1:] |
|
| 24 |
-} |
| 25 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,90 +0,0 @@ |
| 1 |
-// Copyright 2016, Google Inc. |
|
| 2 |
-// All rights reserved. |
|
| 3 |
-// |
|
| 4 |
-// Redistribution and use in source and binary forms, with or without |
|
| 5 |
-// modification, are permitted provided that the following conditions are |
|
| 6 |
-// met: |
|
| 7 |
-// |
|
| 8 |
-// * Redistributions of source code must retain the above copyright |
|
| 9 |
-// notice, this list of conditions and the following disclaimer. |
|
| 10 |
-// * Redistributions in binary form must reproduce the above |
|
| 11 |
-// copyright notice, this list of conditions and the following disclaimer |
|
| 12 |
-// in the documentation and/or other materials provided with the |
|
| 13 |
-// distribution. |
|
| 14 |
-// * Neither the name of Google Inc. nor the names of its |
|
| 15 |
-// contributors may be used to endorse or promote products derived from |
|
| 16 |
-// this software without specific prior written permission. |
|
| 17 |
-// |
|
| 18 |
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
| 19 |
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
| 20 |
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
| 21 |
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
| 22 |
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
| 23 |
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 24 |
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
| 25 |
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
| 26 |
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
| 27 |
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
| 28 |
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
| 29 |
- |
|
| 30 |
-package gax |
|
| 31 |
- |
|
| 32 |
-import ( |
|
| 33 |
- "time" |
|
| 34 |
- |
|
| 35 |
- "golang.org/x/net/context" |
|
| 36 |
-) |
|
| 37 |
- |
|
| 38 |
-// A user defined call stub. |
|
| 39 |
-type APICall func(context.Context, CallSettings) error |
|
| 40 |
- |
|
| 41 |
-// Invoke calls the given APICall, |
|
| 42 |
-// performing retries as specified by opts, if any. |
|
| 43 |
-func Invoke(ctx context.Context, call APICall, opts ...CallOption) error {
|
|
| 44 |
- var settings CallSettings |
|
| 45 |
- for _, opt := range opts {
|
|
| 46 |
- opt.Resolve(&settings) |
|
| 47 |
- } |
|
| 48 |
- return invoke(ctx, call, settings, Sleep) |
|
| 49 |
-} |
|
| 50 |
- |
|
| 51 |
-// Sleep is similar to time.Sleep, but it can be interrupted by ctx.Done() closing. |
|
| 52 |
-// If interrupted, Sleep returns ctx.Err(). |
|
| 53 |
-func Sleep(ctx context.Context, d time.Duration) error {
|
|
| 54 |
- t := time.NewTimer(d) |
|
| 55 |
- select {
|
|
| 56 |
- case <-ctx.Done(): |
|
| 57 |
- t.Stop() |
|
| 58 |
- return ctx.Err() |
|
| 59 |
- case <-t.C: |
|
| 60 |
- return nil |
|
| 61 |
- } |
|
| 62 |
-} |
|
| 63 |
- |
|
| 64 |
-type sleeper func(ctx context.Context, d time.Duration) error |
|
| 65 |
- |
|
| 66 |
-// invoke implements Invoke, taking an additional sleeper argument for testing. |
|
| 67 |
-func invoke(ctx context.Context, call APICall, settings CallSettings, sp sleeper) error {
|
|
| 68 |
- var retryer Retryer |
|
| 69 |
- for {
|
|
| 70 |
- err := call(ctx, settings) |
|
| 71 |
- if err == nil {
|
|
| 72 |
- return nil |
|
| 73 |
- } |
|
| 74 |
- if settings.Retry == nil {
|
|
| 75 |
- return err |
|
| 76 |
- } |
|
| 77 |
- if retryer == nil {
|
|
| 78 |
- if r := settings.Retry(); r != nil {
|
|
| 79 |
- retryer = r |
|
| 80 |
- } else {
|
|
| 81 |
- return err |
|
| 82 |
- } |
|
| 83 |
- } |
|
| 84 |
- if d, ok := retryer.Retry(err); !ok {
|
|
| 85 |
- return err |
|
| 86 |
- } else if err = sp(ctx, d); err != nil {
|
|
| 87 |
- return err |
|
| 88 |
- } |
|
| 89 |
- } |
|
| 90 |
-} |
| 91 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,161 @@ |
| 0 |
+// Copyright 2016, Google Inc. |
|
| 1 |
+// All rights reserved. |
|
| 2 |
+// |
|
| 3 |
+// Redistribution and use in source and binary forms, with or without |
|
| 4 |
+// modification, are permitted provided that the following conditions are |
|
| 5 |
+// met: |
|
| 6 |
+// |
|
| 7 |
+// * Redistributions of source code must retain the above copyright |
|
| 8 |
+// notice, this list of conditions and the following disclaimer. |
|
| 9 |
+// * Redistributions in binary form must reproduce the above |
|
| 10 |
+// copyright notice, this list of conditions and the following disclaimer |
|
| 11 |
+// in the documentation and/or other materials provided with the |
|
| 12 |
+// distribution. |
|
| 13 |
+// * Neither the name of Google Inc. nor the names of its |
|
| 14 |
+// contributors may be used to endorse or promote products derived from |
|
| 15 |
+// this software without specific prior written permission. |
|
| 16 |
+// |
|
| 17 |
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
| 18 |
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
| 19 |
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
| 20 |
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
| 21 |
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
| 22 |
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 23 |
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
| 24 |
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
| 25 |
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
| 26 |
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
| 27 |
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
| 28 |
+ |
|
| 29 |
+package gax |
|
| 30 |
+ |
|
| 31 |
+import ( |
|
| 32 |
+ "math/rand" |
|
| 33 |
+ "time" |
|
| 34 |
+ |
|
| 35 |
+ "google.golang.org/grpc" |
|
| 36 |
+ "google.golang.org/grpc/codes" |
|
| 37 |
+ "google.golang.org/grpc/status" |
|
| 38 |
+) |
|
| 39 |
+ |
|
| 40 |
+// CallOption is an option used by Invoke to control behaviors of RPC calls. |
|
| 41 |
+// CallOption works by modifying relevant fields of CallSettings. |
|
| 42 |
+type CallOption interface {
|
|
| 43 |
+ // Resolve applies the option by modifying cs. |
|
| 44 |
+ Resolve(cs *CallSettings) |
|
| 45 |
+} |
|
| 46 |
+ |
|
| 47 |
+// Retryer is used by Invoke to determine retry behavior. |
|
| 48 |
+type Retryer interface {
|
|
| 49 |
+ // Retry reports whether a request should be retriedand how long to pause before retrying |
|
| 50 |
+ // if the previous attempt returned with err. Invoke never calls Retry with nil error. |
|
| 51 |
+ Retry(err error) (pause time.Duration, shouldRetry bool) |
|
| 52 |
+} |
|
| 53 |
+ |
|
| 54 |
+type retryerOption func() Retryer |
|
| 55 |
+ |
|
| 56 |
+func (o retryerOption) Resolve(s *CallSettings) {
|
|
| 57 |
+ s.Retry = o |
|
| 58 |
+} |
|
| 59 |
+ |
|
| 60 |
+// WithRetry sets CallSettings.Retry to fn. |
|
| 61 |
+func WithRetry(fn func() Retryer) CallOption {
|
|
| 62 |
+ return retryerOption(fn) |
|
| 63 |
+} |
|
| 64 |
+ |
|
| 65 |
+// OnCodes returns a Retryer that retries if and only if |
|
| 66 |
+// the previous attempt returns a GRPC error whose error code is stored in cc. |
|
| 67 |
+// Pause times between retries are specified by bo. |
|
| 68 |
+// |
|
| 69 |
+// bo is only used for its parameters; each Retryer has its own copy. |
|
| 70 |
+func OnCodes(cc []codes.Code, bo Backoff) Retryer {
|
|
| 71 |
+ return &boRetryer{
|
|
| 72 |
+ backoff: bo, |
|
| 73 |
+ codes: append([]codes.Code(nil), cc...), |
|
| 74 |
+ } |
|
| 75 |
+} |
|
| 76 |
+ |
|
| 77 |
+type boRetryer struct {
|
|
| 78 |
+ backoff Backoff |
|
| 79 |
+ codes []codes.Code |
|
| 80 |
+} |
|
| 81 |
+ |
|
| 82 |
+func (r *boRetryer) Retry(err error) (time.Duration, bool) {
|
|
| 83 |
+ st, ok := status.FromError(err) |
|
| 84 |
+ if !ok {
|
|
| 85 |
+ return 0, false |
|
| 86 |
+ } |
|
| 87 |
+ c := st.Code() |
|
| 88 |
+ for _, rc := range r.codes {
|
|
| 89 |
+ if c == rc {
|
|
| 90 |
+ return r.backoff.Pause(), true |
|
| 91 |
+ } |
|
| 92 |
+ } |
|
| 93 |
+ return 0, false |
|
| 94 |
+} |
|
| 95 |
+ |
|
| 96 |
+// Backoff implements exponential backoff. |
|
| 97 |
+// The wait time between retries is a random value between 0 and the "retry envelope". |
|
| 98 |
+// The envelope starts at Initial and increases by the factor of Multiplier every retry, |
|
| 99 |
+// but is capped at Max. |
|
| 100 |
+type Backoff struct {
|
|
| 101 |
+ // Initial is the initial value of the retry envelope, defaults to 1 second. |
|
| 102 |
+ Initial time.Duration |
|
| 103 |
+ |
|
| 104 |
+ // Max is the maximum value of the retry envelope, defaults to 30 seconds. |
|
| 105 |
+ Max time.Duration |
|
| 106 |
+ |
|
| 107 |
+ // Multiplier is the factor by which the retry envelope increases. |
|
| 108 |
+ // It should be greater than 1 and defaults to 2. |
|
| 109 |
+ Multiplier float64 |
|
| 110 |
+ |
|
| 111 |
+ // cur is the current retry envelope |
|
| 112 |
+ cur time.Duration |
|
| 113 |
+} |
|
| 114 |
+ |
|
| 115 |
+// Pause returns the next time.Duration that the caller should use to backoff. |
|
| 116 |
+func (bo *Backoff) Pause() time.Duration {
|
|
| 117 |
+ if bo.Initial == 0 {
|
|
| 118 |
+ bo.Initial = time.Second |
|
| 119 |
+ } |
|
| 120 |
+ if bo.cur == 0 {
|
|
| 121 |
+ bo.cur = bo.Initial |
|
| 122 |
+ } |
|
| 123 |
+ if bo.Max == 0 {
|
|
| 124 |
+ bo.Max = 30 * time.Second |
|
| 125 |
+ } |
|
| 126 |
+ if bo.Multiplier < 1 {
|
|
| 127 |
+ bo.Multiplier = 2 |
|
| 128 |
+ } |
|
| 129 |
+ // Select a duration between 1ns and the current max. It might seem |
|
| 130 |
+ // counterintuitive to have so much jitter, but |
|
| 131 |
+ // https://www.awsarchitectureblog.com/2015/03/backoff.html argues that |
|
| 132 |
+ // that is the best strategy. |
|
| 133 |
+ d := time.Duration(1 + rand.Int63n(int64(bo.cur))) |
|
| 134 |
+ bo.cur = time.Duration(float64(bo.cur) * bo.Multiplier) |
|
| 135 |
+ if bo.cur > bo.Max {
|
|
| 136 |
+ bo.cur = bo.Max |
|
| 137 |
+ } |
|
| 138 |
+ return d |
|
| 139 |
+} |
|
| 140 |
+ |
|
| 141 |
+type grpcOpt []grpc.CallOption |
|
| 142 |
+ |
|
| 143 |
+func (o grpcOpt) Resolve(s *CallSettings) {
|
|
| 144 |
+ s.GRPC = o |
|
| 145 |
+} |
|
| 146 |
+ |
|
| 147 |
+// WithGRPCOptions allows passing gRPC call options during client creation. |
|
| 148 |
+func WithGRPCOptions(opt ...grpc.CallOption) CallOption {
|
|
| 149 |
+ return grpcOpt(append([]grpc.CallOption(nil), opt...)) |
|
| 150 |
+} |
|
| 151 |
+ |
|
| 152 |
+// CallSettings allow fine-grained control over how calls are made. |
|
| 153 |
+type CallSettings struct {
|
|
| 154 |
+ // Retry returns a Retryer to be used to control retry logic of a method call. |
|
| 155 |
+ // If Retry is nil or the returned Retryer is nil, the call will not be retried. |
|
| 156 |
+ Retry func() Retryer |
|
| 157 |
+ |
|
| 158 |
+ // CallOptions to be forwarded to GRPC. |
|
| 159 |
+ GRPC []grpc.CallOption |
|
| 160 |
+} |
| 0 | 161 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,39 @@ |
| 0 |
+// Copyright 2016, Google Inc. |
|
| 1 |
+// All rights reserved. |
|
| 2 |
+// |
|
| 3 |
+// Redistribution and use in source and binary forms, with or without |
|
| 4 |
+// modification, are permitted provided that the following conditions are |
|
| 5 |
+// met: |
|
| 6 |
+// |
|
| 7 |
+// * Redistributions of source code must retain the above copyright |
|
| 8 |
+// notice, this list of conditions and the following disclaimer. |
|
| 9 |
+// * Redistributions in binary form must reproduce the above |
|
| 10 |
+// copyright notice, this list of conditions and the following disclaimer |
|
| 11 |
+// in the documentation and/or other materials provided with the |
|
| 12 |
+// distribution. |
|
| 13 |
+// * Neither the name of Google Inc. nor the names of its |
|
| 14 |
+// contributors may be used to endorse or promote products derived from |
|
| 15 |
+// this software without specific prior written permission. |
|
| 16 |
+// |
|
| 17 |
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
| 18 |
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
| 19 |
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
| 20 |
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
| 21 |
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
| 22 |
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 23 |
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
| 24 |
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
| 25 |
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
| 26 |
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
| 27 |
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
| 28 |
+ |
|
| 29 |
+// Package gax contains a set of modules which aid the development of APIs |
|
| 30 |
+// for clients and servers based on gRPC and Google API conventions. |
|
| 31 |
+// |
|
| 32 |
+// Application code will rarely need to use this library directly. |
|
| 33 |
+// However, code generated automatically from API definition files can use it |
|
| 34 |
+// to simplify code generation and to provide more convenient and idiomatic API surfaces. |
|
| 35 |
+package gax |
|
| 36 |
+ |
|
| 37 |
+// Version specifies the gax-go version being used. |
|
| 38 |
+const Version = "2.0.4" |
| 0 | 3 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,53 @@ |
| 0 |
+// Copyright 2018, Google Inc. |
|
| 1 |
+// All rights reserved. |
|
| 2 |
+// |
|
| 3 |
+// Redistribution and use in source and binary forms, with or without |
|
| 4 |
+// modification, are permitted provided that the following conditions are |
|
| 5 |
+// met: |
|
| 6 |
+// |
|
| 7 |
+// * Redistributions of source code must retain the above copyright |
|
| 8 |
+// notice, this list of conditions and the following disclaimer. |
|
| 9 |
+// * Redistributions in binary form must reproduce the above |
|
| 10 |
+// copyright notice, this list of conditions and the following disclaimer |
|
| 11 |
+// in the documentation and/or other materials provided with the |
|
| 12 |
+// distribution. |
|
| 13 |
+// * Neither the name of Google Inc. nor the names of its |
|
| 14 |
+// contributors may be used to endorse or promote products derived from |
|
| 15 |
+// this software without specific prior written permission. |
|
| 16 |
+// |
|
| 17 |
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
| 18 |
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
| 19 |
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
| 20 |
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
| 21 |
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
| 22 |
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 23 |
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
| 24 |
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
| 25 |
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
| 26 |
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
| 27 |
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
| 28 |
+ |
|
| 29 |
+package gax |
|
| 30 |
+ |
|
| 31 |
+import "bytes" |
|
| 32 |
+ |
|
| 33 |
+// XGoogHeader is for use by the Google Cloud Libraries only. |
|
| 34 |
+// |
|
| 35 |
+// XGoogHeader formats key-value pairs. |
|
| 36 |
+// The resulting string is suitable for x-goog-api-client header. |
|
| 37 |
+func XGoogHeader(keyval ...string) string {
|
|
| 38 |
+ if len(keyval) == 0 {
|
|
| 39 |
+ return "" |
|
| 40 |
+ } |
|
| 41 |
+ if len(keyval)%2 != 0 {
|
|
| 42 |
+ panic("gax.Header: odd argument count")
|
|
| 43 |
+ } |
|
| 44 |
+ var buf bytes.Buffer |
|
| 45 |
+ for i := 0; i < len(keyval); i += 2 {
|
|
| 46 |
+ buf.WriteByte(' ')
|
|
| 47 |
+ buf.WriteString(keyval[i]) |
|
| 48 |
+ buf.WriteByte('/')
|
|
| 49 |
+ buf.WriteString(keyval[i+1]) |
|
| 50 |
+ } |
|
| 51 |
+ return buf.String()[1:] |
|
| 52 |
+} |
| 0 | 53 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,99 @@ |
| 0 |
+// Copyright 2016, Google Inc. |
|
| 1 |
+// All rights reserved. |
|
| 2 |
+// |
|
| 3 |
+// Redistribution and use in source and binary forms, with or without |
|
| 4 |
+// modification, are permitted provided that the following conditions are |
|
| 5 |
+// met: |
|
| 6 |
+// |
|
| 7 |
+// * Redistributions of source code must retain the above copyright |
|
| 8 |
+// notice, this list of conditions and the following disclaimer. |
|
| 9 |
+// * Redistributions in binary form must reproduce the above |
|
| 10 |
+// copyright notice, this list of conditions and the following disclaimer |
|
| 11 |
+// in the documentation and/or other materials provided with the |
|
| 12 |
+// distribution. |
|
| 13 |
+// * Neither the name of Google Inc. nor the names of its |
|
| 14 |
+// contributors may be used to endorse or promote products derived from |
|
| 15 |
+// this software without specific prior written permission. |
|
| 16 |
+// |
|
| 17 |
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
| 18 |
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
| 19 |
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
| 20 |
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
| 21 |
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
| 22 |
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 23 |
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
| 24 |
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
| 25 |
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
| 26 |
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
| 27 |
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
| 28 |
+ |
|
| 29 |
+package gax |
|
| 30 |
+ |
|
| 31 |
+import ( |
|
| 32 |
+ "context" |
|
| 33 |
+ "strings" |
|
| 34 |
+ "time" |
|
| 35 |
+) |
|
| 36 |
+ |
|
| 37 |
+// APICall is a user defined call stub. |
|
| 38 |
+type APICall func(context.Context, CallSettings) error |
|
| 39 |
+ |
|
| 40 |
+// Invoke calls the given APICall, |
|
| 41 |
+// performing retries as specified by opts, if any. |
|
| 42 |
+func Invoke(ctx context.Context, call APICall, opts ...CallOption) error {
|
|
| 43 |
+ var settings CallSettings |
|
| 44 |
+ for _, opt := range opts {
|
|
| 45 |
+ opt.Resolve(&settings) |
|
| 46 |
+ } |
|
| 47 |
+ return invoke(ctx, call, settings, Sleep) |
|
| 48 |
+} |
|
| 49 |
+ |
|
| 50 |
+// Sleep is similar to time.Sleep, but it can be interrupted by ctx.Done() closing. |
|
| 51 |
+// If interrupted, Sleep returns ctx.Err(). |
|
| 52 |
+func Sleep(ctx context.Context, d time.Duration) error {
|
|
| 53 |
+ t := time.NewTimer(d) |
|
| 54 |
+ select {
|
|
| 55 |
+ case <-ctx.Done(): |
|
| 56 |
+ t.Stop() |
|
| 57 |
+ return ctx.Err() |
|
| 58 |
+ case <-t.C: |
|
| 59 |
+ return nil |
|
| 60 |
+ } |
|
| 61 |
+} |
|
| 62 |
+ |
|
| 63 |
+type sleeper func(ctx context.Context, d time.Duration) error |
|
| 64 |
+ |
|
| 65 |
+// invoke implements Invoke, taking an additional sleeper argument for testing. |
|
| 66 |
+func invoke(ctx context.Context, call APICall, settings CallSettings, sp sleeper) error {
|
|
| 67 |
+ var retryer Retryer |
|
| 68 |
+ for {
|
|
| 69 |
+ err := call(ctx, settings) |
|
| 70 |
+ if err == nil {
|
|
| 71 |
+ return nil |
|
| 72 |
+ } |
|
| 73 |
+ if settings.Retry == nil {
|
|
| 74 |
+ return err |
|
| 75 |
+ } |
|
| 76 |
+ // Never retry permanent certificate errors. (e.x. if ca-certificates |
|
| 77 |
+ // are not installed). We should only make very few, targeted |
|
| 78 |
+ // exceptions: many (other) status=Unavailable should be retried, such |
|
| 79 |
+ // as if there's a network hiccup, or the internet goes out for a |
|
| 80 |
+ // minute. This is also why here we are doing string parsing instead of |
|
| 81 |
+ // simply making Unavailable a non-retried code elsewhere. |
|
| 82 |
+ if strings.Contains(err.Error(), "x509: certificate signed by unknown authority") {
|
|
| 83 |
+ return err |
|
| 84 |
+ } |
|
| 85 |
+ if retryer == nil {
|
|
| 86 |
+ if r := settings.Retry(); r != nil {
|
|
| 87 |
+ retryer = r |
|
| 88 |
+ } else {
|
|
| 89 |
+ return err |
|
| 90 |
+ } |
|
| 91 |
+ } |
|
| 92 |
+ if d, ok := retryer.Retry(err); !ok {
|
|
| 93 |
+ return err |
|
| 94 |
+ } else if err = sp(ctx, d); err != nil {
|
|
| 95 |
+ return err |
|
| 96 |
+ } |
|
| 97 |
+ } |
|
| 98 |
+} |
| 0 | 99 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,27 @@ |
| 0 |
+Copyright (c) 2009 The Go Authors. All rights reserved. |
|
| 1 |
+ |
|
| 2 |
+Redistribution and use in source and binary forms, with or without |
|
| 3 |
+modification, are permitted provided that the following conditions are |
|
| 4 |
+met: |
|
| 5 |
+ |
|
| 6 |
+ * Redistributions of source code must retain the above copyright |
|
| 7 |
+notice, this list of conditions and the following disclaimer. |
|
| 8 |
+ * Redistributions in binary form must reproduce the above |
|
| 9 |
+copyright notice, this list of conditions and the following disclaimer |
|
| 10 |
+in the documentation and/or other materials provided with the |
|
| 11 |
+distribution. |
|
| 12 |
+ * Neither the name of Google Inc. nor the names of its |
|
| 13 |
+contributors may be used to endorse or promote products derived from |
|
| 14 |
+this software without specific prior written permission. |
|
| 15 |
+ |
|
| 16 |
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
| 17 |
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
| 18 |
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
| 19 |
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
| 20 |
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
| 21 |
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 22 |
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
| 23 |
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
| 24 |
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
| 25 |
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
| 26 |
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 0 | 27 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,22 @@ |
| 0 |
+Additional IP Rights Grant (Patents) |
|
| 1 |
+ |
|
| 2 |
+"This implementation" means the copyrightable works distributed by |
|
| 3 |
+Google as part of the Go project. |
|
| 4 |
+ |
|
| 5 |
+Google hereby grants to You a perpetual, worldwide, non-exclusive, |
|
| 6 |
+no-charge, royalty-free, irrevocable (except as stated in this section) |
|
| 7 |
+patent license to make, have made, use, offer to sell, sell, import, |
|
| 8 |
+transfer and otherwise run, modify and propagate the contents of this |
|
| 9 |
+implementation of Go, where such license applies only to those patent |
|
| 10 |
+claims, both currently owned or controlled by Google and acquired in |
|
| 11 |
+the future, licensable by Google that are necessarily infringed by this |
|
| 12 |
+implementation of Go. This grant does not include claims that would be |
|
| 13 |
+infringed only as a consequence of further modification of this |
|
| 14 |
+implementation. If you or your agent or exclusive licensee institute or |
|
| 15 |
+order or agree to the institution of patent litigation against any |
|
| 16 |
+entity (including a cross-claim or counterclaim in a lawsuit) alleging |
|
| 17 |
+that this implementation of Go or any code incorporated within this |
|
| 18 |
+implementation of Go constitutes direct or contributory patent |
|
| 19 |
+infringement, or inducement of patent infringement, then any patent |
|
| 20 |
+rights granted to you under this License for this implementation of Go |
|
| 21 |
+shall terminate as of the date such litigation is filed. |
| 0 | 22 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,27 @@ |
| 0 |
+# Go Tools |
|
| 1 |
+ |
|
| 2 |
+This subrepository holds the source for various packages and tools that support |
|
| 3 |
+the Go programming language. |
|
| 4 |
+ |
|
| 5 |
+Some of the tools, `godoc` and `vet` for example, are included in binary Go |
|
| 6 |
+distributions. |
|
| 7 |
+ |
|
| 8 |
+Others, including the Go `guru` and the test coverage tool, can be fetched with |
|
| 9 |
+`go get`. |
|
| 10 |
+ |
|
| 11 |
+Packages include a type-checker for Go and an implementation of the |
|
| 12 |
+Static Single Assignment form (SSA) representation for Go programs. |
|
| 13 |
+ |
|
| 14 |
+## Download/Install |
|
| 15 |
+ |
|
| 16 |
+The easiest way to install is to run `go get -u golang.org/x/tools/...`. You can |
|
| 17 |
+also manually git clone the repository to `$GOPATH/src/golang.org/x/tools`. |
|
| 18 |
+ |
|
| 19 |
+## Report Issues / Send Patches |
|
| 20 |
+ |
|
| 21 |
+This repository uses Gerrit for code changes. To learn how to submit changes to |
|
| 22 |
+this repository, see https://golang.org/doc/contribute.html. |
|
| 23 |
+ |
|
| 24 |
+The main issue tracker for the tools repository is located at |
|
| 25 |
+https://github.com/golang/go/issues. Prefix your issue with "x/tools/(your |
|
| 26 |
+subdir):" in the subject line, so it is easy to find. |
| 0 | 27 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,20 @@ |
| 0 |
+// Copyright 2015 The Go Authors. All rights reserved. |
|
| 1 |
+// Use of this source code is governed by a BSD-style |
|
| 2 |
+// license that can be found in the LICENSE file. |
|
| 3 |
+ |
|
| 4 |
+// +build amd64,!appengine,!gccgo |
|
| 5 |
+ |
|
| 6 |
+package intsets |
|
| 7 |
+ |
|
| 8 |
+func popcnt(x word) int |
|
| 9 |
+func havePOPCNT() bool |
|
| 10 |
+ |
|
| 11 |
+var hasPOPCNT = havePOPCNT() |
|
| 12 |
+ |
|
| 13 |
+// popcount returns the population count (number of set bits) of x. |
|
| 14 |
+func popcount(x word) int {
|
|
| 15 |
+ if hasPOPCNT {
|
|
| 16 |
+ return popcnt(x) |
|
| 17 |
+ } |
|
| 18 |
+ return popcountTable(x) // faster than Hacker's Delight |
|
| 19 |
+} |
| 0 | 20 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,30 @@ |
| 0 |
+// Copyright 2015 The Go Authors. All rights reserved. |
|
| 1 |
+// Use of this source code is governed by a BSD-style |
|
| 2 |
+// license that can be found in the LICENSE file. |
|
| 3 |
+ |
|
| 4 |
+// +build amd64,!appengine,!gccgo |
|
| 5 |
+ |
|
| 6 |
+#include "textflag.h" |
|
| 7 |
+ |
|
| 8 |
+// func havePOPCNT() bool |
|
| 9 |
+TEXT ·havePOPCNT(SB),4,$0 |
|
| 10 |
+ MOVQ $1, AX |
|
| 11 |
+ CPUID |
|
| 12 |
+ SHRQ $23, CX |
|
| 13 |
+ ANDQ $1, CX |
|
| 14 |
+ MOVB CX, ret+0(FP) |
|
| 15 |
+ RET |
|
| 16 |
+ |
|
| 17 |
+// func popcnt(word) int |
|
| 18 |
+TEXT ·popcnt(SB),NOSPLIT,$0-8 |
|
| 19 |
+ XORQ AX, AX |
|
| 20 |
+ MOVQ x+0(FP), SI |
|
| 21 |
+ // POPCNT (SI), AX is not recognized by Go assembler, |
|
| 22 |
+ // so we assemble it ourselves. |
|
| 23 |
+ BYTE $0xf3 |
|
| 24 |
+ BYTE $0x48 |
|
| 25 |
+ BYTE $0x0f |
|
| 26 |
+ BYTE $0xb8 |
|
| 27 |
+ BYTE $0xc6 |
|
| 28 |
+ MOVQ AX, ret+8(FP) |
|
| 29 |
+ RET |
| 0 | 9 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,19 @@ |
| 0 |
+// Copyright 2015 The Go Authors. All rights reserved. |
|
| 1 |
+// Use of this source code is governed by a BSD-style |
|
| 2 |
+// license that can be found in the LICENSE file. |
|
| 3 |
+ |
|
| 4 |
+// +build gccgo |
|
| 5 |
+ |
|
| 6 |
+#include <errno.h> |
|
| 7 |
+#include <stdint.h> |
|
| 8 |
+#include <unistd.h> |
|
| 9 |
+ |
|
| 10 |
+#define _STRINGIFY2_(x) #x |
|
| 11 |
+#define _STRINGIFY_(x) _STRINGIFY2_(x) |
|
| 12 |
+#define GOSYM_PREFIX _STRINGIFY_(__USER_LABEL_PREFIX__) |
|
| 13 |
+ |
|
| 14 |
+extern intptr_t popcount(uintptr_t x) __asm__(GOSYM_PREFIX GOPKGPATH ".popcount"); |
|
| 15 |
+ |
|
| 16 |
+intptr_t popcount(uintptr_t x) {
|
|
| 17 |
+ return __builtin_popcountl((unsigned long)(x)); |
|
| 18 |
+} |
| 0 | 19 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,33 @@ |
| 0 |
+// Copyright 2015 The Go Authors. All rights reserved. |
|
| 1 |
+// Use of this source code is governed by a BSD-style |
|
| 2 |
+// license that can be found in the LICENSE file. |
|
| 3 |
+ |
|
| 4 |
+// +build !amd64 appengine |
|
| 5 |
+// +build !gccgo |
|
| 6 |
+ |
|
| 7 |
+package intsets |
|
| 8 |
+ |
|
| 9 |
+import "runtime" |
|
| 10 |
+ |
|
| 11 |
+// We compared three algorithms---Hacker's Delight, table lookup, |
|
| 12 |
+// and AMD64's SSE4.1 hardware POPCNT---on a 2.67GHz Xeon X5550. |
|
| 13 |
+// |
|
| 14 |
+// % GOARCH=amd64 go test -run=NONE -bench=Popcount |
|
| 15 |
+// POPCNT 5.12 ns/op |
|
| 16 |
+// Table 8.53 ns/op |
|
| 17 |
+// HackersDelight 9.96 ns/op |
|
| 18 |
+// |
|
| 19 |
+// % GOARCH=386 go test -run=NONE -bench=Popcount |
|
| 20 |
+// Table 10.4 ns/op |
|
| 21 |
+// HackersDelight 5.23 ns/op |
|
| 22 |
+// |
|
| 23 |
+// (AMD64's ABM1 hardware supports ntz and nlz too, |
|
| 24 |
+// but they aren't critical.) |
|
| 25 |
+ |
|
| 26 |
+// popcount returns the population count (number of set bits) of x. |
|
| 27 |
+func popcount(x word) int {
|
|
| 28 |
+ if runtime.GOARCH == "386" {
|
|
| 29 |
+ return popcountHD(uint32(x)) |
|
| 30 |
+ } |
|
| 31 |
+ return popcountTable(x) |
|
| 32 |
+} |
| 0 | 33 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,1091 @@ |
| 0 |
+// Copyright 2014 The Go Authors. All rights reserved. |
|
| 1 |
+// Use of this source code is governed by a BSD-style |
|
| 2 |
+// license that can be found in the LICENSE file. |
|
| 3 |
+ |
|
| 4 |
+// Package intsets provides Sparse, a compact and fast representation |
|
| 5 |
+// for sparse sets of int values. |
|
| 6 |
+// |
|
| 7 |
+// The time complexity of the operations Len, Insert, Remove and Has |
|
| 8 |
+// is in O(n) but in practice those methods are faster and more |
|
| 9 |
+// space-efficient than equivalent operations on sets based on the Go |
|
| 10 |
+// map type. The IsEmpty, Min, Max, Clear and TakeMin operations |
|
| 11 |
+// require constant time. |
|
| 12 |
+// |
|
| 13 |
+package intsets // import "golang.org/x/tools/container/intsets" |
|
| 14 |
+ |
|
| 15 |
+// TODO(adonovan): |
|
| 16 |
+// - Add InsertAll(...int), RemoveAll(...int) |
|
| 17 |
+// - Add 'bool changed' results for {Intersection,Difference}With too.
|
|
| 18 |
+// |
|
| 19 |
+// TODO(adonovan): implement Dense, a dense bit vector with a similar API. |
|
| 20 |
+// The space usage would be proportional to Max(), not Len(), and the |
|
| 21 |
+// implementation would be based upon big.Int. |
|
| 22 |
+// |
|
| 23 |
+// TODO(adonovan): opt: make UnionWith and Difference faster. |
|
| 24 |
+// These are the hot-spots for go/pointer. |
|
| 25 |
+ |
|
| 26 |
+import ( |
|
| 27 |
+ "bytes" |
|
| 28 |
+ "fmt" |
|
| 29 |
+) |
|
| 30 |
+ |
|
| 31 |
+// A Sparse is a set of int values. |
|
| 32 |
+// Sparse operations (even queries) are not concurrency-safe. |
|
| 33 |
+// |
|
| 34 |
+// The zero value for Sparse is a valid empty set. |
|
| 35 |
+// |
|
| 36 |
+// Sparse sets must be copied using the Copy method, not by assigning |
|
| 37 |
+// a Sparse value. |
|
| 38 |
+// |
|
| 39 |
+type Sparse struct {
|
|
| 40 |
+ // An uninitialized Sparse represents an empty set. |
|
| 41 |
+ // An empty set may also be represented by |
|
| 42 |
+ // root.next == root.prev == &root. |
|
| 43 |
+ // |
|
| 44 |
+ // The root is always the block with the smallest offset. |
|
| 45 |
+ // It can be empty, but only if it is the only block; in that case, offset is |
|
| 46 |
+ // MaxInt (which is not a valid offset). |
|
| 47 |
+ root block |
|
| 48 |
+} |
|
| 49 |
+ |
|
| 50 |
+type word uintptr |
|
| 51 |
+ |
|
| 52 |
+const ( |
|
| 53 |
+ _m = ^word(0) |
|
| 54 |
+ bitsPerWord = 8 << (_m>>8&1 + _m>>16&1 + _m>>32&1) |
|
| 55 |
+ bitsPerBlock = 256 // optimal value for go/pointer solver performance |
|
| 56 |
+ wordsPerBlock = bitsPerBlock / bitsPerWord |
|
| 57 |
+) |
|
| 58 |
+ |
|
| 59 |
+// Limit values of implementation-specific int type. |
|
| 60 |
+const ( |
|
| 61 |
+ MaxInt = int(^uint(0) >> 1) |
|
| 62 |
+ MinInt = -MaxInt - 1 |
|
| 63 |
+) |
|
| 64 |
+ |
|
| 65 |
+// -- block ------------------------------------------------------------ |
|
| 66 |
+ |
|
| 67 |
+// A set is represented as a circular doubly-linked list of blocks, |
|
| 68 |
+// each containing an offset and a bit array of fixed size |
|
| 69 |
+// bitsPerBlock; the blocks are ordered by increasing offset. |
|
| 70 |
+// |
|
| 71 |
+// The set contains an element x iff the block whose offset is x - (x |
|
| 72 |
+// mod bitsPerBlock) has the bit (x mod bitsPerBlock) set, where mod |
|
| 73 |
+// is the Euclidean remainder. |
|
| 74 |
+// |
|
| 75 |
+// A block may only be empty transiently. |
|
| 76 |
+// |
|
| 77 |
+type block struct {
|
|
| 78 |
+ offset int // offset mod bitsPerBlock == 0 |
|
| 79 |
+ bits [wordsPerBlock]word // contains at least one set bit |
|
| 80 |
+ next, prev *block // doubly-linked list of blocks |
|
| 81 |
+} |
|
| 82 |
+ |
|
| 83 |
+// wordMask returns the word index (in block.bits) |
|
| 84 |
+// and single-bit mask for the block's ith bit. |
|
| 85 |
+func wordMask(i uint) (w uint, mask word) {
|
|
| 86 |
+ w = i / bitsPerWord |
|
| 87 |
+ mask = 1 << (i % bitsPerWord) |
|
| 88 |
+ return |
|
| 89 |
+} |
|
| 90 |
+ |
|
| 91 |
+// insert sets the block b's ith bit and |
|
| 92 |
+// returns true if it was not already set. |
|
| 93 |
+// |
|
| 94 |
+func (b *block) insert(i uint) bool {
|
|
| 95 |
+ w, mask := wordMask(i) |
|
| 96 |
+ if b.bits[w]&mask == 0 {
|
|
| 97 |
+ b.bits[w] |= mask |
|
| 98 |
+ return true |
|
| 99 |
+ } |
|
| 100 |
+ return false |
|
| 101 |
+} |
|
| 102 |
+ |
|
| 103 |
+// remove clears the block's ith bit and |
|
| 104 |
+// returns true if the bit was previously set. |
|
| 105 |
+// NB: may leave the block empty. |
|
| 106 |
+// |
|
| 107 |
+func (b *block) remove(i uint) bool {
|
|
| 108 |
+ w, mask := wordMask(i) |
|
| 109 |
+ if b.bits[w]&mask != 0 {
|
|
| 110 |
+ b.bits[w] &^= mask |
|
| 111 |
+ return true |
|
| 112 |
+ } |
|
| 113 |
+ return false |
|
| 114 |
+} |
|
| 115 |
+ |
|
| 116 |
+// has reports whether the block's ith bit is set. |
|
| 117 |
+func (b *block) has(i uint) bool {
|
|
| 118 |
+ w, mask := wordMask(i) |
|
| 119 |
+ return b.bits[w]&mask != 0 |
|
| 120 |
+} |
|
| 121 |
+ |
|
| 122 |
+// empty reports whether b.len()==0, but more efficiently. |
|
| 123 |
+func (b *block) empty() bool {
|
|
| 124 |
+ for _, w := range b.bits {
|
|
| 125 |
+ if w != 0 {
|
|
| 126 |
+ return false |
|
| 127 |
+ } |
|
| 128 |
+ } |
|
| 129 |
+ return true |
|
| 130 |
+} |
|
| 131 |
+ |
|
| 132 |
+// len returns the number of set bits in block b. |
|
| 133 |
+func (b *block) len() int {
|
|
| 134 |
+ var l int |
|
| 135 |
+ for _, w := range b.bits {
|
|
| 136 |
+ l += popcount(w) |
|
| 137 |
+ } |
|
| 138 |
+ return l |
|
| 139 |
+} |
|
| 140 |
+ |
|
| 141 |
+// max returns the maximum element of the block. |
|
| 142 |
+// The block must not be empty. |
|
| 143 |
+func (b *block) max() int {
|
|
| 144 |
+ bi := b.offset + bitsPerBlock |
|
| 145 |
+ // Decrement bi by number of high zeros in last.bits. |
|
| 146 |
+ for i := len(b.bits) - 1; i >= 0; i-- {
|
|
| 147 |
+ if w := b.bits[i]; w != 0 {
|
|
| 148 |
+ return bi - nlz(w) - 1 |
|
| 149 |
+ } |
|
| 150 |
+ bi -= bitsPerWord |
|
| 151 |
+ } |
|
| 152 |
+ panic("BUG: empty block")
|
|
| 153 |
+} |
|
| 154 |
+ |
|
| 155 |
+// min returns the minimum element of the block, |
|
| 156 |
+// and also removes it if take is set. |
|
| 157 |
+// The block must not be initially empty. |
|
| 158 |
+// NB: may leave the block empty. |
|
| 159 |
+func (b *block) min(take bool) int {
|
|
| 160 |
+ for i, w := range b.bits {
|
|
| 161 |
+ if w != 0 {
|
|
| 162 |
+ tz := ntz(w) |
|
| 163 |
+ if take {
|
|
| 164 |
+ b.bits[i] = w &^ (1 << uint(tz)) |
|
| 165 |
+ } |
|
| 166 |
+ return b.offset + int(i*bitsPerWord) + tz |
|
| 167 |
+ } |
|
| 168 |
+ } |
|
| 169 |
+ panic("BUG: empty block")
|
|
| 170 |
+} |
|
| 171 |
+ |
|
| 172 |
+// lowerBound returns the smallest element of the block that is greater than or |
|
| 173 |
+// equal to the element corresponding to the ith bit. If there is no such |
|
| 174 |
+// element, the second return value is false. |
|
| 175 |
+func (b *block) lowerBound(i uint) (int, bool) {
|
|
| 176 |
+ w := i / bitsPerWord |
|
| 177 |
+ bit := i % bitsPerWord |
|
| 178 |
+ |
|
| 179 |
+ if val := b.bits[w] >> bit; val != 0 {
|
|
| 180 |
+ return b.offset + int(i) + ntz(val), true |
|
| 181 |
+ } |
|
| 182 |
+ |
|
| 183 |
+ for w++; w < wordsPerBlock; w++ {
|
|
| 184 |
+ if val := b.bits[w]; val != 0 {
|
|
| 185 |
+ return b.offset + int(w*bitsPerWord) + ntz(val), true |
|
| 186 |
+ } |
|
| 187 |
+ } |
|
| 188 |
+ |
|
| 189 |
+ return 0, false |
|
| 190 |
+} |
|
| 191 |
+ |
|
| 192 |
+// forEach calls f for each element of block b. |
|
| 193 |
+// f must not mutate b's enclosing Sparse. |
|
| 194 |
+func (b *block) forEach(f func(int)) {
|
|
| 195 |
+ for i, w := range b.bits {
|
|
| 196 |
+ offset := b.offset + i*bitsPerWord |
|
| 197 |
+ for bi := 0; w != 0 && bi < bitsPerWord; bi++ {
|
|
| 198 |
+ if w&1 != 0 {
|
|
| 199 |
+ f(offset) |
|
| 200 |
+ } |
|
| 201 |
+ offset++ |
|
| 202 |
+ w >>= 1 |
|
| 203 |
+ } |
|
| 204 |
+ } |
|
| 205 |
+} |
|
| 206 |
+ |
|
| 207 |
+// offsetAndBitIndex returns the offset of the block that would |
|
| 208 |
+// contain x and the bit index of x within that block. |
|
| 209 |
+// |
|
| 210 |
+func offsetAndBitIndex(x int) (int, uint) {
|
|
| 211 |
+ mod := x % bitsPerBlock |
|
| 212 |
+ if mod < 0 {
|
|
| 213 |
+ // Euclidean (non-negative) remainder |
|
| 214 |
+ mod += bitsPerBlock |
|
| 215 |
+ } |
|
| 216 |
+ return x - mod, uint(mod) |
|
| 217 |
+} |
|
| 218 |
+ |
|
| 219 |
+// -- Sparse -------------------------------------------------------------- |
|
| 220 |
+ |
|
| 221 |
+// none is a shared, empty, sentinel block that indicates the end of a block |
|
| 222 |
+// list. |
|
| 223 |
+var none block |
|
| 224 |
+ |
|
| 225 |
+// Dummy type used to generate an implicit panic. This must be defined at the |
|
| 226 |
+// package level; if it is defined inside a function, it prevents the inlining |
|
| 227 |
+// of that function. |
|
| 228 |
+type to_copy_a_sparse_you_must_call_its_Copy_method struct{}
|
|
| 229 |
+ |
|
| 230 |
+// init ensures s is properly initialized. |
|
| 231 |
+func (s *Sparse) init() {
|
|
| 232 |
+ root := &s.root |
|
| 233 |
+ if root.next == nil {
|
|
| 234 |
+ root.offset = MaxInt |
|
| 235 |
+ root.next = root |
|
| 236 |
+ root.prev = root |
|
| 237 |
+ } else if root.next.prev != root {
|
|
| 238 |
+ // Copying a Sparse x leads to pernicious corruption: the |
|
| 239 |
+ // new Sparse y shares the old linked list, but iteration |
|
| 240 |
+ // on y will never encounter &y.root so it goes into a |
|
| 241 |
+ // loop. Fail fast before this occurs. |
|
| 242 |
+ // We don't want to call panic here because it prevents the |
|
| 243 |
+ // inlining of this function. |
|
| 244 |
+ _ = (interface{}(nil)).(to_copy_a_sparse_you_must_call_its_Copy_method)
|
|
| 245 |
+ } |
|
| 246 |
+} |
|
| 247 |
+ |
|
| 248 |
+func (s *Sparse) first() *block {
|
|
| 249 |
+ s.init() |
|
| 250 |
+ if s.root.offset == MaxInt {
|
|
| 251 |
+ return &none |
|
| 252 |
+ } |
|
| 253 |
+ return &s.root |
|
| 254 |
+} |
|
| 255 |
+ |
|
| 256 |
+// next returns the next block in the list, or end if b is the last block. |
|
| 257 |
+func (s *Sparse) next(b *block) *block {
|
|
| 258 |
+ if b.next == &s.root {
|
|
| 259 |
+ return &none |
|
| 260 |
+ } |
|
| 261 |
+ return b.next |
|
| 262 |
+} |
|
| 263 |
+ |
|
| 264 |
+// prev returns the previous block in the list, or end if b is the first block. |
|
| 265 |
+func (s *Sparse) prev(b *block) *block {
|
|
| 266 |
+ if b.prev == &s.root {
|
|
| 267 |
+ return &none |
|
| 268 |
+ } |
|
| 269 |
+ return b.prev |
|
| 270 |
+} |
|
| 271 |
+ |
|
| 272 |
+// IsEmpty reports whether the set s is empty. |
|
| 273 |
+func (s *Sparse) IsEmpty() bool {
|
|
| 274 |
+ return s.root.next == nil || s.root.offset == MaxInt |
|
| 275 |
+} |
|
| 276 |
+ |
|
| 277 |
+// Len returns the number of elements in the set s. |
|
| 278 |
+func (s *Sparse) Len() int {
|
|
| 279 |
+ var l int |
|
| 280 |
+ for b := s.first(); b != &none; b = s.next(b) {
|
|
| 281 |
+ l += b.len() |
|
| 282 |
+ } |
|
| 283 |
+ return l |
|
| 284 |
+} |
|
| 285 |
+ |
|
| 286 |
+// Max returns the maximum element of the set s, or MinInt if s is empty. |
|
| 287 |
+func (s *Sparse) Max() int {
|
|
| 288 |
+ if s.IsEmpty() {
|
|
| 289 |
+ return MinInt |
|
| 290 |
+ } |
|
| 291 |
+ return s.root.prev.max() |
|
| 292 |
+} |
|
| 293 |
+ |
|
| 294 |
+// Min returns the minimum element of the set s, or MaxInt if s is empty. |
|
| 295 |
+func (s *Sparse) Min() int {
|
|
| 296 |
+ if s.IsEmpty() {
|
|
| 297 |
+ return MaxInt |
|
| 298 |
+ } |
|
| 299 |
+ return s.root.min(false) |
|
| 300 |
+} |
|
| 301 |
+ |
|
| 302 |
+// LowerBound returns the smallest element >= x, or MaxInt if there is no such |
|
| 303 |
+// element. |
|
| 304 |
+func (s *Sparse) LowerBound(x int) int {
|
|
| 305 |
+ offset, i := offsetAndBitIndex(x) |
|
| 306 |
+ for b := s.first(); b != &none; b = s.next(b) {
|
|
| 307 |
+ if b.offset > offset {
|
|
| 308 |
+ return b.min(false) |
|
| 309 |
+ } |
|
| 310 |
+ if b.offset == offset {
|
|
| 311 |
+ if y, ok := b.lowerBound(i); ok {
|
|
| 312 |
+ return y |
|
| 313 |
+ } |
|
| 314 |
+ } |
|
| 315 |
+ } |
|
| 316 |
+ return MaxInt |
|
| 317 |
+} |
|
| 318 |
+ |
|
| 319 |
+// block returns the block that would contain offset, |
|
| 320 |
+// or nil if s contains no such block. |
|
| 321 |
+// Precondition: offset is a multiple of bitsPerBlock. |
|
| 322 |
+func (s *Sparse) block(offset int) *block {
|
|
| 323 |
+ for b := s.first(); b != &none && b.offset <= offset; b = s.next(b) {
|
|
| 324 |
+ if b.offset == offset {
|
|
| 325 |
+ return b |
|
| 326 |
+ } |
|
| 327 |
+ } |
|
| 328 |
+ return nil |
|
| 329 |
+} |
|
| 330 |
+ |
|
| 331 |
+// Insert adds x to the set s, and reports whether the set grew. |
|
| 332 |
+func (s *Sparse) Insert(x int) bool {
|
|
| 333 |
+ offset, i := offsetAndBitIndex(x) |
|
| 334 |
+ |
|
| 335 |
+ b := s.first() |
|
| 336 |
+ for ; b != &none && b.offset <= offset; b = s.next(b) {
|
|
| 337 |
+ if b.offset == offset {
|
|
| 338 |
+ return b.insert(i) |
|
| 339 |
+ } |
|
| 340 |
+ } |
|
| 341 |
+ |
|
| 342 |
+ // Insert new block before b. |
|
| 343 |
+ new := s.insertBlockBefore(b) |
|
| 344 |
+ new.offset = offset |
|
| 345 |
+ return new.insert(i) |
|
| 346 |
+} |
|
| 347 |
+ |
|
| 348 |
+// removeBlock removes a block and returns the block that followed it (or end if |
|
| 349 |
+// it was the last block). |
|
| 350 |
+func (s *Sparse) removeBlock(b *block) *block {
|
|
| 351 |
+ if b != &s.root {
|
|
| 352 |
+ b.prev.next = b.next |
|
| 353 |
+ b.next.prev = b.prev |
|
| 354 |
+ if b.next == &s.root {
|
|
| 355 |
+ return &none |
|
| 356 |
+ } |
|
| 357 |
+ return b.next |
|
| 358 |
+ } |
|
| 359 |
+ |
|
| 360 |
+ first := s.root.next |
|
| 361 |
+ if first == &s.root {
|
|
| 362 |
+ // This was the only block. |
|
| 363 |
+ s.Clear() |
|
| 364 |
+ return &none |
|
| 365 |
+ } |
|
| 366 |
+ s.root.offset = first.offset |
|
| 367 |
+ s.root.bits = first.bits |
|
| 368 |
+ if first.next == &s.root {
|
|
| 369 |
+ // Single block remaining. |
|
| 370 |
+ s.root.next = &s.root |
|
| 371 |
+ s.root.prev = &s.root |
|
| 372 |
+ } else {
|
|
| 373 |
+ s.root.next = first.next |
|
| 374 |
+ first.next.prev = &s.root |
|
| 375 |
+ } |
|
| 376 |
+ return &s.root |
|
| 377 |
+} |
|
| 378 |
+ |
|
| 379 |
+// Remove removes x from the set s, and reports whether the set shrank. |
|
| 380 |
+func (s *Sparse) Remove(x int) bool {
|
|
| 381 |
+ offset, i := offsetAndBitIndex(x) |
|
| 382 |
+ if b := s.block(offset); b != nil {
|
|
| 383 |
+ if !b.remove(i) {
|
|
| 384 |
+ return false |
|
| 385 |
+ } |
|
| 386 |
+ if b.empty() {
|
|
| 387 |
+ s.removeBlock(b) |
|
| 388 |
+ } |
|
| 389 |
+ return true |
|
| 390 |
+ } |
|
| 391 |
+ return false |
|
| 392 |
+} |
|
| 393 |
+ |
|
| 394 |
+// Clear removes all elements from the set s. |
|
| 395 |
+func (s *Sparse) Clear() {
|
|
| 396 |
+ s.root = block{
|
|
| 397 |
+ offset: MaxInt, |
|
| 398 |
+ next: &s.root, |
|
| 399 |
+ prev: &s.root, |
|
| 400 |
+ } |
|
| 401 |
+} |
|
| 402 |
+ |
|
| 403 |
+// If set s is non-empty, TakeMin sets *p to the minimum element of |
|
| 404 |
+// the set s, removes that element from the set and returns true. |
|
| 405 |
+// Otherwise, it returns false and *p is undefined. |
|
| 406 |
+// |
|
| 407 |
+// This method may be used for iteration over a worklist like so: |
|
| 408 |
+// |
|
| 409 |
+// var x int |
|
| 410 |
+// for worklist.TakeMin(&x) { use(x) }
|
|
| 411 |
+// |
|
| 412 |
+func (s *Sparse) TakeMin(p *int) bool {
|
|
| 413 |
+ if s.IsEmpty() {
|
|
| 414 |
+ return false |
|
| 415 |
+ } |
|
| 416 |
+ *p = s.root.min(true) |
|
| 417 |
+ if s.root.empty() {
|
|
| 418 |
+ s.removeBlock(&s.root) |
|
| 419 |
+ } |
|
| 420 |
+ return true |
|
| 421 |
+} |
|
| 422 |
+ |
|
| 423 |
+// Has reports whether x is an element of the set s. |
|
| 424 |
+func (s *Sparse) Has(x int) bool {
|
|
| 425 |
+ offset, i := offsetAndBitIndex(x) |
|
| 426 |
+ if b := s.block(offset); b != nil {
|
|
| 427 |
+ return b.has(i) |
|
| 428 |
+ } |
|
| 429 |
+ return false |
|
| 430 |
+} |
|
| 431 |
+ |
|
| 432 |
+// forEach applies function f to each element of the set s in order. |
|
| 433 |
+// |
|
| 434 |
+// f must not mutate s. Consequently, forEach is not safe to expose |
|
| 435 |
+// to clients. In any case, using "range s.AppendTo()" allows more |
|
| 436 |
+// natural control flow with continue/break/return. |
|
| 437 |
+// |
|
| 438 |
+func (s *Sparse) forEach(f func(int)) {
|
|
| 439 |
+ for b := s.first(); b != &none; b = s.next(b) {
|
|
| 440 |
+ b.forEach(f) |
|
| 441 |
+ } |
|
| 442 |
+} |
|
| 443 |
+ |
|
| 444 |
+// Copy sets s to the value of x. |
|
| 445 |
+func (s *Sparse) Copy(x *Sparse) {
|
|
| 446 |
+ if s == x {
|
|
| 447 |
+ return |
|
| 448 |
+ } |
|
| 449 |
+ |
|
| 450 |
+ xb := x.first() |
|
| 451 |
+ sb := s.first() |
|
| 452 |
+ for xb != &none {
|
|
| 453 |
+ if sb == &none {
|
|
| 454 |
+ sb = s.insertBlockBefore(sb) |
|
| 455 |
+ } |
|
| 456 |
+ sb.offset = xb.offset |
|
| 457 |
+ sb.bits = xb.bits |
|
| 458 |
+ xb = x.next(xb) |
|
| 459 |
+ sb = s.next(sb) |
|
| 460 |
+ } |
|
| 461 |
+ s.discardTail(sb) |
|
| 462 |
+} |
|
| 463 |
+ |
|
| 464 |
+// insertBlockBefore returns a new block, inserting it before next. |
|
| 465 |
+// If next is the root, the root is replaced. If next is end, the block is |
|
| 466 |
+// inserted at the end. |
|
| 467 |
+func (s *Sparse) insertBlockBefore(next *block) *block {
|
|
| 468 |
+ if s.IsEmpty() {
|
|
| 469 |
+ if next != &none {
|
|
| 470 |
+ panic("BUG: passed block with empty set")
|
|
| 471 |
+ } |
|
| 472 |
+ return &s.root |
|
| 473 |
+ } |
|
| 474 |
+ |
|
| 475 |
+ if next == &s.root {
|
|
| 476 |
+ // Special case: we need to create a new block that will become the root |
|
| 477 |
+ // block.The old root block becomes the second block. |
|
| 478 |
+ second := s.root |
|
| 479 |
+ s.root = block{
|
|
| 480 |
+ next: &second, |
|
| 481 |
+ } |
|
| 482 |
+ if second.next == &s.root {
|
|
| 483 |
+ s.root.prev = &second |
|
| 484 |
+ } else {
|
|
| 485 |
+ s.root.prev = second.prev |
|
| 486 |
+ second.next.prev = &second |
|
| 487 |
+ second.prev = &s.root |
|
| 488 |
+ } |
|
| 489 |
+ return &s.root |
|
| 490 |
+ } |
|
| 491 |
+ if next == &none {
|
|
| 492 |
+ // Insert before root. |
|
| 493 |
+ next = &s.root |
|
| 494 |
+ } |
|
| 495 |
+ b := new(block) |
|
| 496 |
+ b.next = next |
|
| 497 |
+ b.prev = next.prev |
|
| 498 |
+ b.prev.next = b |
|
| 499 |
+ next.prev = b |
|
| 500 |
+ return b |
|
| 501 |
+} |
|
| 502 |
+ |
|
| 503 |
+// discardTail removes block b and all its successors from s. |
|
| 504 |
+func (s *Sparse) discardTail(b *block) {
|
|
| 505 |
+ if b != &none {
|
|
| 506 |
+ if b == &s.root {
|
|
| 507 |
+ s.Clear() |
|
| 508 |
+ } else {
|
|
| 509 |
+ b.prev.next = &s.root |
|
| 510 |
+ s.root.prev = b.prev |
|
| 511 |
+ } |
|
| 512 |
+ } |
|
| 513 |
+} |
|
| 514 |
+ |
|
| 515 |
+// IntersectionWith sets s to the intersection s ∩ x. |
|
| 516 |
+func (s *Sparse) IntersectionWith(x *Sparse) {
|
|
| 517 |
+ if s == x {
|
|
| 518 |
+ return |
|
| 519 |
+ } |
|
| 520 |
+ |
|
| 521 |
+ xb := x.first() |
|
| 522 |
+ sb := s.first() |
|
| 523 |
+ for xb != &none && sb != &none {
|
|
| 524 |
+ switch {
|
|
| 525 |
+ case xb.offset < sb.offset: |
|
| 526 |
+ xb = x.next(xb) |
|
| 527 |
+ |
|
| 528 |
+ case xb.offset > sb.offset: |
|
| 529 |
+ sb = s.removeBlock(sb) |
|
| 530 |
+ |
|
| 531 |
+ default: |
|
| 532 |
+ var sum word |
|
| 533 |
+ for i := range sb.bits {
|
|
| 534 |
+ r := xb.bits[i] & sb.bits[i] |
|
| 535 |
+ sb.bits[i] = r |
|
| 536 |
+ sum |= r |
|
| 537 |
+ } |
|
| 538 |
+ if sum != 0 {
|
|
| 539 |
+ sb = s.next(sb) |
|
| 540 |
+ } else {
|
|
| 541 |
+ // sb will be overwritten or removed |
|
| 542 |
+ } |
|
| 543 |
+ |
|
| 544 |
+ xb = x.next(xb) |
|
| 545 |
+ } |
|
| 546 |
+ } |
|
| 547 |
+ |
|
| 548 |
+ s.discardTail(sb) |
|
| 549 |
+} |
|
| 550 |
+ |
|
| 551 |
+// Intersection sets s to the intersection x ∩ y. |
|
| 552 |
+func (s *Sparse) Intersection(x, y *Sparse) {
|
|
| 553 |
+ switch {
|
|
| 554 |
+ case s == x: |
|
| 555 |
+ s.IntersectionWith(y) |
|
| 556 |
+ return |
|
| 557 |
+ case s == y: |
|
| 558 |
+ s.IntersectionWith(x) |
|
| 559 |
+ return |
|
| 560 |
+ case x == y: |
|
| 561 |
+ s.Copy(x) |
|
| 562 |
+ return |
|
| 563 |
+ } |
|
| 564 |
+ |
|
| 565 |
+ xb := x.first() |
|
| 566 |
+ yb := y.first() |
|
| 567 |
+ sb := s.first() |
|
| 568 |
+ for xb != &none && yb != &none {
|
|
| 569 |
+ switch {
|
|
| 570 |
+ case xb.offset < yb.offset: |
|
| 571 |
+ xb = x.next(xb) |
|
| 572 |
+ continue |
|
| 573 |
+ case xb.offset > yb.offset: |
|
| 574 |
+ yb = y.next(yb) |
|
| 575 |
+ continue |
|
| 576 |
+ } |
|
| 577 |
+ |
|
| 578 |
+ if sb == &none {
|
|
| 579 |
+ sb = s.insertBlockBefore(sb) |
|
| 580 |
+ } |
|
| 581 |
+ sb.offset = xb.offset |
|
| 582 |
+ |
|
| 583 |
+ var sum word |
|
| 584 |
+ for i := range sb.bits {
|
|
| 585 |
+ r := xb.bits[i] & yb.bits[i] |
|
| 586 |
+ sb.bits[i] = r |
|
| 587 |
+ sum |= r |
|
| 588 |
+ } |
|
| 589 |
+ if sum != 0 {
|
|
| 590 |
+ sb = s.next(sb) |
|
| 591 |
+ } else {
|
|
| 592 |
+ // sb will be overwritten or removed |
|
| 593 |
+ } |
|
| 594 |
+ |
|
| 595 |
+ xb = x.next(xb) |
|
| 596 |
+ yb = y.next(yb) |
|
| 597 |
+ } |
|
| 598 |
+ |
|
| 599 |
+ s.discardTail(sb) |
|
| 600 |
+} |
|
| 601 |
+ |
|
| 602 |
+// Intersects reports whether s ∩ x ≠∅. |
|
| 603 |
+func (s *Sparse) Intersects(x *Sparse) bool {
|
|
| 604 |
+ sb := s.first() |
|
| 605 |
+ xb := x.first() |
|
| 606 |
+ for sb != &none && xb != &none {
|
|
| 607 |
+ switch {
|
|
| 608 |
+ case xb.offset < sb.offset: |
|
| 609 |
+ xb = x.next(xb) |
|
| 610 |
+ case xb.offset > sb.offset: |
|
| 611 |
+ sb = s.next(sb) |
|
| 612 |
+ default: |
|
| 613 |
+ for i := range sb.bits {
|
|
| 614 |
+ if sb.bits[i]&xb.bits[i] != 0 {
|
|
| 615 |
+ return true |
|
| 616 |
+ } |
|
| 617 |
+ } |
|
| 618 |
+ sb = s.next(sb) |
|
| 619 |
+ xb = x.next(xb) |
|
| 620 |
+ } |
|
| 621 |
+ } |
|
| 622 |
+ return false |
|
| 623 |
+} |
|
| 624 |
+ |
|
| 625 |
+// UnionWith sets s to the union s ∪ x, and reports whether s grew. |
|
| 626 |
+func (s *Sparse) UnionWith(x *Sparse) bool {
|
|
| 627 |
+ if s == x {
|
|
| 628 |
+ return false |
|
| 629 |
+ } |
|
| 630 |
+ |
|
| 631 |
+ var changed bool |
|
| 632 |
+ xb := x.first() |
|
| 633 |
+ sb := s.first() |
|
| 634 |
+ for xb != &none {
|
|
| 635 |
+ if sb != &none && sb.offset == xb.offset {
|
|
| 636 |
+ for i := range xb.bits {
|
|
| 637 |
+ if sb.bits[i] != xb.bits[i] {
|
|
| 638 |
+ sb.bits[i] |= xb.bits[i] |
|
| 639 |
+ changed = true |
|
| 640 |
+ } |
|
| 641 |
+ } |
|
| 642 |
+ xb = x.next(xb) |
|
| 643 |
+ } else if sb == &none || sb.offset > xb.offset {
|
|
| 644 |
+ sb = s.insertBlockBefore(sb) |
|
| 645 |
+ sb.offset = xb.offset |
|
| 646 |
+ sb.bits = xb.bits |
|
| 647 |
+ changed = true |
|
| 648 |
+ |
|
| 649 |
+ xb = x.next(xb) |
|
| 650 |
+ } |
|
| 651 |
+ sb = s.next(sb) |
|
| 652 |
+ } |
|
| 653 |
+ return changed |
|
| 654 |
+} |
|
| 655 |
+ |
|
| 656 |
+// Union sets s to the union x ∪ y. |
|
| 657 |
+func (s *Sparse) Union(x, y *Sparse) {
|
|
| 658 |
+ switch {
|
|
| 659 |
+ case x == y: |
|
| 660 |
+ s.Copy(x) |
|
| 661 |
+ return |
|
| 662 |
+ case s == x: |
|
| 663 |
+ s.UnionWith(y) |
|
| 664 |
+ return |
|
| 665 |
+ case s == y: |
|
| 666 |
+ s.UnionWith(x) |
|
| 667 |
+ return |
|
| 668 |
+ } |
|
| 669 |
+ |
|
| 670 |
+ xb := x.first() |
|
| 671 |
+ yb := y.first() |
|
| 672 |
+ sb := s.first() |
|
| 673 |
+ for xb != &none || yb != &none {
|
|
| 674 |
+ if sb == &none {
|
|
| 675 |
+ sb = s.insertBlockBefore(sb) |
|
| 676 |
+ } |
|
| 677 |
+ switch {
|
|
| 678 |
+ case yb == &none || (xb != &none && xb.offset < yb.offset): |
|
| 679 |
+ sb.offset = xb.offset |
|
| 680 |
+ sb.bits = xb.bits |
|
| 681 |
+ xb = x.next(xb) |
|
| 682 |
+ |
|
| 683 |
+ case xb == &none || (yb != &none && yb.offset < xb.offset): |
|
| 684 |
+ sb.offset = yb.offset |
|
| 685 |
+ sb.bits = yb.bits |
|
| 686 |
+ yb = y.next(yb) |
|
| 687 |
+ |
|
| 688 |
+ default: |
|
| 689 |
+ sb.offset = xb.offset |
|
| 690 |
+ for i := range xb.bits {
|
|
| 691 |
+ sb.bits[i] = xb.bits[i] | yb.bits[i] |
|
| 692 |
+ } |
|
| 693 |
+ xb = x.next(xb) |
|
| 694 |
+ yb = y.next(yb) |
|
| 695 |
+ } |
|
| 696 |
+ sb = s.next(sb) |
|
| 697 |
+ } |
|
| 698 |
+ |
|
| 699 |
+ s.discardTail(sb) |
|
| 700 |
+} |
|
| 701 |
+ |
|
| 702 |
+// DifferenceWith sets s to the difference s ∖ x. |
|
| 703 |
+func (s *Sparse) DifferenceWith(x *Sparse) {
|
|
| 704 |
+ if s == x {
|
|
| 705 |
+ s.Clear() |
|
| 706 |
+ return |
|
| 707 |
+ } |
|
| 708 |
+ |
|
| 709 |
+ xb := x.first() |
|
| 710 |
+ sb := s.first() |
|
| 711 |
+ for xb != &none && sb != &none {
|
|
| 712 |
+ switch {
|
|
| 713 |
+ case xb.offset > sb.offset: |
|
| 714 |
+ sb = s.next(sb) |
|
| 715 |
+ |
|
| 716 |
+ case xb.offset < sb.offset: |
|
| 717 |
+ xb = x.next(xb) |
|
| 718 |
+ |
|
| 719 |
+ default: |
|
| 720 |
+ var sum word |
|
| 721 |
+ for i := range sb.bits {
|
|
| 722 |
+ r := sb.bits[i] & ^xb.bits[i] |
|
| 723 |
+ sb.bits[i] = r |
|
| 724 |
+ sum |= r |
|
| 725 |
+ } |
|
| 726 |
+ if sum == 0 {
|
|
| 727 |
+ sb = s.removeBlock(sb) |
|
| 728 |
+ } else {
|
|
| 729 |
+ sb = s.next(sb) |
|
| 730 |
+ } |
|
| 731 |
+ xb = x.next(xb) |
|
| 732 |
+ } |
|
| 733 |
+ } |
|
| 734 |
+} |
|
| 735 |
+ |
|
| 736 |
+// Difference sets s to the difference x ∖ y. |
|
| 737 |
+func (s *Sparse) Difference(x, y *Sparse) {
|
|
| 738 |
+ switch {
|
|
| 739 |
+ case x == y: |
|
| 740 |
+ s.Clear() |
|
| 741 |
+ return |
|
| 742 |
+ case s == x: |
|
| 743 |
+ s.DifferenceWith(y) |
|
| 744 |
+ return |
|
| 745 |
+ case s == y: |
|
| 746 |
+ var y2 Sparse |
|
| 747 |
+ y2.Copy(y) |
|
| 748 |
+ s.Difference(x, &y2) |
|
| 749 |
+ return |
|
| 750 |
+ } |
|
| 751 |
+ |
|
| 752 |
+ xb := x.first() |
|
| 753 |
+ yb := y.first() |
|
| 754 |
+ sb := s.first() |
|
| 755 |
+ for xb != &none && yb != &none {
|
|
| 756 |
+ if xb.offset > yb.offset {
|
|
| 757 |
+ // y has block, x has &none |
|
| 758 |
+ yb = y.next(yb) |
|
| 759 |
+ continue |
|
| 760 |
+ } |
|
| 761 |
+ |
|
| 762 |
+ if sb == &none {
|
|
| 763 |
+ sb = s.insertBlockBefore(sb) |
|
| 764 |
+ } |
|
| 765 |
+ sb.offset = xb.offset |
|
| 766 |
+ |
|
| 767 |
+ switch {
|
|
| 768 |
+ case xb.offset < yb.offset: |
|
| 769 |
+ // x has block, y has &none |
|
| 770 |
+ sb.bits = xb.bits |
|
| 771 |
+ |
|
| 772 |
+ sb = s.next(sb) |
|
| 773 |
+ |
|
| 774 |
+ default: |
|
| 775 |
+ // x and y have corresponding blocks |
|
| 776 |
+ var sum word |
|
| 777 |
+ for i := range sb.bits {
|
|
| 778 |
+ r := xb.bits[i] & ^yb.bits[i] |
|
| 779 |
+ sb.bits[i] = r |
|
| 780 |
+ sum |= r |
|
| 781 |
+ } |
|
| 782 |
+ if sum != 0 {
|
|
| 783 |
+ sb = s.next(sb) |
|
| 784 |
+ } else {
|
|
| 785 |
+ // sb will be overwritten or removed |
|
| 786 |
+ } |
|
| 787 |
+ |
|
| 788 |
+ yb = y.next(yb) |
|
| 789 |
+ } |
|
| 790 |
+ xb = x.next(xb) |
|
| 791 |
+ } |
|
| 792 |
+ |
|
| 793 |
+ for xb != &none {
|
|
| 794 |
+ if sb == &none {
|
|
| 795 |
+ sb = s.insertBlockBefore(sb) |
|
| 796 |
+ } |
|
| 797 |
+ sb.offset = xb.offset |
|
| 798 |
+ sb.bits = xb.bits |
|
| 799 |
+ sb = s.next(sb) |
|
| 800 |
+ |
|
| 801 |
+ xb = x.next(xb) |
|
| 802 |
+ } |
|
| 803 |
+ |
|
| 804 |
+ s.discardTail(sb) |
|
| 805 |
+} |
|
| 806 |
+ |
|
| 807 |
+// SymmetricDifferenceWith sets s to the symmetric difference s ∆ x. |
|
| 808 |
+func (s *Sparse) SymmetricDifferenceWith(x *Sparse) {
|
|
| 809 |
+ if s == x {
|
|
| 810 |
+ s.Clear() |
|
| 811 |
+ return |
|
| 812 |
+ } |
|
| 813 |
+ |
|
| 814 |
+ sb := s.first() |
|
| 815 |
+ xb := x.first() |
|
| 816 |
+ for xb != &none && sb != &none {
|
|
| 817 |
+ switch {
|
|
| 818 |
+ case sb.offset < xb.offset: |
|
| 819 |
+ sb = s.next(sb) |
|
| 820 |
+ case xb.offset < sb.offset: |
|
| 821 |
+ nb := s.insertBlockBefore(sb) |
|
| 822 |
+ nb.offset = xb.offset |
|
| 823 |
+ nb.bits = xb.bits |
|
| 824 |
+ xb = x.next(xb) |
|
| 825 |
+ default: |
|
| 826 |
+ var sum word |
|
| 827 |
+ for i := range sb.bits {
|
|
| 828 |
+ r := sb.bits[i] ^ xb.bits[i] |
|
| 829 |
+ sb.bits[i] = r |
|
| 830 |
+ sum |= r |
|
| 831 |
+ } |
|
| 832 |
+ if sum == 0 {
|
|
| 833 |
+ sb = s.removeBlock(sb) |
|
| 834 |
+ } else {
|
|
| 835 |
+ sb = s.next(sb) |
|
| 836 |
+ } |
|
| 837 |
+ xb = x.next(xb) |
|
| 838 |
+ } |
|
| 839 |
+ } |
|
| 840 |
+ |
|
| 841 |
+ for xb != &none { // append the tail of x to s
|
|
| 842 |
+ sb = s.insertBlockBefore(sb) |
|
| 843 |
+ sb.offset = xb.offset |
|
| 844 |
+ sb.bits = xb.bits |
|
| 845 |
+ sb = s.next(sb) |
|
| 846 |
+ xb = x.next(xb) |
|
| 847 |
+ } |
|
| 848 |
+} |
|
| 849 |
+ |
|
| 850 |
+// SymmetricDifference sets s to the symmetric difference x ∆ y. |
|
| 851 |
+func (s *Sparse) SymmetricDifference(x, y *Sparse) {
|
|
| 852 |
+ switch {
|
|
| 853 |
+ case x == y: |
|
| 854 |
+ s.Clear() |
|
| 855 |
+ return |
|
| 856 |
+ case s == x: |
|
| 857 |
+ s.SymmetricDifferenceWith(y) |
|
| 858 |
+ return |
|
| 859 |
+ case s == y: |
|
| 860 |
+ s.SymmetricDifferenceWith(x) |
|
| 861 |
+ return |
|
| 862 |
+ } |
|
| 863 |
+ |
|
| 864 |
+ sb := s.first() |
|
| 865 |
+ xb := x.first() |
|
| 866 |
+ yb := y.first() |
|
| 867 |
+ for xb != &none && yb != &none {
|
|
| 868 |
+ if sb == &none {
|
|
| 869 |
+ sb = s.insertBlockBefore(sb) |
|
| 870 |
+ } |
|
| 871 |
+ switch {
|
|
| 872 |
+ case yb.offset < xb.offset: |
|
| 873 |
+ sb.offset = yb.offset |
|
| 874 |
+ sb.bits = yb.bits |
|
| 875 |
+ sb = s.next(sb) |
|
| 876 |
+ yb = y.next(yb) |
|
| 877 |
+ case xb.offset < yb.offset: |
|
| 878 |
+ sb.offset = xb.offset |
|
| 879 |
+ sb.bits = xb.bits |
|
| 880 |
+ sb = s.next(sb) |
|
| 881 |
+ xb = x.next(xb) |
|
| 882 |
+ default: |
|
| 883 |
+ var sum word |
|
| 884 |
+ for i := range sb.bits {
|
|
| 885 |
+ r := xb.bits[i] ^ yb.bits[i] |
|
| 886 |
+ sb.bits[i] = r |
|
| 887 |
+ sum |= r |
|
| 888 |
+ } |
|
| 889 |
+ if sum != 0 {
|
|
| 890 |
+ sb.offset = xb.offset |
|
| 891 |
+ sb = s.next(sb) |
|
| 892 |
+ } |
|
| 893 |
+ xb = x.next(xb) |
|
| 894 |
+ yb = y.next(yb) |
|
| 895 |
+ } |
|
| 896 |
+ } |
|
| 897 |
+ |
|
| 898 |
+ for xb != &none { // append the tail of x to s
|
|
| 899 |
+ if sb == &none {
|
|
| 900 |
+ sb = s.insertBlockBefore(sb) |
|
| 901 |
+ } |
|
| 902 |
+ sb.offset = xb.offset |
|
| 903 |
+ sb.bits = xb.bits |
|
| 904 |
+ sb = s.next(sb) |
|
| 905 |
+ xb = x.next(xb) |
|
| 906 |
+ } |
|
| 907 |
+ |
|
| 908 |
+ for yb != &none { // append the tail of y to s
|
|
| 909 |
+ if sb == &none {
|
|
| 910 |
+ sb = s.insertBlockBefore(sb) |
|
| 911 |
+ } |
|
| 912 |
+ sb.offset = yb.offset |
|
| 913 |
+ sb.bits = yb.bits |
|
| 914 |
+ sb = s.next(sb) |
|
| 915 |
+ yb = y.next(yb) |
|
| 916 |
+ } |
|
| 917 |
+ |
|
| 918 |
+ s.discardTail(sb) |
|
| 919 |
+} |
|
| 920 |
+ |
|
| 921 |
+// SubsetOf reports whether s ∖ x = ∅. |
|
| 922 |
+func (s *Sparse) SubsetOf(x *Sparse) bool {
|
|
| 923 |
+ if s == x {
|
|
| 924 |
+ return true |
|
| 925 |
+ } |
|
| 926 |
+ |
|
| 927 |
+ sb := s.first() |
|
| 928 |
+ xb := x.first() |
|
| 929 |
+ for sb != &none {
|
|
| 930 |
+ switch {
|
|
| 931 |
+ case xb == &none || xb.offset > sb.offset: |
|
| 932 |
+ return false |
|
| 933 |
+ case xb.offset < sb.offset: |
|
| 934 |
+ xb = x.next(xb) |
|
| 935 |
+ default: |
|
| 936 |
+ for i := range sb.bits {
|
|
| 937 |
+ if sb.bits[i]&^xb.bits[i] != 0 {
|
|
| 938 |
+ return false |
|
| 939 |
+ } |
|
| 940 |
+ } |
|
| 941 |
+ sb = s.next(sb) |
|
| 942 |
+ xb = x.next(xb) |
|
| 943 |
+ } |
|
| 944 |
+ } |
|
| 945 |
+ return true |
|
| 946 |
+} |
|
| 947 |
+ |
|
| 948 |
+// Equals reports whether the sets s and t have the same elements. |
|
| 949 |
+func (s *Sparse) Equals(t *Sparse) bool {
|
|
| 950 |
+ if s == t {
|
|
| 951 |
+ return true |
|
| 952 |
+ } |
|
| 953 |
+ sb := s.first() |
|
| 954 |
+ tb := t.first() |
|
| 955 |
+ for {
|
|
| 956 |
+ switch {
|
|
| 957 |
+ case sb == &none && tb == &none: |
|
| 958 |
+ return true |
|
| 959 |
+ case sb == &none || tb == &none: |
|
| 960 |
+ return false |
|
| 961 |
+ case sb.offset != tb.offset: |
|
| 962 |
+ return false |
|
| 963 |
+ case sb.bits != tb.bits: |
|
| 964 |
+ return false |
|
| 965 |
+ } |
|
| 966 |
+ |
|
| 967 |
+ sb = s.next(sb) |
|
| 968 |
+ tb = t.next(tb) |
|
| 969 |
+ } |
|
| 970 |
+} |
|
| 971 |
+ |
|
| 972 |
+// String returns a human-readable description of the set s. |
|
| 973 |
+func (s *Sparse) String() string {
|
|
| 974 |
+ var buf bytes.Buffer |
|
| 975 |
+ buf.WriteByte('{')
|
|
| 976 |
+ s.forEach(func(x int) {
|
|
| 977 |
+ if buf.Len() > 1 {
|
|
| 978 |
+ buf.WriteByte(' ')
|
|
| 979 |
+ } |
|
| 980 |
+ fmt.Fprintf(&buf, "%d", x) |
|
| 981 |
+ }) |
|
| 982 |
+ buf.WriteByte('}')
|
|
| 983 |
+ return buf.String() |
|
| 984 |
+} |
|
| 985 |
+ |
|
| 986 |
+// BitString returns the set as a string of 1s and 0s denoting the sum |
|
| 987 |
+// of the i'th powers of 2, for each i in s. A radix point, always |
|
| 988 |
+// preceded by a digit, appears if the sum is non-integral. |
|
| 989 |
+// |
|
| 990 |
+// Examples: |
|
| 991 |
+// {}.BitString() = "0"
|
|
| 992 |
+// {4,5}.BitString() = "110000"
|
|
| 993 |
+// {-3}.BitString() = "0.001"
|
|
| 994 |
+// {-3,0,4,5}.BitString() = "110001.001"
|
|
| 995 |
+// |
|
| 996 |
+func (s *Sparse) BitString() string {
|
|
| 997 |
+ if s.IsEmpty() {
|
|
| 998 |
+ return "0" |
|
| 999 |
+ } |
|
| 1000 |
+ |
|
| 1001 |
+ min, max := s.Min(), s.Max() |
|
| 1002 |
+ var nbytes int |
|
| 1003 |
+ if max > 0 {
|
|
| 1004 |
+ nbytes = max |
|
| 1005 |
+ } |
|
| 1006 |
+ nbytes++ // zero bit |
|
| 1007 |
+ radix := nbytes |
|
| 1008 |
+ if min < 0 {
|
|
| 1009 |
+ nbytes += len(".") - min
|
|
| 1010 |
+ } |
|
| 1011 |
+ |
|
| 1012 |
+ b := make([]byte, nbytes) |
|
| 1013 |
+ for i := range b {
|
|
| 1014 |
+ b[i] = '0' |
|
| 1015 |
+ } |
|
| 1016 |
+ if radix < nbytes {
|
|
| 1017 |
+ b[radix] = '.' |
|
| 1018 |
+ } |
|
| 1019 |
+ s.forEach(func(x int) {
|
|
| 1020 |
+ if x >= 0 {
|
|
| 1021 |
+ x += len(".")
|
|
| 1022 |
+ } |
|
| 1023 |
+ b[radix-x] = '1' |
|
| 1024 |
+ }) |
|
| 1025 |
+ return string(b) |
|
| 1026 |
+} |
|
| 1027 |
+ |
|
| 1028 |
+// GoString returns a string showing the internal representation of |
|
| 1029 |
+// the set s. |
|
| 1030 |
+// |
|
| 1031 |
+func (s *Sparse) GoString() string {
|
|
| 1032 |
+ var buf bytes.Buffer |
|
| 1033 |
+ for b := s.first(); b != &none; b = s.next(b) {
|
|
| 1034 |
+ fmt.Fprintf(&buf, "block %p {offset=%d next=%p prev=%p",
|
|
| 1035 |
+ b, b.offset, b.next, b.prev) |
|
| 1036 |
+ for _, w := range b.bits {
|
|
| 1037 |
+ fmt.Fprintf(&buf, " 0%016x", w) |
|
| 1038 |
+ } |
|
| 1039 |
+ fmt.Fprintf(&buf, "}\n") |
|
| 1040 |
+ } |
|
| 1041 |
+ return buf.String() |
|
| 1042 |
+} |
|
| 1043 |
+ |
|
| 1044 |
+// AppendTo returns the result of appending the elements of s to slice |
|
| 1045 |
+// in order. |
|
| 1046 |
+func (s *Sparse) AppendTo(slice []int) []int {
|
|
| 1047 |
+ s.forEach(func(x int) {
|
|
| 1048 |
+ slice = append(slice, x) |
|
| 1049 |
+ }) |
|
| 1050 |
+ return slice |
|
| 1051 |
+} |
|
| 1052 |
+ |
|
| 1053 |
+// -- Testing/debugging ------------------------------------------------ |
|
| 1054 |
+ |
|
| 1055 |
+// check returns an error if the representation invariants of s are violated. |
|
| 1056 |
+func (s *Sparse) check() error {
|
|
| 1057 |
+ s.init() |
|
| 1058 |
+ if s.root.empty() {
|
|
| 1059 |
+ // An empty set must have only the root block with offset MaxInt. |
|
| 1060 |
+ if s.root.next != &s.root {
|
|
| 1061 |
+ return fmt.Errorf("multiple blocks with empty root block")
|
|
| 1062 |
+ } |
|
| 1063 |
+ if s.root.offset != MaxInt {
|
|
| 1064 |
+ return fmt.Errorf("empty set has offset %d, should be MaxInt", s.root.offset)
|
|
| 1065 |
+ } |
|
| 1066 |
+ return nil |
|
| 1067 |
+ } |
|
| 1068 |
+ for b := s.first(); ; b = s.next(b) {
|
|
| 1069 |
+ if b.offset%bitsPerBlock != 0 {
|
|
| 1070 |
+ return fmt.Errorf("bad offset modulo: %d", b.offset)
|
|
| 1071 |
+ } |
|
| 1072 |
+ if b.empty() {
|
|
| 1073 |
+ return fmt.Errorf("empty block")
|
|
| 1074 |
+ } |
|
| 1075 |
+ if b.prev.next != b {
|
|
| 1076 |
+ return fmt.Errorf("bad prev.next link")
|
|
| 1077 |
+ } |
|
| 1078 |
+ if b.next.prev != b {
|
|
| 1079 |
+ return fmt.Errorf("bad next.prev link")
|
|
| 1080 |
+ } |
|
| 1081 |
+ if b.next == &s.root {
|
|
| 1082 |
+ break |
|
| 1083 |
+ } |
|
| 1084 |
+ if b.offset >= b.next.offset {
|
|
| 1085 |
+ return fmt.Errorf("bad offset order: b.offset=%d, b.next.offset=%d",
|
|
| 1086 |
+ b.offset, b.next.offset) |
|
| 1087 |
+ } |
|
| 1088 |
+ } |
|
| 1089 |
+ return nil |
|
| 1090 |
+} |
| 0 | 1091 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,84 @@ |
| 0 |
+// Copyright 2013 The Go Authors. All rights reserved. |
|
| 1 |
+// Use of this source code is governed by a BSD-style |
|
| 2 |
+// license that can be found in the LICENSE file. |
|
| 3 |
+ |
|
| 4 |
+package intsets |
|
| 5 |
+ |
|
| 6 |
+// From Hacker's Delight, fig 5.2. |
|
| 7 |
+func popcountHD(x uint32) int {
|
|
| 8 |
+ x -= (x >> 1) & 0x55555555 |
|
| 9 |
+ x = (x & 0x33333333) + ((x >> 2) & 0x33333333) |
|
| 10 |
+ x = (x + (x >> 4)) & 0x0f0f0f0f |
|
| 11 |
+ x = x + (x >> 8) |
|
| 12 |
+ x = x + (x >> 16) |
|
| 13 |
+ return int(x & 0x0000003f) |
|
| 14 |
+} |
|
| 15 |
+ |
|
| 16 |
+var a [1 << 8]byte |
|
| 17 |
+ |
|
| 18 |
+func init() {
|
|
| 19 |
+ for i := range a {
|
|
| 20 |
+ var n byte |
|
| 21 |
+ for x := i; x != 0; x >>= 1 {
|
|
| 22 |
+ if x&1 != 0 {
|
|
| 23 |
+ n++ |
|
| 24 |
+ } |
|
| 25 |
+ } |
|
| 26 |
+ a[i] = n |
|
| 27 |
+ } |
|
| 28 |
+} |
|
| 29 |
+ |
|
| 30 |
+func popcountTable(x word) int {
|
|
| 31 |
+ return int(a[byte(x>>(0*8))] + |
|
| 32 |
+ a[byte(x>>(1*8))] + |
|
| 33 |
+ a[byte(x>>(2*8))] + |
|
| 34 |
+ a[byte(x>>(3*8))] + |
|
| 35 |
+ a[byte(x>>(4*8))] + |
|
| 36 |
+ a[byte(x>>(5*8))] + |
|
| 37 |
+ a[byte(x>>(6*8))] + |
|
| 38 |
+ a[byte(x>>(7*8))]) |
|
| 39 |
+} |
|
| 40 |
+ |
|
| 41 |
+// nlz returns the number of leading zeros of x. |
|
| 42 |
+// From Hacker's Delight, fig 5.11. |
|
| 43 |
+func nlz(x word) int {
|
|
| 44 |
+ x |= (x >> 1) |
|
| 45 |
+ x |= (x >> 2) |
|
| 46 |
+ x |= (x >> 4) |
|
| 47 |
+ x |= (x >> 8) |
|
| 48 |
+ x |= (x >> 16) |
|
| 49 |
+ x |= (x >> 32) |
|
| 50 |
+ return popcount(^x) |
|
| 51 |
+} |
|
| 52 |
+ |
|
| 53 |
+// ntz returns the number of trailing zeros of x. |
|
| 54 |
+// From Hacker's Delight, fig 5.13. |
|
| 55 |
+func ntz(x word) int {
|
|
| 56 |
+ if x == 0 {
|
|
| 57 |
+ return bitsPerWord |
|
| 58 |
+ } |
|
| 59 |
+ n := 1 |
|
| 60 |
+ if bitsPerWord == 64 {
|
|
| 61 |
+ if (x & 0xffffffff) == 0 {
|
|
| 62 |
+ n = n + 32 |
|
| 63 |
+ x = x >> 32 |
|
| 64 |
+ } |
|
| 65 |
+ } |
|
| 66 |
+ if (x & 0x0000ffff) == 0 {
|
|
| 67 |
+ n = n + 16 |
|
| 68 |
+ x = x >> 16 |
|
| 69 |
+ } |
|
| 70 |
+ if (x & 0x000000ff) == 0 {
|
|
| 71 |
+ n = n + 8 |
|
| 72 |
+ x = x >> 8 |
|
| 73 |
+ } |
|
| 74 |
+ if (x & 0x0000000f) == 0 {
|
|
| 75 |
+ n = n + 4 |
|
| 76 |
+ x = x >> 4 |
|
| 77 |
+ } |
|
| 78 |
+ if (x & 0x00000003) == 0 {
|
|
| 79 |
+ n = n + 2 |
|
| 80 |
+ x = x >> 2 |
|
| 81 |
+ } |
|
| 82 |
+ return n - int(x&1) |
|
| 83 |
+} |