Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
P
protobuf
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
protobuf
Commits
d522479a
Commit
d522479a
authored
Jan 16, 2016
by
Jon Skeet
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1145 from jskeet/conformance
JSON Conformance improvements
parents
1e113df2
030c2684
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
537 additions
and
233 deletions
+537
-233
failure_list_csharp.txt
conformance/failure_list_csharp.txt
+0
-73
Program.cs
csharp/src/Google.Protobuf.Conformance/Program.cs
+19
-8
JsonFormatterTest.cs
csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs
+43
-14
JsonParserTest.cs
csharp/src/Google.Protobuf.Test/JsonParserTest.cs
+100
-2
UnittestWellKnownTypes.cs
...Google.Protobuf.Test/TestProtos/UnittestWellKnownTypes.cs
+40
-3
DurationTest.cs
...p/src/Google.Protobuf.Test/WellKnownTypes/DurationTest.cs
+25
-5
TimestampTest.cs
.../src/Google.Protobuf.Test/WellKnownTypes/TimestampTest.cs
+23
-0
WrappersTest.cs
...p/src/Google.Protobuf.Test/WellKnownTypes/WrappersTest.cs
+24
-0
RepeatedField.cs
csharp/src/Google.Protobuf/Collections/RepeatedField.cs
+4
-5
FieldCodec.cs
csharp/src/Google.Protobuf/FieldCodec.cs
+66
-72
InvalidProtocolBufferException.cs
csharp/src/Google.Protobuf/InvalidProtocolBufferException.cs
+11
-0
JsonFormatter.cs
csharp/src/Google.Protobuf/JsonFormatter.cs
+51
-27
JsonParser.cs
csharp/src/Google.Protobuf/JsonParser.cs
+72
-18
MessageDescriptor.cs
csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs
+15
-3
DurationPartial.cs
csharp/src/Google.Protobuf/WellKnownTypes/DurationPartial.cs
+23
-0
TimestampPartial.cs
...rp/src/Google.Protobuf/WellKnownTypes/TimestampPartial.cs
+19
-3
unittest_well_known_types.proto
src/google/protobuf/unittest_well_known_types.proto
+2
-0
No files found.
conformance/failure_list_csharp.txt
View file @
d522479a
DurationProtoInputTooLarge.JsonOutput
DurationProtoInputTooSmall.JsonOutput
FieldMaskNumbersDontRoundTrip.JsonOutput
FieldMaskPathsDontRoundTrip.JsonOutput
FieldMaskTooManyUnderscore.JsonOutput
JsonInput.AnyWithValueForInteger.JsonOutput
JsonInput.AnyWithValueForInteger.JsonOutput
JsonInput.AnyWithValueForJsonObject.JsonOutput
JsonInput.AnyWithValueForJsonObject.JsonOutput
JsonInput.BoolFieldAllCapitalFalse
JsonInput.BoolFieldAllCapitalTrue
JsonInput.BoolFieldCamelCaseFalse
JsonInput.BoolFieldCamelCaseTrue
JsonInput.BoolMapFieldKeyNotQuoted
JsonInput.BytesFieldInvalidBase64Characters
JsonInput.BytesFieldNoPadding
JsonInput.DoubleFieldInfinityNotQuoted
JsonInput.DoubleFieldNanNotQuoted
JsonInput.DoubleFieldNegativeInfinityNotQuoted
JsonInput.DoubleFieldTooLarge
JsonInput.DoubleFieldTooSmall
JsonInput.DurationHas3FractionalDigits.Validator
JsonInput.DurationHas6FractionalDigits.Validator
JsonInput.DurationHas9FractionalDigits.Validator
JsonInput.DurationMaxValue.JsonOutput
JsonInput.DurationMaxValue.ProtobufOutput
JsonInput.DurationMinValue.JsonOutput
JsonInput.DurationMinValue.ProtobufOutput
JsonInput.EnumFieldNotQuoted
JsonInput.EnumFieldNumericValueNonZero.JsonOutput
JsonInput.EnumFieldNumericValueNonZero.ProtobufOutput
JsonInput.EnumFieldNumericValueZero.JsonOutput
JsonInput.EnumFieldNumericValueZero.ProtobufOutput
JsonInput.EnumFieldUnknownValue.Validator
JsonInput.FieldMaskInvalidCharacter
JsonInput.FieldNameDuplicate
JsonInput.FieldNameDuplicateDifferentCasing2
JsonInput.FieldNameInLowerCamelCase.Validator
JsonInput.FieldNameInLowerCamelCase.Validator
JsonInput.FieldNameInSnakeCase.JsonOutput
JsonInput.FieldNameInSnakeCase.JsonOutput
JsonInput.FieldNameInSnakeCase.ProtobufOutput
JsonInput.FieldNameInSnakeCase.ProtobufOutput
JsonInput.FieldNameNotQuoted
JsonInput.FieldNameWithMixedCases.JsonOutput
JsonInput.FieldNameWithMixedCases.JsonOutput
JsonInput.FieldNameWithMixedCases.ProtobufOutput
JsonInput.FieldNameWithMixedCases.ProtobufOutput
JsonInput.FieldNameWithMixedCases.Validator
JsonInput.FieldNameWithMixedCases.Validator
JsonInput.FloatFieldInfinityNotQuoted
JsonInput.FloatFieldNanNotQuoted
JsonInput.FloatFieldNegativeInfinityNotQuoted
JsonInput.Int32FieldLeadingZero
JsonInput.Int32FieldMinFloatValue.JsonOutput
JsonInput.Int32FieldMinFloatValue.JsonOutput
JsonInput.Int32FieldMinValue.JsonOutput
JsonInput.Int32FieldMinValue.JsonOutput
JsonInput.Int32FieldNegativeWithLeadingZero
JsonInput.Int32FieldPlusSign
JsonInput.Int32MapFieldKeyNotQuoted
JsonInput.Int64FieldMaxValueNotQuoted.JsonOutput
JsonInput.Int64FieldMaxValueNotQuoted.JsonOutput
JsonInput.Int64FieldMaxValueNotQuoted.ProtobufOutput
JsonInput.Int64FieldMaxValueNotQuoted.ProtobufOutput
JsonInput.Int64MapFieldKeyNotQuoted
JsonInput.JsonWithComments
JsonInput.MapFieldKeyIsNull
JsonInput.MapFieldValueIsNull
JsonInput.OneofFieldDuplicate
JsonInput.OriginalProtoFieldName.JsonOutput
JsonInput.OriginalProtoFieldName.JsonOutput
JsonInput.OriginalProtoFieldName.ProtobufOutput
JsonInput.RepeatedBoolWrapper.ProtobufOutput
JsonInput.RepeatedDoubleWrapper.ProtobufOutput
JsonInput.RepeatedFieldMessageElementIsNull
JsonInput.RepeatedFieldPrimitiveElementIsNull
JsonInput.RepeatedFieldTrailingComma
JsonInput.RepeatedFloatWrapper.ProtobufOutput
JsonInput.RepeatedInt32Wrapper.ProtobufOutput
JsonInput.RepeatedInt64Wrapper.ProtobufOutput
JsonInput.RepeatedUint32Wrapper.ProtobufOutput
JsonInput.RepeatedUint64Wrapper.ProtobufOutput
JsonInput.StringFieldInvalidEscape
JsonInput.StringFieldSurrogateInWrongOrder
JsonInput.StringFieldSurrogatePair.JsonOutput
JsonInput.StringFieldSurrogatePair.JsonOutput
JsonInput.StringFieldUnpairedHighSurrogate
JsonInput.StringFieldUnpairedLowSurrogate
JsonInput.StringFieldUnterminatedEscape
JsonInput.StringFieldUppercaseEscapeLetter
JsonInput.TimestampHas3FractionalDigits.Validator
JsonInput.TimestampHas6FractionalDigits.Validator
JsonInput.TimestampHas9FractionalDigits.Validator
JsonInput.TrailingCommaInAnObject
JsonInput.Uint32MapFieldKeyNotQuoted
JsonInput.Uint64FieldMaxValueNotQuoted.JsonOutput
JsonInput.Uint64FieldMaxValueNotQuoted.JsonOutput
JsonInput.Uint64FieldMaxValueNotQuoted.ProtobufOutput
JsonInput.Uint64FieldMaxValueNotQuoted.ProtobufOutput
JsonInput.Uint64MapFieldKeyNotQuoted
JsonInput.ValueAcceptNull.JsonOutput
JsonInput.ValueAcceptNull.ProtobufOutput
TimestampProtoInputTooLarge.JsonOutput
TimestampProtoInputTooSmall.JsonOutput
csharp/src/Google.Protobuf.Conformance/Program.cs
View file @
d522479a
...
@@ -101,15 +101,26 @@ namespace Google.Protobuf.Conformance
...
@@ -101,15 +101,26 @@ namespace Google.Protobuf.Conformance
{
{
return
new
ConformanceResponse
{
ParseError
=
e
.
Message
};
return
new
ConformanceResponse
{
ParseError
=
e
.
Message
};
}
}
switch
(
request
.
RequestedOutputFormat
)
catch
(
InvalidJsonException
e
)
{
{
case
global
::
Conformance
.
WireFormat
.
JSON
:
return
new
ConformanceResponse
{
ParseError
=
e
.
Message
};
var
formatter
=
new
JsonFormatter
(
new
JsonFormatter
.
Settings
(
false
,
typeRegistry
));
}
return
new
ConformanceResponse
{
JsonPayload
=
formatter
.
Format
(
message
)
};
try
case
global
::
Conformance
.
WireFormat
.
PROTOBUF
:
{
return
new
ConformanceResponse
{
ProtobufPayload
=
message
.
ToByteString
()
};
switch
(
request
.
RequestedOutputFormat
)
default
:
{
throw
new
Exception
(
"Unsupported request output format: "
+
request
.
PayloadCase
);
case
global
::
Conformance
.
WireFormat
.
JSON
:
var
formatter
=
new
JsonFormatter
(
new
JsonFormatter
.
Settings
(
false
,
typeRegistry
));
return
new
ConformanceResponse
{
JsonPayload
=
formatter
.
Format
(
message
)
};
case
global
::
Conformance
.
WireFormat
.
PROTOBUF
:
return
new
ConformanceResponse
{
ProtobufPayload
=
message
.
ToByteString
()
};
default
:
throw
new
Exception
(
"Unsupported request output format: "
+
request
.
PayloadCase
);
}
}
catch
(
InvalidOperationException
e
)
{
return
new
ConformanceResponse
{
SerializeError
=
e
.
Message
};
}
}
}
}
...
...
csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs
View file @
d522479a
...
@@ -165,34 +165,31 @@ namespace Google.Protobuf
...
@@ -165,34 +165,31 @@ namespace Google.Protobuf
}
}
[
Test
]
[
Test
]
public
void
UnknownEnumValue
Omitted
_SingleField
()
public
void
UnknownEnumValue
Numeric
_SingleField
()
{
{
var
message
=
new
TestAllTypes
{
SingleForeignEnum
=
(
ForeignEnum
)
100
};
var
message
=
new
TestAllTypes
{
SingleForeignEnum
=
(
ForeignEnum
)
100
};
AssertJson
(
"{ }"
,
JsonFormatter
.
Default
.
Format
(
message
));
AssertJson
(
"{
'singleForeignEnum': 100
}"
,
JsonFormatter
.
Default
.
Format
(
message
));
}
}
[
Test
]
[
Test
]
public
void
UnknownEnumValue
Omitted
_RepeatedField
()
public
void
UnknownEnumValue
Numeric
_RepeatedField
()
{
{
var
message
=
new
TestAllTypes
{
RepeatedForeignEnum
=
{
ForeignEnum
.
FOREIGN_BAZ
,
(
ForeignEnum
)
100
,
ForeignEnum
.
FOREIGN_FOO
}
};
var
message
=
new
TestAllTypes
{
RepeatedForeignEnum
=
{
ForeignEnum
.
FOREIGN_BAZ
,
(
ForeignEnum
)
100
,
ForeignEnum
.
FOREIGN_FOO
}
};
AssertJson
(
"{ 'repeatedForeignEnum': [ 'FOREIGN_BAZ', 'FOREIGN_FOO' ] }"
,
JsonFormatter
.
Default
.
Format
(
message
));
AssertJson
(
"{ 'repeatedForeignEnum': [ 'FOREIGN_BAZ',
100,
'FOREIGN_FOO' ] }"
,
JsonFormatter
.
Default
.
Format
(
message
));
}
}
[
Test
]
[
Test
]
public
void
UnknownEnumValue
Omitted
_MapField
()
public
void
UnknownEnumValue
Numeric
_MapField
()
{
{
// This matches the C++ behaviour.
var
message
=
new
TestMap
{
MapInt32Enum
=
{
{
1
,
MapEnum
.
MAP_ENUM_FOO
},
{
2
,
(
MapEnum
)
100
},
{
3
,
MapEnum
.
MAP_ENUM_BAR
}
}
};
var
message
=
new
TestMap
{
MapInt32Enum
=
{
{
1
,
MapEnum
.
MAP_ENUM_FOO
},
{
2
,
(
MapEnum
)
100
},
{
3
,
MapEnum
.
MAP_ENUM_BAR
}
}
};
AssertJson
(
"{ 'mapInt32Enum': { '1': 'MAP_ENUM_FOO', '3': 'MAP_ENUM_BAR' } }"
,
JsonFormatter
.
Default
.
Format
(
message
));
AssertJson
(
"{ 'mapInt32Enum': { '1': 'MAP_ENUM_FOO', '
2': 100, '
3': 'MAP_ENUM_BAR' } }"
,
JsonFormatter
.
Default
.
Format
(
message
));
}
}
[
Test
]
[
Test
]
public
void
UnknownEnumValue
Omitted
_RepeatedField_AllEntriesUnknown
()
public
void
UnknownEnumValue_RepeatedField_AllEntriesUnknown
()
{
{
// *Maybe* we should hold off on writing the "[" until we find that we've got at least one value to write...
// but this is what happens at the moment, and it doesn't seem too awful.
var
message
=
new
TestAllTypes
{
RepeatedForeignEnum
=
{
(
ForeignEnum
)
200
,
(
ForeignEnum
)
100
}
};
var
message
=
new
TestAllTypes
{
RepeatedForeignEnum
=
{
(
ForeignEnum
)
200
,
(
ForeignEnum
)
100
}
};
AssertJson
(
"{ 'repeatedForeignEnum': [ ] }"
,
JsonFormatter
.
Default
.
Format
(
message
));
AssertJson
(
"{ 'repeatedForeignEnum': [
200, 100
] }"
,
JsonFormatter
.
Default
.
Format
(
message
));
}
}
[
Test
]
[
Test
]
...
@@ -315,6 +312,15 @@ namespace Google.Protobuf
...
@@ -315,6 +312,15 @@ namespace Google.Protobuf
[
Test
]
[
Test
]
[
TestCase
(
"1970-01-01T00:00:00Z"
,
0
)]
[
TestCase
(
"1970-01-01T00:00:00Z"
,
0
)]
[
TestCase
(
"1970-01-01T00:00:00.000000001Z"
,
1
)]
[
TestCase
(
"1970-01-01T00:00:00.000000010Z"
,
10
)]
[
TestCase
(
"1970-01-01T00:00:00.000000100Z"
,
100
)]
[
TestCase
(
"1970-01-01T00:00:00.000001Z"
,
1000
)]
[
TestCase
(
"1970-01-01T00:00:00.000010Z"
,
10000
)]
[
TestCase
(
"1970-01-01T00:00:00.000100Z"
,
100000
)]
[
TestCase
(
"1970-01-01T00:00:00.001Z"
,
1000000
)]
[
TestCase
(
"1970-01-01T00:00:00.010Z"
,
10000000
)]
[
TestCase
(
"1970-01-01T00:00:00.100Z"
,
100000000
)]
[
TestCase
(
"1970-01-01T00:00:00.100Z"
,
100000000
)]
[
TestCase
(
"1970-01-01T00:00:00.100Z"
,
100000000
)]
[
TestCase
(
"1970-01-01T00:00:00.120Z"
,
120000000
)]
[
TestCase
(
"1970-01-01T00:00:00.120Z"
,
120000000
)]
[
TestCase
(
"1970-01-01T00:00:00.123Z"
,
123000000
)]
[
TestCase
(
"1970-01-01T00:00:00.123Z"
,
123000000
)]
...
@@ -350,6 +356,14 @@ namespace Google.Protobuf
...
@@ -350,6 +356,14 @@ namespace Google.Protobuf
[
TestCase
(
0
,
0
,
"0s"
)]
[
TestCase
(
0
,
0
,
"0s"
)]
[
TestCase
(
1
,
0
,
"1s"
)]
[
TestCase
(
1
,
0
,
"1s"
)]
[
TestCase
(-
1
,
0
,
"-1s"
)]
[
TestCase
(-
1
,
0
,
"-1s"
)]
[
TestCase
(
0
,
1
,
"0.000000001s"
)]
[
TestCase
(
0
,
10
,
"0.000000010s"
)]
[
TestCase
(
0
,
100
,
"0.000000100s"
)]
[
TestCase
(
0
,
1000
,
"0.000001s"
)]
[
TestCase
(
0
,
10000
,
"0.000010s"
)]
[
TestCase
(
0
,
100000
,
"0.000100s"
)]
[
TestCase
(
0
,
1000000
,
"0.001s"
)]
[
TestCase
(
0
,
10000000
,
"0.010s"
)]
[
TestCase
(
0
,
100000000
,
"0.100s"
)]
[
TestCase
(
0
,
100000000
,
"0.100s"
)]
[
TestCase
(
0
,
120000000
,
"0.120s"
)]
[
TestCase
(
0
,
120000000
,
"0.120s"
)]
[
TestCase
(
0
,
123000000
,
"0.123s"
)]
[
TestCase
(
0
,
123000000
,
"0.123s"
)]
...
@@ -362,14 +376,19 @@ namespace Google.Protobuf
...
@@ -362,14 +376,19 @@ namespace Google.Protobuf
[
TestCase
(
0
,
-
100000000
,
"-0.100s"
)]
[
TestCase
(
0
,
-
100000000
,
"-0.100s"
)]
[
TestCase
(
1
,
100000000
,
"1.100s"
)]
[
TestCase
(
1
,
100000000
,
"1.100s"
)]
[
TestCase
(-
1
,
-
100000000
,
"-1.100s"
)]
[
TestCase
(-
1
,
-
100000000
,
"-1.100s"
)]
// Non-normalized examples
[
TestCase
(
1
,
2123456789
,
"3.123456789s"
)]
[
TestCase
(
1
,
-
100000000
,
"0.900s"
)]
public
void
DurationStandalone
(
long
seconds
,
int
nanoseconds
,
string
expected
)
public
void
DurationStandalone
(
long
seconds
,
int
nanoseconds
,
string
expected
)
{
{
Assert
.
AreEqual
(
WrapInQuotes
(
expected
),
new
Duration
{
Seconds
=
seconds
,
Nanos
=
nanoseconds
}.
ToString
());
Assert
.
AreEqual
(
WrapInQuotes
(
expected
),
new
Duration
{
Seconds
=
seconds
,
Nanos
=
nanoseconds
}.
ToString
());
}
}
[
Test
]
[
TestCase
(
1
,
2123456789
)]
[
TestCase
(
1
,
-
100000000
)]
public
void
DurationStandalone_NonNormalized
(
long
seconds
,
int
nanoseconds
)
{
Assert
.
Throws
<
InvalidOperationException
>(()
=>
new
Duration
{
Seconds
=
seconds
,
Nanos
=
nanoseconds
}.
ToString
());
}
[
Test
]
[
Test
]
public
void
DurationField
()
public
void
DurationField
()
{
{
...
@@ -395,6 +414,16 @@ namespace Google.Protobuf
...
@@ -395,6 +414,16 @@ namespace Google.Protobuf
AssertJson
(
"{ 'a': null, 'b': false, 'c': 10.5, 'd': 'text', 'e': [ 't1', 5 ], 'f': { 'nested': 'value' } }"
,
message
.
ToString
());
AssertJson
(
"{ 'a': null, 'b': false, 'c': 10.5, 'd': 'text', 'e': [ 't1', 5 ], 'f': { 'nested': 'value' } }"
,
message
.
ToString
());
}
}
[
Test
]
[
TestCase
(
"foo__bar"
)]
[
TestCase
(
"foo_3_ar"
)]
[
TestCase
(
"fooBar"
)]
public
void
FieldMaskInvalid
(
string
input
)
{
var
mask
=
new
FieldMask
{
Paths
=
{
input
}
};
Assert
.
Throws
<
InvalidOperationException
>(()
=>
JsonFormatter
.
Default
.
Format
(
mask
));
}
[
Test
]
[
Test
]
public
void
FieldMaskStandalone
()
public
void
FieldMaskStandalone
()
{
{
...
...
csharp/src/Google.Protobuf.Test/JsonParserTest.cs
View file @
d522479a
...
@@ -71,6 +71,14 @@ namespace Google.Protobuf
...
@@ -71,6 +71,14 @@ namespace Google.Protobuf
Assert
.
Throws
<
InvalidProtocolBufferException
>(()
=>
JsonParser
.
Default
.
Parse
<
TestMap
>(
json
));
Assert
.
Throws
<
InvalidProtocolBufferException
>(()
=>
JsonParser
.
Default
.
Parse
<
TestMap
>(
json
));
}
}
[
Test
]
public
void
OriginalFieldNameAccepted
()
{
var
json
=
"{ \"single_int32\": 10 }"
;
var
expected
=
new
TestAllTypes
{
SingleInt32
=
10
};
Assert
.
AreEqual
(
expected
,
TestAllTypes
.
Parser
.
ParseJson
(
json
));
}
[
Test
]
[
Test
]
public
void
SourceContextRoundtrip
()
public
void
SourceContextRoundtrip
()
{
{
...
@@ -116,7 +124,9 @@ namespace Google.Protobuf
...
@@ -116,7 +124,9 @@ namespace Google.Protobuf
[
Test
]
[
Test
]
public
void
SingularWrappers_ExplicitNulls
()
public
void
SingularWrappers_ExplicitNulls
()
{
{
var
message
=
new
TestWellKnownTypes
();
// When we parse the "valueField": null part, we remember it... basically, it's one case
// where explicit default values don't fully roundtrip.
var
message
=
new
TestWellKnownTypes
{
ValueField
=
Value
.
ForNull
()
};
var
json
=
new
JsonFormatter
(
new
JsonFormatter
.
Settings
(
true
)).
Format
(
message
);
var
json
=
new
JsonFormatter
(
new
JsonFormatter
.
Settings
(
true
)).
Format
(
message
);
var
parsed
=
JsonParser
.
Default
.
Parse
<
TestWellKnownTypes
>(
json
);
var
parsed
=
JsonParser
.
Default
.
Parse
<
TestWellKnownTypes
>(
json
);
Assert
.
AreEqual
(
message
,
parsed
);
Assert
.
AreEqual
(
message
,
parsed
);
...
@@ -142,6 +152,14 @@ namespace Google.Protobuf
...
@@ -142,6 +152,14 @@ namespace Google.Protobuf
Assert
.
AreEqual
(
expected
,
parsed
);
Assert
.
AreEqual
(
expected
,
parsed
);
}
}
[
Test
]
public
void
ExplicitNullValue
()
{
string
json
=
"{\"valueField\": null}"
;
var
message
=
JsonParser
.
Default
.
Parse
<
TestWellKnownTypes
>(
json
);
Assert
.
AreEqual
(
new
TestWellKnownTypes
{
ValueField
=
Value
.
ForNull
()
},
message
);
}
[
Test
]
[
Test
]
public
void
BytesWrapper_Standalone
()
public
void
BytesWrapper_Standalone
()
{
{
...
@@ -170,6 +188,36 @@ namespace Google.Protobuf
...
@@ -170,6 +188,36 @@ namespace Google.Protobuf
AssertRoundtrip
(
message
);
AssertRoundtrip
(
message
);
}
}
[
Test
]
public
void
RepeatedField_NullElementProhibited
()
{
string
json
=
"{ \"repeated_foreign_message\": [null] }"
;
Assert
.
Throws
<
InvalidProtocolBufferException
>(()
=>
TestAllTypes
.
Parser
.
ParseJson
(
json
));
}
[
Test
]
public
void
RepeatedField_NullOverallValueAllowed
()
{
string
json
=
"{ \"repeated_foreign_message\": null }"
;
Assert
.
AreEqual
(
new
TestAllTypes
(),
TestAllTypes
.
Parser
.
ParseJson
(
json
));
}
[
Test
]
[
TestCase
(
"{ \"mapInt32Int32\": { \"10\": null }"
)]
[
TestCase
(
"{ \"mapStringString\": { \"abc\": null }"
)]
[
TestCase
(
"{ \"mapInt32ForeignMessage\": { \"10\": null }"
)]
public
void
MapField_NullValueProhibited
(
string
json
)
{
Assert
.
Throws
<
InvalidProtocolBufferException
>(()
=>
TestMap
.
Parser
.
ParseJson
(
json
));
}
[
Test
]
public
void
MapField_NullOverallValueAllowed
()
{
string
json
=
"{ \"mapInt32Int32\": null }"
;
Assert
.
AreEqual
(
new
TestMap
(),
TestMap
.
Parser
.
ParseJson
(
json
));
}
[
Test
]
[
Test
]
public
void
IndividualWrapperTypes
()
public
void
IndividualWrapperTypes
()
{
{
...
@@ -715,7 +763,6 @@ namespace Google.Protobuf
...
@@ -715,7 +763,6 @@ namespace Google.Protobuf
[
TestCase
(
"--0.123456789s"
,
Description
=
"Double minus sign"
)]
[
TestCase
(
"--0.123456789s"
,
Description
=
"Double minus sign"
)]
// Violate upper/lower bounds in various ways
// Violate upper/lower bounds in various ways
[
TestCase
(
"315576000001s"
,
Description
=
"Integer part too large"
)]
[
TestCase
(
"315576000001s"
,
Description
=
"Integer part too large"
)]
[
TestCase
(
"315576000000.000000001s"
,
Description
=
"Integer part is upper bound; non-zero fraction"
)]
[
TestCase
(
"3155760000000s"
,
Description
=
"Integer part too long (positive)"
)]
[
TestCase
(
"3155760000000s"
,
Description
=
"Integer part too long (positive)"
)]
[
TestCase
(
"-3155760000000s"
,
Description
=
"Integer part too long (negative)"
)]
[
TestCase
(
"-3155760000000s"
,
Description
=
"Integer part too long (negative)"
)]
public
void
Duration_Invalid
(
string
jsonValue
)
public
void
Duration_Invalid
(
string
jsonValue
)
...
@@ -741,6 +788,14 @@ namespace Google.Protobuf
...
@@ -741,6 +788,14 @@ namespace Google.Protobuf
CollectionAssert
.
AreEqual
(
expectedPaths
,
parsed
.
Paths
);
CollectionAssert
.
AreEqual
(
expectedPaths
,
parsed
.
Paths
);
}
}
[
Test
]
[
TestCase
(
"foo_bar"
)]
public
void
FieldMask_Invalid
(
string
jsonValue
)
{
string
json
=
WrapInQuotes
(
jsonValue
);
Assert
.
Throws
<
InvalidProtocolBufferException
>(()
=>
FieldMask
.
Parser
.
ParseJson
(
json
));
}
[
Test
]
[
Test
]
public
void
Any_RegularMessage
()
public
void
Any_RegularMessage
()
{
{
...
@@ -762,6 +817,13 @@ namespace Google.Protobuf
...
@@ -762,6 +817,13 @@ namespace Google.Protobuf
Assert
.
Throws
<
InvalidOperationException
>(()
=>
Any
.
Parser
.
ParseJson
(
json
));
Assert
.
Throws
<
InvalidOperationException
>(()
=>
Any
.
Parser
.
ParseJson
(
json
));
}
}
[
Test
]
public
void
Any_NoTypeUrl
()
{
string
json
=
"{ \"foo\": \"bar\" }"
;
Assert
.
Throws
<
InvalidProtocolBufferException
>(()
=>
Any
.
Parser
.
ParseJson
(
json
));
}
[
Test
]
[
Test
]
public
void
Any_WellKnownType
()
public
void
Any_WellKnownType
()
{
{
...
@@ -814,6 +876,42 @@ namespace Google.Protobuf
...
@@ -814,6 +876,42 @@ namespace Google.Protobuf
Assert
.
Throws
<
InvalidProtocolBufferException
>(()
=>
parser63
.
Parse
<
TestRecursiveMessage
>(
data64
));
Assert
.
Throws
<
InvalidProtocolBufferException
>(()
=>
parser63
.
Parse
<
TestRecursiveMessage
>(
data64
));
}
}
[
Test
]
[
TestCase
(
"AQI"
)]
[
TestCase
(
"_-=="
)]
public
void
Bytes_InvalidBase64
(
string
badBase64
)
{
string
json
=
"{ \"singleBytes\": \""
+
badBase64
+
"\" }"
;
Assert
.
Throws
<
InvalidProtocolBufferException
>(()
=>
TestAllTypes
.
Parser
.
ParseJson
(
json
));
}
[
Test
]
[
TestCase
(
"\"FOREIGN_BAR\""
,
ForeignEnum
.
FOREIGN_BAR
)]
[
TestCase
(
"5"
,
ForeignEnum
.
FOREIGN_BAR
)]
[
TestCase
(
"100"
,
(
ForeignEnum
)
100
)]
public
void
EnumValid
(
string
value
,
ForeignEnum
expectedValue
)
{
string
json
=
"{ \"singleForeignEnum\": "
+
value
+
" }"
;
var
parsed
=
TestAllTypes
.
Parser
.
ParseJson
(
json
);
Assert
.
AreEqual
(
new
TestAllTypes
{
SingleForeignEnum
=
expectedValue
},
parsed
);
}
[
Test
]
[
TestCase
(
"\"NOT_A_VALID_VALUE\""
)]
[
TestCase
(
"5.5"
)]
public
void
Enum_Invalid
(
string
value
)
{
string
json
=
"{ \"singleForeignEnum\": "
+
value
+
" }"
;
Assert
.
Throws
<
InvalidProtocolBufferException
>(()
=>
TestAllTypes
.
Parser
.
ParseJson
(
json
));
}
[
Test
]
public
void
OneofDuplicate_Invalid
()
{
string
json
=
"{ \"oneofString\": \"x\", \"oneofUint32\": 10 }"
;
Assert
.
Throws
<
InvalidProtocolBufferException
>(()
=>
TestAllTypes
.
Parser
.
ParseJson
(
json
));
}
/// <summary>
/// <summary>
/// Various tests use strings which have quotes round them for parsing or as the result
/// Various tests use strings which have quotes round them for parsing or as the result
/// of formatting, but without those quotes being specified in the tests (for the sake of readability).
/// of formatting, but without those quotes being specified in the tests (for the sake of readability).
...
...
csharp/src/Google.Protobuf.Test/TestProtos/UnittestWellKnownTypes.cs
View file @
d522479a
...
@@ -31,7 +31,7 @@ namespace Google.Protobuf.TestProtos {
...
@@ -31,7 +31,7 @@ namespace Google.Protobuf.TestProtos {
"L3Byb3RvYnVmL3NvdXJjZV9jb250ZXh0LnByb3RvGhxnb29nbGUvcHJvdG9i"
,
"L3Byb3RvYnVmL3NvdXJjZV9jb250ZXh0LnByb3RvGhxnb29nbGUvcHJvdG9i"
,
"dWYvc3RydWN0LnByb3RvGh9nb29nbGUvcHJvdG9idWYvdGltZXN0YW1wLnBy"
,
"dWYvc3RydWN0LnByb3RvGh9nb29nbGUvcHJvdG9idWYvdGltZXN0YW1wLnBy"
,
"b3RvGhpnb29nbGUvcHJvdG9idWYvdHlwZS5wcm90bxoeZ29vZ2xlL3Byb3Rv"
,
"b3RvGhpnb29nbGUvcHJvdG9idWYvdHlwZS5wcm90bxoeZ29vZ2xlL3Byb3Rv"
,
"YnVmL3dyYXBwZXJzLnByb3RvI
pE
HChJUZXN0V2VsbEtub3duVHlwZXMSJwoJ"
,
"YnVmL3dyYXBwZXJzLnByb3RvI
r4
HChJUZXN0V2VsbEtub3duVHlwZXMSJwoJ"
,
"YW55X2ZpZWxkGAEgASgLMhQuZ29vZ2xlLnByb3RvYnVmLkFueRInCglhcGlf"
,
"YW55X2ZpZWxkGAEgASgLMhQuZ29vZ2xlLnByb3RvYnVmLkFueRInCglhcGlf"
,
"ZmllbGQYAiABKAsyFC5nb29nbGUucHJvdG9idWYuQXBpEjEKDmR1cmF0aW9u"
,
"ZmllbGQYAiABKAsyFC5nb29nbGUucHJvdG9idWYuQXBpEjEKDmR1cmF0aW9u"
,
"X2ZpZWxkGAMgASgLMhkuZ29vZ2xlLnByb3RvYnVmLkR1cmF0aW9uEisKC2Vt"
,
"X2ZpZWxkGAMgASgLMhkuZ29vZ2xlLnByb3RvYnVmLkR1cmF0aW9uEisKC2Vt"
,
...
@@ -51,7 +51,8 @@ namespace Google.Protobuf.TestProtos {
...
@@ -51,7 +51,8 @@ namespace Google.Protobuf.TestProtos {
"cm90b2J1Zi5VSW50MzJWYWx1ZRIuCgpib29sX2ZpZWxkGBAgASgLMhouZ29v"
,
"cm90b2J1Zi5VSW50MzJWYWx1ZRIuCgpib29sX2ZpZWxkGBAgASgLMhouZ29v"
,
"Z2xlLnByb3RvYnVmLkJvb2xWYWx1ZRIyCgxzdHJpbmdfZmllbGQYESABKAsy"
,
"Z2xlLnByb3RvYnVmLkJvb2xWYWx1ZRIyCgxzdHJpbmdfZmllbGQYESABKAsy"
,
"HC5nb29nbGUucHJvdG9idWYuU3RyaW5nVmFsdWUSMAoLYnl0ZXNfZmllbGQY"
,
"HC5nb29nbGUucHJvdG9idWYuU3RyaW5nVmFsdWUSMAoLYnl0ZXNfZmllbGQY"
,
"EiABKAsyGy5nb29nbGUucHJvdG9idWYuQnl0ZXNWYWx1ZSKVBwoWUmVwZWF0"
,
"EiABKAsyGy5nb29nbGUucHJvdG9idWYuQnl0ZXNWYWx1ZRIrCgt2YWx1ZV9m"
,
"aWVsZBgTIAEoCzIWLmdvb2dsZS5wcm90b2J1Zi5WYWx1ZSKVBwoWUmVwZWF0"
,
"ZWRXZWxsS25vd25UeXBlcxInCglhbnlfZmllbGQYASADKAsyFC5nb29nbGUu"
,
"ZWRXZWxsS25vd25UeXBlcxInCglhbnlfZmllbGQYASADKAsyFC5nb29nbGUu"
,
"cHJvdG9idWYuQW55EicKCWFwaV9maWVsZBgCIAMoCzIULmdvb2dsZS5wcm90"
,
"cHJvdG9idWYuQW55EicKCWFwaV9maWVsZBgCIAMoCzIULmdvb2dsZS5wcm90"
,
"b2J1Zi5BcGkSMQoOZHVyYXRpb25fZmllbGQYAyADKAsyGS5nb29nbGUucHJv"
,
"b2J1Zi5BcGkSMQoOZHVyYXRpb25fZmllbGQYAyADKAsyGS5nb29nbGUucHJv"
,
...
@@ -162,7 +163,7 @@ namespace Google.Protobuf.TestProtos {
...
@@ -162,7 +163,7 @@ namespace Google.Protobuf.TestProtos {
descriptor
=
pbr
::
FileDescriptor
.
FromGeneratedCode
(
descriptorData
,
descriptor
=
pbr
::
FileDescriptor
.
FromGeneratedCode
(
descriptorData
,
new
pbr
::
FileDescriptor
[]
{
global
::
Google
.
Protobuf
.
WellKnownTypes
.
AnyReflection
.
Descriptor
,
global
::
Google
.
Protobuf
.
WellKnownTypes
.
ApiReflection
.
Descriptor
,
global
::
Google
.
Protobuf
.
WellKnownTypes
.
DurationReflection
.
Descriptor
,
global
::
Google
.
Protobuf
.
WellKnownTypes
.
EmptyReflection
.
Descriptor
,
global
::
Google
.
Protobuf
.
WellKnownTypes
.
FieldMaskReflection
.
Descriptor
,
global
::
Google
.
Protobuf
.
WellKnownTypes
.
SourceContextReflection
.
Descriptor
,
global
::
Google
.
Protobuf
.
WellKnownTypes
.
StructReflection
.
Descriptor
,
global
::
Google
.
Protobuf
.
WellKnownTypes
.
TimestampReflection
.
Descriptor
,
global
::
Google
.
Protobuf
.
WellKnownTypes
.
TypeReflection
.
Descriptor
,
global
::
Google
.
Protobuf
.
WellKnownTypes
.
WrappersReflection
.
Descriptor
,
},
new
pbr
::
FileDescriptor
[]
{
global
::
Google
.
Protobuf
.
WellKnownTypes
.
AnyReflection
.
Descriptor
,
global
::
Google
.
Protobuf
.
WellKnownTypes
.
ApiReflection
.
Descriptor
,
global
::
Google
.
Protobuf
.
WellKnownTypes
.
DurationReflection
.
Descriptor
,
global
::
Google
.
Protobuf
.
WellKnownTypes
.
EmptyReflection
.
Descriptor
,
global
::
Google
.
Protobuf
.
WellKnownTypes
.
FieldMaskReflection
.
Descriptor
,
global
::
Google
.
Protobuf
.
WellKnownTypes
.
SourceContextReflection
.
Descriptor
,
global
::
Google
.
Protobuf
.
WellKnownTypes
.
StructReflection
.
Descriptor
,
global
::
Google
.
Protobuf
.
WellKnownTypes
.
TimestampReflection
.
Descriptor
,
global
::
Google
.
Protobuf
.
WellKnownTypes
.
TypeReflection
.
Descriptor
,
global
::
Google
.
Protobuf
.
WellKnownTypes
.
WrappersReflection
.
Descriptor
,
},
new
pbr
::
GeneratedCodeInfo
(
null
,
new
pbr
::
GeneratedCodeInfo
[]
{
new
pbr
::
GeneratedCodeInfo
(
null
,
new
pbr
::
GeneratedCodeInfo
[]
{
new
pbr
::
GeneratedCodeInfo
(
typeof
(
global
::
Google
.
Protobuf
.
TestProtos
.
TestWellKnownTypes
),
global
::
Google
.
Protobuf
.
TestProtos
.
TestWellKnownTypes
.
Parser
,
new
[]{
"AnyField"
,
"ApiField"
,
"DurationField"
,
"EmptyField"
,
"FieldMaskField"
,
"SourceContextField"
,
"StructField"
,
"TimestampField"
,
"TypeField"
,
"DoubleField"
,
"FloatField"
,
"Int64Field"
,
"Uint64Field"
,
"Int32Field"
,
"Uint32Field"
,
"BoolField"
,
"StringField"
,
"BytesField"
},
null
,
null
,
null
),
new
pbr
::
GeneratedCodeInfo
(
typeof
(
global
::
Google
.
Protobuf
.
TestProtos
.
TestWellKnownTypes
),
global
::
Google
.
Protobuf
.
TestProtos
.
TestWellKnownTypes
.
Parser
,
new
[]{
"AnyField"
,
"ApiField"
,
"DurationField"
,
"EmptyField"
,
"FieldMaskField"
,
"SourceContextField"
,
"StructField"
,
"TimestampField"
,
"TypeField"
,
"DoubleField"
,
"FloatField"
,
"Int64Field"
,
"Uint64Field"
,
"Int32Field"
,
"Uint32Field"
,
"BoolField"
,
"StringField"
,
"BytesField"
,
"ValueField"
},
null
,
null
,
null
),
new
pbr
::
GeneratedCodeInfo
(
typeof
(
global
::
Google
.
Protobuf
.
TestProtos
.
RepeatedWellKnownTypes
),
global
::
Google
.
Protobuf
.
TestProtos
.
RepeatedWellKnownTypes
.
Parser
,
new
[]{
"AnyField"
,
"ApiField"
,
"DurationField"
,
"EmptyField"
,
"FieldMaskField"
,
"SourceContextField"
,
"StructField"
,
"TimestampField"
,
"TypeField"
,
"DoubleField"
,
"FloatField"
,
"Int64Field"
,
"Uint64Field"
,
"Int32Field"
,
"Uint32Field"
,
"BoolField"
,
"StringField"
,
"BytesField"
},
null
,
null
,
null
),
new
pbr
::
GeneratedCodeInfo
(
typeof
(
global
::
Google
.
Protobuf
.
TestProtos
.
RepeatedWellKnownTypes
),
global
::
Google
.
Protobuf
.
TestProtos
.
RepeatedWellKnownTypes
.
Parser
,
new
[]{
"AnyField"
,
"ApiField"
,
"DurationField"
,
"EmptyField"
,
"FieldMaskField"
,
"SourceContextField"
,
"StructField"
,
"TimestampField"
,
"TypeField"
,
"DoubleField"
,
"FloatField"
,
"Int64Field"
,
"Uint64Field"
,
"Int32Field"
,
"Uint32Field"
,
"BoolField"
,
"StringField"
,
"BytesField"
},
null
,
null
,
null
),
new
pbr
::
GeneratedCodeInfo
(
typeof
(
global
::
Google
.
Protobuf
.
TestProtos
.
OneofWellKnownTypes
),
global
::
Google
.
Protobuf
.
TestProtos
.
OneofWellKnownTypes
.
Parser
,
new
[]{
"AnyField"
,
"ApiField"
,
"DurationField"
,
"EmptyField"
,
"FieldMaskField"
,
"SourceContextField"
,
"StructField"
,
"TimestampField"
,
"TypeField"
,
"DoubleField"
,
"FloatField"
,
"Int64Field"
,
"Uint64Field"
,
"Int32Field"
,
"Uint32Field"
,
"BoolField"
,
"StringField"
,
"BytesField"
},
new
[]{
"OneofField"
},
null
,
null
),
new
pbr
::
GeneratedCodeInfo
(
typeof
(
global
::
Google
.
Protobuf
.
TestProtos
.
OneofWellKnownTypes
),
global
::
Google
.
Protobuf
.
TestProtos
.
OneofWellKnownTypes
.
Parser
,
new
[]{
"AnyField"
,
"ApiField"
,
"DurationField"
,
"EmptyField"
,
"FieldMaskField"
,
"SourceContextField"
,
"StructField"
,
"TimestampField"
,
"TypeField"
,
"DoubleField"
,
"FloatField"
,
"Int64Field"
,
"Uint64Field"
,
"Int32Field"
,
"Uint32Field"
,
"BoolField"
,
"StringField"
,
"BytesField"
},
new
[]{
"OneofField"
},
null
,
null
),
new
pbr
::
GeneratedCodeInfo
(
typeof
(
global
::
Google
.
Protobuf
.
TestProtos
.
MapWellKnownTypes
),
global
::
Google
.
Protobuf
.
TestProtos
.
MapWellKnownTypes
.
Parser
,
new
[]{
"AnyField"
,
"ApiField"
,
"DurationField"
,
"EmptyField"
,
"FieldMaskField"
,
"SourceContextField"
,
"StructField"
,
"TimestampField"
,
"TypeField"
,
"DoubleField"
,
"FloatField"
,
"Int64Field"
,
"Uint64Field"
,
"Int32Field"
,
"Uint32Field"
,
"BoolField"
,
"StringField"
,
"BytesField"
},
null
,
null
,
new
pbr
::
GeneratedCodeInfo
[]
{
null
,
null
,
null
,
null
,
null
,
null
,
null
,
null
,
null
,
null
,
null
,
null
,
null
,
null
,
null
,
null
,
null
,
null
,
})
new
pbr
::
GeneratedCodeInfo
(
typeof
(
global
::
Google
.
Protobuf
.
TestProtos
.
MapWellKnownTypes
),
global
::
Google
.
Protobuf
.
TestProtos
.
MapWellKnownTypes
.
Parser
,
new
[]{
"AnyField"
,
"ApiField"
,
"DurationField"
,
"EmptyField"
,
"FieldMaskField"
,
"SourceContextField"
,
"StructField"
,
"TimestampField"
,
"TypeField"
,
"DoubleField"
,
"FloatField"
,
"Int64Field"
,
"Uint64Field"
,
"Int32Field"
,
"Uint32Field"
,
"BoolField"
,
"StringField"
,
"BytesField"
},
null
,
null
,
new
pbr
::
GeneratedCodeInfo
[]
{
null
,
null
,
null
,
null
,
null
,
null
,
null
,
null
,
null
,
null
,
null
,
null
,
null
,
null
,
null
,
null
,
null
,
null
,
})
...
@@ -215,6 +216,7 @@ namespace Google.Protobuf.TestProtos {
...
@@ -215,6 +216,7 @@ namespace Google.Protobuf.TestProtos {
BoolField
=
other
.
BoolField
;
BoolField
=
other
.
BoolField
;
StringField
=
other
.
StringField
;
StringField
=
other
.
StringField
;
BytesField
=
other
.
BytesField
;
BytesField
=
other
.
BytesField
;
ValueField
=
other
.
valueField_
!=
null
?
other
.
ValueField
.
Clone
()
:
null
;
}
}
public
TestWellKnownTypes
Clone
()
{
public
TestWellKnownTypes
Clone
()
{
...
@@ -410,6 +412,19 @@ namespace Google.Protobuf.TestProtos {
...
@@ -410,6 +412,19 @@ namespace Google.Protobuf.TestProtos {
}
}
}
}
/// <summary>Field number for the "value_field" field.</summary>
public
const
int
ValueFieldFieldNumber
=
19
;
private
global
::
Google
.
Protobuf
.
WellKnownTypes
.
Value
valueField_
;
/// <summary>
/// Part of struct, but useful to be able to test separately
/// </summary>
public
global
::
Google
.
Protobuf
.
WellKnownTypes
.
Value
ValueField
{
get
{
return
valueField_
;
}
set
{
valueField_
=
value
;
}
}
public
override
bool
Equals
(
object
other
)
{
public
override
bool
Equals
(
object
other
)
{
return
Equals
(
other
as
TestWellKnownTypes
);
return
Equals
(
other
as
TestWellKnownTypes
);
}
}
...
@@ -439,6 +454,7 @@ namespace Google.Protobuf.TestProtos {
...
@@ -439,6 +454,7 @@ namespace Google.Protobuf.TestProtos {
if
(
BoolField
!=
other
.
BoolField
)
return
false
;
if
(
BoolField
!=
other
.
BoolField
)
return
false
;
if
(
StringField
!=
other
.
StringField
)
return
false
;
if
(
StringField
!=
other
.
StringField
)
return
false
;
if
(
BytesField
!=
other
.
BytesField
)
return
false
;
if
(
BytesField
!=
other
.
BytesField
)
return
false
;
if
(!
object
.
Equals
(
ValueField
,
other
.
ValueField
))
return
false
;
return
true
;
return
true
;
}
}
...
@@ -462,6 +478,7 @@ namespace Google.Protobuf.TestProtos {
...
@@ -462,6 +478,7 @@ namespace Google.Protobuf.TestProtos {
if
(
boolField_
!=
null
)
hash
^=
BoolField
.
GetHashCode
();
if
(
boolField_
!=
null
)
hash
^=
BoolField
.
GetHashCode
();
if
(
stringField_
!=
null
)
hash
^=
StringField
.
GetHashCode
();
if
(
stringField_
!=
null
)
hash
^=
StringField
.
GetHashCode
();
if
(
bytesField_
!=
null
)
hash
^=
BytesField
.
GetHashCode
();
if
(
bytesField_
!=
null
)
hash
^=
BytesField
.
GetHashCode
();
if
(
valueField_
!=
null
)
hash
^=
ValueField
.
GetHashCode
();
return
hash
;
return
hash
;
}
}
...
@@ -533,6 +550,10 @@ namespace Google.Protobuf.TestProtos {
...
@@ -533,6 +550,10 @@ namespace Google.Protobuf.TestProtos {
if
(
bytesField_
!=
null
)
{
if
(
bytesField_
!=
null
)
{
_single_bytesField_codec
.
WriteTagAndValue
(
output
,
BytesField
);
_single_bytesField_codec
.
WriteTagAndValue
(
output
,
BytesField
);
}
}
if
(
valueField_
!=
null
)
{
output
.
WriteRawTag
(
154
,
1
);
output
.
WriteMessage
(
ValueField
);
}
}
}
public
int
CalculateSize
()
{
public
int
CalculateSize
()
{
...
@@ -591,6 +612,9 @@ namespace Google.Protobuf.TestProtos {
...
@@ -591,6 +612,9 @@ namespace Google.Protobuf.TestProtos {
if
(
bytesField_
!=
null
)
{
if
(
bytesField_
!=
null
)
{
size
+=
_single_bytesField_codec
.
CalculateSizeWithTag
(
BytesField
);
size
+=
_single_bytesField_codec
.
CalculateSizeWithTag
(
BytesField
);
}
}
if
(
valueField_
!=
null
)
{
size
+=
2
+
pb
::
CodedOutputStream
.
ComputeMessageSize
(
ValueField
);
}
return
size
;
return
size
;
}
}
...
@@ -697,6 +721,12 @@ namespace Google.Protobuf.TestProtos {
...
@@ -697,6 +721,12 @@ namespace Google.Protobuf.TestProtos {
BytesField
=
other
.
BytesField
;
BytesField
=
other
.
BytesField
;
}
}
}
}
if
(
other
.
valueField_
!=
null
)
{
if
(
valueField_
==
null
)
{
valueField_
=
new
global
::
Google
.
Protobuf
.
WellKnownTypes
.
Value
();
}
ValueField
.
MergeFrom
(
other
.
ValueField
);
}
}
}
public
void
MergeFrom
(
pb
::
CodedInputStream
input
)
{
public
void
MergeFrom
(
pb
::
CodedInputStream
input
)
{
...
@@ -832,6 +862,13 @@ namespace Google.Protobuf.TestProtos {
...
@@ -832,6 +862,13 @@ namespace Google.Protobuf.TestProtos {
}
}
break
;
break
;
}
}
case
154
:
{
if
(
valueField_
==
null
)
{
valueField_
=
new
global
::
Google
.
Protobuf
.
WellKnownTypes
.
Value
();
}
input
.
ReadMessage
(
valueField_
);
break
;
}
}
}
}
}
}
}
...
...
csharp/src/Google.Protobuf.Test/WellKnownTypes/DurationTest.cs
View file @
d522479a
...
@@ -50,11 +50,6 @@ namespace Google.Protobuf.WellKnownTypes
...
@@ -50,11 +50,6 @@ namespace Google.Protobuf.WellKnownTypes
// Rounding is towards 0
// Rounding is towards 0
Assert
.
AreEqual
(
TimeSpan
.
FromTicks
(
2
),
new
Duration
{
Nanos
=
250
}.
ToTimeSpan
());
Assert
.
AreEqual
(
TimeSpan
.
FromTicks
(
2
),
new
Duration
{
Nanos
=
250
}.
ToTimeSpan
());
Assert
.
AreEqual
(
TimeSpan
.
FromTicks
(-
2
),
new
Duration
{
Nanos
=
-
250
}.
ToTimeSpan
());
Assert
.
AreEqual
(
TimeSpan
.
FromTicks
(-
2
),
new
Duration
{
Nanos
=
-
250
}.
ToTimeSpan
());
// Non-normalized durations
Assert
.
AreEqual
(
TimeSpan
.
FromSeconds
(
3
),
new
Duration
{
Seconds
=
1
,
Nanos
=
2
*
Duration
.
NanosecondsPerSecond
}.
ToTimeSpan
());
Assert
.
AreEqual
(
TimeSpan
.
FromSeconds
(
1
),
new
Duration
{
Seconds
=
3
,
Nanos
=
-
2
*
Duration
.
NanosecondsPerSecond
}.
ToTimeSpan
());
Assert
.
AreEqual
(
TimeSpan
.
FromSeconds
(-
1
),
new
Duration
{
Seconds
=
1
,
Nanos
=
-
2
*
Duration
.
NanosecondsPerSecond
}.
ToTimeSpan
());
}
}
[
Test
]
[
Test
]
...
@@ -100,5 +95,30 @@ namespace Google.Protobuf.WellKnownTypes
...
@@ -100,5 +95,30 @@ namespace Google.Protobuf.WellKnownTypes
Assert
.
AreEqual
(
new
Duration
{
Seconds
=
1
},
Duration
.
FromTimeSpan
(
TimeSpan
.
FromSeconds
(
1
)));
Assert
.
AreEqual
(
new
Duration
{
Seconds
=
1
},
Duration
.
FromTimeSpan
(
TimeSpan
.
FromSeconds
(
1
)));
Assert
.
AreEqual
(
new
Duration
{
Nanos
=
Duration
.
NanosecondsPerTick
},
Duration
.
FromTimeSpan
(
TimeSpan
.
FromTicks
(
1
)));
Assert
.
AreEqual
(
new
Duration
{
Nanos
=
Duration
.
NanosecondsPerTick
},
Duration
.
FromTimeSpan
(
TimeSpan
.
FromTicks
(
1
)));
}
}
[
Test
]
[
TestCase
(
0
,
Duration
.
MaxNanoseconds
+
1
)]
[
TestCase
(
0
,
Duration
.
MinNanoseconds
-
1
)]
[
TestCase
(
Duration
.
MinSeconds
-
1
,
0
)]
[
TestCase
(
Duration
.
MaxSeconds
+
1
,
0
)]
[
TestCase
(
1
,
-
1
)]
[
TestCase
(-
1
,
1
)]
public
void
ToTimeSpan_Invalid
(
long
seconds
,
int
nanoseconds
)
{
var
duration
=
new
Duration
{
Seconds
=
seconds
,
Nanos
=
nanoseconds
};
Assert
.
Throws
<
InvalidOperationException
>(()
=>
duration
.
ToTimeSpan
());
}
[
Test
]
[
TestCase
(
0
,
Duration
.
MaxNanoseconds
)]
[
TestCase
(
0
,
Duration
.
MinNanoseconds
)]
[
TestCase
(
Duration
.
MinSeconds
,
Duration
.
MinNanoseconds
)]
[
TestCase
(
Duration
.
MaxSeconds
,
Duration
.
MaxNanoseconds
)]
public
void
ToTimeSpan_Valid
(
long
seconds
,
int
nanoseconds
)
{
// Only testing that these values don't throw, unlike their similar tests in ToTimeSpan_Invalid
var
duration
=
new
Duration
{
Seconds
=
seconds
,
Nanos
=
nanoseconds
};
duration
.
ToTimeSpan
();
}
}
}
}
}
csharp/src/Google.Protobuf.Test/WellKnownTypes/TimestampTest.cs
View file @
d522479a
...
@@ -61,6 +61,29 @@ namespace Google.Protobuf.WellKnownTypes
...
@@ -61,6 +61,29 @@ namespace Google.Protobuf.WellKnownTypes
Assert
.
AreEqual
(
new
DateTime
(
1969
,
12
,
31
,
23
,
59
,
59
).
AddMilliseconds
(
1
),
t2
.
ToDateTime
());
Assert
.
AreEqual
(
new
DateTime
(
1969
,
12
,
31
,
23
,
59
,
59
).
AddMilliseconds
(
1
),
t2
.
ToDateTime
());
}
}
[
Test
]
[
TestCase
(
Timestamp
.
UnixSecondsAtBclMinValue
-
1
,
Timestamp
.
MaxNanos
)]
[
TestCase
(
Timestamp
.
UnixSecondsAtBclMaxValue
+
1
,
0
)]
[
TestCase
(
0
,
-
1
)]
[
TestCase
(
0
,
Timestamp
.
MaxNanos
+
1
)]
public
void
ToDateTime_OutOfRange
(
long
seconds
,
int
nanoseconds
)
{
var
value
=
new
Timestamp
{
Seconds
=
seconds
,
Nanos
=
nanoseconds
};
Assert
.
Throws
<
InvalidOperationException
>(()
=>
value
.
ToDateTime
());
}
// 1ns larger or smaller than the above values
[
Test
]
[
TestCase
(
Timestamp
.
UnixSecondsAtBclMinValue
,
0
)]
[
TestCase
(
Timestamp
.
UnixSecondsAtBclMaxValue
,
Timestamp
.
MaxNanos
)]
[
TestCase
(
0
,
0
)]
[
TestCase
(
0
,
Timestamp
.
MaxNanos
)]
public
void
ToDateTime_ValidBoundaries
(
long
seconds
,
int
nanoseconds
)
{
var
value
=
new
Timestamp
{
Seconds
=
seconds
,
Nanos
=
nanoseconds
};
value
.
ToDateTime
();
}
private
static
void
AssertRoundtrip
(
Timestamp
timestamp
,
DateTime
dateTime
)
private
static
void
AssertRoundtrip
(
Timestamp
timestamp
,
DateTime
dateTime
)
{
{
Assert
.
AreEqual
(
timestamp
,
Timestamp
.
FromDateTime
(
dateTime
));
Assert
.
AreEqual
(
timestamp
,
Timestamp
.
FromDateTime
(
dateTime
));
...
...
csharp/src/Google.Protobuf.Test/WellKnownTypes/WrappersTest.cs
View file @
d522479a
...
@@ -148,6 +148,30 @@ namespace Google.Protobuf.WellKnownTypes
...
@@ -148,6 +148,30 @@ namespace Google.Protobuf.WellKnownTypes
Assert
.
AreEqual
(
"Second"
,
message
.
StringField
[
1
]);
Assert
.
AreEqual
(
"Second"
,
message
.
StringField
[
1
]);
}
}
[
Test
]
public
void
RepeatedWrappersBinaryFormat
()
{
// At one point we accidentally used a packed format for repeated wrappers, which is wrong (and weird).
// This test is just to prove that we use the right format.
var
rawOutput
=
new
MemoryStream
();
var
output
=
new
CodedOutputStream
(
rawOutput
);
// Write a value of 5
output
.
WriteTag
(
RepeatedWellKnownTypes
.
Int32FieldFieldNumber
,
WireFormat
.
WireType
.
LengthDelimited
);
output
.
WriteLength
(
2
);
output
.
WriteTag
(
WrappersReflection
.
WrapperValueFieldNumber
,
WireFormat
.
WireType
.
Varint
);
output
.
WriteInt32
(
5
);
// Write a value of 0 (empty message)
output
.
WriteTag
(
RepeatedWellKnownTypes
.
Int32FieldFieldNumber
,
WireFormat
.
WireType
.
LengthDelimited
);
output
.
WriteLength
(
0
);
output
.
Flush
();
var
expectedBytes
=
rawOutput
.
ToArray
();
var
message
=
new
RepeatedWellKnownTypes
{
Int32Field
=
{
5
,
0
}
};
var
actualBytes
=
message
.
ToByteArray
();
Assert
.
AreEqual
(
expectedBytes
,
actualBytes
);
}
[
Test
]
[
Test
]
public
void
MapWrappersSerializeDeserialize
()
public
void
MapWrappersSerializeDeserialize
()
{
{
...
...
csharp/src/Google.Protobuf/Collections/RepeatedField.cs
View file @
d522479a
...
@@ -34,7 +34,6 @@ using System;
...
@@ -34,7 +34,6 @@ using System;
using
System.Collections
;
using
System.Collections
;
using
System.Collections.Generic
;
using
System.Collections.Generic
;
using
System.Text
;
using
System.Text
;
using
Google.Protobuf.Compatibility
;
namespace
Google.Protobuf.Collections
namespace
Google.Protobuf.Collections
{
{
...
@@ -96,8 +95,8 @@ namespace Google.Protobuf.Collections
...
@@ -96,8 +95,8 @@ namespace Google.Protobuf.Collections
// iteration.
// iteration.
uint
tag
=
input
.
LastTag
;
uint
tag
=
input
.
LastTag
;
var
reader
=
codec
.
ValueReader
;
var
reader
=
codec
.
ValueReader
;
//
V
alue types can be packed or not.
//
Non-nullable v
alue types can be packed or not.
if
(
typeof
(
T
).
IsValueType
()
&&
WireFormat
.
GetTagWireType
(
tag
)
==
WireFormat
.
WireType
.
LengthDelimited
)
if
(
FieldCodec
<
T
>.
IsPackedRepeatedField
(
tag
)
)
{
{
int
length
=
input
.
ReadLength
();
int
length
=
input
.
ReadLength
();
if
(
length
>
0
)
if
(
length
>
0
)
...
@@ -134,7 +133,7 @@ namespace Google.Protobuf.Collections
...
@@ -134,7 +133,7 @@ namespace Google.Protobuf.Collections
return
0
;
return
0
;
}
}
uint
tag
=
codec
.
Tag
;
uint
tag
=
codec
.
Tag
;
if
(
typeof
(
T
).
IsValueType
()
&&
WireFormat
.
GetTagWireType
(
tag
)
==
WireFormat
.
WireType
.
LengthDelimite
d
)
if
(
codec
.
PackedRepeatedFiel
d
)
{
{
int
dataSize
=
CalculatePackedDataSize
(
codec
);
int
dataSize
=
CalculatePackedDataSize
(
codec
);
return
CodedOutputStream
.
ComputeRawVarint32Size
(
tag
)
+
return
CodedOutputStream
.
ComputeRawVarint32Size
(
tag
)
+
...
@@ -186,7 +185,7 @@ namespace Google.Protobuf.Collections
...
@@ -186,7 +185,7 @@ namespace Google.Protobuf.Collections
}
}
var
writer
=
codec
.
ValueWriter
;
var
writer
=
codec
.
ValueWriter
;
var
tag
=
codec
.
Tag
;
var
tag
=
codec
.
Tag
;
if
(
typeof
(
T
).
IsValueType
()
&&
WireFormat
.
GetTagWireType
(
tag
)
==
WireFormat
.
WireType
.
LengthDelimite
d
)
if
(
codec
.
PackedRepeatedFiel
d
)
{
{
// Packed primitive type
// Packed primitive type
uint
size
=
(
uint
)
CalculatePackedDataSize
(
codec
);
uint
size
=
(
uint
)
CalculatePackedDataSize
(
codec
);
...
...
csharp/src/Google.Protobuf/FieldCodec.cs
View file @
d522479a
...
@@ -30,6 +30,7 @@
...
@@ -30,6 +30,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
#endregion
using
Google.Protobuf.Compatibility
;
using
Google.Protobuf.WellKnownTypes
;
using
Google.Protobuf.WellKnownTypes
;
using
System
;
using
System
;
using
System.Collections.Generic
;
using
System.Collections.Generic
;
...
@@ -329,17 +330,24 @@ namespace Google.Protobuf
...
@@ -329,17 +330,24 @@ namespace Google.Protobuf
}
}
/// <summary>
/// <summary>
/// <para>
/// An encode/decode pair for a single field. This effectively encapsulates
/// An encode/decode pair for a single field. This effectively encapsulates
/// all the information needed to read or write the field value from/to a coded
/// all the information needed to read or write the field value from/to a coded
/// stream.
/// stream.
/// </para>
/// <para>
/// This class is public and has to be as it is used by generated code, but its public
/// API is very limited - just what the generated code needs to call directly.
/// </para>
/// </summary>
/// </summary>
/// <remarks>
/// <remarks>
/// This never writes default values to the stream, and
is not currently designed
/// This never writes default values to the stream, and
does not address "packedness"
///
to play well with packed arrays
.
///
in repeated fields itself, other than to know whether or not the field *should* be packed
.
/// </remarks>
/// </remarks>
public
sealed
class
FieldCodec
<
T
>
public
sealed
class
FieldCodec
<
T
>
{
{
private
static
readonly
T
DefaultDefault
;
private
static
readonly
T
DefaultDefault
;
private
static
readonly
bool
TypeSupportsPacking
=
typeof
(
T
).
IsValueType
()
&&
Nullable
.
GetUnderlyingType
(
typeof
(
T
))
==
null
;
static
FieldCodec
()
static
FieldCodec
()
{
{
...
@@ -354,75 +362,31 @@ namespace Google.Protobuf
...
@@ -354,75 +362,31 @@ namespace Google.Protobuf
// Otherwise it's the default value of the CLR type
// Otherwise it's the default value of the CLR type
}
}
private
readonly
Func
<
CodedInputStream
,
T
>
reader
;
internal
static
bool
IsPackedRepeatedField
(
uint
tag
)
=>
private
readonly
Action
<
CodedOutputStream
,
T
>
writer
;
TypeSupportsPacking
&&
WireFormat
.
GetTagWireType
(
tag
)
==
WireFormat
.
WireType
.
LengthDelimited
;
private
readonly
Func
<
T
,
int
>
sizeCalculator
;
private
readonly
uint
tag
;
private
readonly
int
tagSize
;
private
readonly
int
fixedSize
;
// Default value for this codec. Usually the same for every instance of the same type, but
// for string/ByteString wrapper fields the codec's default value is null, whereas for
// other string/ByteString fields it's "" or ByteString.Empty.
private
readonly
T
defaultValue
;
internal
FieldCodec
(
internal
bool
PackedRepeatedField
{
get
;
}
Func
<
CodedInputStream
,
T
>
reader
,
Action
<
CodedOutputStream
,
T
>
writer
,
Func
<
T
,
int
>
sizeCalculator
,
uint
tag
)
:
this
(
reader
,
writer
,
sizeCalculator
,
tag
,
DefaultDefault
)
{
}
internal
FieldCodec
(
Func
<
CodedInputStream
,
T
>
reader
,
Action
<
CodedOutputStream
,
T
>
writer
,
Func
<
T
,
int
>
sizeCalculator
,
uint
tag
,
T
defaultValue
)
{
this
.
reader
=
reader
;
this
.
writer
=
writer
;
this
.
sizeCalculator
=
sizeCalculator
;
this
.
fixedSize
=
0
;
this
.
tag
=
tag
;
this
.
defaultValue
=
defaultValue
;
tagSize
=
CodedOutputStream
.
ComputeRawVarint32Size
(
tag
);
}
internal
FieldCodec
(
Func
<
CodedInputStream
,
T
>
reader
,
Action
<
CodedOutputStream
,
T
>
writer
,
int
fixedSize
,
uint
tag
)
{
this
.
reader
=
reader
;
this
.
writer
=
writer
;
this
.
sizeCalculator
=
_
=>
fixedSize
;
this
.
fixedSize
=
fixedSize
;
this
.
tag
=
tag
;
tagSize
=
CodedOutputStream
.
ComputeRawVarint32Size
(
tag
);
}
/// <summary>
/// <summary>
/// Returns
the size calculator for just a value
.
/// Returns
a delegate to write a value (unconditionally) to a coded output stream
.
/// </summary>
/// </summary>
internal
Func
<
T
,
int
>
ValueSizeCalculator
{
get
{
return
sizeCalculator
;
}
}
internal
Action
<
CodedOutputStream
,
T
>
ValueWriter
{
get
;
}
/// <summary>
/// <summary>
/// Returns
a delegate to write a value (unconditionally) to a coded output stream
.
/// Returns
the size calculator for just a value
.
/// </summary>
/// </summary>
internal
Action
<
CodedOutputStream
,
T
>
ValueWriter
{
get
{
return
writer
;
}
}
internal
Func
<
T
,
int
>
ValueSizeCalculator
{
get
;
}
/// <summary>
/// <summary>
/// Returns a delegate to read a value from a coded input stream. It is assumed that
/// Returns a delegate to read a value from a coded input stream. It is assumed that
/// the stream is already positioned on the appropriate tag.
/// the stream is already positioned on the appropriate tag.
/// </summary>
/// </summary>
internal
Func
<
CodedInputStream
,
T
>
ValueReader
{
get
{
return
reader
;
}
}
internal
Func
<
CodedInputStream
,
T
>
ValueReader
{
get
;
}
/// <summary>
/// <summary>
/// Returns the fixed size for an entry, or 0 if sizes vary.
/// Returns the fixed size for an entry, or 0 if sizes vary.
/// </summary>
/// </summary>
internal
int
FixedSize
{
get
{
return
fixedSize
;
}
}
internal
int
FixedSize
{
get
;
}
/// <summary>
/// <summary>
/// Gets the tag of the codec.
/// Gets the tag of the codec.
...
@@ -430,15 +394,54 @@ namespace Google.Protobuf
...
@@ -430,15 +394,54 @@ namespace Google.Protobuf
/// <value>
/// <value>
/// The tag of the codec.
/// The tag of the codec.
/// </value>
/// </value>
public
uint
Tag
{
get
{
return
tag
;
}
}
internal
uint
Tag
{
get
;
}
/// <summary>
/// <summary>
/// Gets the default value of the codec's type.
/// Default value for this codec. Usually the same for every instance of the same type, but
/// for string/ByteString wrapper fields the codec's default value is null, whereas for
/// other string/ByteString fields it's "" or ByteString.Empty.
/// </summary>
/// </summary>
/// <value>
/// <value>
/// The default value of the codec's type.
/// The default value of the codec's type.
/// </value>
/// </value>
public
T
DefaultValue
{
get
{
return
defaultValue
;
}
}
internal
T
DefaultValue
{
get
;
}
private
readonly
int
tagSize
;
internal
FieldCodec
(
Func
<
CodedInputStream
,
T
>
reader
,
Action
<
CodedOutputStream
,
T
>
writer
,
int
fixedSize
,
uint
tag
)
:
this
(
reader
,
writer
,
_
=>
fixedSize
,
tag
)
{
FixedSize
=
fixedSize
;
}
internal
FieldCodec
(
Func
<
CodedInputStream
,
T
>
reader
,
Action
<
CodedOutputStream
,
T
>
writer
,
Func
<
T
,
int
>
sizeCalculator
,
uint
tag
)
:
this
(
reader
,
writer
,
sizeCalculator
,
tag
,
DefaultDefault
)
{
}
internal
FieldCodec
(
Func
<
CodedInputStream
,
T
>
reader
,
Action
<
CodedOutputStream
,
T
>
writer
,
Func
<
T
,
int
>
sizeCalculator
,
uint
tag
,
T
defaultValue
)
{
ValueReader
=
reader
;
ValueWriter
=
writer
;
ValueSizeCalculator
=
sizeCalculator
;
FixedSize
=
0
;
Tag
=
tag
;
DefaultValue
=
defaultValue
;
tagSize
=
CodedOutputStream
.
ComputeRawVarint32Size
(
tag
);
// Detect packed-ness once, so we can check for it within RepeatedField<T>.
PackedRepeatedField
=
IsPackedRepeatedField
(
tag
);
}
/// <summary>
/// <summary>
/// Write a tag and the given value, *if* the value is not the default.
/// Write a tag and the given value, *if* the value is not the default.
...
@@ -447,8 +450,8 @@ namespace Google.Protobuf
...
@@ -447,8 +450,8 @@ namespace Google.Protobuf
{
{
if
(!
IsDefault
(
value
))
if
(!
IsDefault
(
value
))
{
{
output
.
WriteTag
(
t
ag
);
output
.
WriteTag
(
T
ag
);
w
riter
(
output
,
value
);
ValueW
riter
(
output
,
value
);
}
}
}
}
...
@@ -457,23 +460,14 @@ namespace Google.Protobuf
...
@@ -457,23 +460,14 @@ namespace Google.Protobuf
/// </summary>
/// </summary>
/// <param name="input">The input stream to read from.</param>
/// <param name="input">The input stream to read from.</param>
/// <returns>The value read from the stream.</returns>
/// <returns>The value read from the stream.</returns>
public
T
Read
(
CodedInputStream
input
)
public
T
Read
(
CodedInputStream
input
)
=>
ValueReader
(
input
);
{
return
reader
(
input
);
}
/// <summary>
/// <summary>
/// Calculates the size required to write the given value, with a tag,
/// Calculates the size required to write the given value, with a tag,
/// if the value is not the default.
/// if the value is not the default.
/// </summary>
/// </summary>
public
int
CalculateSizeWithTag
(
T
value
)
public
int
CalculateSizeWithTag
(
T
value
)
=>
IsDefault
(
value
)
?
0
:
ValueSizeCalculator
(
value
)
+
tagSize
;
{
return
IsDefault
(
value
)
?
0
:
sizeCalculator
(
value
)
+
tagSize
;
}
private
bool
IsDefault
(
T
value
)
private
bool
IsDefault
(
T
value
)
=>
EqualityComparer
<
T
>.
Default
.
Equals
(
value
,
DefaultValue
);
{
return
EqualityComparer
<
T
>.
Default
.
Equals
(
value
,
defaultValue
);
}
}
}
}
}
csharp/src/Google.Protobuf/InvalidProtocolBufferException.cs
View file @
d522479a
...
@@ -30,6 +30,7 @@
...
@@ -30,6 +30,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
#endregion
using
System
;
using
System.IO
;
using
System.IO
;
namespace
Google.Protobuf
namespace
Google.Protobuf
...
@@ -45,6 +46,11 @@ namespace Google.Protobuf
...
@@ -45,6 +46,11 @@ namespace Google.Protobuf
{
{
}
}
internal
InvalidProtocolBufferException
(
string
message
,
Exception
innerException
)
:
base
(
message
,
innerException
)
{
}
internal
static
InvalidProtocolBufferException
MoreDataAvailable
()
internal
static
InvalidProtocolBufferException
MoreDataAvailable
()
{
{
return
new
InvalidProtocolBufferException
(
return
new
InvalidProtocolBufferException
(
...
@@ -82,6 +88,11 @@ namespace Google.Protobuf
...
@@ -82,6 +88,11 @@ namespace Google.Protobuf
"Protocol message contained an invalid tag (zero)."
);
"Protocol message contained an invalid tag (zero)."
);
}
}
internal
static
InvalidProtocolBufferException
InvalidBase64
(
Exception
innerException
)
{
return
new
InvalidProtocolBufferException
(
"Invalid base64 data"
,
innerException
);
}
internal
static
InvalidProtocolBufferException
InvalidEndTag
()
internal
static
InvalidProtocolBufferException
InvalidEndTag
()
{
{
return
new
InvalidProtocolBufferException
(
return
new
InvalidProtocolBufferException
(
...
...
csharp/src/Google.Protobuf/JsonFormatter.cs
View file @
d522479a
...
@@ -205,11 +205,6 @@ namespace Google.Protobuf
...
@@ -205,11 +205,6 @@ namespace Google.Protobuf
{
{
continue
;
continue
;
}
}
// Omit awkward (single) values such as unknown enum values
if
(!
field
.
IsRepeated
&&
!
field
.
IsMap
&&
!
CanWriteSingleValue
(
value
))
{
continue
;
}
// Okay, all tests complete: let's write the field value...
// Okay, all tests complete: let's write the field value...
if
(!
first
)
if
(!
first
)
...
@@ -224,6 +219,31 @@ namespace Google.Protobuf
...
@@ -224,6 +219,31 @@ namespace Google.Protobuf
return
!
first
;
return
!
first
;
}
}
/// <summary>
/// Camel-case converter with added strictness for field mask formatting.
/// </summary>
/// <exception cref="InvalidOperationException">The field mask is invalid for JSON representation</exception>
private
static
string
ToCamelCaseForFieldMask
(
string
input
)
{
for
(
int
i
=
0
;
i
<
input
.
Length
;
i
++)
{
char
c
=
input
[
i
];
if
(
c
>=
'A'
&&
c
<=
'Z'
)
{
throw
new
InvalidOperationException
(
$"Invalid field mask to be converted to JSON:
{
input
}
"
);
}
if
(
c
==
'_'
&&
i
<
input
.
Length
-
1
)
{
char
next
=
input
[
i
+
1
];
if
(
next
<
'a'
||
next
>
'z'
)
{
throw
new
InvalidOperationException
(
$"Invalid field mask to be converted to JSON:
{
input
}
"
);
}
}
}
return
ToCamelCase
(
input
);
}
// Converted from src/google/protobuf/util/internal/utility.cc ToCamelCase
// Converted from src/google/protobuf/util/internal/utility.cc ToCamelCase
// TODO: Use the new field in FieldDescriptor.
// TODO: Use the new field in FieldDescriptor.
internal
static
string
ToCamelCase
(
string
input
)
internal
static
string
ToCamelCase
(
string
input
)
...
@@ -372,7 +392,14 @@ namespace Google.Protobuf
...
@@ -372,7 +392,14 @@ namespace Google.Protobuf
}
}
else
if
(
value
is
System
.
Enum
)
else
if
(
value
is
System
.
Enum
)
{
{
WriteString
(
builder
,
value
.
ToString
());
if
(
System
.
Enum
.
IsDefined
(
value
.
GetType
(),
value
))
{
WriteString
(
builder
,
value
.
ToString
());
}
else
{
WriteValue
(
builder
,
(
int
)
value
);
}
}
}
else
if
(
value
is
float
||
value
is
double
)
else
if
(
value
is
float
||
value
is
double
)
{
{
...
@@ -485,13 +512,14 @@ namespace Google.Protobuf
...
@@ -485,13 +512,14 @@ namespace Google.Protobuf
int
nanos
=
(
int
)
value
.
Descriptor
.
Fields
[
Timestamp
.
NanosFieldNumber
].
Accessor
.
GetValue
(
value
);
int
nanos
=
(
int
)
value
.
Descriptor
.
Fields
[
Timestamp
.
NanosFieldNumber
].
Accessor
.
GetValue
(
value
);
long
seconds
=
(
long
)
value
.
Descriptor
.
Fields
[
Timestamp
.
SecondsFieldNumber
].
Accessor
.
GetValue
(
value
);
long
seconds
=
(
long
)
value
.
Descriptor
.
Fields
[
Timestamp
.
SecondsFieldNumber
].
Accessor
.
GetValue
(
value
);
// Even if the original message isn't using the built-in classes, we can still build one... and then
// Even if the original message isn't using the built-in classes, we can still build one... and its
// rely on it being normalized.
// conversion will check whether or not it's normalized.
Timestamp
normalized
=
Timestamp
.
Normalize
(
seconds
,
nanos
);
// TODO: Perhaps the diagnostic-only formatter should not throw for non-normalized values?
Timestamp
ts
=
new
Timestamp
{
Seconds
=
seconds
,
Nanos
=
nanos
};
// Use .NET's formatting for the value down to the second, including an opening double quote (as it's a string value)
// Use .NET's formatting for the value down to the second, including an opening double quote (as it's a string value)
DateTime
dateTime
=
normalized
.
ToDateTime
();
DateTime
dateTime
=
ts
.
ToDateTime
();
builder
.
Append
(
dateTime
.
ToString
(
"yyyy'-'MM'-'dd'T'HH:mm:ss"
,
CultureInfo
.
InvariantCulture
));
builder
.
Append
(
dateTime
.
ToString
(
"yyyy'-'MM'-'dd'T'HH:mm:ss"
,
CultureInfo
.
InvariantCulture
));
AppendNanoseconds
(
builder
,
Math
.
Abs
(
normalized
.
Nanos
));
AppendNanoseconds
(
builder
,
Math
.
Abs
(
ts
.
Nanos
));
builder
.
Append
(
"Z\""
);
builder
.
Append
(
"Z\""
);
}
}
...
@@ -502,25 +530,29 @@ namespace Google.Protobuf
...
@@ -502,25 +530,29 @@ namespace Google.Protobuf
int
nanos
=
(
int
)
value
.
Descriptor
.
Fields
[
Duration
.
NanosFieldNumber
].
Accessor
.
GetValue
(
value
);
int
nanos
=
(
int
)
value
.
Descriptor
.
Fields
[
Duration
.
NanosFieldNumber
].
Accessor
.
GetValue
(
value
);
long
seconds
=
(
long
)
value
.
Descriptor
.
Fields
[
Duration
.
SecondsFieldNumber
].
Accessor
.
GetValue
(
value
);
long
seconds
=
(
long
)
value
.
Descriptor
.
Fields
[
Duration
.
SecondsFieldNumber
].
Accessor
.
GetValue
(
value
);
// TODO: Perhaps the diagnostic-only formatter should not throw for non-normalized values?
// Even if the original message isn't using the built-in classes, we can still build one... and then
// Even if the original message isn't using the built-in classes, we can still build one... and then
// rely on it being normalized.
// rely on it being normalized.
Duration
normalized
=
Duration
.
Normalize
(
seconds
,
nanos
);
if
(!
Duration
.
IsNormalized
(
seconds
,
nanos
))
{
throw
new
InvalidOperationException
(
"Non-normalized duration value"
);
}
// The seconds part will normally provide the minus sign if we need it, but not if it's 0...
// The seconds part will normally provide the minus sign if we need it, but not if it's 0...
if
(
normalized
.
Seconds
==
0
&&
normalized
.
N
anos
<
0
)
if
(
seconds
==
0
&&
n
anos
<
0
)
{
{
builder
.
Append
(
'-'
);
builder
.
Append
(
'-'
);
}
}
builder
.
Append
(
normalized
.
S
econds
.
ToString
(
"d"
,
CultureInfo
.
InvariantCulture
));
builder
.
Append
(
s
econds
.
ToString
(
"d"
,
CultureInfo
.
InvariantCulture
));
AppendNanoseconds
(
builder
,
Math
.
Abs
(
n
ormalized
.
N
anos
));
AppendNanoseconds
(
builder
,
Math
.
Abs
(
nanos
));
builder
.
Append
(
"s\""
);
builder
.
Append
(
"s\""
);
}
}
private
void
WriteFieldMask
(
StringBuilder
builder
,
IMessage
value
)
private
void
WriteFieldMask
(
StringBuilder
builder
,
IMessage
value
)
{
{
IList
paths
=
(
IList
)
value
.
Descriptor
.
Fields
[
FieldMask
.
PathsFieldNumber
].
Accessor
.
GetValue
(
value
);
IList
paths
=
(
IList
)
value
.
Descriptor
.
Fields
[
FieldMask
.
PathsFieldNumber
].
Accessor
.
GetValue
(
value
);
WriteString
(
builder
,
string
.
Join
(
","
,
paths
.
Cast
<
string
>().
Select
(
ToCamelCase
)));
WriteString
(
builder
,
string
.
Join
(
","
,
paths
.
Cast
<
string
>().
Select
(
ToCamelCase
ForFieldMask
)));
}
}
private
void
WriteAny
(
StringBuilder
builder
,
IMessage
value
)
private
void
WriteAny
(
StringBuilder
builder
,
IMessage
value
)
...
@@ -598,15 +630,15 @@ namespace Google.Protobuf
...
@@ -598,15 +630,15 @@ namespace Google.Protobuf
// Output to 3, 6 or 9 digits.
// Output to 3, 6 or 9 digits.
if
(
nanos
%
1000000
==
0
)
if
(
nanos
%
1000000
==
0
)
{
{
builder
.
Append
((
nanos
/
1000000
).
ToString
(
"d"
,
CultureInfo
.
InvariantCulture
));
builder
.
Append
((
nanos
/
1000000
).
ToString
(
"d
3
"
,
CultureInfo
.
InvariantCulture
));
}
}
else
if
(
nanos
%
1000
==
0
)
else
if
(
nanos
%
1000
==
0
)
{
{
builder
.
Append
((
nanos
/
1000
).
ToString
(
"d"
,
CultureInfo
.
InvariantCulture
));
builder
.
Append
((
nanos
/
1000
).
ToString
(
"d
6
"
,
CultureInfo
.
InvariantCulture
));
}
}
else
else
{
{
builder
.
Append
(
nanos
.
ToString
(
"d"
,
CultureInfo
.
InvariantCulture
));
builder
.
Append
(
nanos
.
ToString
(
"d
9
"
,
CultureInfo
.
InvariantCulture
));
}
}
}
}
}
}
...
@@ -674,10 +706,6 @@ namespace Google.Protobuf
...
@@ -674,10 +706,6 @@ namespace Google.Protobuf
bool
first
=
true
;
bool
first
=
true
;
foreach
(
var
value
in
list
)
foreach
(
var
value
in
list
)
{
{
if
(!
CanWriteSingleValue
(
value
))
{
continue
;
}
if
(!
first
)
if
(!
first
)
{
{
builder
.
Append
(
PropertySeparator
);
builder
.
Append
(
PropertySeparator
);
...
@@ -695,10 +723,6 @@ namespace Google.Protobuf
...
@@ -695,10 +723,6 @@ namespace Google.Protobuf
// This will box each pair. Could use IDictionaryEnumerator, but that's ugly in terms of disposal.
// This will box each pair. Could use IDictionaryEnumerator, but that's ugly in terms of disposal.
foreach
(
DictionaryEntry
pair
in
dictionary
)
foreach
(
DictionaryEntry
pair
in
dictionary
)
{
{
if
(!
CanWriteSingleValue
(
pair
.
Value
))
{
continue
;
}
if
(!
first
)
if
(!
first
)
{
{
builder
.
Append
(
PropertySeparator
);
builder
.
Append
(
PropertySeparator
);
...
...
csharp/src/Google.Protobuf/JsonParser.cs
View file @
d522479a
...
@@ -168,6 +168,10 @@ namespace Google.Protobuf
...
@@ -168,6 +168,10 @@ namespace Google.Protobuf
}
}
var
descriptor
=
message
.
Descriptor
;
var
descriptor
=
message
.
Descriptor
;
var
jsonFieldMap
=
descriptor
.
Fields
.
ByJsonName
();
var
jsonFieldMap
=
descriptor
.
Fields
.
ByJsonName
();
// All the oneof fields we've already accounted for - we can only see each of them once.
// The set is created lazily to avoid the overhead of creating a set for every message
// we parsed, when oneofs are relatively rare.
HashSet
<
OneofDescriptor
>
seenOneofs
=
null
;
while
(
true
)
while
(
true
)
{
{
token
=
tokenizer
.
Next
();
token
=
tokenizer
.
Next
();
...
@@ -183,6 +187,17 @@ namespace Google.Protobuf
...
@@ -183,6 +187,17 @@ namespace Google.Protobuf
FieldDescriptor
field
;
FieldDescriptor
field
;
if
(
jsonFieldMap
.
TryGetValue
(
name
,
out
field
))
if
(
jsonFieldMap
.
TryGetValue
(
name
,
out
field
))
{
{
if
(
field
.
ContainingOneof
!=
null
)
{
if
(
seenOneofs
==
null
)
{
seenOneofs
=
new
HashSet
<
OneofDescriptor
>();
}
if
(!
seenOneofs
.
Add
(
field
.
ContainingOneof
))
{
throw
new
InvalidProtocolBufferException
(
$"Multiple values specified for oneof
{
field
.
ContainingOneof
.
Name
}
"
);
}
}
MergeField
(
message
,
field
,
tokenizer
);
MergeField
(
message
,
field
,
tokenizer
);
}
}
else
else
...
@@ -200,10 +215,15 @@ namespace Google.Protobuf
...
@@ -200,10 +215,15 @@ namespace Google.Protobuf
var
token
=
tokenizer
.
Next
();
var
token
=
tokenizer
.
Next
();
if
(
token
.
Type
==
JsonToken
.
TokenType
.
Null
)
if
(
token
.
Type
==
JsonToken
.
TokenType
.
Null
)
{
{
// Clear the field if we see a null token, unless it's for a singular field of type
// google.protobuf.Value.
// Note: different from Java API, which just ignores it.
// Note: different from Java API, which just ignores it.
// TODO: Bring it more in line? Discuss...
// TODO: Bring it more in line? Discuss...
field
.
Accessor
.
Clear
(
message
);
if
(
field
.
IsMap
||
field
.
IsRepeated
||
!
IsGoogleProtobufValueField
(
field
))
return
;
{
field
.
Accessor
.
Clear
(
message
);
return
;
}
}
}
tokenizer
.
PushBack
(
token
);
tokenizer
.
PushBack
(
token
);
...
@@ -239,6 +259,10 @@ namespace Google.Protobuf
...
@@ -239,6 +259,10 @@ namespace Google.Protobuf
return
;
return
;
}
}
tokenizer
.
PushBack
(
token
);
tokenizer
.
PushBack
(
token
);
if
(
token
.
Type
==
JsonToken
.
TokenType
.
Null
)
{
throw
new
InvalidProtocolBufferException
(
"Repeated field elements cannot be null"
);
}
list
.
Add
(
ParseSingleValue
(
field
,
tokenizer
));
list
.
Add
(
ParseSingleValue
(
field
,
tokenizer
));
}
}
}
}
...
@@ -270,19 +294,30 @@ namespace Google.Protobuf
...
@@ -270,19 +294,30 @@ namespace Google.Protobuf
}
}
object
key
=
ParseMapKey
(
keyField
,
token
.
StringValue
);
object
key
=
ParseMapKey
(
keyField
,
token
.
StringValue
);
object
value
=
ParseSingleValue
(
valueField
,
tokenizer
);
object
value
=
ParseSingleValue
(
valueField
,
tokenizer
);
// TODO: Null handling
if
(
value
==
null
)
{
throw
new
InvalidProtocolBufferException
(
"Map values must not be null"
);
}
dictionary
[
key
]
=
value
;
dictionary
[
key
]
=
value
;
}
}
}
}
private
static
bool
IsGoogleProtobufValueField
(
FieldDescriptor
field
)
{
return
field
.
FieldType
==
FieldType
.
Message
&&
field
.
MessageType
.
FullName
==
Value
.
Descriptor
.
FullName
;
}
private
object
ParseSingleValue
(
FieldDescriptor
field
,
JsonTokenizer
tokenizer
)
private
object
ParseSingleValue
(
FieldDescriptor
field
,
JsonTokenizer
tokenizer
)
{
{
var
token
=
tokenizer
.
Next
();
var
token
=
tokenizer
.
Next
();
if
(
token
.
Type
==
JsonToken
.
TokenType
.
Null
)
if
(
token
.
Type
==
JsonToken
.
TokenType
.
Null
)
{
{
if
(
field
.
FieldType
==
FieldType
.
Message
&&
field
.
MessageType
.
FullName
==
Value
.
Descriptor
.
FullName
)
// TODO: In order to support dynamic messages, we should really build this up
// dynamically.
if
(
IsGoogleProtobufValueField
(
field
))
{
{
return
new
Value
{
NullValue
=
NullValue
.
NULL_VALUE
}
;
return
Value
.
ForNull
()
;
}
}
return
null
;
return
null
;
}
}
...
@@ -464,6 +499,11 @@ namespace Google.Protobuf
...
@@ -464,6 +499,11 @@ namespace Google.Protobuf
{
{
tokens
.
Add
(
token
);
tokens
.
Add
(
token
);
token
=
tokenizer
.
Next
();
token
=
tokenizer
.
Next
();
if
(
tokenizer
.
ObjectDepth
<
typeUrlObjectDepth
)
{
throw
new
InvalidProtocolBufferException
(
"Any message with no @type"
);
}
}
}
// Don't add the @type property or its value to the recorded token list
// Don't add the @type property or its value to the recorded token list
...
@@ -603,6 +643,11 @@ namespace Google.Protobuf
...
@@ -603,6 +643,11 @@ namespace Google.Protobuf
throw
new
InvalidProtocolBufferException
(
$"Value out of range:
{
value
}
"
);
throw
new
InvalidProtocolBufferException
(
$"Value out of range:
{
value
}
"
);
}
}
return
(
float
)
value
;
return
(
float
)
value
;
case
FieldType
.
Enum
:
CheckInteger
(
value
);
// Just return it as an int, and let the CLR convert it.
// Note that we deliberately don't check that it's a known value.
return
(
int
)
value
;
default
:
default
:
throw
new
InvalidProtocolBufferException
(
$"Unsupported conversion from JSON number for field type
{
field
.
FieldType
}
"
);
throw
new
InvalidProtocolBufferException
(
$"Unsupported conversion from JSON number for field type
{
field
.
FieldType
}
"
);
}
}
...
@@ -633,7 +678,14 @@ namespace Google.Protobuf
...
@@ -633,7 +678,14 @@ namespace Google.Protobuf
case
FieldType
.
String
:
case
FieldType
.
String
:
return
text
;
return
text
;
case
FieldType
.
Bytes
:
case
FieldType
.
Bytes
:
return
ByteString
.
FromBase64
(
text
);
try
{
return
ByteString
.
FromBase64
(
text
);
}
catch
(
FormatException
e
)
{
throw
InvalidProtocolBufferException
.
InvalidBase64
(
e
);
}
case
FieldType
.
Int32
:
case
FieldType
.
Int32
:
case
FieldType
.
SInt32
:
case
FieldType
.
SInt32
:
case
FieldType
.
SFixed32
:
case
FieldType
.
SFixed32
:
...
@@ -826,28 +878,24 @@ namespace Google.Protobuf
...
@@ -826,28 +878,24 @@ namespace Google.Protobuf
try
try
{
{
long
seconds
=
long
.
Parse
(
secondsText
,
CultureInfo
.
InvariantCulture
);
long
seconds
=
long
.
Parse
(
secondsText
,
CultureInfo
.
InvariantCulture
)
*
multiplier
;
int
nanos
=
0
;
int
nanos
=
0
;
if
(
subseconds
!=
""
)
if
(
subseconds
!=
""
)
{
{
// This should always work, as we've got 1-9 digits.
// This should always work, as we've got 1-9 digits.
int
parsedFraction
=
int
.
Parse
(
subseconds
.
Substring
(
1
));
int
parsedFraction
=
int
.
Parse
(
subseconds
.
Substring
(
1
));
nanos
=
parsedFraction
*
SubsecondScalingFactors
[
subseconds
.
Length
];
nanos
=
parsedFraction
*
SubsecondScalingFactors
[
subseconds
.
Length
]
*
multiplier
;
}
}
if
(
seconds
>=
Duration
.
MaxSeconds
)
if
(
!
Duration
.
IsNormalized
(
seconds
,
nanos
)
)
{
{
// Allow precisely 315576000000 seconds, but prohibit even 1ns more.
throw
new
InvalidProtocolBufferException
(
$"Invalid Duration value:
{
token
.
StringValue
}
"
);
if
(
seconds
>
Duration
.
MaxSeconds
||
nanos
>
0
)
{
throw
new
InvalidProtocolBufferException
(
"Invalid Duration value: "
+
token
.
StringValue
);
}
}
}
message
.
Descriptor
.
Fields
[
Duration
.
SecondsFieldNumber
].
Accessor
.
SetValue
(
message
,
seconds
*
multiplier
);
message
.
Descriptor
.
Fields
[
Duration
.
SecondsFieldNumber
].
Accessor
.
SetValue
(
message
,
seconds
);
message
.
Descriptor
.
Fields
[
Duration
.
NanosFieldNumber
].
Accessor
.
SetValue
(
message
,
nanos
*
multiplier
);
message
.
Descriptor
.
Fields
[
Duration
.
NanosFieldNumber
].
Accessor
.
SetValue
(
message
,
nanos
);
}
}
catch
(
FormatException
)
catch
(
FormatException
)
{
{
throw
new
InvalidProtocolBufferException
(
"Invalid Duration value: "
+
token
.
StringValue
);
throw
new
InvalidProtocolBufferException
(
$"Invalid Duration value:
{
token
.
StringValue
}
"
);
}
}
}
}
...
@@ -870,6 +918,8 @@ namespace Google.Protobuf
...
@@ -870,6 +918,8 @@ namespace Google.Protobuf
private
static
string
ToSnakeCase
(
string
text
)
private
static
string
ToSnakeCase
(
string
text
)
{
{
var
builder
=
new
StringBuilder
(
text
.
Length
*
2
);
var
builder
=
new
StringBuilder
(
text
.
Length
*
2
);
// Note: this is probably unnecessary now, but currently retained to be as close as possible to the
// C++, whilst still throwing an exception on underscores.
bool
wasNotUnderscore
=
false
;
// Initialize to false for case 1 (below)
bool
wasNotUnderscore
=
false
;
// Initialize to false for case 1 (below)
bool
wasNotCap
=
false
;
bool
wasNotCap
=
false
;
...
@@ -903,7 +953,11 @@ namespace Google.Protobuf
...
@@ -903,7 +953,11 @@ namespace Google.Protobuf
else
else
{
{
builder
.
Append
(
c
);
builder
.
Append
(
c
);
wasNotUnderscore
=
c
!=
'_'
;
if
(
c
==
'_'
)
{
throw
new
InvalidProtocolBufferException
(
$"Invalid field mask:
{
text
}
"
);
}
wasNotUnderscore
=
true
;
wasNotCap
=
true
;
wasNotCap
=
true
;
}
}
}
}
...
...
csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs
View file @
d522479a
...
@@ -92,11 +92,22 @@ namespace Google.Protobuf.Reflection
...
@@ -92,11 +92,22 @@ namespace Google.Protobuf.Reflection
new
FieldDescriptor
(
field
,
file
,
this
,
index
,
generatedCodeInfo
?.
PropertyNames
[
index
]));
new
FieldDescriptor
(
field
,
file
,
this
,
index
,
generatedCodeInfo
?.
PropertyNames
[
index
]));
fieldsInNumberOrder
=
new
ReadOnlyCollection
<
FieldDescriptor
>(
fieldsInDeclarationOrder
.
OrderBy
(
field
=>
field
.
FieldNumber
).
ToArray
());
fieldsInNumberOrder
=
new
ReadOnlyCollection
<
FieldDescriptor
>(
fieldsInDeclarationOrder
.
OrderBy
(
field
=>
field
.
FieldNumber
).
ToArray
());
// TODO: Use field => field.Proto.JsonName when we're confident it's appropriate. (And then use it in the formatter, too.)
// TODO: Use field => field.Proto.JsonName when we're confident it's appropriate. (And then use it in the formatter, too.)
jsonFieldMap
=
new
ReadOnlyDictionary
<
string
,
FieldDescriptor
>(
fieldsInNumberOrder
.
ToDictionary
(
field
=>
JsonFormatter
.
ToCamelCase
(
field
.
Name
))
);
jsonFieldMap
=
CreateJsonFieldMap
(
fieldsInNumberOrder
);
file
.
DescriptorPool
.
AddSymbol
(
this
);
file
.
DescriptorPool
.
AddSymbol
(
this
);
Fields
=
new
FieldCollection
(
this
);
Fields
=
new
FieldCollection
(
this
);
}
}
private
static
ReadOnlyDictionary
<
string
,
FieldDescriptor
>
CreateJsonFieldMap
(
IList
<
FieldDescriptor
>
fields
)
{
var
map
=
new
Dictionary
<
string
,
FieldDescriptor
>();
foreach
(
var
field
in
fields
)
{
map
[
JsonFormatter
.
ToCamelCase
(
field
.
Name
)]
=
field
;
map
[
field
.
Name
]
=
field
;
}
return
new
ReadOnlyDictionary
<
string
,
FieldDescriptor
>(
map
);
}
/// <summary>
/// <summary>
/// The brief name of the descriptor's target.
/// The brief name of the descriptor's target.
/// </summary>
/// </summary>
...
@@ -255,9 +266,10 @@ namespace Google.Protobuf.Reflection
...
@@ -255,9 +266,10 @@ namespace Google.Protobuf.Reflection
// TODO: consider making this public in the future. (Being conservative for now...)
// TODO: consider making this public in the future. (Being conservative for now...)
/// <value>
/// <value>
/// Returns a read-only dictionary mapping the field names in this message as they're
used
/// Returns a read-only dictionary mapping the field names in this message as they're
available
/// in the JSON representation to the field descriptors. For example, a field <c>foo_bar</c>
/// in the JSON representation to the field descriptors. For example, a field <c>foo_bar</c>
/// in the message would result in an entry with a key <c>fooBar</c>.
/// in the message would result two entries, one with a key <c>fooBar</c> and one with a key
/// <c>foo_bar</c>, both referring to the same field.
/// </value>
/// </value>
internal
IDictionary
<
string
,
FieldDescriptor
>
ByJsonName
()
=>
messageDescriptor
.
jsonFieldMap
;
internal
IDictionary
<
string
,
FieldDescriptor
>
ByJsonName
()
=>
messageDescriptor
.
jsonFieldMap
;
...
...
csharp/src/Google.Protobuf/WellKnownTypes/DurationPartial.cs
View file @
d522479a
...
@@ -57,15 +57,38 @@ namespace Google.Protobuf.WellKnownTypes
...
@@ -57,15 +57,38 @@ namespace Google.Protobuf.WellKnownTypes
/// </summary>
/// </summary>
public
const
long
MinSeconds
=
-
315576000000L
;
public
const
long
MinSeconds
=
-
315576000000L
;
internal
const
int
MaxNanoseconds
=
NanosecondsPerSecond
-
1
;
internal
const
int
MinNanoseconds
=
-
NanosecondsPerSecond
+
1
;
internal
static
bool
IsNormalized
(
long
seconds
,
int
nanoseconds
)
{
// Simple boundaries
if
(
seconds
<
MinSeconds
||
seconds
>
MaxSeconds
||
nanoseconds
<
MinNanoseconds
||
nanoseconds
>
MaxNanoseconds
)
{
return
false
;
}
// We only have a problem is one is strictly negative and the other is
// strictly positive.
return
Math
.
Sign
(
seconds
)
*
Math
.
Sign
(
nanoseconds
)
!=
-
1
;
}
/// <summary>
/// <summary>
/// Converts this <see cref="Duration"/> to a <see cref="TimeSpan"/>.
/// Converts this <see cref="Duration"/> to a <see cref="TimeSpan"/>.
/// </summary>
/// </summary>
/// <remarks>If the duration is not a precise number of ticks, it is truncated towards 0.</remarks>
/// <remarks>If the duration is not a precise number of ticks, it is truncated towards 0.</remarks>
/// <returns>The value of this duration, as a <c>TimeSpan</c>.</returns>
/// <returns>The value of this duration, as a <c>TimeSpan</c>.</returns>
/// <exception cref="InvalidOperationException">This value isn't a valid normalized duration, as
/// described in the documentation.</exception>
public
TimeSpan
ToTimeSpan
()
public
TimeSpan
ToTimeSpan
()
{
{
checked
checked
{
{
if
(!
IsNormalized
(
Seconds
,
Nanos
))
{
throw
new
InvalidOperationException
(
"Duration was not a valid normalized duration"
);
}
long
ticks
=
Seconds
*
TimeSpan
.
TicksPerSecond
+
Nanos
/
NanosecondsPerTick
;
long
ticks
=
Seconds
*
TimeSpan
.
TicksPerSecond
+
Nanos
/
NanosecondsPerTick
;
return
TimeSpan
.
FromTicks
(
ticks
);
return
TimeSpan
.
FromTicks
(
ticks
);
}
}
...
...
csharp/src/Google.Protobuf/WellKnownTypes/TimestampPartial.cs
View file @
d522479a
...
@@ -37,9 +37,17 @@ namespace Google.Protobuf.WellKnownTypes
...
@@ -37,9 +37,17 @@ namespace Google.Protobuf.WellKnownTypes
public
partial
class
Timestamp
public
partial
class
Timestamp
{
{
private
static
readonly
DateTime
UnixEpoch
=
new
DateTime
(
1970
,
1
,
1
,
0
,
0
,
0
,
DateTimeKind
.
Utc
);
private
static
readonly
DateTime
UnixEpoch
=
new
DateTime
(
1970
,
1
,
1
,
0
,
0
,
0
,
DateTimeKind
.
Utc
);
private
static
readonly
long
BclSecondsAtUnixEpoch
=
UnixEpoch
.
Ticks
/
TimeSpan
.
TicksPerSecond
;
// Constants determined programmatically, but then hard-coded so they can be constant expressions.
internal
static
readonly
long
UnixSecondsAtBclMinValue
=
-
BclSecondsAtUnixEpoch
;
private
const
long
BclSecondsAtUnixEpoch
=
62135596800
;
internal
static
readonly
long
UnixSecondsAtBclMaxValue
=
(
DateTime
.
MaxValue
.
Ticks
/
TimeSpan
.
TicksPerSecond
)
-
BclSecondsAtUnixEpoch
;
internal
const
long
UnixSecondsAtBclMaxValue
=
253402300799
;
internal
const
long
UnixSecondsAtBclMinValue
=
-
BclSecondsAtUnixEpoch
;
internal
const
int
MaxNanos
=
Duration
.
NanosecondsPerSecond
-
1
;
private
bool
IsNormalized
=>
Nanos
>=
0
&&
Nanos
<=
MaxNanos
&&
Seconds
>=
UnixSecondsAtBclMinValue
&&
Seconds
<=
UnixSecondsAtBclMaxValue
;
/// <summary>
/// <summary>
/// Returns the difference between one <see cref="Timestamp"/> and another, as a <see cref="Duration"/>.
/// Returns the difference between one <see cref="Timestamp"/> and another, as a <see cref="Duration"/>.
...
@@ -99,8 +107,14 @@ namespace Google.Protobuf.WellKnownTypes
...
@@ -99,8 +107,14 @@ namespace Google.Protobuf.WellKnownTypes
/// <see cref="DateTime"/> value precisely on a second.
/// <see cref="DateTime"/> value precisely on a second.
/// </remarks>
/// </remarks>
/// <returns>This timestamp as a <c>DateTime</c>.</returns>
/// <returns>This timestamp as a <c>DateTime</c>.</returns>
/// <exception cref="InvalidOperationException">The timestamp contains invalid values; either it is
/// incorrectly normalized or is outside the valid range.</exception>
public
DateTime
ToDateTime
()
public
DateTime
ToDateTime
()
{
{
if
(!
IsNormalized
)
{
throw
new
InvalidOperationException
(
@"Timestamp contains invalid values: Seconds={Seconds}; Nanos={Nanos}"
);
}
return
UnixEpoch
.
AddSeconds
(
Seconds
).
AddTicks
(
Nanos
/
Duration
.
NanosecondsPerTick
);
return
UnixEpoch
.
AddSeconds
(
Seconds
).
AddTicks
(
Nanos
/
Duration
.
NanosecondsPerTick
);
}
}
...
@@ -114,6 +128,8 @@ namespace Google.Protobuf.WellKnownTypes
...
@@ -114,6 +128,8 @@ namespace Google.Protobuf.WellKnownTypes
/// <see cref="DateTimeOffset"/> value precisely on a second.
/// <see cref="DateTimeOffset"/> value precisely on a second.
/// </remarks>
/// </remarks>
/// <returns>This timestamp as a <c>DateTimeOffset</c>.</returns>
/// <returns>This timestamp as a <c>DateTimeOffset</c>.</returns>
/// <exception cref="InvalidOperationException">The timestamp contains invalid values; either it is
/// incorrectly normalized or is outside the valid range.</exception>
public
DateTimeOffset
ToDateTimeOffset
()
public
DateTimeOffset
ToDateTimeOffset
()
{
{
return
new
DateTimeOffset
(
ToDateTime
(),
TimeSpan
.
Zero
);
return
new
DateTimeOffset
(
ToDateTime
(),
TimeSpan
.
Zero
);
...
...
src/google/protobuf/unittest_well_known_types.proto
View file @
d522479a
...
@@ -39,6 +39,8 @@ message TestWellKnownTypes {
...
@@ -39,6 +39,8 @@ message TestWellKnownTypes {
google.protobuf.BoolValue
bool_field
=
16
;
google.protobuf.BoolValue
bool_field
=
16
;
google.protobuf.StringValue
string_field
=
17
;
google.protobuf.StringValue
string_field
=
17
;
google.protobuf.BytesValue
bytes_field
=
18
;
google.protobuf.BytesValue
bytes_field
=
18
;
// Part of struct, but useful to be able to test separately
google.protobuf.Value
value_field
=
19
;
}
}
// A repeated field for each well-known type.
// A repeated field for each well-known type.
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment