Skip to content

Commit c0564ad

Browse files
committed
Making all char[] fields readonly, adding a static init function.
1 parent 0ff9359 commit c0564ad

1 file changed

Lines changed: 52 additions & 38 deletions

File tree

src/Hashids.net/Hashids.cs

Lines changed: 52 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ public partial class Hashids : IHashids
2020

2121
private const int MaxNumberHashLength = 12; // Length of long.MaxValue;
2222

23-
private char[] _alphabet;
24-
private long _alphabetLength;
25-
private char[] _seps;
26-
private char[] _guards;
27-
private char[] _salt;
23+
private readonly char[] _alphabet;
24+
private readonly long _alphabetLength;
25+
private readonly char[] _seps;
26+
private readonly char[] _guards;
27+
private readonly char[] _salt;
2828
private readonly int _minHashLength;
2929

3030
private readonly StringBuilderPool _sbPool = new();
@@ -36,7 +36,7 @@ public partial class Hashids : IHashids
3636
/// <summary>
3737
/// Instantiates a new Hashids encoder/decoder with defaults.
3838
/// </summary>
39-
public Hashids() : this(string.Empty, 0, DEFAULT_ALPHABET, DEFAULT_SEPS)
39+
public Hashids() : this(salt: string.Empty, minHashLength: 0, alphabet: DEFAULT_ALPHABET, seps: DEFAULT_SEPS)
4040
{
4141
// empty constructor with defaults needed to allow mocking of public methods
4242
}
@@ -65,68 +65,82 @@ public Hashids(
6565
throw new ArgumentNullException(nameof(seps));
6666

6767
_salt = salt.Trim().ToCharArray();
68-
_alphabet = alphabet.ToCharArray().Distinct().ToArray();
69-
_seps = seps.ToCharArray();
7068
_minHashLength = minHashLength;
7169

72-
if (_alphabet.Length < MIN_ALPHABET_LENGTH)
73-
throw new ArgumentException($"Alphabet must contain at least {MIN_ALPHABET_LENGTH} unique characters.",
74-
nameof(alphabet));
75-
76-
SetupSeps();
77-
SetupGuards();
70+
InitCharArrays(alphabet: alphabet, seps: seps, salt: _salt, alphabetChars: out _alphabet, sepChars: out _seps, guardChars: out _guards);
7871

7972
_alphabetLength = _alphabet.Length;
8073
}
8174

82-
private void SetupSeps()
75+
/// <remarks>This method uses <c>out</c> params instead of returning a ValueTuple so it works with .NET 4.6.1.</remarks>
76+
private static void InitCharArrays(string alphabet, string seps, char[] salt, out char[] alphabetChars, out char[] sepChars, out char[] guardChars)
8377
{
84-
// seps should contain only characters present in alphabet;
85-
_seps = _seps.Intersect(_alphabet).ToArray();
78+
alphabetChars = alphabet.ToCharArray().Distinct().ToArray();
79+
sepChars = seps.ToCharArray();
8680

87-
// alphabet should not contain seps.
88-
_alphabet = _alphabet.Except(_seps).ToArray();
81+
if (alphabetChars.Length < MIN_ALPHABET_LENGTH)
82+
{
83+
throw new ArgumentException($"Alphabet must contain at least {MIN_ALPHABET_LENGTH:N0} unique characters.", paramName: nameof(alphabet));
84+
}
8985

90-
ConsistentShuffle(_seps, _seps.Length, _salt, _salt.Length);
86+
// SetupGuards():
9187

92-
if (_seps.Length == 0 || ((float)_alphabet.Length / _seps.Length) > SEP_DIV)
88+
// seps should contain only characters present in alphabet:
89+
if (sepChars.Length > 0)
90+
{
91+
sepChars = sepChars.Intersect(alphabetChars).ToArray();
92+
}
93+
94+
// alphabet should not contain seps // TODO: This comment contradicts the above, it needs rephrasing.
95+
if (sepChars.Length > 0 )
9396
{
94-
var sepsLength = (int)Math.Ceiling((float)_alphabet.Length / SEP_DIV);
97+
alphabetChars = alphabetChars.Except(sepChars).ToArray();
98+
}
99+
100+
if (alphabetChars.Length < (MIN_ALPHABET_LENGTH - 6)) // TODO: What should the minimum length be after removing chars in `sep`?
101+
{
102+
throw new ArgumentException($"Alphabet must contain at least {MIN_ALPHABET_LENGTH:N0} unique characters that are also not present in .", paramName: nameof(alphabet));
103+
}
104+
105+
ConsistentShuffle(alphabet: sepChars, alphabetLength: sepChars.Length, salt: salt, saltLength: salt.Length);
106+
107+
if (sepChars.Length == 0 || ((float)alphabetChars.Length / sepChars.Length) > SEP_DIV)
108+
{
109+
var sepsLength = (int)Math.Ceiling((float)alphabetChars.Length / SEP_DIV);
95110

96111
if (sepsLength == 1)
97112
{
98113
sepsLength = 2;
99114
}
100115

101-
if (sepsLength > _seps.Length)
116+
if (sepsLength > sepChars.Length)
102117
{
103-
var diff = sepsLength - _seps.Length;
104-
_seps = _seps.Append(_alphabet, 0, diff);
105-
_alphabet = _alphabet.SubArray(diff);
118+
var diff = sepsLength - sepChars.Length;
119+
sepChars = sepChars.Append(alphabetChars, 0, diff);
120+
alphabetChars = alphabetChars.SubArray(diff);
106121
}
107122
else
108123
{
109-
_seps = _seps.SubArray(0, sepsLength);
124+
sepChars = sepChars.SubArray(0, sepsLength);
110125
}
111126
}
112127

113-
ConsistentShuffle(_alphabet, _alphabet.Length, _salt, _salt.Length);
114-
}
115-
116-
private void SetupGuards()
117-
{
118-
var guardCount = (int)Math.Ceiling(_alphabet.Length / GUARD_DIV);
128+
ConsistentShuffle(alphabet: alphabetChars, alphabetChars.Length, salt: salt, salt.Length);
129+
130+
// SetupGuards():
131+
132+
var guardCount = (int)Math.Ceiling(alphabetChars.Length / GUARD_DIV);
119133

120-
if (_alphabet.Length < 3)
134+
if (alphabetChars.Length < 3)
121135
{
122-
_guards = _seps.SubArray(0, guardCount);
123-
_seps = _seps.SubArray(guardCount);
136+
guardChars = sepChars.SubArray(index: 0, length: guardCount);
137+
sepChars = sepChars.SubArray(index: guardCount);
124138
}
125139

126140
else
127141
{
128-
_guards = _alphabet.SubArray(0, guardCount);
129-
_alphabet = _alphabet.SubArray(guardCount);
142+
guardChars = alphabetChars.SubArray(index: 0, length: guardCount);
143+
alphabetChars = alphabetChars.SubArray(index: guardCount);
130144
}
131145
}
132146

0 commit comments

Comments
 (0)