Skip to content

Commit

Permalink
PR Review feedback take protocolbuffers#1
Browse files Browse the repository at this point in the history
  • Loading branch information
fmg-lydonchandra committed Jun 29, 2023
1 parent 13087fe commit 590c61d
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 39 deletions.
47 changes: 36 additions & 11 deletions csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -692,45 +692,70 @@ public void OneofSerialization_DefaultValue()
}

[Test]
public void MapSerializationDeterministicTrue_ThenBytesIdentical()
public void MapStringString_DeterministicTrue_ThenBytesIdentical()
{
// Define three strings consisting of different versions of the letter I.
// LATIN CAPITAL LETTER I (U+0049)
string capitalLetterI = "I";
// LATIN SMALL LETTER I (U+0069)
string smallLetterI = "i";
// LATIN SMALL LETTER DOTLESS I (U+0131)
string smallLetterDotlessI = "\u0131";
var testMap1 = new TestMap();

testMap1.MapStringString.Add(smallLetterDotlessI, "value_"+smallLetterDotlessI);
testMap1.MapStringString.Add(smallLetterI, "value_"+smallLetterI);
testMap1.MapStringString.Add(capitalLetterI, "content_"+capitalLetterI);
var bytes1 = SerializeTestMap(testMap1, true);

var testMap2 = new TestMap();
testMap2.MapStringString.Add(capitalLetterI, "content_"+capitalLetterI);
testMap2.MapStringString.Add(smallLetterI, "value_"+smallLetterI);
testMap2.MapStringString.Add(smallLetterDotlessI, "value_"+smallLetterDotlessI);

var bytes2 = SerializeTestMap(testMap2, true);
var parsedBytes2 = TestMap.Parser.ParseFrom(bytes2);
var parsedBytes1 = TestMap.Parser.ParseFrom(bytes1);
Assert.IsTrue(bytes1.SequenceEqual(bytes2));
}

[Test]
public void MapInt32Bytes_DeterministicTrue_ThenBytesIdentical()
{
var testMap1 = new TestMap();
testMap1.MapInt32Bytes.Add(1, ByteString.CopyFromUtf8("test"));
testMap1.MapInt32Bytes.Add(1, ByteString.CopyFromUtf8("test1"));
testMap1.MapInt32Bytes.Add(2, ByteString.CopyFromUtf8("test2"));
var bytes1 = SerializeTestMap(testMap1, true);

var testMap2 = new TestMap();
testMap2.MapInt32Bytes.Add(2, ByteString.CopyFromUtf8("test2"));
testMap2.MapInt32Bytes.Add(1, ByteString.CopyFromUtf8("test"));
testMap2.MapInt32Bytes.Add(1, ByteString.CopyFromUtf8("test1"));
var bytes2 = SerializeTestMap(testMap2, true);

Assert.IsTrue(bytes1.SequenceEqual(bytes2));
}

[Test]
public void MapSerializationDeterministicFalse_ThenBytesDifferent()
public void MapInt32Bytes_DeterministicFalse_ThenBytesDifferent()
{
var testMap1 = new TestMap();
testMap1.MapInt32Bytes.Add(1, ByteString.CopyFromUtf8("test"));
testMap1.MapInt32Bytes.Add(1, ByteString.CopyFromUtf8("test1"));
testMap1.MapInt32Bytes.Add(2, ByteString.CopyFromUtf8("test2"));
var bytes1 = SerializeTestMap(testMap1, false);

var testMap2 = new TestMap();
testMap2.MapInt32Bytes.Add(2, ByteString.CopyFromUtf8("test2"));
testMap2.MapInt32Bytes.Add(1, ByteString.CopyFromUtf8("test"));
testMap2.MapInt32Bytes.Add(1, ByteString.CopyFromUtf8("test1"));
var bytes2 = SerializeTestMap(testMap2, false);

Assert.IsFalse(bytes1.SequenceEqual(bytes2));
}

