commit
a077f68b1c
249 changed files with 9463 additions and 5376 deletions
File diff suppressed because it is too large
Load Diff
@ -1,20 +1,40 @@ |
||||
Recommended.Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeSurrogateFirstOnlyBytes |
||||
Recommended.Editions_Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeSurrogateFirstOnlyBytes |
||||
Recommended.Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeSurrogateFirstOnlyString |
||||
Recommended.Editions_Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeSurrogateFirstOnlyString |
||||
Recommended.Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeSurrogatePairBytes |
||||
Recommended.Editions_Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeSurrogatePairBytes |
||||
Recommended.Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeSurrogatePairString |
||||
Recommended.Editions_Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeSurrogatePairString |
||||
Recommended.Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeSurrogateSecondOnlyBytes |
||||
Recommended.Editions_Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeSurrogateSecondOnlyBytes |
||||
Recommended.Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeSurrogateSecondOnlyString |
||||
Recommended.Editions_Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeSurrogateSecondOnlyString |
||||
Recommended.Proto3.TextFormatInput.StringLiteralShortUnicodeEscapeSurrogateFirstOnlyBytes |
||||
Recommended.Editions_Proto3.TextFormatInput.StringLiteralShortUnicodeEscapeSurrogateFirstOnlyBytes |
||||
Recommended.Proto3.TextFormatInput.StringLiteralShortUnicodeEscapeSurrogateFirstOnlyString |
||||
Recommended.Editions_Proto3.TextFormatInput.StringLiteralShortUnicodeEscapeSurrogateFirstOnlyString |
||||
Recommended.Proto3.TextFormatInput.StringLiteralShortUnicodeEscapeSurrogatePairBytes |
||||
Recommended.Editions_Proto3.TextFormatInput.StringLiteralShortUnicodeEscapeSurrogatePairBytes |
||||
Recommended.Proto3.TextFormatInput.StringLiteralShortUnicodeEscapeSurrogatePairString |
||||
Recommended.Editions_Proto3.TextFormatInput.StringLiteralShortUnicodeEscapeSurrogatePairString |
||||
Recommended.Proto3.TextFormatInput.StringLiteralShortUnicodeEscapeSurrogateSecondOnlyBytes |
||||
Recommended.Editions_Proto3.TextFormatInput.StringLiteralShortUnicodeEscapeSurrogateSecondOnlyBytes |
||||
Recommended.Proto3.TextFormatInput.StringLiteralShortUnicodeEscapeSurrogateSecondOnlyString |
||||
Recommended.Editions_Proto3.TextFormatInput.StringLiteralShortUnicodeEscapeSurrogateSecondOnlyString |
||||
Recommended.Proto3.TextFormatInput.StringLiteralUnicodeEscapeSurrogatePairLongShortBytes |
||||
Recommended.Editions_Proto3.TextFormatInput.StringLiteralUnicodeEscapeSurrogatePairLongShortBytes |
||||
Recommended.Proto3.TextFormatInput.StringLiteralUnicodeEscapeSurrogatePairLongShortString |
||||
Recommended.Editions_Proto3.TextFormatInput.StringLiteralUnicodeEscapeSurrogatePairLongShortString |
||||
Recommended.Proto3.TextFormatInput.StringLiteralUnicodeEscapeSurrogatePairShortLongBytes |
||||
Recommended.Editions_Proto3.TextFormatInput.StringLiteralUnicodeEscapeSurrogatePairShortLongBytes |
||||
Recommended.Proto3.TextFormatInput.StringLiteralUnicodeEscapeSurrogatePairShortLongString |
||||
Recommended.Editions_Proto3.TextFormatInput.StringLiteralUnicodeEscapeSurrogatePairShortLongString |
||||
Required.Proto3.TextFormatInput.StringFieldBadUTF8Hex |
||||
Required.Editions_Proto3.TextFormatInput.StringFieldBadUTF8Hex |
||||
Required.Proto3.TextFormatInput.StringFieldBadUTF8Octal |
||||
Required.Editions_Proto3.TextFormatInput.StringFieldBadUTF8Octal |
||||
Required.Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeTooLargeBytes |
||||
Required.Editions_Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeTooLargeBytes |
||||
Required.Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeTooLargeString |
||||
Required.Editions_Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeTooLargeString |
||||
|
@ -0,0 +1,150 @@ |
||||
# Editions: Feature Extension Layout |
||||
|
||||
**Author:** [@mkruskal-google](https://github.com/mkruskal-google), |
||||
[@zhangskz](https://github.com/zhangskz) |
||||
|
||||
**Approved:** 2023-08-23 |
||||
|
||||
## Background |
||||
|
||||
"[What are Protobuf Editions](what-are-protobuf-editions.md)" lays out a plan |
||||
for allowing for more targeted features not owned by the protobuf team. It uses |
||||
extensions of the global features proto to implement this. One thing that was |
||||
left a bit ambiguous was *who* should own these extensions. Language, code |
||||
generator, and runtime implementations are all similar but not identical |
||||
distinctions. |
||||
|
||||
"Editions Zero Feature: utf8_validation" (not available externally, though a |
||||
later version, |
||||
"[Editions Zero: utf8_validation Without Problematic Options](editions-zero-utf8_validation.md)" |
||||
is) is a recent plan to add a new set of generator features for utf8 validation. |
||||
While the sole feature we had originally created (`legacy_closed_enum` in Java |
||||
and C++) didn't have any ambiguity here, this one did. Specifically in Python, |
||||
the current behaviors across proto2/proto3 are distinct for all 3 |
||||
implementations: pure python, Python/C++, Python/upb. |
||||
|
||||
## Overview |
||||
|
||||
In meetings, we've discussed various alternatives, captured below. The original |
||||
plan was to make feature extensions runtime implementation-specific (e.g. C++, |
||||
Java, Python, upb). There are some notable complications that came up though: |
||||
|
||||
1. **Polyglot** - it's not clear how upb or C++ runtimes should behave in |
||||
multi-language situations. Which feature sets do they consider for runtime |
||||
behaviors? *Note: this is already a serious issue today, where all proto2 |
||||
strings and many proto3 strings are completely unsafe across languages.* |
||||
|
||||
2. **Shared Implementations** - Runtimes like upb and C++ are used as backing |
||||
implementations of multiple other languages (e.g. Python, Rust, Ruby, PHP). |
||||
If we have a single set of `upb` or `cpp` features, migrating to those |
||||
shared implementations would be more difficult (since there's no independent |
||||
switches per-language). *Note: this is already the situation we're in today, |
||||
where switching the runtime implementation can cause subtle and dangerous |
||||
behavior changes.* |
||||
|
||||
Given that we only have two behaviors, and one of them is unambiguous, it seems |
||||
reasonable to punt on this decision until we have more information. We may |
||||
encounter more edge cases that require feature extensions (and give us more |
||||
information) during the rollout of edition zero. We also have a lot of freedom |
||||
to re-model features in later editions, so keeping the initial implementation as |
||||
simple as possible seems best (i.e. Alternative 2). |
||||
|
||||
## Alternatives |
||||
|
||||
### Alternative 1: Runtime Implementation Features |
||||
|
||||
Features would be per-runtime implementation as originally described in |
||||
"Editions Zero Feature: utf8_validation." For example, Protobuf Python users |
||||
would set different features depending on the backing implementation (e.g. |
||||
`features.(pb.cpp).<feature>`, `features.(pb.upb).<feature>`). |
||||
|
||||
#### Pros |
||||
|
||||
* Most consistent with range of behaviors expressible pre-Editions |
||||
|
||||
#### Cons |
||||
|
||||
* Implementation may / should not be obvious to users. |
||||
* Lack of levers specifically for language / implementation combos. For |
||||
example, there is no way to set Python-C++ behavior independently of C++ |
||||
behavior which may make migration harder from other Python implementations. |
||||
|
||||
### Alternative 2: Generator Features |
||||
|
||||
Features would be per-generator only (i.e. each protoc plugin would own one set |
||||
of features). This was the second decision we made in later discussions, and |
||||
while very similar to the above alternative, it's more inline with our goal of |
||||
making features primarily for codegen. |
||||
|
||||
For example, all Python implementations would share the same set of features |
||||
(e.g. `features.(pb.python).<feature>`). However, certain features could be |
||||
targeted to specific implementations (e.g. |
||||
`features.(pb.python).upb_utf8_validation` would only be used by Python/upb). |
||||
|
||||
#### Pros |
||||
|
||||
* Allows independent controls of shared implementations in different target |
||||
languages (e.g. Python's upb feature won't affect PHP). |
||||
|
||||
#### Cons |
||||
|
||||
* Possible complexity in upb to understand which language's features to |
||||
respect. UPB is not currently aware of what language it is being used for. |
||||
* Limits in-process sharing across languages with shared implementations (e.g. |
||||
Python upb, PHP upb) in the case of conflicting behaviors. |
||||
* Additional checks may be needed. |
||||
|
||||
### Alternative 3: Migrate to bytes |
||||
|
||||
Since this whole discussion revolves around the utf8 validation feature, one |
||||
option would be to just remove it from edition zero. Instead of adding a new |
||||
toggle for UTF8 behavior, we could simply migrate everyone who doesn't enforce |
||||
utf8 today to `bytes`. This would likely need another new *codegen* feature for |
||||
generating byte getters/setters as strings, but that wouldn't have any of the |
||||
ambiguity we're seeing today. |
||||
|
||||
Unfortunately, this doesn't seem feasible because of all the different behaviors |
||||
laid out in "Editions Zero Feature: utf8_validation." UTF8 validation isn't |
||||
really a binary on/off decision, and it can vary widely between languages. There |
||||
are many cases where UTF8 is validated in **some** languages but not others, and |
||||
there's also the C++ "hint" behavior that logs errors but allows invalid UTF8. |
||||
|
||||
**Note:** This could still be partially done in a follow-up LSC by targeting |
||||
specific combinations of the new feature that disable validation in all relevant |
||||
languages. |
||||
|
||||
#### Pros |
||||
|
||||
* Punts on the issue, we wouldn't need any upb features and C++ features would |
||||
all be code-gen only |
||||
* Simplifies the situation, avoids adding a very complicated feature in |
||||
edition zero |
||||
|
||||
#### Cons |
||||
|
||||
* Not really possible given the current complexity |
||||
* There are O(10M) proto2 string fields that would be blindly changed to bytes |
||||
|
||||
### Alternative 4: Nested Features |
||||
|
||||
Another option is to allow for shared feature set messages. For example, upb |
||||
would define a feature message, but *not* make it an extension of the global |
||||
`FeatureSet`. Instead, languages with upb implementations would have a field of |
||||
this type to allow for finer-grained controls. C++ would both extend the global |
||||
`FeatureSet` and also be allowed as a field in other languages. |
||||
|
||||
For example, python utf8 validation could be specified as: |
||||
|
||||
We could have checks during feature validation that enforce that impossible |
||||
combinations aren't specified. For example, with our current implementation |
||||
`features.(pb.python).cpp` should always be identical to `features.(pb.cpp)`, |
||||
since we don't have any mechanism for distinguishing them. |
||||
|
||||
#### Pros |
||||
|
||||
* Much more explicit than options 1 and 2 |
||||
|
||||
#### Cons |
||||
|
||||
* Maybe too explicit? Proto owners would be forced to duplicate a lot of |
||||
features |
@ -0,0 +1,144 @@ |
||||
# Legacy Syntax Editions |
||||
|
||||
**Author:** [@mkruskal-google](https://github.com/mkruskal-google) |
||||
|
||||
**Approved:** 2023-09-08 |
||||
|
||||
Should proto2/proto3 be treated as editions? |
||||
|
||||
## Background |
||||
|
||||
[Edition Zero Features](edition-zero-features.md) lays out our plan for edition |
||||
2023, which will unify proto2 and proto3. Since early in the design process, |
||||
we've discussed the possibility of making proto2 and proto3 "special" editions, |
||||
but never laid out what exactly it would look like or determined if it was |
||||
necessary. |
||||
|
||||
We recently redesigned editions to be represented as enums |
||||
([Edition Naming](edition-naming.md)), and also how edition defaults are |
||||
propagated to generators and runtimes |
||||
([Editions: Life of a FeatureSet](editions-life-of-a-featureset.md)). With these |
||||
changes, there could be an opportunity to special-case proto2 and proto3 in a |
||||
beneficial way. |
||||
|
||||
## Problem Description |
||||
|
||||
While the original plan was to keep editions and syntax orthogonal, that naively |
||||
means we'd be supporting two very different codebases. This has some serious |
||||
maintenance costs though, especially when it comes to test coverage. We could |
||||
expect to have sub-optimal test coverage of editions initially, which would |
||||
gradually become poor coverage of syntax later. Since we need to support both |
||||
syntax and editions long-term, this isn't ideal. |
||||
|
||||
In the implementation of editions in C++, we decided to unify a lot of the |
||||
infrastructure to avoid this issue. We define global feature sets for proto2 and |
||||
proto3, and try to use those internally instead of checking syntax directly. By |
||||
pushing the syntax/editions branch earlier in the stack, it gives us a lot of |
||||
indirect test coverage for editions much earlier. |
||||
|
||||
A separate issue is how Prototiller will support the conversion of syntax to |
||||
edition 2023. For features it knows about, we can hardcode defaults into the |
||||
transforms. However, third party feature owners will have no way of signaling |
||||
what the old proto2/proto3 behavior was, so Prototiller won't be able to provide |
||||
any transformations by default. They'd need to provide custom Prototiller |
||||
transforms hardcoding all of their features. |
||||
|
||||
## Recommended Solution |
||||
|
||||
We recommend adding two new special editions to our current set: |
||||
|
||||
``` |
||||
enum Edition { |
||||
EDITION_UNKNOWN = 0; |
||||
EDITION_PROTO2 = 998; |
||||
EDITION_PROTO3 = 999; |
||||
EDITION_2023 = 1000; |
||||
} |
||||
``` |
||||
|
||||
These will be treated the same as any other edition, except in our parser which |
||||
will reject `edition = "proto2"` and `edition = "proto3"` in proto files. The |
||||
real benefit here is that this allows features to specify what their |
||||
proto2/proto3 defaults are, making it easier for Prototiller to handle |
||||
migration. It also allows generators and runtimes to unify their internals more |
||||
completely, treating proto2/proto3 files exactly the same as editions. |
||||
|
||||
### Serialized Descriptors |
||||
|
||||
As we now know, there are a lot of serialized `descriptor.proto` descriptor sets |
||||
out there that need to continue working for O(months). In order to avoid |
||||
blocking edition zero for that long, we may need fallbacks in protoc for the |
||||
case where feature resolution *fails*. If the file is proto2/proto3, failure |
||||
should result in a fallback to the existing hardcoded defaults. We can remove |
||||
these later once we're willing to break stale `descriptor.proto` snapshots that |
||||
predate the changes in this doc. |
||||
|
||||
### Bootstrapping |
||||
|
||||
In order to get feature resolution running in proto2 and proto3, we need to be |
||||
able to support bootstrapped protos. For these builds, we can't use any |
||||
reflection without deadlocking, which means feature defaults can't be compiled |
||||
during runtime. We would have had to solve this problem anyway when it came time |
||||
to migrate these protos to editions, but this proposal forces our hand early. |
||||
Luckily, "Editions: Life of a FeatureSet" already set us up for this scenario, |
||||
and we have Blaze rules for embedding these defaults into code. For C++ |
||||
specifically, this will need to be checked in alongside the other bootstrapped |
||||
protos. Other languages will be able to do this more dynamically via genrules. |
||||
|
||||
### Feature Inference |
||||
|
||||
While we can calculate defaults using the same logic as in editions, actually |
||||
inferring "features" from proto2/proto3 needs some custom code. For example: |
||||
|
||||
* The `required` keyword sets `LEGACY_REQUIRED` feature |
||||
* The `optional` keyword in proto3 sets `EXPLICIT` presence |
||||
* The `group` keyword implies `DELIMITED` encoding |
||||
* The `enforce_utf8` options flips between `PACKED` and `EXPANDED` encoding |
||||
|
||||
This logic needs to be written in code, and will need to be duplicated in every |
||||
language we support. Any language-specific feature transformations will also |
||||
need to be included in that language. To make this as portable as possible, we |
||||
will define functions like: |
||||
|
||||
Each type of descriptor will have its own set of transformations that should be |
||||
applied to its features for legacy editions. |
||||
|
||||
#### Pros |
||||
|
||||
* Makes it clearer that proto2/proto3 are "like" editions |
||||
|
||||
* Gives Prototiller a little more information in the transformation from |
||||
proto2/proto3 to editions (not necessarily 2023) |
||||
|
||||
* Allows proto2/proto3 defaults to be specified in a single location |
||||
|
||||
* Makes unification of syntax/edition code easier to implement in runtimes |
||||
|
||||
* Allows cross-language proto2/proto3 testing with the conformance framework |
||||
mentioned in "Editions: Life of a FeatureSet" |
||||
|
||||
#### Cons |
||||
|
||||
* Adds special-case legacy editions, which may be somewhat confusing |
||||
|
||||
* We will need to port feature inference logic across all languages. This is |
||||
arguably cheaper than maintaining branched proto2/proto3 code in all |
||||
languages though |
||||
|
||||
## Considered Alternatives |
||||
|
||||
### Do Nothing |
||||
|
||||
If we do nothing, there will be no built-in unification of syntax and editions. |
||||
Runtimes could choose any point to split the logic. |
||||
|
||||
#### Pros |
||||
|
||||
* Requires no changes to editions code |
||||
|
||||
#### Cons |
||||
|
||||
* Likely results in lower test coverage |
||||
* May hide issues until we start rolling out edition 2023 |
||||
* Prototiller would have to hard-code proto2/proto3 defaults of features it |
||||
knows, and couldn't even try to migrate runtimes it doesn't |
@ -0,0 +1,139 @@ |
||||
# Minimum Required Edition |
||||
|
||||
**Author:** [@mcy](https://github.com/mcy) |
||||
|
||||
**Approved:** 2022-11-15 |
||||
|
||||
A versioning mechanism for descriptors that ensures old runtimes do not load |
||||
descriptors that are "too new." |
||||
|
||||
## Background |
||||
|
||||
Suppose we decide to add a novel definition like |
||||
|
||||
``` |
||||
const int32 MY_CONSTANT = 42; |
||||
``` |
||||
|
||||
to the Protobuf language. This would entail a descriptor change to track the |
||||
values of constants, but they would not be loaded properly by older runtimes. |
||||
This document describes an addition to `descriptor.proto` that prevents this |
||||
version mismatch issue. |
||||
|
||||
[Protobuf Editions](what-are-protobuf-editions.md) intends to add the concept of |
||||
an edition to Protobuf, which will be an approximately-annually incrementing |
||||
value. Because of their annual nature, and because runtimes need to be updated |
||||
to handle new features they implement regardless, we can use them as a poison |
||||
pill for old runtimes that try to load descriptors that are "too new." |
||||
|
||||
## Overview |
||||
|
||||
We propose adding a new field to `FileDescriptorProto`: |
||||
|
||||
``` |
||||
optional string minimum_required_edition = ...; |
||||
``` |
||||
|
||||
This field would exist alongside the `edition` field, and would have the |
||||
following semantics: |
||||
|
||||
Every Protobuf runtime implementation must specify the newest edition whose |
||||
constructs it can handle (at a particular rev of that implementation). If that |
||||
edition is less than `minimum_required_edition`, loading the descriptor must |
||||
fail. |
||||
|
||||
"Less than" is defined per the edition total order given in |
||||
[Life of an Edition](life-of-an-edition.md). To restate it, it is the following |
||||
algorithm: |
||||
|
||||
``` |
||||
def edition_less_than(a, b): |
||||
parts_a = a.split(".") |
||||
parts_b = b.split(".") |
||||
for i in range(0, min(len(parts_a), len(parts_b))): |
||||
if int(parts_a[i]) < int(parts_b[i]): return True |
||||
return len(a) < len(b) |
||||
``` |
||||
|
||||
`protoc` should keep track of which constructions require which minimum edition. |
||||
For example, if constants are introduced in edition 2025, but they are not |
||||
present in a file, `protoc` should not require that runtimes understand |
||||
constants by picking a lower edition, like 2023 (assuming no other construct |
||||
requires a higher edition). |
||||
|
||||
In particular, the following changes should keep the minimum edition constant, |
||||
with all other things unchanged: |
||||
|
||||
* An upgrade of the proto compiler. |
||||
* Upgrading the specified edition of a file via Prototiller. |
||||
|
||||
### Bootstrapping Concerns |
||||
|
||||
"Epochs for `descriptor.proto`" (not available externally) describes a potential |
||||
issue with bootstrapping. It is not the case here: minimum edition is only |
||||
incremented once a particular file uses a new feature. Since `descriptor.proto` |
||||
and other schemas used by `protoc` and the backends would not use new features |
||||
immediately, introducing a new feature does not immediately stop the compiler |
||||
from being able to compile itself. |
||||
|
||||
### Concerns for Schema Producers |
||||
|
||||
Schema producers should consider changes to their schemas that increase the |
||||
minimum required edition to be breaking changes, since it will stop compiled |
||||
descriptors from being loaded at runtime. |
||||
|
||||
## Recommendation |
||||
|
||||
We recommend adding the aforementioned minimum required edition field, along |
||||
with the semantics it entails. This logic should be implemented entirely in the |
||||
protoc frontend. |
||||
|
||||
## Alternatives |
||||
|
||||
### Use a Non-Editions Version Number |
||||
|
||||
Rather than using the editions value, use some other version number. This number |
||||
would be incremented rarely (3-5 year horizon). This is the approach proposed |
||||
in "Epochs for `descriptor.proto`." |
||||
|
||||
#### Pros |
||||
|
||||
* Does not re-use the editions value for a semantically-different meaning; the |
||||
edition remains being "just" a key into a table of features defaults. |
||||
|
||||
#### Cons |
||||
|
||||
* Introduces another version number to Protobuf that increments at its own |
||||
cadence. |
||||
* Could potentially be confused with the edition value, even though they serve |
||||
distinct purposes. |
||||
|
||||
### Minimum Required Edition Should Not Be Minimal |
||||
|
||||
The proto compiler should not guarantee that the minimum required edition is as |
||||
small as it could possibly be. |
||||
|
||||
#### Pros |
||||
|
||||
* Reduces implementation burden. |
||||
|
||||
#### Cons |
||||
|
||||
* This introduces situations where an upgrade of the proto compiler, or an |
||||
innocuous change to a schema, can lead the the minimum required edition |
||||
being incremented. This is a problem for schema producers. |
||||
|
||||
### Do Nothing |
||||
|
||||
#### Pros |
||||
|
||||
* Reduces churn in runtimes, since they do not need to implement new handling |
||||
for new *editions* (as contrasted to just *features)* regularly. |
||||
* Avoids a situation where old software cannot load new descriptors at |
||||
runtime. |
||||
|
||||
#### Cons |
||||
|
||||
* Commits us to never changing the descriptor wire format in |
||||
backwards-incompatible ways, which has far-reaching effects on evolution. |
||||
These consequences are discussed in "Epochs for `descriptor.proto`." |
@ -0,0 +1,597 @@ |
||||
<?php |
||||
|
||||
require_once('test_base.php'); |
||||
require_once('test_util.php'); |
||||
|
||||
use Google\Protobuf\Internal\RepeatedField; |
||||
use Google\Protobuf\Internal\GPBType; |
||||
use Foo\EmptyAnySerialization; |
||||
use Foo\TestInt32Value; |
||||
use Foo\TestStringValue; |
||||
use Foo\TestAny; |
||||
use Foo\TestEnum; |
||||
use Foo\TestLargeFieldNumber; |
||||
use Foo\TestMessage; |
||||
use Foo\TestMessage\Sub; |
||||
use Foo\TestPackedMessage; |
||||
use Foo\TestRandomFieldOrder; |
||||
use Foo\TestUnpackedMessage; |
||||
use Google\Protobuf\Any; |
||||
use Google\Protobuf\DoubleValue; |
||||
use Google\Protobuf\FieldMask; |
||||
use Google\Protobuf\FloatValue; |
||||
use Google\Protobuf\Int32Value; |
||||
use Google\Protobuf\UInt32Value; |
||||
use Google\Protobuf\Int64Value; |
||||
use Google\Protobuf\UInt64Value; |
||||
use Google\Protobuf\BoolValue; |
||||
use Google\Protobuf\StringValue; |
||||
use Google\Protobuf\BytesValue; |
||||
use Google\Protobuf\Value; |
||||
use Google\Protobuf\ListValue; |
||||
use Google\Protobuf\Struct; |
||||
use Google\Protobuf\GPBEmpty; |
||||
|
||||
class DebugInfoTest extends TestBase |
||||
{ |
||||
public function setUp(): void |
||||
{ |
||||
if (extension_loaded('protobuf')) { |
||||
$this->markTestSkipped('__debugInfo is not supported for the protobuf extension'); |
||||
} |
||||
} |
||||
|
||||
public function testVarDumpOutput() |
||||
{ |
||||
$m = new DoubleValue(); |
||||
$m->setValue(1.5); |
||||
var_dump($m); |
||||
$regex = <<<EOL |
||||
object(Google\Protobuf\DoubleValue)#%s (1) { |
||||
["value"]=> |
||||
float(1.5) |
||||
} |
||||
EOL; |
||||
$this->expectOutputRegex('/' . sprintf(preg_quote($regex), '\d+') . '/'); |
||||
} |
||||
|
||||
public function testTopLevelDoubleValue() |
||||
{ |
||||
$m = new DoubleValue(); |
||||
$m->setValue(1.5); |
||||
$this->assertSame(['value' => 1.5], $m->__debugInfo()); |
||||
} |
||||
|
||||
public function testTopLevelFloatValue() |
||||
{ |
||||
$m = new FloatValue(); |
||||
$m->setValue(1.5); |
||||
$this->assertSame(['value' => 1.5], $m->__debugInfo()); |
||||
} |
||||
|
||||
public function testTopLevelInt32Value() |
||||
{ |
||||
$m = new Int32Value(); |
||||
$m->setValue(1); |
||||
$this->assertSame(['value' => 1], $m->__debugInfo()); |
||||
} |
||||
|
||||
public function testTopLevelUInt32Value() |
||||
{ |
||||
$m = new UInt32Value(); |
||||
$m->setValue(1); |
||||
$this->assertSame(['value' => 1], $m->__debugInfo()); |
||||
} |
||||
|
||||
public function testTopLevelInt64Value() |
||||
{ |
||||
$m = new Int64Value(); |
||||
$m->setValue(1); |
||||
$this->assertSame(['value' => '1'], $m->__debugInfo()); |
||||
} |
||||
|
||||
public function testTopLevelUInt64Value() |
||||
{ |
||||
$m = new UInt64Value(); |
||||
$m->setValue(1); |
||||
$this->assertSame(['value' => '1'], $m->__debugInfo()); |
||||
} |
||||
|
||||
public function testTopLevelStringValue() |
||||
{ |
||||
$m = new StringValue(); |
||||
$m->setValue("a"); |
||||
$this->assertSame(['value' => 'a'], $m->__debugInfo()); |
||||
} |
||||
|
||||
public function testTopLevelBoolValue() |
||||
{ |
||||
$m = new BoolValue(); |
||||
$m->setValue(true); |
||||
$this->assertSame(['value' => true], $m->__debugInfo()); |
||||
} |
||||
|
||||
public function testTopLevelBytesValue() |
||||
{ |
||||
$m = new BytesValue(); |
||||
$m->setValue("a"); |
||||
$this->assertSame(['value' => 'YQ=='], $m->__debugInfo()); |
||||
} |
||||
|
||||
public function testTopLevelLongBytesValue() |
||||
{ |
||||
$m = new BytesValue(); |
||||
$data = $this->generateRandomString(12007); |
||||
$m->setValue($data); |
||||
$expected = base64_encode($data); |
||||
$this->assertSame(['value' => $expected], $m->__debugInfo()); |
||||
} |
||||
|
||||
public function testJsonEncodeNullSubMessage() |
||||
{ |
||||
$from = new TestMessage(); |
||||
$from->setOptionalMessage(null); |
||||
$data = $from->__debugInfo(); |
||||
$this->assertEquals([], $data); |
||||
} |
||||
|
||||
public function testDuration() |
||||
{ |
||||
$m = new Google\Protobuf\Duration(); |
||||
$m->setSeconds(1234); |
||||
$m->setNanos(999999999); |
||||
$this->assertEquals([ |
||||
'seconds' => 1234, |
||||
'nanos' => 999999999, |
||||
], $m->__debugInfo()); |
||||
} |
||||
|
||||
public function testTimestamp() |
||||
{ |
||||
$m = new Google\Protobuf\Timestamp(); |
||||
$m->setSeconds(946684800); |
||||
$m->setNanos(123456789); |
||||
$this->assertEquals([ |
||||
'seconds' => 946684800, |
||||
'nanos' => 123456789, |
||||
], $m->__debugInfo()); |
||||
} |
||||
|
||||
public function testTopLevelValue() |
||||
{ |
||||
$m = new Value(); |
||||
$m->setStringValue("a"); |
||||
$this->assertSame(['stringValue' => 'a'], $m->__debugInfo()); |
||||
|
||||
$m = new Value(); |
||||
$m->setNumberValue(1.5); |
||||
$this->assertSame(['numberValue' => 1.5], $m->__debugInfo()); |
||||
|
||||
$m = new Value(); |
||||
$m->setBoolValue(true); |
||||
$this->assertSame(['boolValue' => true], $m->__debugInfo()); |
||||
|
||||
$m = new Value(); |
||||
$m->setNullValue(\Google\Protobuf\NullValue::NULL_VALUE); |
||||
$this->assertSame(['nullValue' => 0], $m->__debugInfo()); |
||||
|
||||
$m = new Value(); |
||||
$struct = new Struct(); |
||||
$map = $struct->getFields(); |
||||
$sub = new Value(); |
||||
$sub->setNumberValue(1.5); |
||||
$map["a"] = $sub; |
||||
$m->setStructValue($struct); |
||||
$this->assertSame(['structValue' => ['a' => 1.5]], $m->__debugInfo()); |
||||
|
||||
$m = new Value(); |
||||
$list = new ListValue(); |
||||
$arr = $list->getValues(); |
||||
$sub = new Value(); |
||||
$sub->setNumberValue(1.5); |
||||
$arr[] = $sub; |
||||
$m->setListValue($list); |
||||
$this->assertSame(['listValue' => [1.5]], $m->__debugInfo()); |
||||
} |
||||
|
||||
public function testTopLevelListValue() |
||||
{ |
||||
$m = new ListValue(); |
||||
$arr = $m->getValues(); |
||||
$sub = new Value(); |
||||
$sub->setNumberValue(1.5); |
||||
$arr[] = $sub; |
||||
$this->assertSame([1.5], $m->__debugInfo()); |
||||
} |
||||
|
||||
public function testEmptyListValue() |
||||
{ |
||||
$m = new Struct(); |
||||
$m->setFields(['test' => (new Value())->setListValue(new ListValue())]); |
||||
$this->assertSame(['test' => []], $m->__debugInfo()); |
||||
} |
||||
|
||||
public function testTopLevelStruct() |
||||
{ |
||||
$m = new Struct(); |
||||
$map = $m->getFields(); |
||||
$sub = new Value(); |
||||
$sub->setNumberValue(1.5); |
||||
$map["a"] = $sub; |
||||
$this->assertSame(['a' => 1.5], $m->__debugInfo()); |
||||
} |
||||
|
||||
public function testEmptyStruct() |
||||
{ |
||||
$m = new Struct(); |
||||
$m->setFields(['test' => (new Value())->setStructValue(new Struct())]); |
||||
$this->assertSame(['test' => []], $m->__debugInfo()); |
||||
} |
||||
|
||||
public function testEmptyValue() |
||||
{ |
||||
$m = new Value(); |
||||
$this->assertSame([], $m->__debugInfo()); |
||||
} |
||||
|
||||
public function testTopLevelAny() |
||||
{ |
||||
// Test a normal message. |
||||
$packed = new TestMessage(); |
||||
$packed->setOptionalInt32(123); |
||||
$packed->setOptionalString("abc"); |
||||
|
||||
$m = new Any(); |
||||
$m->pack($packed); |
||||
$expected = [ |
||||
'@type' => 'type.googleapis.com/foo.TestMessage', |
||||
'optionalInt32' => 123, |
||||
'optionalString' => 'abc', |
||||
]; |
||||
$result = $m->__debugInfo(); |
||||
$this->assertSame($expected, $result); |
||||
|
||||
// Test a well known message. |
||||
$packed = new Int32Value(); |
||||
$packed->setValue(123); |
||||
|
||||
$m = new Any(); |
||||
$m->pack($packed); |
||||
$this->assertSame([ |
||||
'@type' => 'type.googleapis.com/google.protobuf.Int32Value', |
||||
'value' => 123 |
||||
], $m->__debugInfo()); |
||||
|
||||
// Test an Any message. |
||||
$outer = new Any(); |
||||
$outer->pack($m); |
||||
$this->assertSame([ |
||||
'@type' => 'type.googleapis.com/google.protobuf.Any', |
||||
'value' => [ |
||||
'@type' => 'type.googleapis.com/google.protobuf.Int32Value', |
||||
'value' => 123 |
||||
], |
||||
], $outer->__debugInfo()); |
||||
|
||||
// Test a Timestamp message. |
||||
$packed = new Google\Protobuf\Timestamp(); |
||||
$packed->setSeconds(946684800); |
||||
$packed->setNanos(123456789); |
||||
$m = new Any(); |
||||
$m->pack($packed); |
||||
$this->assertSame([ |
||||
'@type' => 'type.googleapis.com/google.protobuf.Timestamp', |
||||
'value' => '2000-01-01T00:00:00.123456789Z', |
||||
], $m->__debugInfo()); |
||||
} |
||||
|
||||
public function testAnyWithDefaultWrapperMessagePacked() |
||||
{ |
||||
$any = new Any(); |
||||
$any->pack(new TestInt32Value([ |
||||
'field' => new Int32Value(['value' => 0]), |
||||
])); |
||||
$this->assertSame( |
||||
['@type' => 'type.googleapis.com/foo.TestInt32Value', 'field' => 0], |
||||
$any->__debugInfo() |
||||
); |
||||
} |
||||
|
||||
public function testTopLevelFieldMask() |
||||
{ |
||||
$m = new FieldMask(); |
||||
$m->setPaths(["foo.bar_baz", "qux"]); |
||||
$this->assertSame(['paths' => ['foo.bar_baz', 'qux']], $m->__debugInfo()); |
||||
} |
||||
|
||||
public function testEmptyAnySerialization() |
||||
{ |
||||
$m = new EmptyAnySerialization(); |
||||
|
||||
$any = new Any(); |
||||
$any->pack($m); |
||||
|
||||
$data = $any->__debugInfo(); |
||||
$this->assertEquals(['@type' => 'type.googleapis.com/foo.EmptyAnySerialization'], $data); |
||||
} |
||||
|
||||
public function testRepeatedStringValue() |
||||
{ |
||||
$m = new TestStringValue(); |
||||
$r = [new StringValue(['value' => 'a'])]; |
||||
$m->setRepeatedField($r); |
||||
$this->assertSame(['repeatedField' => ['a']], $m->__debugInfo()); |
||||
} |
||||
|
||||
public function testMapStringValue() |
||||
{ |
||||
$m = new TestStringValue(); |
||||
$m->mergeFromJsonString("{\"map_field\":{\"1\": \"a\"}}"); |
||||
$this->assertSame(['mapField' => [1 => 'a']], $m->__debugInfo()); |
||||
} |
||||
|
||||
public function testNestedAny() |
||||
{ |
||||
// Make sure packed message has been created at least once. |
||||
$m = new TestAny(); |
||||
$m->mergeFromJsonString( |
||||
"{\"any\":{\"optionalInt32\": 1, " . |
||||
"\"@type\":\"type.googleapis.com/foo.TestMessage\", " . |
||||
"\"optionalInt64\": 2}}"); |
||||
$this->assertSame([ |
||||
'any' => [ |
||||
'@type' => 'type.googleapis.com/foo.TestMessage', |
||||
'optionalInt32' => 1, |
||||
'optionalInt64' => '2', |
||||
] |
||||
], $m->__debugInfo()); |
||||
} |
||||
|
||||
public function testEnum() |
||||
{ |
||||
$m = new TestMessage(); |
||||
$m->setOneofEnum(TestEnum::ZERO); |
||||
$this->assertSame(['oneofEnum' => 'ZERO'], $m->__debugInfo()); |
||||
} |
||||
|
||||
public function testTopLevelRepeatedField() |
||||
{ |
||||
$r1 = new RepeatedField(GPBType::class); |
||||
$r1[] = 'a'; |
||||
$this->assertSame(['a'], $r1->__debugInfo()); |
||||
|
||||
$r2 = new RepeatedField(TestMessage::class); |
||||
$r2[] = new TestMessage(['optional_int32' => -42]); |
||||
$r2[] = new TestMessage(['optional_int64' => 43]); |
||||
$this->assertSame([ |
||||
['optionalInt32' => -42], |
||||
['optionalInt64' => '43'], |
||||
], $r2->__debugInfo()); |
||||
|
||||
$r3 = new RepeatedField(RepeatedField::class); |
||||
$r3[] = $r1; |
||||
$r3[] = $r2; |
||||
|
||||
$this->assertSame([ |
||||
['a'], |
||||
[ |
||||
['optionalInt32' => -42], |
||||
['optionalInt64' => '43'], |
||||
], |
||||
], $r3->__debugInfo()); |
||||
} |
||||
|
||||
public function testEverything() |
||||
{ |
||||
$m = new TestMessage([ |
||||
'optional_int32' => -42, |
||||
'optional_int64' => -43, |
||||
'optional_uint32' => 42, |
||||
'optional_uint64' => 43, |
||||
'optional_sint32' => -44, |
||||
'optional_sint64' => -45, |
||||
'optional_fixed32' => 46, |
||||
'optional_fixed64' => 47, |
||||
'optional_sfixed32' => -46, |
||||
'optional_sfixed64' => -47, |
||||
'optional_float' => 1.5, |
||||
'optional_double' => 1.6, |
||||
'optional_bool' => true, |
||||
'optional_string' => 'a', |
||||
'optional_bytes' => 'bbbb', |
||||
'optional_enum' => TestEnum::ONE, |
||||
'optional_message' => new Sub([ |
||||
'a' => 33 |
||||
]), |
||||
'repeated_int32' => [-42, -52], |
||||
'repeated_int64' => [-43, -53], |
||||
'repeated_uint32' => [42, 52], |
||||
'repeated_uint64' => [43, 53], |
||||
'repeated_sint32' => [-44, -54], |
||||
'repeated_sint64' => [-45, -55], |
||||
'repeated_fixed32' => [46, 56], |
||||
'repeated_fixed64' => [47, 57], |
||||
'repeated_sfixed32' => [-46, -56], |
||||
'repeated_sfixed64' => [-47, -57], |
||||
'repeated_float' => [1.5, 2.5], |
||||
'repeated_double' => [1.6, 2.6], |
||||
'repeated_bool' => [true, false], |
||||
'repeated_string' => ['a', 'c'], |
||||
'repeated_bytes' => ['bbbb', 'dddd'], |
||||
'repeated_enum' => [TestEnum::ZERO, TestEnum::ONE], |
||||
'repeated_message' => [new Sub(['a' => 34]), |
||||
new Sub(['a' => 35])], |
||||
'map_int32_int32' => [-62 => -62], |
||||
'map_int64_int64' => [-63 => -63], |
||||
'map_uint32_uint32' => [62 => 62], |
||||
'map_uint64_uint64' => [63 => 63], |
||||
'map_sint32_sint32' => [-64 => -64], |
||||
'map_sint64_sint64' => [-65 => -65], |
||||
'map_fixed32_fixed32' => [66 => 66], |
||||
'map_fixed64_fixed64' => [67 => 67], |
||||
'map_sfixed32_sfixed32' => [-68 => -68], |
||||
'map_sfixed64_sfixed64' => [-69 => -69], |
||||
'map_int32_float' => [1 => 3.5], |
||||
'map_int32_double' => [1 => 3.6], |
||||
'map_bool_bool' => [true => true], |
||||
'map_string_string' => ['e' => 'e'], |
||||
'map_int32_bytes' => [1 => 'ffff'], |
||||
'map_int32_enum' => [1 => TestEnum::ONE], |
||||
'map_int32_message' => [1 => new Sub(['a' => 36])], |
||||
]); |
||||
|
||||
$this->assertSame([ |
||||
'optionalInt32' => -42, |
||||
'optionalInt64' => '-43', |
||||
'optionalUint32' => 42, |
||||
'optionalUint64' => '43', |
||||
'optionalSint32' => -44, |
||||
'optionalSint64' => '-45', |
||||
'optionalFixed32' => 46, |
||||
'optionalFixed64' => '47', |
||||
'optionalSfixed32' => -46, |
||||
'optionalSfixed64' => '-47', |
||||
'optionalFloat' => 1.5, |
||||
'optionalDouble' => 1.6, |
||||
'optionalBool' => true, |
||||
'optionalString' => 'a', |
||||
'optionalBytes' => 'YmJiYg==', |
||||
'optionalEnum' => 'ONE', |
||||
'optionalMessage' => [ |
||||
'a' => 33, |
||||
], |
||||
'repeatedInt32' => [ |
||||
-42, |
||||
-52, |
||||
], |
||||
'repeatedInt64' => [ |
||||
'-43', |
||||
'-53', |
||||
], |
||||
'repeatedUint32' => [ |
||||
42, |
||||
52, |
||||
], |
||||
'repeatedUint64' => [ |
||||
'43', |
||||
'53', |
||||
], |
||||
'repeatedSint32' => [ |
||||
-44, |
||||
-54, |
||||
], |
||||
'repeatedSint64' => [ |
||||
'-45', |
||||
'-55', |
||||
], |
||||
'repeatedFixed32' => [ |
||||
46, |
||||
56, |
||||
], |
||||
'repeatedFixed64' => [ |
||||
'47', |
||||
'57', |
||||
], |
||||
'repeatedSfixed32' => [ |
||||
-46, |
||||
-56, |
||||
], |
||||
'repeatedSfixed64' => [ |
||||
'-47', |
||||
'-57', |
||||
], |
||||
'repeatedFloat' => [ |
||||
1.5, |
||||
2.5, |
||||
], |
||||
'repeatedDouble' => [ |
||||
1.6, |
||||
2.6, |
||||
], |
||||
'repeatedBool' => [ |
||||
true, |
||||
false, |
||||
], |
||||
'repeatedString' => [ |
||||
'a', |
||||
'c', |
||||
], |
||||
'repeatedBytes' => [ |
||||
'YmJiYg==', |
||||
'ZGRkZA==', |
||||
], |
||||
'repeatedEnum' => [ |
||||
'ZERO', |
||||
'ONE', |
||||
], |
||||
'repeatedMessage' => [ |
||||
[ |
||||
'a' => 34, |
||||
], |
||||
[ |
||||
'a' => 35, |
||||
], |
||||
], |
||||
'mapInt32Int32' => [ |
||||
-62 => -62, |
||||
], |
||||
'mapInt64Int64' => [ |
||||
-63 => '-63', |
||||
], |
||||
'mapUint32Uint32' => [ |
||||
62 => 62, |
||||
], |
||||
'mapUint64Uint64' => [ |
||||
63 => '63', |
||||
], |
||||
'mapSint32Sint32' => [ |
||||
-64 => -64, |
||||
], |
||||
'mapSint64Sint64' => [ |
||||
-65 => '-65', |
||||
], |
||||
'mapFixed32Fixed32' => [ |
||||
66 => 66, |
||||
], |
||||
'mapFixed64Fixed64' => [ |
||||
67 => '67', |
||||
], |
||||
'mapSfixed32Sfixed32' => [ |
||||
-68 => -68, |
||||
], |
||||
'mapSfixed64Sfixed64' => [ |
||||
-69 => '-69', |
||||
], |
||||
'mapInt32Float' => [ |
||||
1 => 3.5, |
||||
], |
||||
'mapInt32Double' => [ |
||||
1 => 3.6, |
||||
], |
||||
'mapBoolBool' => [ |
||||
'true' => true, |
||||
], |
||||
'mapStringString' => [ |
||||
'e' => 'e', |
||||
], |
||||
'mapInt32Bytes' => [ |
||||
1 => 'ZmZmZg==', |
||||
], |
||||
'mapInt32Enum' => [ |
||||
1 => 'ONE', |
||||
], |
||||
'mapInt32Message' => [ |
||||
1 => ['a' => 36], |
||||
], |
||||
], $m->__debugInfo()); |
||||
} |
||||
|
||||
private function generateRandomString($length = 10) |
||||
{ |
||||
$randomString = str_repeat("+", $length); |
||||
for ($i = 0; $i < $length; $i++) { |
||||
$randomString[$i] = chr(rand(0, 255)); |
||||
} |
||||
return $randomString; |
||||
} |
||||
} |
@ -0,0 +1,39 @@ |
||||
#include "google/protobuf/repeated_field.h" |
||||
|
||||
extern "C" { |
||||
|
||||
#define expose_repeated_field_methods(ty, rust_ty) \ |
||||
google::protobuf::RepeatedField<ty>* __pb_rust_RepeatedField_##rust_ty##_new() { \
|
||||
return new google::protobuf::RepeatedField<ty>(); \
|
||||
} \
|
||||
void __pb_rust_RepeatedField_##rust_ty##_add(google::protobuf::RepeatedField<ty>* r, \
|
||||
ty val) { \
|
||||
r->Add(val); \
|
||||
} \
|
||||
size_t __pb_rust_RepeatedField_##rust_ty##_size( \
|
||||
google::protobuf::RepeatedField<ty>* r) { \
|
||||
return r->size(); \
|
||||
} \
|
||||
ty __pb_rust_RepeatedField_##rust_ty##_get(google::protobuf::RepeatedField<ty>* r, \
|
||||
size_t index) { \
|
||||
return r->Get(index); \
|
||||
} \
|
||||
void __pb_rust_RepeatedField_##rust_ty##_set(google::protobuf::RepeatedField<ty>* r, \
|
||||
size_t index, ty val) { \
|
||||
return r->Set(index, val); \
|
||||
} \
|
||||
void __pb_rust_RepeatedField_##rust_ty##_copy_from( \
|
||||
google::protobuf::RepeatedField<ty> const& src, google::protobuf::RepeatedField<ty>& dst) { \
|
||||
dst.CopyFrom(src); \
|
||||
} |
||||
|
||||
expose_repeated_field_methods(int32_t, i32); |
||||
expose_repeated_field_methods(uint32_t, u32); |
||||
expose_repeated_field_methods(float, f32); |
||||
expose_repeated_field_methods(double, f64); |
||||
expose_repeated_field_methods(bool, bool); |
||||
expose_repeated_field_methods(uint64_t, u64); |
||||
expose_repeated_field_methods(int64_t, i64); |
||||
|
||||
#undef expose_repeated_field_methods |
||||
} |
@ -0,0 +1,233 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2023 Google LLC. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
/// Repeated scalar fields are implemented around the runtime-specific
|
||||
/// `RepeatedField` struct. `RepeatedField` stores an opaque pointer to the
|
||||
/// runtime-specific representation of a repeated scalar (`upb_Array*` on upb,
|
||||
/// and `RepeatedField<T>*` on cpp).
|
||||
use std::marker::PhantomData; |
||||
|
||||
use crate::{ |
||||
Mut, MutProxy, Proxied, SettableValue, View, ViewProxy, |
||||
__internal::{Private, RawRepeatedField}, |
||||
__runtime::{RepeatedField, RepeatedFieldInner}, |
||||
primitive::PrimitiveMut, |
||||
vtable::ProxiedWithRawVTable, |
||||
}; |
||||
|
||||
#[derive(Clone, Copy)] |
||||
pub struct RepeatedFieldRef<'a> { |
||||
pub repeated_field: RawRepeatedField, |
||||
pub _phantom: PhantomData<&'a mut ()>, |
||||
} |
||||
|
||||
unsafe impl<'a> Send for RepeatedFieldRef<'a> {} |
||||
unsafe impl<'a> Sync for RepeatedFieldRef<'a> {} |
||||
|
||||
#[derive(Clone, Copy)] |
||||
#[repr(transparent)] |
||||
pub struct RepeatedView<'a, T: ?Sized> { |
||||
inner: RepeatedField<'a, T>, |
||||
} |
||||
|
||||
unsafe impl<'a, T: ProxiedWithRawVTable> Sync for RepeatedView<'a, T> {} |
||||
unsafe impl<'a, T: ProxiedWithRawVTable> Send for RepeatedView<'a, T> {} |
||||
|
||||
impl<'msg, T: ?Sized> RepeatedView<'msg, T> { |
||||
pub fn from_inner(_private: Private, inner: RepeatedFieldInner<'msg>) -> Self { |
||||
Self { inner: RepeatedField::<'msg>::from_inner(_private, inner) } |
||||
} |
||||
} |
||||
|
||||
pub struct RepeatedFieldIter<'a, T> { |
||||
inner: RepeatedField<'a, T>, |
||||
current_index: usize, |
||||
} |
||||
|
||||
impl<'a, T> std::fmt::Debug for RepeatedView<'a, T> { |
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
||||
f.debug_tuple("RepeatedView").finish() |
||||
} |
||||
} |
||||
|
||||
#[repr(transparent)] |
||||
#[derive(Debug)] |
||||
pub struct RepeatedMut<'a, T: ?Sized> { |
||||
inner: RepeatedField<'a, T>, |
||||
} |
||||
|
||||
unsafe impl<'a, T: ProxiedWithRawVTable> Sync for RepeatedMut<'a, T> {} |
||||
|
||||
impl<'msg, T: ?Sized> RepeatedMut<'msg, T> { |
||||
pub fn from_inner(_private: Private, inner: RepeatedFieldInner<'msg>) -> Self { |
||||
Self { inner: RepeatedField::from_inner(_private, inner) } |
||||
} |
||||
pub fn as_mut(&self) -> RepeatedMut<'_, T> { |
||||
Self { inner: self.inner } |
||||
} |
||||
} |
||||
|
||||
impl<'a, T> std::ops::Deref for RepeatedMut<'a, T> { |
||||
type Target = RepeatedView<'a, T>; |
||||
fn deref(&self) -> &Self::Target { |
||||
// SAFETY:
|
||||
// - `Repeated{View,Mut}<'a, T>` are both `#[repr(transparent)]` over
|
||||
// `RepeatedField<'a, T>`.
|
||||
// - `RepeatedField` is a type alias for `NonNull`.
|
||||
unsafe { &*(self as *const Self as *const RepeatedView<'a, T>) } |
||||
} |
||||
} |
||||
|
||||
pub struct RepeatedFieldIterMut<'a, T> { |
||||
inner: RepeatedMut<'a, T>, |
||||
current_index: usize, |
||||
} |
||||
|
||||
pub struct Repeated<T>(PhantomData<T>); |
||||
|
||||
macro_rules! impl_repeated_primitives { |
||||
($($t:ty),*) => { |
||||
$( |
||||
impl Proxied for Repeated<$t> { |
||||
type View<'a> = RepeatedView<'a, $t>; |
||||
type Mut<'a> = RepeatedMut<'a, $t>; |
||||
} |
||||
|
||||
impl<'a> ViewProxy<'a> for RepeatedView<'a, $t> { |
||||
type Proxied = Repeated<$t>; |
||||
|
||||
fn as_view(&self) -> View<'_, Self::Proxied> { |
||||
*self |
||||
} |
||||
|
||||
fn into_view<'shorter>(self) -> View<'shorter, Self::Proxied> |
||||
where 'a: 'shorter, |
||||
{ |
||||
RepeatedView { inner: self.inner } |
||||
} |
||||
} |
||||
|
||||
impl<'a> ViewProxy<'a> for RepeatedMut<'a, $t> { |
||||
type Proxied = Repeated<$t>; |
||||
|
||||
fn as_view(&self) -> View<'_, Self::Proxied> { |
||||
**self |
||||
} |
||||
|
||||
fn into_view<'shorter>(self) -> View<'shorter, Self::Proxied> |
||||
where 'a: 'shorter, |
||||
{ |
||||
*self.into_mut::<'shorter>() |
||||
} |
||||
} |
||||
|
||||
impl<'a> MutProxy<'a> for RepeatedMut<'a, $t> { |
||||
fn as_mut(&mut self) -> Mut<'_, Self::Proxied> { |
||||
RepeatedMut { inner: self.inner } |
||||
} |
||||
|
||||
fn into_mut<'shorter>(self) -> Mut<'shorter, Self::Proxied> |
||||
where 'a: 'shorter, |
||||
{ |
||||
RepeatedMut { inner: self.inner } |
||||
} |
||||
} |
||||
|
||||
impl <'a> SettableValue<Repeated<$t>> for RepeatedView<'a, $t> { |
||||
fn set_on(self, _private: Private, mut mutator: Mut<'_, Repeated<$t>>) { |
||||
mutator.copy_from(self); |
||||
} |
||||
} |
||||
|
||||
impl<'a> RepeatedView<'a, $t> { |
||||
pub fn len(&self) -> usize { |
||||
self.inner.len() |
||||
} |
||||
pub fn is_empty(&self) -> bool { |
||||
self.len() == 0 |
||||
} |
||||
pub fn get(&self, index: usize) -> Option<$t> { |
||||
self.inner.get(index) |
||||
} |
||||
pub fn iter(&self) -> RepeatedFieldIter<'_, $t> { |
||||
(*self).into_iter() |
||||
} |
||||
} |
||||
|
||||
impl<'a> RepeatedMut<'a, $t> { |
||||
pub fn push(&mut self, val: $t) { |
||||
self.inner.push(val) |
||||
} |
||||
pub fn set(&mut self, index: usize, val: $t) { |
||||
self.inner.set(index, val) |
||||
} |
||||
pub fn get_mut(&mut self, index: usize) -> Option<Mut<'_, $t>> { |
||||
if index >= self.len() { |
||||
return None; |
||||
} |
||||
Some(PrimitiveMut::Repeated(self.as_mut(), index)) |
||||
} |
||||
pub fn iter(&self) -> RepeatedFieldIter<'_, $t> { |
||||
self.as_view().into_iter() |
||||
} |
||||
pub fn iter_mut(&mut self) -> RepeatedFieldIterMut<'_, $t> { |
||||
self.as_mut().into_iter() |
||||
} |
||||
pub fn copy_from(&mut self, src: RepeatedView<'_, $t>) { |
||||
self.inner.copy_from(&src.inner); |
||||
} |
||||
} |
||||
|
||||
impl<'a> std::iter::Iterator for RepeatedFieldIter<'a, $t> { |
||||
type Item = $t; |
||||
fn next(&mut self) -> Option<Self::Item> { |
||||
let val = self.inner.get(self.current_index); |
||||
if val.is_some() { |
||||
self.current_index += 1; |
||||
} |
||||
val |
||||
} |
||||
} |
||||
|
||||
impl<'a> std::iter::IntoIterator for RepeatedView<'a, $t> { |
||||
type Item = $t; |
||||
type IntoIter = RepeatedFieldIter<'a, $t>; |
||||
fn into_iter(self) -> Self::IntoIter { |
||||
RepeatedFieldIter { inner: self.inner, current_index: 0 } |
||||
} |
||||
} |
||||
|
||||
impl <'a> std::iter::Iterator for RepeatedFieldIterMut<'a, $t> { |
||||
type Item = Mut<'a, $t>; |
||||
fn next(&mut self) -> Option<Self::Item> { |
||||
if self.current_index >= self.inner.len() { |
||||
return None; |
||||
} |
||||
let elem = PrimitiveMut::Repeated( |
||||
// While this appears to allow mutable aliasing
|
||||
// (multiple `Self::Item`s can co-exist), each `Item`
|
||||
// only references a specific unique index.
|
||||
RepeatedMut{ inner: self.inner.inner }, |
||||
self.current_index, |
||||
); |
||||
self.current_index += 1; |
||||
Some(elem) |
||||
} |
||||
} |
||||
|
||||
impl<'a> std::iter::IntoIterator for RepeatedMut<'a, $t> { |
||||
type Item = Mut<'a, $t>; |
||||
type IntoIter = RepeatedFieldIterMut<'a, $t>; |
||||
fn into_iter(self) -> Self::IntoIter { |
||||
RepeatedFieldIterMut { inner: self, current_index: 0 } |
||||
} |
||||
} |
||||
)* |
||||
} |
||||
} |
||||
|
||||
impl_repeated_primitives!(i32, u32, bool, f32, f64, i64, u64); |
@ -1,29 +0,0 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
#include "google/protobuf/compiler/allowlists/allowlists.h" |
||||
|
||||
#include "absl/strings/string_view.h" |
||||
#include "google/protobuf/compiler/allowlists/allowlist.h" |
||||
|
||||
namespace google { |
||||
namespace protobuf { |
||||
namespace compiler { |
||||
|
||||
// NOTE: Allowlists in this file are not accepting new entries unless otherwise
|
||||
// specified.
|
||||
|
||||
static constexpr auto kWeakImports = internal::MakeAllowlist({ |
||||
// Intentionally left blank.
|
||||
}); |
||||
|
||||
bool IsWeakImportFile(absl::string_view file) { |
||||
return kWeakImports.Allows(file); |
||||
} |
||||
} // namespace compiler
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue