diff --git a/Minio/ApiEndpoints/ObjectOperations.cs b/Minio/ApiEndpoints/ObjectOperations.cs index b695f0c65..dcc528391 100644 --- a/Minio/ApiEndpoints/ObjectOperations.cs +++ b/Minio/ApiEndpoints/ObjectOperations.cs @@ -15,6 +15,7 @@ * limitations under the License. */ +using System.Buffers; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Net; @@ -576,8 +577,10 @@ public async Task PutObjectAsync(PutObjectArgs args, if ((args.ObjectSize < Constants.MinimumPartSize || isSnowball) && args.ObjectSize >= 0 && args.ObjectStreamData is not null) { - var bytes = await ReadFullAsync(args.ObjectStreamData, (int)args.ObjectSize).ConfigureAwait(false); - var bytesRead = bytes.Length; + using var tempMemory = MemoryPool.Shared.Rent((int)args.ObjectSize); + var bytesRead = await ReadFullAsync(args.ObjectStreamData, tempMemory.Memory) + .ConfigureAwait(false); + var bytes = tempMemory.Memory[..bytesRead]; if (bytesRead != (int)args.ObjectSize) throw new UnexpectedShortReadException( $"Data read {bytesRead.ToString(CultureInfo.InvariantCulture)} is shorter than the size {args.ObjectSize.ToString(CultureInfo.InvariantCulture)} of input buffer."); @@ -916,9 +919,11 @@ private async Task> PutObjectPartAsync(PutObjectPartArg var etags = new Dictionary(); var progressReport = new ProgressReport(); args.Progress?.Report(progressReport); + using var tempMemory = MemoryPool.Shared.Rent((int)partSize); for (partNumber = 1; partNumber <= partCount; partNumber++) { - var dataToCopy = await ReadFullAsync(args.ObjectStreamData, (int)partSize).ConfigureAwait(false); + var bytesRead = await ReadFullAsync(args.ObjectStreamData, tempMemory.Memory).ConfigureAwait(false); + var dataToCopy = tempMemory.Memory[..bytesRead]; if (dataToCopy.IsEmpty && numPartsUploaded > 0) break; if (partNumber == partCount) expectedReadSize = lastPartSize; var putObjectArgs = new PutObjectArgs(args) @@ -1140,23 +1145,10 @@ await this.ExecuteTaskAsync(requestMessageBuilder, /// Advances in the stream upto currentPartSize or End of Stream /// /// - /// - /// bytes read in a byte array - internal static async Task> ReadFullAsync(Stream data, int currentPartSize) + /// + /// bytes count read + internal static async Task ReadFullAsync(Stream data, Memory destination) { - Memory result = new byte[currentPartSize]; - var totalRead = 0; - while (totalRead < currentPartSize) - { - var curData = result[totalRead..currentPartSize]; - var curRead = await data.ReadAsync(curData).ConfigureAwait(false); - if (curRead == 0) break; - totalRead += curRead; - } - - if (totalRead == 0) return null; - - // Return only the valid portion without allocating a new buffer - return result[..totalRead]; + return await data.ReadAsync(destination).ConfigureAwait(false); } } diff --git a/Minio/Helper/S3utils.cs b/Minio/Helper/S3utils.cs index eaae70026..cc1823b60 100644 --- a/Minio/Helper/S3utils.cs +++ b/Minio/Helper/S3utils.cs @@ -22,13 +22,16 @@ internal static class S3utils { internal static readonly Regex TrimWhitespaceRegex = new("\\s+", RegexOptions.None, TimeSpan.FromHours(1)); + internal static readonly Regex AmazonEndpointRegex = new( + "^s3[.-]?(.*?)\\.amazonaws\\.com$", + RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture | RegexOptions.CultureInvariant, + TimeSpan.FromHours(1) + ); + internal static bool IsAmazonEndPoint(string endpoint) { if (IsAmazonChinaEndPoint(endpoint)) return true; - var rgx = new Regex("^s3[.-]?(.*?)\\.amazonaws\\.com$", RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture, - TimeSpan.FromHours(1)); - var matches = rgx.Matches(endpoint); - return matches.Count > 0; + return AmazonEndpointRegex.IsMatch(endpoint); } // IsAmazonChinaEndpoint - Match if it is exactly Amazon S3 China endpoint.