private byte[] SerializeTestMap(TestMap testMap, bool useDeterministicSerialization)
private byte[] SerializeTestMap(TestMap testMap, bool deterministic)
{
using var memoryStream = new MemoryStream();
var codedOutputStream = new CodedOutputStream(memoryStream);
if (useDeterministicSerialization)
{
codedOutputStream.UseDeterministicSerialization();
}
codedOutputStream.Deterministic = deterministic;

testMap.WriteTo(codedOutputStream);
codedOutputStream.Flush();
Expand Down
10 changes: 1 addition & 9 deletions csharp/src/Google.Protobuf/CodedOutputStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -185,15 +185,7 @@ public long Position
/// maps are sorted on the lexicographical order of the UTF8 encoded keys.</description></item>
/// </list>
/// </remarks>
public void UseDeterministicSerialization() {
serializationDeterministic = true;
}

internal bool IsSerializationDeterministic() {
return serializationDeterministic;
}

private bool serializationDeterministic;
public bool Deterministic { get; set; }

#region Writing of values (not including tags)

Expand Down
36 changes: 17 additions & 19 deletions csharp/src/Google.Protobuf/Collections/MapField.cs
Original file line number Diff line number Diff line change
Expand Up @@ -452,24 +452,29 @@ public void WriteTo(CodedOutputStream output, Codec codec)
WriteContext.Initialize(output, out WriteContext ctx);
try
{
if (output.IsSerializationDeterministic())
{
var sorted = new List<KeyValuePair<TKey, TValue>>(list);
sorted.Sort((pair1, pair2) => Comparer<TKey>.Default.Compare(pair1.Key, pair2.Key));
IEnumerable<KeyValuePair<TKey, TValue>> listToWrite = list;

WriteTo(ref ctx, codec, sorted);
}
else
if (output.Deterministic)
{
WriteTo(ref ctx, codec, list);
listToWrite = GetSortedListCopy(list);
}
WriteTo(ref ctx, codec, listToWrite);
}
finally
{
ctx.CopyStateTo(output);
}
}

internal IEnumerable<KeyValuePair<TKey, TValue>> GetSortedListCopy(IEnumerable<KeyValuePair<TKey, TValue>> listToSort)
{
// We can't sort the list in place, as that would invalidate the linked list.
// Instead, we create a new list, sort that, and then write it out.
var listToWrite = new List<KeyValuePair<TKey, TValue>>(listToSort);
listToWrite.Sort((pair1, pair2) => Comparer<TKey>.Default.Compare(pair1.Key, pair2.Key));
return listToWrite;
}

/// <summary>
/// Writes the contents of this map to the given write context, using the specified codec
/// to encode each entry.
Expand All @@ -479,19 +484,12 @@ public void WriteTo(CodedOutputStream output, Codec codec)
[SecuritySafeCritical]
public void WriteTo(ref WriteContext ctx, Codec codec)
{
if (ctx.state.CodedOutputStream is not null && ctx.state.CodedOutputStream.IsSerializationDeterministic())
{
// We can't sort the list in place, as that would invalidate the linked list.
// Instead, we create a new list, sort that, and then write it out.
var sorted = new List<KeyValuePair<TKey, TValue>>(list);
sorted.Sort((pair1, pair2) => Comparer<TKey>.Default.Compare(pair1.Key, pair2.Key));

WriteTo(ref ctx, codec, sorted);
}
else
IEnumerable<KeyValuePair<TKey, TValue>> listToWrite = list;
if (ctx.state.CodedOutputStream?.Deterministic ?? false)
{
WriteTo(ref ctx, codec, list);
listToWrite = GetSortedListCopy(list);
}
WriteTo(ref ctx, codec, listToWrite);
}

private void WriteTo(ref WriteContext ctx, Codec codec, IEnumerable<KeyValuePair<TKey, TValue>> listKvp)
Expand Down

0 comments on commit 590c61d

Please sign in to comment.