Skip to content

Commit f2246a4

Browse files
author
krivchenko-kv
committed
deep refactoring
1 parent e6e8180 commit f2246a4

17 files changed

Lines changed: 414 additions & 403 deletions

src/Storage.Benchmark/InternalBenchmarks/HashBenchmark.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using BenchmarkDotNet.Attributes;
1+
using BenchmarkDotNet.Attributes;
22
using BenchmarkDotNet.Jobs;
33
using Storage.Benchmark.Utils;
44
using Storage.Utils;
@@ -26,15 +26,15 @@ public void Config()
2626
public int ByteHash()
2727
{
2828
return HashHelper
29-
.GetPayloadHash(_byteData, DefaultArrayPool.Instance)
29+
.GetPayloadHash(_byteData)
3030
.Length;
3131
}
3232

3333
[Benchmark]
3434
public int StringHash()
3535
{
3636
return HashHelper
37-
.GetPayloadHash(_stringData, DefaultArrayPool.Instance)
37+
.GetPayloadHash(_stringData)
3838
.Length;
3939
}
4040
}

src/Storage.Benchmark/InternalBenchmarks/SignatureBenchmark.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,11 @@ public void Config()
2323
var data = BenchmarkHelper.ReadBigArray(config);
2424
var settings = BenchmarkHelper.ReadSettings(config);
2525

26-
var urlBuilder = new UrlBuilder(settings.SecretKey, settings.Region, settings.Service, DefaultArrayPool.Instance);
27-
2826
_headers = ["host", "x-amz-content-sha256", "x-amz-date"];
2927
_now = DateTime.UtcNow;
3028
_request = new HttpRequestMessage(HttpMethod.Post, "http://company-name.com/controller");
31-
_payloadHash = HashHelper.GetPayloadHash(data, DefaultArrayPool.Instance);
32-
_signature = new Signature(urlBuilder, settings.SecretKey, DefaultArrayPool.Instance);
29+
_payloadHash = HashHelper.GetPayloadHash(data);
30+
_signature = new Signature(settings.Region, settings.Service, settings.SecretKey);
3331
}
3432

