Skip to content

Commit e8cfaa1

Browse files
committed
removed SIMD validation. only one passthrough for ValidateFormat method
1 parent 0b16dcd commit e8cfaa1

2 files changed

Lines changed: 39 additions & 93 deletions

File tree

benchmarks/PolylineAlgorithm.Benchmarks/PolylineEncodingBenchmark.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,5 @@ public void Setup() {
2828
}
2929

3030
[Benchmark(Baseline = true)]
31-
public void ValidateCharRange() => PolylineEncoding.ValidateCharRange(polyline);
32-
33-
[Benchmark]
34-
public void ValidateBlockLength() => PolylineEncoding.ValidateBlockLength(polyline);
35-
36-
[Benchmark]
3731
public void ValidateFormat() => PolylineEncoding.ValidateFormat(polyline);
3832
}

src/PolylineAlgorithm/PolylineEncoding.cs

Lines changed: 39 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -330,123 +330,75 @@ public static int GetRequiredBufferSize(int delta) {
330330
private const ushort End = (Defaults.Algorithm.QuestionMark + Defaults.Algorithm.Space);
331331

332332
/// <summary>
333-
/// SIMD vector containing the minimum valid character value for efficient range validation of polyline segments.
334-
/// </summary>
335-
private static readonly Vector<ushort> MinVector = new(Min);
336-
337-
/// <summary>
338-
/// SIMD vector containing the maximum valid character value for efficient range validation of polyline segments.
339-
/// </summary>
340-
private static readonly Vector<ushort> MaxVector = new(Max);
341-
342-
/// <summary>
343-
/// Validates the format of a polyline segment, ensuring all characters are valid and block structure is correct.
333+
/// Validates a single encoded polyline segment for both character correctness and encoded-value block structure.
344334
/// </summary>
345335
/// <remarks>
346336
/// <para>
347-
/// This method performs two levels of validation on the provided polyline segment:
337+
/// The method performs two validations in a single linear pass over <paramref name="polyline"/>:
348338
/// </para>
349339
/// <list type="number">
350340
/// <item>
351341
/// <description>
352-
/// <b>Character Range Validation:</b> Checks that every character in the polyline is within the valid ASCII range for polyline encoding ('?' [63] to '_' [95], inclusive).
353-
/// Uses SIMD acceleration for efficient validation of large segments.
342+
/// <b>Character range:</b> every character must be within the allowed ASCII range for the polyline algorithm
343+
/// (from '?' (63) up to '_' (95), inclusive). If a character outside this range is encountered, an exception is raised
344+
/// (see <see cref="ExceptionGuard.ThrowInvalidPolylineCharacter(char,int)"/>).
354345
/// </description>
355346
/// </item>
356347
/// <item>
357348
/// <description>
358-
/// <b>Block Structure Validation:</b> Ensures that each encoded value (block) does not exceed 7 characters and that the polyline ends with a valid block terminator.
349+
/// <b>Block structure:</b> each encoded value is composed of one or more 5-bit chunks and must end with a terminator
350+
/// (a character with value less than <see cref="End"/>). Blocks may not exceed the configured maximum length
351+
/// (<see cref="Defaults.Polyline.Block.Length.Max"/>); violations raise
352+
/// <see cref="ExceptionGuard.ThrowPolylineBlockTooLong(int)"/>. The method also ensures the entire polyline ends with
353+
/// a valid block terminator (<see cref="ExceptionGuard.ThrowInvalidPolylineBlockTerminator"/>).
359354
/// </description>
360355
/// </item>
361356
/// </list>
362357
/// <para>
363-
/// If an invalid character or block structure is detected, an <see cref="ArgumentException"/> is thrown with details about the error.
358+
/// Implementation notes:
359+
/// <list type="bullet">
360+
/// <item><description>Time: O(n) where n is the number of characters in <paramref name="polyline"/>.</description></item>
361+
/// <item><description>Memory: O(1) additional allocations (scalar single-pass validation).</description></item>
362+
/// <item><description>For character-only validation that may benefit from SIMD/vectorization, use <see cref="ValidateCharRange(ReadOnlySpan{char})"/>.</description></item>
363+
/// <item><description>The method reports specific errors through the <see cref="ExceptionGuard"/> helpers:
364+
/// <see cref="ExceptionGuard.ThrowInvalidPolylineCharacter(char,int)"/>,
365+
/// <see cref="ExceptionGuard.ThrowPolylineBlockTooLong(int)"/>, and
366+
/// <see cref="ExceptionGuard.ThrowInvalidPolylineBlockTerminator()"/>.</description></item>
367+
/// </list>
364368
/// </para>
365369
/// </remarks>
366-
/// <param name="polyline">A span representing the polyline segment to validate.</param>
370+
/// <param name="polyline">A span containing the encoded polyline segment to validate.</param>
367371
/// <exception cref="ArgumentException">
368-
/// Thrown when an invalid character is found or the block structure is invalid.
372+
/// Thrown when the polyline contains an invalid character, a block exceeds the maximum allowed length, or the polyline
373+
/// does not end with a valid block terminator. Concrete exceptions are raised via <see cref="ExceptionGuard"/>.
369374
/// </exception>
375+
/// <returns>Nothing. The method completes successfully if validation passes; otherwise it throws via <see cref="ExceptionGuard"/>.</returns>
376+
/// <example>
377+
/// <code language="csharp">
378+
/// // Validates an encoded segment and throws if malformed:
379+
/// PolylineEncoding.ValidateFormat(somePolylineSpan);
380+
/// </code>
381+
/// </example>
370382
public static void ValidateFormat(ReadOnlySpan<char> polyline) {
371-
// 1. SIMD character check (reuse existing method)
372-
ValidateCharRange(polyline);
373-
// 2. Block structure check
374-
ValidateBlockLength(polyline);
375-
}
376-
377-
/// <summary>
378-
/// Validates that all characters in the polyline segment are within the allowed ASCII range for polyline encoding.
379-
/// </summary>
380-
/// <remarks>
381-
/// <para>
382-
/// Uses SIMD vectorization for efficient validation of large spans. Falls back to scalar checks for any block where an invalid character is detected.
383-
/// </para>
384-
/// <para>
385-
/// The valid range is from '?' (63) to '_' (95), inclusive. If an invalid character is found, an <see cref="ArgumentException"/> is thrown.
386-
/// </para>
387-
/// </remarks>
388-
/// <param name="polyline">A span representing the polyline segment to validate.</param>
389-
/// <exception cref="ArgumentException">
390-
/// Thrown when an invalid character is found in the polyline segment.
391-
/// </exception>
392-
public static void ValidateCharRange(ReadOnlySpan<char> polyline) {
393-
int length = polyline.Length;
394-
int vectorSize = Vector<ushort>.Count;
395-
396-
int i = 0;
397-
for (; i <= length - vectorSize; i += vectorSize) {
398-
var span = MemoryMarshal.Cast<char, ushort>(polyline.Slice(i, vectorSize));
399-
#if NET5_0_OR_GREATER
400-
var chars = new Vector<ushort>(span);
401-
#else
402-
var chars = new Vector<ushort>(span.ToArray());
403-
#endif
404-
var belowMin = Vector.LessThan(chars, MinVector);
405-
var aboveMax = Vector.GreaterThan(chars, MaxVector);
406-
if (Vector.BitwiseOr(belowMin, aboveMax) != Vector<ushort>.Zero) {
407-
// Fallback to scalar check for this block
408-
for (int j = 0; j < vectorSize; j++) {
409-
char character = polyline[i + j];
410-
if (character < Min || character > Max) {
411-
ExceptionGuard.ThrowInvalidPolylineCharacter(character, i + j);
412-
}
413-
}
414-
}
415-
}
416-
417-
for (; i < length; i++) {
418-
char character = polyline[i];
419-
if (character < Min || character > Max) {
420-
ExceptionGuard.ThrowInvalidPolylineCharacter(character, i);
421-
}
422-
}
423-
}
424-
425-
/// <summary>
426-
/// Validates the block structure of a polyline segment, ensuring each encoded value does not exceed 7 characters and the polyline ends correctly.
427-
/// </summary>
428-
/// <remarks>
429-
/// <para>
430-
/// Iterates through the polyline, counting the length of each block (a sequence of characters representing an encoded value).
431-
/// Throws an <see cref="ArgumentException"/> if any block exceeds 7 characters or if the polyline does not end with a valid block terminator.
432-
/// </para>
433-
/// </remarks>
434-
/// <param name="polyline">A span representing the polyline segment to validate.</param>
435-
/// <exception cref="ArgumentException">
436-
/// Thrown when a block exceeds 7 characters or the polyline does not end with a valid block terminator.
437-
/// </exception>
438-
public static void ValidateBlockLength(ReadOnlySpan<char> polyline) {
439383
int blockLen = 0;
440384
bool foundBlockEnd = false;
441385

442386
for (int i = 0; i < polyline.Length; i++) {
387+
char c = polyline[i];
388+
389+
if (c < Min || c > Max) {
390+
ExceptionGuard.ThrowInvalidPolylineCharacter(c, i);
391+
}
392+
443393
blockLen++;
444394

445-
if (polyline[i] < End) {
395+
if (c < End) {
446396
foundBlockEnd = true;
397+
447398
if (blockLen > Defaults.Polyline.Block.Length.Max) {
448399
ExceptionGuard.ThrowPolylineBlockTooLong(i - blockLen + 1);
449400
}
401+
450402
blockLen = 0;
451403
} else {
452404
foundBlockEnd = false;

0 commit comments

Comments
 (0)