From 00778041ffcc1edc511bc3f2f13c595668ec683d Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Mon, 6 Jul 2020 10:59:39 +1200 Subject: [PATCH] Mirror Java's UnsafeByteOperations type --- Makefile.am | 3 +- .../Google.Protobuf.Test/ByteStringTest.cs | 8 +- csharp/src/Google.Protobuf/ByteString.cs | 19 ++--- .../Google.Protobuf/UnsafeByteOperations.cs | 83 +++++++++++++++++++ 4 files changed, 99 insertions(+), 14 deletions(-) create mode 100644 csharp/src/Google.Protobuf/UnsafeByteOperations.cs diff --git a/Makefile.am b/Makefile.am index a542008d84a7..faea6c12e18c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -269,7 +269,8 @@ csharp_EXTRA_DIST= \ csharp/src/Google.Protobuf/WriteContext.cs \ csharp/src/Google.Protobuf/WriteBufferHelper.cs \ csharp/src/Google.Protobuf/UnknownField.cs \ - csharp/src/Google.Protobuf/UnknownFieldSet.cs + csharp/src/Google.Protobuf/UnknownFieldSet.cs \ + csharp/src/Google.Protobuf/UnsafeByteOperations.cs java_EXTRA_DIST= \ java/README.md \ diff --git a/csharp/src/Google.Protobuf.Test/ByteStringTest.cs b/csharp/src/Google.Protobuf.Test/ByteStringTest.cs index a52993f932ce..dbc1d649deec 100644 --- a/csharp/src/Google.Protobuf.Test/ByteStringTest.cs +++ b/csharp/src/Google.Protobuf.Test/ByteStringTest.cs @@ -178,16 +178,20 @@ public void GetEnumerator() } [Test] - public void UnsafeFromBytes() + public void UnsafeWrap() { byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 }; - ByteString bs = ByteString.UnsafeFromBytes(data.AsMemory(2, 3)); + ByteString bs = UnsafeByteOperations.UnsafeWrap(data.AsMemory(2, 3)); ReadOnlySpan s = bs.Span; Assert.AreEqual(3, s.Length); Assert.AreEqual(2, s[0]); Assert.AreEqual(3, s[1]); Assert.AreEqual(4, s[2]); + + // Check that the value is not a copy + data[2] = byte.MaxValue; + Assert.AreEqual(byte.MaxValue, s[0]); } [Test] diff --git a/csharp/src/Google.Protobuf/ByteString.cs b/csharp/src/Google.Protobuf/ByteString.cs index 2d22b85d5481..32562aec72e3 100644 --- a/csharp/src/Google.Protobuf/ByteString.cs +++ b/csharp/src/Google.Protobuf/ByteString.cs @@ -69,6 +69,14 @@ internal static ByteString AttachBytes(byte[] bytes) return new ByteString(bytes); } + /// + /// Internal use only. Ensure that the provided array is not mutated and belongs to this instance. + /// + internal static ByteString AttachBytes(ReadOnlyMemory bytes) + { + return new ByteString(bytes); + } + #if GOOGLE_PROTOBUF_SUPPORT_SYSTEM_MEMORY /// /// Constructs a new ByteString from the given byte array. The array is @@ -169,17 +177,6 @@ public string ToBase64() #endif } -#if GOOGLE_PROTOBUF_SUPPORT_SYSTEM_MEMORY - /// - /// Constructs a new from the given bytes. The bytes are not copied, - /// and must not be modified while the is in use. - /// - public static ByteString UnsafeFromBytes(ReadOnlyMemory bytes) - { - return new ByteString(bytes); - } -#endif - /// /// Constructs a from the Base64 Encoded String. /// diff --git a/csharp/src/Google.Protobuf/UnsafeByteOperations.cs b/csharp/src/Google.Protobuf/UnsafeByteOperations.cs new file mode 100644 index 000000000000..7116bc44c6e3 --- /dev/null +++ b/csharp/src/Google.Protobuf/UnsafeByteOperations.cs @@ -0,0 +1,83 @@ +#region Copyright notice and license +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#endregion + +using System; +using System.Security; + +namespace Google.Protobuf +{ +#if GOOGLE_PROTOBUF_SUPPORT_SYSTEM_MEMORY + /// + /// Provides a number of unsafe byte operations to be used by advanced applications with high performance + /// requirements. These methods are referred to as "unsafe" due to the fact that they potentially expose + /// the backing buffer of a to the application. + /// + /// + /// + /// The methods in this class should only be called if it is guaranteed that the buffer backing the + /// will never change! Mutation of a can lead to unexpected + /// and undesirable consequences in your application, and will likely be difficult to debug. Proceed with caution! + /// + /// + /// This can have a number of significant side affects that have spooky-action-at-a-distance-like behavior. In + /// particular, if the bytes value changes out from under a Protocol Buffer: + /// + /// + /// + /// serialization may throw + /// + /// + /// serialization may succeed but the wrong bytes may be written out + /// + /// + /// messages are no longer threadsafe + /// + /// + /// hashCode may be incorrect + /// + /// + /// + [SecuritySafeCritical] + public static class UnsafeByteOperations + { + /// + /// Constructs a new from the given bytes. The bytes are not copied, + /// and must not be modified while the is in use. + /// This API is experimental and subject to change. + /// + public static ByteString UnsafeWrap(ReadOnlyMemory bytes) + { + return ByteString.AttachBytes(bytes); + } + } +#endif +}