3533
[Benchmark]
Lines changed: 90 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,90 @@
1-
//using Storage.Utils;
2-
3-
//namespace Storage.Tests.Utils;
4-
5-
//public class QueryParameterTests
6-
//{
7-
// public UrlBuilder appender = new QueryParameterAppender(new HttpDescription())
8-
9-
10-
11-
// [Fact]
12-
// public void Test_EmptyQuery()
13-
// {
14-
// var appender = new QueryParameterAppender();
15-
// var builder = new ValueStringBuilder();
16-
17-
// appender.AppendCanonicalQueryParameters1(ref builder, null);
18-
// Assert.Empty(builder.ToString());
19-
20-
// appender.AppendCanonicalQueryParameters1(ref builder, "");
21-
// Assert.Empty(builder.ToString());
22-
23-
// appender.AppendCanonicalQueryParameters1(ref builder, "?");
24-
// Assert.Empty(builder.ToString());
25-
// }
26-
27-
// [Fact]
28-
// public void Test_SingleParameter()
29-
// {
30-
// var appender = new QueryParameterAppender();
31-
// var builder = new ValueStringBuilder();
32-
33-
// appender.AppendCanonicalQueryParameters1(ref builder, "?key=value");
34-
// Assert.Equal("key=value", builder.ToString());
35-
// }
36-
37-
// [Fact]
38-
// public void Test_MultipleParameters()
39-
// {
40-
// var appender = new QueryParameterAppender();
41-
// var builder = new ValueStringBuilder();
42-
43-
// appender.AppendCanonicalQueryParameters1(ref builder, "?key1=value1&key2=value2");
44-
// Assert.Equal("key1=value1&key2=value2", builder.ToString());
45-
// }
46-
47-
// [Fact]
48-
// public void Test_ParameterWithWhitespace()
49-
// {
50-
// var appender = new QueryParameterAppender();
51-
// var builder = new ValueStringBuilder();
52-
53-
// appender.AppendCanonicalQueryParameters1(ref builder, "? key1 = value1 & key2 = value2 ");
54-
// Assert.Equal("key1=value1&key2=value2", builder.ToString());
55-
// }
56-
57-
// [Fact]
58-
// public void Test_ParameterWithoutValue()
59-
// {
60-
// var appender = new QueryParameterAppender();
61-
// var builder = new ValueStringBuilder();
62-
63-
// appender.AppendCanonicalQueryParameters1(ref builder, "?key1&key2=value2");
64-
// Assert.Equal("key1=&key2=value2", builder.ToString());
65-
// }
66-
67-
// [Fact]
68-
// public void Test_ParameterWithEmptyValue()
69-
// {
70-
// var appender = new QueryParameterAppender();
71-
// var builder = new ValueStringBuilder();
72-
73-
// appender.AppendCanonicalQueryParameters1(ref builder, "?key1=&key2=");
74-
// Assert.Equal("key1=&key2=", builder.ToString());
75-
// }
76-
77-
// [Fact]
78-
// public void Test_ParameterWithSpecialCharacters()
79-
// {
80-
// var appender = new QueryParameterAppender();
81-
// var builder = new ValueStringBuilder();
82-
83-
// appender.AppendCanonicalQueryParameters1(ref builder, "?key1=value%20with%20spaces&key2=value%26with%26ampersands");
84-
// Assert.Equal("key1=value with spaces&key2=value&with&ampersands", builder.ToString());
85-
// }
86-
//}
1+
using Storage.Utils;
2+
3+
namespace Storage.Tests.Utils;
4+
5+
public class QueryParameterTests
6+
{
7+
8+
[Fact]
9+
public void Test_EmptyQuery()
10+
{
11+
var builder = new ValueStringBuilder(stackalloc char[512]);
12+
13+
StringUtils.AppendCanonicalQueryParameters(ref builder, null);
14+
Assert.Empty(builder.ToString());
15+
16+
StringUtils.AppendCanonicalQueryParameters(ref builder, "");
17+
Assert.Empty(builder.ToString());
18+
19+
StringUtils.AppendCanonicalQueryParameters(ref builder, "?");
20+
Assert.Empty(builder.ToString());
21+
22+
builder.Dispose();
23+
}
24+
25+
[Fact]
26+
public void Test_SingleParameter()
27+
{
28+
var builder = new ValueStringBuilder(stackalloc char[512]);
29+
30+
StringUtils.AppendCanonicalQueryParameters(ref builder, "?key=value");
31+
Assert.Equal("key=value", builder.ToString());
32+
33+
builder.Dispose();
34+
}
35+
36+
[Fact]
37+
public void Test_MultipleParameters()
38+
{
39+
var builder = new ValueStringBuilder(stackalloc char[512]);
40+
41+
StringUtils.AppendCanonicalQueryParameters(ref builder, "?key1=value1&key2=value2");
42+
Assert.Equal("key1=value1&key2=value2", builder.ToString());
43+
44+
builder.Dispose();
45+
}
46+
47+
[Fact]
48+
public void Test_ParameterWithWhitespace()
49+
{
50+
var builder = new ValueStringBuilder(stackalloc char[512]);
51+
52+
StringUtils.AppendCanonicalQueryParameters(ref builder, "? key1 = value1 & key2 = value2 ");
53+
Assert.Equal("key1%20=%20value1%20&key2%20=%20value2%20", builder.ToString());
54+
55+
builder.Dispose();
56+
}
57+
58+
[Fact]
59+
public void Test_ParameterWithoutValue()
60+
{
61+
var builder = new ValueStringBuilder(stackalloc char[512]);
62+
63+
StringUtils.AppendCanonicalQueryParameters(ref builder, "?key1&key2=value2");
64+
Assert.Equal("key1=&key2=value2", builder.ToString());
65+
66+
builder.Dispose();
67+
}
68+
69+
[Fact]
70+
public void Test_ParameterWithEmptyValue()
71+
{
72+
var builder = new ValueStringBuilder(stackalloc char[512]);
73+
74+
StringUtils.AppendCanonicalQueryParameters(ref builder, "?key1=&key2=");
75+
Assert.Equal("key1=&key2=", builder.ToString());
76+
77+
builder.Dispose();
78+
}
79+
80+
[Fact]
81+
public void Test_ParameterWithSpecialCharacters()
82+
{
83+
var builder = new ValueStringBuilder(stackalloc char[512]);
84+
85+
StringUtils.AppendCanonicalQueryParameters(ref builder, "?key1=value%20with%20spaces&key2=value%26with%26ampersands");
86+
Assert.Equal("key1=value%20with%20spaces&key2=value%26with%26ampersands", builder.ToString());
87+
88+
builder.Dispose();
89+
}
90+
}

