Skip to content

Commit f7b1584

Browse files
Copilotpetesramek
andauthored
Changes before error encountered
Agent-Logs-Url: https://github.com/petesramek/polyline-algorithm-csharp/sessions/1ae1dd3f-582a-4277-8da4-f0572c87aed3 Co-authored-by: petesramek <2333452+petesramek@users.noreply.github.com>
1 parent 16efc49 commit f7b1584

13 files changed

Lines changed: 545 additions & 237 deletions

File tree

benchmarks/PolylineAlgorithm.Benchmarks/PolylineEncodingBenchmark.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
1+
//
2+
// Copyright © Pete Sramek. All rights reserved.
3+
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
4+
//
5+
16
namespace PolylineAlgorithm.Benchmarks;
27

38
using BenchmarkDotNet.Attributes;
49
using PolylineAlgorithm.Utility;
510

11+
/// <summary>
12+
/// Benchmarks for the polyline encoding validation methods in <see cref="PolylineEncoding"/>.
13+
/// </summary>
614
public class PolylineEncodingBenchmark {
15+
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
716
private string polyline;
17+
#pragma warning restore CS8618
818

919
/// <summary>
1020
/// Number of coordinates for benchmarks. Set by BenchmarkDotNet.

samples/PolylineAlgorithm.NetTopologySuite.Sample/NetTopologyPolylineEncoder.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ protected override double GetLatitude(Point current) {
3535
throw new ArgumentNullException(nameof(current));
3636
}
3737

38-
return current.X;
38+
// NetTopologySuite Point: Y = latitude
39+
return current.Y;
3940
}
4041

4142
/// <summary>
@@ -48,6 +49,7 @@ protected override double GetLongitude(Point current) {
4849
throw new ArgumentNullException(nameof(current));
4950
}
5051

51-
return current.Y;
52+
// NetTopologySuite Point: X = longitude
53+
return current.X;
5254
}
5355
}

src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs

Lines changed: 179 additions & 179 deletions
Large diffs are not rendered by default.

