Skip to content

Commit

Permalink
Add check to Decode for invalid ID with repeating reserved character
Browse files Browse the repository at this point in the history
Resolves #3
  • Loading branch information
aradalvand committed Aug 16, 2023
1 parent 6971279 commit e76a3a4
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 6 deletions.
11 changes: 8 additions & 3 deletions src/Sqids/SqidsEncoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public SqidsEncoder(SqidsOptions options)
w.Length < 3 || // NOTE: Removes words that are less than 3 characters long
w.Any(c => !options.Alphabet.Contains(c)) // NOTE: Removes words that contain characters not found in the alphabet
);
_blockList = options.BlockList.ToArray(); // NOTE: An array is faster to iterate over compared to a HashSet, so we construct and array here.
_blockList = options.BlockList.ToArray(); // NOTE: Arrays are faster to iterate than HashSets, so we construct an array here.

Span<char> shuffledAlphabet = options.Alphabet.Length * sizeof(char) > MaxStackallocSize // NOTE: We multiply the number of characters by the size of a `char` to get the actual amount of memory that would be allocated.
? new char[options.Alphabet.Length]
Expand Down Expand Up @@ -255,13 +255,18 @@ public int[] Decode(ReadOnlySpan<char> id)
char separator = alphabetTemp[^1];

var separatorIndex = id.IndexOf(separator);
var chunk = separatorIndex == -1 ? id : id[..separatorIndex]; // NOTE: The first part of `id` to the left of the separator is the number that we ought to decode.
id = separatorIndex == -1 ? string.Empty : id[(separatorIndex + 1)..]; // NOTE: Everything to the right of the separator will be `id ` for the next iteration
var chunk = separatorIndex == -1 ? id : id[..separatorIndex]; // NOTE: The first part of `id` (every thing to the left of the separator) represents the number that we ought to decode.
id = separatorIndex == -1 ? string.Empty : id[(separatorIndex + 1)..]; // NOTE: Everything to the right of the separator will be `id` for the next iteration

if (chunk.IsEmpty)
continue;

var alphabetWithoutSeparator = alphabetTemp[..^1]; // NOTE: Exclude the last character from the alphabet (which is the separator)

foreach (char c in chunk)
if (!alphabetWithoutSeparator.Contains(c))
return Array.Empty<int>();

var decodedNumber = ToNumber(chunk, alphabetWithoutSeparator);
result.Add(decodedNumber);

Expand Down
7 changes: 4 additions & 3 deletions test/Sqids.Tests/EncodingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,13 @@ public void EncodeAndDecode_MultipleNumbers_RoundTripsSuccessfully(int[] numbers
sqids.Decode(sqids.Encode(numbers)).ShouldBeEquivalentTo(numbers);
}

[Test]
public void Decode_CharactersNotInAlphabet_ReturnsEmptyArray()
[TestCase("*")] // NOTE: Character not found in the alphabet
[TestCase("fff")] // NOTE: Repeating reserved character
public void Decode_WithInvalidCharacters_ReturnsEmptyArray(string id)
{
var sqids = new SqidsEncoder();

sqids.Decode("*").ShouldBeEmpty();
sqids.Decode(id).ShouldBeEmpty();
}

[Test]
Expand Down

0 comments on commit e76a3a4

Please sign in to comment.