src/Storage.Tests/Utils/ValueStringBuilderShould.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public void Grow()
1010
const int stringLength = 256;
1111
var chars = Enumerable.Range(0, stringLength).Select(i => (char)i);
1212

13-
var builder = new ValueStringBuilder(stackalloc char[64], DefaultArrayPool.Instance);
13+
var builder = new ValueStringBuilder(stackalloc char[64]);
1414
foreach (var c in chars)
1515
{
1616
builder.Append(c);
@@ -23,7 +23,7 @@ public void Grow()
2323
[Fact]
2424
public void NotCreateEmptyString()
2525
{
26-
var builder = new ValueStringBuilder(stackalloc char[64], DefaultArrayPool.Instance);
26+
var builder = new ValueStringBuilder(stackalloc char[64]);
2727
builder
2828
.ToString()
2929
.Should().BeEmpty();
@@ -33,7 +33,7 @@ public void NotCreateEmptyString()
3333
[Fact]
3434
public void RemoveLastCorrectly()
3535
{
36-
var builder = new ValueStringBuilder(stackalloc char[64], DefaultArrayPool.Instance);
36+
var builder = new ValueStringBuilder(stackalloc char[64]);
3737
builder.RemoveLast();
3838

3939
builder.Length

src/Storage/DefaultArrayPool.cs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,28 @@
11
namespace Storage;
22

3-
internal sealed class DefaultArrayPool : IArrayPool
3+
public sealed class DefaultArrayPool
44
{
5-
public static readonly IArrayPool Instance = new DefaultArrayPool();
6-
7-
private DefaultArrayPool()
5+
class ArrayPool : IArrayPool
86
{
7+
public T[] Rent<T>(int minimumLength) => ArrayPool<T>.Shared.Rent(minimumLength);
8+
public void Return<T>(T[] array, bool clear = false) => ArrayPool<T>.Shared.Return(array, clear);
99
}
1010

11-
public T[] Rent<T>(int minimumLength)
12-
{
13-
return ArrayPool<T>.Shared.Rent(minimumLength);
14-
}
1511

16-
public void Return<T>(T[] array, bool clear = false)
12+
private static Lazy<IArrayPool> s_instance = new(new ArrayPool());
13+
14+
public static IArrayPool Instance => s_instance.Value;
15+
16+
17+
public static bool SetDefault(IArrayPool arrayPool)
1718
{
18-
ArrayPool<T>.Shared.Return(array, clear);
19+
ArgumentNullException.ThrowIfNull(arrayPool);
20+
21+
if (!s_instance.IsValueCreated)
22+
{
23+
s_instance = new Lazy<IArrayPool>(arrayPool);
24+
return true;
25+
}
26+
return false;
1927
}
2028
}

src/Storage/S3BucketClient.Multipart.cs

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
using System.Text;
21
using Storage.Utils;
2+
using System.Text;
33
using static Storage.Utils.HashHelper;
44

5-
namespace Storage;
6-
5+
namespace Storage;
6+
77
/// <summary>
88
/// Функции управления multipart-загрузкой
99
/// </summary>
@@ -21,8 +21,8 @@ internal async Task<bool> MultipartAbort(string encodedFileName, string uploadId
2121
response = await Send(request, EmptyPayloadHash, ct).ConfigureAwait(false);
2222
}
2323
catch
24-
{
25-
// ignored
24+
{
25+
// ignored
2626
}
2727
}
2828

@@ -69,12 +69,12 @@ internal async Task<bool> MultipartComplete(
6969
.Append("</CompleteMultipartUpload>")
7070
.Flush();
7171

72-
var payloadHash = GetPayloadHash(data, _arrayPool);
72+
var payloadHash = GetPayloadHash(data);
7373

7474
HttpResponseMessage response;
75-
using (var request = new HttpRequestMessage(
76-
HttpMethod.Post,
77-
$"{_bucket}/{encodedFileName}?uploadId={uploadId}"))
75+
using (var request = new HttpRequestMessage(
76+
HttpMethod.Post,
77+
$"{_bucket}/{encodedFileName}?uploadId={uploadId}"))
7878
{
7979
using var content = new StringContent(data, Encoding.UTF8);
8080
request.Content = content;
@@ -95,7 +95,7 @@ internal async Task<bool> MultipartComplete(
9595
int partSize,
9696
CancellationToken ct)
9797
{
98-
var payloadHash = GetPayloadHash(partData.AsSpan(0, partSize), _arrayPool);
98+
var payloadHash = GetPayloadHash(partData.AsSpan(0, partSize));
9999
var url = $"{_bucket}/{encodedFileName}?partNumber={partNumber}&uploadId={uploadId}";
100100

101101
HttpResponseMessage response;
@@ -150,8 +150,10 @@ private async Task<bool> ExecuteMultipartUpload(
150150
return false;
151151
}
152152

153-
private async Task<string> MultipartStart(string encodedFileName, string contentType, CancellationToken ct)
154-
{
153+
private async Task<S3Upload> MultipartStart(string fileName, string contentType, CancellationToken ct)
154+
{
155+
var encodedFileName = StringUtils.UrlEncodeName(fileName);
156+
155157
HttpResponseMessage response;
156158
using (var request = new HttpRequestMessage(HttpMethod.Post, $"{_bucket}/{encodedFileName}?uploads"))
157159
{
@@ -165,15 +167,16 @@ private async Task<string> MultipartStart(string encodedFileName, string content
165167
if (response.StatusCode is HttpStatusCode.OK)
166168
{
167169
var responseStream = await response.Content.ReadAsStreamAsync(ct).ConfigureAwait(false);
168-
var result = XmlStreamReader.ReadString(responseStream, "UploadId");
170+
var uploadId = XmlStreamReader.ReadString(responseStream, "UploadId");
169171

170172
await responseStream.DisposeAsync().ConfigureAwait(false);
171173
response.Dispose();
172174

173-
return result;
175+
return new S3Upload(this, fileName, encodedFileName, uploadId, ArrayPool);
174176
}
175177

176-
Errors.UnexpectedResult(response);
177-
return string.Empty;
178+
Errors.UnexpectedResult(response);
179+
180+
return null;
178181
}
179182
}

src/Storage/S3BucketClient.Transport.cs

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,8 @@ public partial class S3BucketClient
1111
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1212
private HttpRequestMessage CreateRequest(HttpMethod method, string? fileName = null)
1313
{
14-
var url = new ValueStringBuilder(stackalloc char[512], _arrayPool);
15-
url.Append(_bucket);
16-
17-
// ReSharper disable once InvertIf
18-
if (!string.IsNullOrEmpty(fileName))
19-
{
20-
url.Append('/');
21-
_urlBuilder.AppendEncodedName(ref url, fileName);
22-
}
23-
24-
return new HttpRequestMessage(method, new Uri(url.Flush(), UriKind.Absolute));
14+
var url = StringUtils.BuildFileUrl(_bucket, fileName);
15+
return new HttpRequestMessage(method, new Uri(url, UriKind.Absolute));
2516
}
2617

2718
private Task<HttpResponseMessage> Send(HttpRequestMessage request, string payloadHash, CancellationToken ct)
@@ -43,8 +34,9 @@ private Task<HttpResponseMessage> Send(HttpRequestMessage request, string payloa
4334
request.Version = HttpVersion.Version20;
4435
}
4536

46-
var signature = _signature.Calculate(request, payloadHash, HeadBuilder.S3Headers, now);
47-
headers.TryAddWithoutValidation("Authorization", _headBuilder.BuildAuthorization(now, signature));
37+
var signature = _signature.Calculate(request, payloadHash, now);
38+
39+
headers.TryAddWithoutValidation("Authorization", _headBuilder.BuildAuthorizationValue(now, signature));
4840

4941
return _client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, ct);
5042
}

0 commit comments

Comments
 (0)