src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ namespace PolylineAlgorithm.Abstraction;
2626
/// <typeparam name="TPolyline">The type that represents the encoded polyline output.</typeparam>
2727
public abstract class AbstractPolylineEncoder<TCoordinate, TPolyline> : IPolylineEncoder<TCoordinate, TPolyline> {
2828
private readonly ILogger<AbstractPolylineEncoder<TCoordinate, TPolyline>> _logger;
29+
2930
/// <summary>
3031
/// Initializes a new instance of the <see cref="AbstractPolylineEncoder{TCoordinate, TPolyline}"/> class with default encoding options.
3132
/// </summary>
@@ -99,6 +100,8 @@ public TPolyline Encode(ReadOnlySpan<TCoordinate> coordinates, CancellationToken
99100

100101
Span<char> buffer = temp is null ? stackalloc char[length] : temp.AsSpan(0, length);
101102

103+
string encodedResult;
104+
102105
try {
103106
for (var i = 0; i < coordinates.Length; i++) {
104107
cancellationToken.ThrowIfCancellationRequested();
@@ -124,6 +127,8 @@ public TPolyline Encode(ReadOnlySpan<TCoordinate> coordinates, CancellationToken
124127

125128
consumed++;
126129
}
130+
131+
encodedResult = buffer[..position].ToString();
127132
} finally {
128133
if (temp is not null) {
129134
ArrayPool<char>.Shared.Return(temp);
@@ -133,7 +138,7 @@ public TPolyline Encode(ReadOnlySpan<TCoordinate> coordinates, CancellationToken
133138
_logger
134139
.LogOperationFinishedDebug(OperationName);
135140

136-
return CreatePolyline(buffer[..position].ToString().AsMemory());
141+
return CreatePolyline(encodedResult.AsMemory());
137142

138143
[MethodImpl(MethodImplOptions.AggressiveInlining)]
139144
static int GetMaxBufferLength(int count) {
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
//
2+
// Copyright © Pete Sramek. All rights reserved.
3+
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
4+
//
5+
6+
namespace PolylineAlgorithm.Extensions;
7+
8+
using PolylineAlgorithm.Abstraction;
9+
using PolylineAlgorithm.Internal.Diagnostics;
10+
using System;
11+
using System.Collections.Generic;
12+
13+
/// <summary>
14+
/// Provides extension methods for the <see cref="IPolylineDecoder{TPolyline, TValue}"/> interface to facilitate decoding encoded polylines.
15+
/// </summary>
16+
public static class PolylineDecoderExtensions {
17+
/// <summary>
18+
/// Decodes an encoded polyline represented as a character array into a sequence of geographic coordinates.
19+
/// </summary>
20+
/// <typeparam name="TValue">The coordinate type returned by the decoder.</typeparam>
21+
/// <param name="decoder">
22+
/// The <see cref="IPolylineDecoder{TPolyline, TValue}"/> instance used to perform the decoding operation.
23+
/// </param>
24+
/// <param name="polyline">
25+
/// The encoded polyline as a character array to decode. The array is converted to a string internally.
26+
/// </param>
27+
/// <returns>
28+
/// An <see cref="IEnumerable{T}"/> of <typeparamref name="TValue"/> containing the decoded coordinate pairs.
29+
/// </returns>
30+
/// <exception cref="ArgumentNullException">
31+
/// Thrown when <paramref name="decoder"/> or <paramref name="polyline"/> is <see langword="null"/>.
32+
/// </exception>
33+
public static IEnumerable<TValue> Decode<TValue>(this IPolylineDecoder<string, TValue> decoder, char[] polyline) {
34+
if (decoder is null) {
35+
ExceptionGuard.ThrowArgumentNull(nameof(decoder));
36+
}
37+
38+
if (polyline is null) {
39+
ExceptionGuard.ThrowArgumentNull(nameof(polyline));
40+
}
41+
42+
return decoder.Decode(new string(polyline));
43+
}
44+
45+
/// <summary>
46+
/// Decodes an encoded polyline represented as a read-only memory of characters into a sequence of geographic coordinates.
47+
/// </summary>
48+
/// <typeparam name="TValue">The coordinate type returned by the decoder.</typeparam>
49+
/// <param name="decoder">
50+
/// The <see cref="IPolylineDecoder{TPolyline, TValue}"/> instance used to perform the decoding operation.
51+
/// </param>
52+
/// <param name="polyline">
53+
/// The encoded polyline as a read-only memory of characters to decode. The memory is converted to a string internally.
54+
/// </param>
55+
/// <returns>
56+
/// An <see cref="IEnumerable{T}"/> of <typeparamref name="TValue"/> containing the decoded coordinate pairs.
57+
/// </returns>
58+
/// <exception cref="ArgumentNullException">
59+
/// Thrown when <paramref name="decoder"/> is <see langword="null"/>.
60+
/// </exception>
61+
public static IEnumerable<TValue> Decode<TValue>(this IPolylineDecoder<string, TValue> decoder, ReadOnlyMemory<char> polyline) {
62+
if (decoder is null) {
63+
ExceptionGuard.ThrowArgumentNull(nameof(decoder));
64+
}
65+
66+
return decoder.Decode(polyline.ToString());
67+
}
68+
69+
/// <summary>
70+
/// Decodes an encoded polyline string into a sequence of geographic coordinates,
71+
/// using a decoder that accepts <see cref="ReadOnlyMemory{T}"/> of <see cref="char"/>.
72+
/// </summary>
73+
/// <typeparam name="TValue">The coordinate type returned by the decoder.</typeparam>
74+
/// <param name="decoder">
75+
/// The <see cref="IPolylineDecoder{TPolyline, TValue}"/> instance used to perform the decoding operation.
76+
/// </param>
77+
/// <param name="polyline">
78+
/// The encoded polyline string to decode. The string is converted to <see cref="ReadOnlyMemory{T}"/> internally.
79+
/// </param>
80+
/// <returns>
81+
/// An <see cref="IEnumerable{T}"/> of <typeparamref name="TValue"/> containing the decoded coordinate pairs.
82+
/// </returns>
83+
/// <exception cref="ArgumentNullException">
84+
/// Thrown when <paramref name="decoder"/> or <paramref name="polyline"/> is <see langword="null"/>.
85+
/// </exception>
86+
public static IEnumerable<TValue> Decode<TValue>(this IPolylineDecoder<ReadOnlyMemory<char>, TValue> decoder, string polyline) {
87+
if (decoder is null) {
88+
ExceptionGuard.ThrowArgumentNull(nameof(decoder));
89+
}
90+
91+
if (polyline is null) {
92+
ExceptionGuard.ThrowArgumentNull(nameof(polyline));
93+
}
94+
95+
return decoder.Decode(polyline.AsMemory());
96+
}
97+
}

src/PolylineAlgorithm/Internal/Diagnostics/ExceptionGuard.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,12 @@ public static void ThrowInvalidPolylineFormat(long position) {
206206
/// <summary>
207207
/// Throws an <see cref="InvalidPolylineException"/> when the polyline block terminator is invalid.
208208
/// </summary>
209+
#if NET6_0_OR_GREATER
210+
[StackTraceHidden]
211+
#else
212+
[MethodImpl(MethodImplOptions.NoInlining)]
213+
#endif
214+
[DoesNotReturn]
209215
public static void ThrowInvalidPolylineBlockTerminator() {
210216
throw new InvalidPolylineException(ExceptionMessage.GetInvalidPolylineBlockTerminator());
211217
}
@@ -240,6 +246,7 @@ internal static class ExceptionMessage {
240246
#endif
241247

242248
private static readonly string ArgumentCannotBeEmptyMessage = ExceptionMessageResource.ArgumentCannotBeEmptyMessage;
249+
private static readonly string ArgumentValueMustBeFiniteNumberMessage = ExceptionMessageResource.ArgumentValueMustBeFiniteNumber;
243250
private static readonly string CouldNotWriteEncodedValueToTheBufferMessage = ExceptionMessageResource.CouldNotWriteEncodedValueToTheBufferMessage;
244251
private static readonly string InvalidPolylineBlockTerminatorMessage = ExceptionMessageResource.InvalidPolylineBlockTerminatorMessage;
245252

@@ -295,7 +302,7 @@ public static string FormatInvalidPolylineLength(int length, int min) =>
295302
/// Gets the message used when a numeric argument must be finite.
296303
/// </summary>
297304
public static string GetArgumentValueMustBeFiniteNumber() =>
298-
ArgumentCannotBeEmptyMessage;
305+
ArgumentValueMustBeFiniteNumberMessage;
299306

300307
/// <summary>
301308
/// Gets the message used when the library could not write an encoded value to a buffer.

src/PolylineAlgorithm/Internal/Diagnostics/LogDebugExtensions.cs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,51 @@ namespace PolylineAlgorithm.Internal.Diagnostics;
77

88
using Microsoft.Extensions.Logging;
99

10+
/// <summary>
11+
/// Provides strongly-typed logging extension methods for debug-level diagnostics used throughout the library.
12+
/// These methods are partial and annotated with <see cref="LoggerMessageAttribute"/> so the .NET
13+
/// source generator can produce efficient logging implementations at compile time.
14+
/// </summary>
15+
/// <remarks>
16+
/// Event IDs are derived from the debug <see cref="LogLevel"/> value: <c>(int)LogLevel.Debug * 100</c>.
17+
/// For the current <see cref="LogLevel.Debug"/> value this yields a base of <c>100</c>,
18+
/// so individual event IDs range from <c>101</c> to <c>104</c>.
19+
/// </remarks>
1020
internal static partial class LogDebugExtensions {
1121
private const LogLevel LOG_LEVEL = LogLevel.Debug;
1222
private const int EVENT_ID_BASE = (int)LOG_LEVEL * Defaults.Logging.LogLevelMultiplier;
1323

24+
/// <summary>
25+
/// Logs a debug message when an operation has started.
26+
/// </summary>
27+
/// <param name="logger">The <see cref="ILogger"/> used to write the log entry.</param>
28+
/// <param name="operationName">The name of the operation that has started.</param>
1429
[LoggerMessage(EVENT_ID_BASE + 1, LOG_LEVEL, "Operation {operationName} has started.")]
1530
internal static partial void LogOperationStartedDebug(this ILogger logger, string operationName);
1631

32+
/// <summary>
33+
/// Logs a debug message when an operation has failed.
34+
/// </summary>
35+
/// <param name="logger">The <see cref="ILogger"/> used to write the log entry.</param>
36+
/// <param name="operationName">The name of the operation that has failed.</param>
1737
[LoggerMessage(EVENT_ID_BASE + 2, LOG_LEVEL, "Operation {operationName} has failed.")]
1838
internal static partial void LogOperationFailedDebug(this ILogger logger, string operationName);
1939

40+
/// <summary>
41+
/// Logs a debug message when an operation has finished successfully.
42+
/// </summary>
43+
/// <param name="logger">The <see cref="ILogger"/> used to write the log entry.</param>
44+
/// <param name="operationName">The name of the operation that has finished.</param>
2045
[LoggerMessage(EVENT_ID_BASE + 3, LOG_LEVEL, "Operation {operationName} has finished.")]
2146
internal static partial void LogOperationFinishedDebug(this ILogger logger, string operationName);
2247

48+
/// <summary>
49+
/// Logs a debug message containing the decoded coordinate values and position.
50+
/// </summary>
51+
/// <param name="logger">The <see cref="ILogger"/> used to write the log entry.</param>
52+
/// <param name="latitude">The decoded latitude value.</param>
53+
/// <param name="longitude">The decoded longitude value.</param>
54+
/// <param name="position">The position in the polyline buffer at which the coordinate was decoded.</param>
2355
[LoggerMessage(EVENT_ID_BASE + 4, LOG_LEVEL, "Decoded coordinate: (Latitude: {latitude}, Longitude: {longitude}) at position {position}.")]
2456
internal static partial void LogDecodedCoordinateDebug(this ILogger logger, double latitude, double longitude, int position);
25-
}
57+
}

src/PolylineAlgorithm/PolylineAlgorithm.csproj

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,6 @@
44
<TargetFramework>netstandard2.1</TargetFramework>
55
</PropertyGroup>
66

7-
<!--<PropertyGroup>
8-
<RunCodeAnalysis>true</RunCodeAnalysis>
9-
<AnalysisMode>Recommended</AnalysisMode>
10-
<EnableNETAnalyzers>true</EnableNETAnalyzers>
11-
<WarningLevel>4</WarningLevel>
12-
<AnalysisLevel>latest</AnalysisLevel>
13-
</PropertyGroup>-->
14-
157
<PropertyGroup>
168
<GenerateDocumentationFile>True</GenerateDocumentationFile>
179
</PropertyGroup>
@@ -52,7 +44,6 @@
5244
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
5345
</PackageReference>
5446
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="[6.0.0,11.0.0)" />
55-
<PackageReference Include="System.Text.Json" Version="10.0.5" />
5647
</ItemGroup>
5748

5849
<ItemGroup>

src/PolylineAlgorithm/Properties/ExceptionMessageResource.Designer.cs

Lines changed: 1 addition & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/PolylineAlgorithm/Properties/ExceptionMessageResource.resx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@
118118
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
119119
</resheader>
120120
<data name="ArgumentCannotBeEmptyMessage" xml:space="preserve">
121-
<value>Argument cannot be an empty.</value>
121+
<value>Argument cannot be empty.</value>
122122
</data>
123123
<data name="PolylineIsMalformedAtFormat" xml:space="preserve">
124124
<value>Polyline is malformed at position {0}.</value>
@@ -129,9 +129,6 @@
129129
<data name="ArgumentValueMustBeFiniteNumber" xml:space="preserve">
130130
<value>Value must be a finite number.</value>
131131
</data>
132-
<data name="ArgumentOutOfRangeFormat" xml:space="preserve">
133-
<value> Value {0} is out of range. Expected range between {1} and {2}.</value>
134-
</data>
135132
<data name="StackAllocLimitMustBeEqualOrGreaterThanFormat" xml:space="preserve">
136133
<value>Stack alloc limit must be equal or greater than {0}.</value>
137134
</data>

0 commit comments

Comments
 (0)