33using System . Linq ;
44using System . Text ;
55using System . Text . RegularExpressions ;
6+ using Microsoft . Extensions . ObjectPool ;
67
78namespace HashidsNet
89{
@@ -24,9 +25,11 @@ public partial class Hashids : IHashids
2425 private char [ ] _salt ;
2526 private readonly int _minHashLength ;
2627
27- // Creates the Regex in the first usage, speed up first use of non hex methods
28- private static readonly Lazy < Regex > hexValidator = new Lazy < Regex > ( ( ) => new Regex ( "^[0-9a-fA-F]+$" , RegexOptions . Compiled ) ) ;
29- private static readonly Lazy < Regex > hexSplitter = new Lazy < Regex > ( ( ) => new Regex ( @"[\w\W]{1,12}" , RegexOptions . Compiled ) ) ;
28+ private readonly ObjectPool < StringBuilder > _sbPool = new DefaultObjectPool < StringBuilder > ( new StringBuilderPooledObjectPolicy ( ) ) ;
29+
30+ // Creates the Regex in the first usage, speed up first use of non-hex methods
31+ private static readonly Lazy < Regex > hexValidator = new ( ( ) => new Regex ( "^[0-9a-fA-F]+$" , RegexOptions . Compiled ) ) ;
32+ private static readonly Lazy < Regex > hexSplitter = new ( ( ) => new Regex ( @"[\w\W]{1,12}" , RegexOptions . Compiled ) ) ;
3033
3134 /// <summary>
3235 /// Instantiates a new Hashids with the default setup.
@@ -70,6 +73,57 @@ public Hashids(
7073 SetupGuards ( ) ;
7174 }
7275
76+ private void SetupSeps ( )
77+ {
78+ // seps should contain only characters present in alphabet;
79+ _seps = _seps . Intersect ( _alphabet ) . ToArray ( ) ;
80+
81+ // alphabet should not contain seps.
82+ _alphabet = _alphabet . Except ( _seps ) . ToArray ( ) ;
83+
84+ ConsistentShuffle ( _seps , _seps . Length , _salt , _salt . Length ) ;
85+
86+ if ( _seps . Length == 0 || ( ( float ) _alphabet . Length / _seps . Length ) > SEP_DIV )
87+ {
88+ var sepsLength = ( int ) Math . Ceiling ( ( float ) _alphabet . Length / SEP_DIV ) ;
89+
90+ if ( sepsLength == 1 )
91+ {
92+ sepsLength = 2 ;
93+ }
94+
95+ if ( sepsLength > _seps . Length )
96+ {
97+ var diff = sepsLength - _seps . Length ;
98+ _seps = _seps . Append ( _alphabet , 0 , diff ) ;
99+ _alphabet = _alphabet . SubArray ( diff ) ;
100+ }
101+ else
102+ {
103+ _seps = _seps . SubArray ( 0 , sepsLength ) ;
104+ }
105+ }
106+
107+ ConsistentShuffle ( _alphabet , _alphabet . Length , _salt , _salt . Length ) ;
108+ }
109+
110+ private void SetupGuards ( )
111+ {
112+ var guardCount = ( int ) Math . Ceiling ( _alphabet . Length / GUARD_DIV ) ;
113+
114+ if ( _alphabet . Length < 3 )
115+ {
116+ _guards = _seps . SubArray ( 0 , guardCount ) ;
117+ _seps = _seps . SubArray ( guardCount ) ;
118+ }
119+
120+ else
121+ {
122+ _guards = _alphabet . SubArray ( 0 , guardCount ) ;
123+ _alphabet = _alphabet . SubArray ( guardCount ) ;
124+ }
125+ }
126+
73127 /// <summary>
74128 /// Encodes the provided numbers into a hashed string.
75129 /// </summary>
@@ -105,46 +159,6 @@ public virtual int[] Decode(string hash)
105159 return Array . ConvertAll ( numbers , n => ( int ) n ) ;
106160 }
107161
108- /// <summary>
109- /// Encodes the provided hex string to a hashids hash.
110- /// </summary>
111- /// <param name="hex"></param>
112- /// <returns></returns>
113- public virtual string EncodeHex ( string hex )
114- {
115- if ( ! hexValidator . Value . IsMatch ( hex ) )
116- return string . Empty ;
117-
118- var matches = hexSplitter . Value . Matches ( hex ) ;
119- var numbers = new List < long > ( matches . Count ) ;
120-
121- foreach ( Match match in matches )
122- {
123- var number = Convert . ToInt64 ( string . Concat ( "1" , match . Value ) , 16 ) ;
124- numbers . Add ( number ) ;
125- }
126-
127- return EncodeLong ( numbers . ToArray ( ) ) ;
128- }
129-
130- /// <summary>
131- /// Decodes the provided hash into a hex-string.
132- /// </summary>
133- /// <param name="hash"></param>
134- /// <returns></returns>
135- public virtual string DecodeHex ( string hash )
136- {
137- var builder = new StringBuilder ( ) ;
138- var numbers = DecodeLong ( hash ) ;
139-
140- foreach ( var number in numbers )
141- {
142- builder . Append ( string . Format ( "{0:X}" , number ) . Substring ( 1 ) ) ;
143- }
144-
145- return builder . ToString ( ) ;
146- }
147-
148162 /// <summary>
149163 /// Decodes the provided hashed string into an array of longs.
150164 /// </summary>
@@ -178,55 +192,45 @@ public string EncodeLong(IEnumerable<long> numbers)
178192 return EncodeLong ( numbers . ToArray ( ) ) ;
179193 }
180194
181- private void SetupSeps ( )
195+ /// <summary>
196+ /// Encodes the provided hex string to a hashids hash.
197+ /// </summary>
198+ /// <param name="hex"></param>
199+ /// <returns></returns>
200+ public virtual string EncodeHex ( string hex )
182201 {
183- // seps should contain only characters present in alphabet;
184- _seps = _seps . Intersect ( _alphabet ) . ToArray ( ) ;
185-
186- // alphabet should not contain seps.
187- _alphabet = _alphabet . Except ( _seps ) . ToArray ( ) ;
202+ if ( ! hexValidator . Value . IsMatch ( hex ) )
203+ return string . Empty ;
188204
189- ConsistentShuffle ( _seps , _seps . Length , _salt , _salt . Length ) ;
205+ var matches = hexSplitter . Value . Matches ( hex ) ;
206+ var numbers = new List < long > ( matches . Count ) ;
190207
191- if ( _seps . Length == 0 || ( ( float ) _alphabet . Length / _seps . Length ) > SEP_DIV )
208+ foreach ( Match match in matches )
192209 {
193- var sepsLength = ( int ) Math . Ceiling ( ( float ) _alphabet . Length / SEP_DIV ) ;
194-
195- if ( sepsLength == 1 )
196- {
197- sepsLength = 2 ;
198- }
199-
200- if ( sepsLength > _seps . Length )
201- {
202- var diff = sepsLength - _seps . Length ;
203- _seps = _seps . Append ( _alphabet , 0 , diff ) ;
204- _alphabet = _alphabet . SubArray ( diff ) ;
205- }
206- else
207- {
208- _seps = _seps . SubArray ( 0 , sepsLength ) ;
209- }
210+ var number = Convert . ToInt64 ( string . Concat ( "1" , match . Value ) , 16 ) ;
211+ numbers . Add ( number ) ;
210212 }
211213
212- ConsistentShuffle ( _alphabet , _alphabet . Length , _salt , _salt . Length ) ;
214+ return EncodeLong ( numbers . ToArray ( ) ) ;
213215 }
214216
215- private void SetupGuards ( )
217+ /// <summary>
218+ /// Decodes the provided hash into a hex-string.
219+ /// </summary>
220+ /// <param name="hash"></param>
221+ /// <returns></returns>
222+ public virtual string DecodeHex ( string hash )
216223 {
217- var guardCount = ( int ) Math . Ceiling ( _alphabet . Length / GUARD_DIV ) ;
224+ var builder = _sbPool . Get ( ) ;
225+ var numbers = DecodeLong ( hash ) ;
218226
219- if ( _alphabet . Length < 3 )
220- {
221- _guards = _seps . SubArray ( 0 , guardCount ) ;
222- _seps = _seps . SubArray ( guardCount ) ;
223- }
227+ foreach ( var number in numbers )
228+ foreach ( var ch in number . ToString ( "X" ) . AsSpan ( ) . Slice ( 1 ) )
229+ builder . Append ( ch ) ;
224230
225- else
226- {
227- _guards = _alphabet . SubArray ( 0 , guardCount ) ;
228- _alphabet = _alphabet . SubArray ( guardCount ) ;
229- }
231+ var result = builder . ToString ( ) ;
232+ _sbPool . Return ( builder ) ;
233+ return result ;
230234 }
231235
232236 /// <summary>
@@ -245,7 +249,7 @@ private string GenerateHashFrom(long[] numbers)
245249 numbersHashInt += numbers [ i ] % ( i + 100 ) ;
246250 }
247251
248- var builder = new StringBuilder ( ) ;
252+ var builder = _sbPool . Get ( ) ;
249253
250254 char [ ] buffer = null ;
251255 var alphabet = _alphabet . CopyPooled ( ) ;
@@ -320,15 +324,9 @@ private string GenerateHashFrom(long[] numbers)
320324 buffer . ReturnToPool ( ) ;
321325 }
322326
323- return builder . ToString ( ) ;
324- }
325-
326- private char [ ] CreateBuffer ( int alphabetLength , char lottery )
327- {
328- var buffer = System . Buffers . ArrayPool < char > . Shared . Rent ( alphabetLength ) ;
329- buffer [ 0 ] = lottery ;
330- Array . Copy ( _salt , 0 , buffer , 1 , Math . Min ( _salt . Length , alphabetLength - 1 ) ) ;
331- return buffer ;
327+ var result = builder . ToString ( ) ;
328+ _sbPool . Return ( builder ) ;
329+ return result ;
332330 }
333331
334332 private char [ ] Hash ( long input , char [ ] alphabet , int alphabetLength )
@@ -352,25 +350,12 @@ private long Unhash(string input, char[] alphabet, int alphabetLength)
352350 for ( var i = 0 ; i < input . Length ; i ++ )
353351 {
354352 var pos = Array . IndexOf ( alphabet , input [ i ] ) ;
355- number += pos * LongPow ( alphabetLength , input . Length - i - 1 ) ;
353+ number = number * alphabetLength + pos ;
356354 }
357355
358356 return number ;
359357 }
360358
361- private static long LongPow ( int target , int power )
362- {
363- if ( power == 0 ) return 1 ;
364- long result = target ;
365- while ( power > 1 )
366- {
367- result *= target ;
368- power -- ;
369- }
370-
371- return result ;
372- }
373-
374359 private long [ ] GetNumbersFrom ( string hash )
375360 {
376361 if ( string . IsNullOrWhiteSpace ( hash ) )
@@ -432,6 +417,14 @@ private long[] GetNumbersFrom(string hash)
432417 return result . ToArray ( ) ;
433418 }
434419
420+ private char [ ] CreateBuffer ( int alphabetLength , char lottery )
421+ {
422+ var buffer = System . Buffers . ArrayPool < char > . Shared . Rent ( alphabetLength ) ;
423+ buffer [ 0 ] = lottery ;
424+ Array . Copy ( _salt , 0 , buffer , 1 , Math . Min ( _salt . Length , alphabetLength - 1 ) ) ;
425+ return buffer ;
426+ }
427+
435428 private void ConsistentShuffle ( char [ ] alphabet , int alphabetLength , char [ ] salt , int saltLength )
436429 {
437430 if ( salt . Length == 0 )
0 commit comments