Skip to content

Commit 6d25983

Browse files
authored
Merge pull request #36 from I-RzR-I/feature/AddBase32Byte
Feature/add base32 byte
2 parents f6915e6 + 852dc9f commit 6d25983

File tree

9 files changed

+274
-8
lines changed

9 files changed

+274
-8
lines changed

docs/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
### **v3.1.0.3090** [[RzR](mailto:108324929+I-RzR-I@users.noreply.github.com)] 13-08-2025
2+
* [bc2ef6d] (RzR) -> Auto commit uncommited files
3+
* [90ce1ef] (RzR) -> Add and adjust test methods.
4+
* [e8fafdc] (RzR) -> Add DateTime method: `Epoch`.
5+
* [8bb8672] (RzR) -> Add byte method: `Base32BytesToString`.
6+
* [896be11] (RzR) -> Add string methods `IsBase32String`, `Base32ToBytes` and adjust `TrimAndReduceSpace`, `TrimAndReplaceSpecialCharacters`.
7+
18
### **v3.0.0.3007** [[RzR](mailto:108324929+I-RzR-I@users.noreply.github.com)] 07-08-2025
29
* [9e7a5e9] (RzR) -> Auto commit uncommited files.
310
* [ddc61a0] (RzR) -> Add new reflection extension methods (available from net45 and up); `GetTypes`, `GetSetMethod`, `GetGetMethod`, `GetGenericArguments`, `GetMethod`, `GetMembers`, `GetInterfaces`, `IsGenericType`, `IsValueType`, `IsAbstract`, `IsAssignableFrom`, `ContainsGenericParameters`, `BaseType`, `IsGenericTypeDefinition`, `IsPrimitive`, `IsNestedPublic`, `IsPublic`, `IsSealed`, `GetGenericParameterConstraints`, `IsClass`, `IsInterface`, `IsGenericParameter`, `GetGenericParameterAttributes`, `GetAssembly`, `GetConstructors`, `GetConstructor`, `IsInNamespace`.

src/DomainCommonExtensions/DataTypeExtensions/ByteExtensions.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
using System.Linq;
2525
using System.Security.Cryptography;
2626
using System.Text;
27+
using DomainCommonExtensions.ArraysExtensions;
28+
using DomainCommonExtensions.Helpers.Internal;
2729
using DomainCommonExtensions.Utilities.Ensure;
2830

2931
#endregion
@@ -188,5 +190,49 @@ public static byte[] GZipCompress(this byte[] source, CompressionLevel compressi
188190
return memory.ToArray();
189191
}
190192
#endif
193+
194+
/// <summary>
195+
/// Convert byte[] to BASE32 string
196+
/// </summary>
197+
/// <param name="sourceBytes">The sourceBytes to act on.</param>
198+
/// <returns>
199+
/// A BASE32 string.
200+
/// </returns>
201+
public static string Base32BytesToString(this byte[] sourceBytes)
202+
{
203+
if(sourceBytes.IsNullOrEmptyEnumerable())
204+
DomainEnsure.IsNotNull(sourceBytes, nameof(sourceBytes));
205+
206+
var charCount = (int)Math.Ceiling(sourceBytes.Length / 5D) * 8;
207+
var returnArray = new char[charCount];
208+
209+
byte nextChar = 0, bitsRemaining = 5;
210+
var arrayIndex = 0;
211+
212+
foreach (var byteItem in sourceBytes)
213+
{
214+
nextChar = (byte)(nextChar | (byteItem >> (8 - bitsRemaining)));
215+
returnArray[arrayIndex++] = Base32EncodingHelper.ByteToChar(nextChar);
216+
217+
if (bitsRemaining < 4)
218+
{
219+
nextChar = (byte)((byteItem >> (3 - bitsRemaining)) & 31);
220+
returnArray[arrayIndex++] = Base32EncodingHelper.ByteToChar(nextChar);
221+
bitsRemaining += 5;
222+
}
223+
224+
bitsRemaining -= 3;
225+
nextChar = (byte)((byteItem << bitsRemaining) & 31);
226+
}
227+
228+
if (arrayIndex.Equals(charCount).IsFalse())
229+
{
230+
returnArray[arrayIndex++] = Base32EncodingHelper.ByteToChar(nextChar);
231+
while (arrayIndex != charCount)
232+
returnArray[arrayIndex++] = '=';
233+
}
234+
235+
return new string(returnArray);
236+
}
191237
}
192238
}

src/DomainCommonExtensions/DataTypeExtensions/DateTimeExtensions.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,5 +524,14 @@ public static double ToExcelTime(this DateTime sourceTime)
524524

525525
return t.TotalDays;
526526
}
527+
528+
/// <summary>
529+
/// Return always epoch date and time. Source DateTime will be ignored.
530+
/// </summary>
531+
/// <param name="sourceTime">Source date time</param>
532+
/// <returns>Return always epoch date.</returns>
533+
/// <remarks></remarks>
534+
public static DateTime Epoch(this DateTime sourceTime)
535+
=> new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
527536
}
528537
}

src/DomainCommonExtensions/DataTypeExtensions/StringExtensions.cs

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
using CodeSource;
3737
using DomainCommonExtensions.ArraysExtensions;
3838
using DomainCommonExtensions.CommonExtensions;
39+
using DomainCommonExtensions.Helpers.Internal;
3940
using DomainCommonExtensions.Resources;
4041
using DomainCommonExtensions.Resources.Enums;
4142
using DomainCommonExtensions.Utilities.Ensure;
@@ -117,7 +118,7 @@ public static bool IsValidEmail(this string email)
117118
/// <returns></returns>
118119
public static SecureString ToSecureString(this string str)
119120
{
120-
DomainEnsure.ThrowExceptionIfFuncIsTrue(ExceptionType.ArgumentNullException,
121+
DomainEnsure.ThrowExceptionIfFuncIsTrue(ExceptionType.ArgumentNullException,
121122
str.IsMissing, str, nameof(str));
122123

123124
var secureString = new SecureString();
@@ -340,13 +341,17 @@ public static string TrimIfNotNull(this string value)
340341
/// Trim and reduce space from string
341342
/// </summary>
342343
/// <param name="value">Input value</param>
344+
/// <param name="reduceAllSpaces">
345+
/// Reduce/remove all spaces from source string data.
346+
/// Default value is 'false'.
347+
/// </param>
343348
/// <returns></returns>
344349
/// <remarks></remarks>
345-
public static string TrimAndReduceSpace(this string value)
350+
public static string TrimAndReduceSpace(this string value, bool reduceAllSpaces = false)
346351
{
347352
if (value.IfNullThenEmpty().IsMissing()) return null;
348353

349-
return Regex.Replace(value, @"\s+", " ").TrimIfNotNull();
354+
return Regex.Replace(value, @"\s+", reduceAllSpaces.IsTrue() ? "" : " ").TrimIfNotNull();
350355
}
351356

352357
/// <summary>
@@ -504,11 +509,15 @@ public static string RemoveSpecialChars(this string str)
504509
/// Trim and replace special characters in string
505510
/// </summary>
506511
/// <param name="value">Input string</param>
512+
/// <param name="reduceAllSpaces">
513+
/// Reduce/remove all spaces from source string data.
514+
/// Default value is 'false'.
515+
/// </param>
507516
/// <returns></returns>
508517
/// <remarks></remarks>
509-
public static string TrimAndReplaceSpecialCharacters(this string value)
518+
public static string TrimAndReplaceSpecialCharacters(this string value, bool reduceAllSpaces = false)
510519
{
511-
return value.TrimAndReduceSpace().RemoveSpecialChars();
520+
return value.TrimAndReduceSpace(reduceAllSpaces).RemoveSpecialChars();
512521
}
513522

514523
/// <summary>
@@ -1637,5 +1646,67 @@ public static string IfNotStartsWith(this string source, string searchValue, str
16371646
{
16381647
return source.StartsWith(searchValue).IsFalse() ? resultValue : source;
16391648
}
1649+
1650+
1651+
/// <summary>
1652+
/// Check if string is in BASE32 format
1653+
/// </summary>
1654+
/// <param name="base32String">Encoded BASE32 string</param>
1655+
/// <returns></returns>
1656+
/// <remarks></remarks>
1657+
public static bool IsBase32String(this string base32String)
1658+
{
1659+
if (base32String.IsNullOrEmpty()) return false;
1660+
1661+
base32String = base32String.TrimIfNotNull();
1662+
1663+
return (base32String.Length % 8).IsZero() &&
1664+
Regex.IsMatch(base32String, RegularExpressions.BASE32, RegexOptions.None);
1665+
}
1666+
1667+
/// <summary>
1668+
/// Convert BASE32 string to byte[]
1669+
/// </summary>
1670+
/// <param name="base32String">Encoded BASE32 string.</param>
1671+
/// <returns>
1672+
/// A byte[].
1673+
/// </returns>
1674+
public static byte[] Base32ToBytes(this string base32String)
1675+
{
1676+
DomainEnsure.IsNotNullOrEmptyArgNull(base32String, nameof(base32String));
1677+
1678+
base32String = base32String.TrimEnd('=');
1679+
var byteCount = base32String.Length * 5 / 8;
1680+
var returnArray = new byte[byteCount];
1681+
1682+
byte curByte = 0, bitsRemaining = 8;
1683+
var arrayIndex = 0;
1684+
1685+
foreach (var character in base32String)
1686+
{
1687+
var charValue = Base32EncodingHelper.CharToInt32(character);
1688+
1689+
int mask;
1690+
if (bitsRemaining > 5)
1691+
{
1692+
mask = charValue << (bitsRemaining - 5);
1693+
curByte = (byte)(curByte | mask);
1694+
bitsRemaining -= 5;
1695+
}
1696+
else
1697+
{
1698+
mask = charValue >> (5 - bitsRemaining);
1699+
curByte = (byte)(curByte | mask);
1700+
returnArray[arrayIndex++] = curByte;
1701+
curByte = (byte)(charValue << (3 + bitsRemaining));
1702+
bitsRemaining += 3;
1703+
}
1704+
}
1705+
1706+
if (arrayIndex.Equals(byteCount).IsFalse())
1707+
returnArray[arrayIndex] = curByte;
1708+
1709+
return returnArray;
1710+
}
16401711
}
16411712
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// ***********************************************************************
2+
// Assembly : RzR.Shared.Extensions.DomainCommonExtensions
3+
// Author : RzR
4+
// Created On : 2025-08-13 00:12
5+
//
6+
// Last Modified By : RzR
7+
// Last Modified On : 2025-08-13 00:16
8+
// ***********************************************************************
9+
// <copyright file="Base32EncodingHelper.cs" company="RzR SOFT & TECH">
10+
// Copyright © RzR. All rights reserved.
11+
// </copyright>
12+
//
13+
// <summary>
14+
// </summary>
15+
// ***********************************************************************
16+
17+
#region U S A G E S
18+
19+
using System;
20+
using DomainCommonExtensions.DataTypeExtensions;
21+
22+
#endregion
23+
24+
namespace DomainCommonExtensions.Helpers.Internal
25+
{
26+
/// -------------------------------------------------------------------------------------------------
27+
/// <summary>
28+
/// A base 32 encoding helper.
29+
/// </summary>
30+
/// =================================================================================================
31+
internal static class Base32EncodingHelper
32+
{
33+
/// -------------------------------------------------------------------------------------------------
34+
/// <summary>
35+
/// A char extension method that character to int 32.
36+
/// </summary>
37+
/// <exception cref="ArgumentException">
38+
/// Thrown when one or more arguments have unsupported or illegal values.
39+
/// </exception>
40+
/// <param name="sourceChar">The sourceChar to act on.</param>
41+
/// <returns>
42+
/// An int.
43+
/// </returns>
44+
/// =================================================================================================
45+
internal static int CharToInt32(char sourceChar)
46+
{
47+
var value = (int)sourceChar;
48+
49+
return value switch
50+
{
51+
//65-90 == uppercase letters
52+
< 91 and > 64 => value - 65,
53+
//50-55 == numbers 2-7
54+
< 56 and > 49 => value - 24,
55+
//97-122 == lowercase letters
56+
< 123 and > 96 => value - 97,
57+
_ => throw new ArgumentException("Supplied character is not a valid value of BASE32.", nameof(sourceChar))
58+
};
59+
}
60+
61+
/// -------------------------------------------------------------------------------------------------
62+
/// <summary>
63+
/// A byte extension method that byte to character.
64+
/// </summary>
65+
/// <exception cref="ArgumentException">
66+
/// Thrown when one or more arguments have unsupported or illegal values.
67+
/// </exception>
68+
/// <param name="sourceByte">The sourceByte to act on.</param>
69+
/// <returns>
70+
/// A char.
71+
/// </returns>
72+
/// =================================================================================================
73+
internal static char ByteToChar(byte sourceByte)
74+
{
75+
return sourceByte switch
76+
{
77+
< 26 => (char)(sourceByte + 65),
78+
< 32 => (char)(sourceByte + 24),
79+
_ => throw new ArgumentException("Supplied byte is not a valid value of BASE32.", nameof(sourceByte))
80+
};
81+
}
82+
}
83+
}

src/DomainCommonExtensions/Resources/RegularExpressions.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,10 @@ public struct RegularExpressions
6262
/// BASE 64 regex
6363
/// </summary>
6464
public const string BASE64 = @"^[a-zA-Z0-9\+/]*={0,3}$";
65+
66+
/// <summary>
67+
/// BASE 32 regex
68+
/// </summary>
69+
public const string BASE32 = @"^[A-Z2-7]+(?:={1,6})?$";
6570
}
6671
}

src/shared/GeneralAssemblyInfo.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,6 @@
4747
[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.MainAssembly)]
4848
#endif
4949

50-
[assembly: AssemblyVersion("3.0.0.3007")]
51-
[assembly: AssemblyFileVersion("3.0.0.3007")]
52-
[assembly: AssemblyInformationalVersion("3.0.0.3007")]
50+
[assembly: AssemblyVersion("3.1.0.3090")]
51+
[assembly: AssemblyFileVersion("3.1.0.3090")]
52+
[assembly: AssemblyInformationalVersion("3.1.0.3090")]

src/tests/DataTypeTests/DataTests/DateTimeTests.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,5 +314,14 @@ public void AsNotNull_CustomDate_Test()
314314
Assert.IsNotNull(newDt);
315315
Assert.AreEqual(newDt, customDt);
316316
}
317+
318+
[TestMethod]
319+
public void Epoch_Test()
320+
{
321+
var epoch = DateTime.Now.Epoch();
322+
323+
Assert.IsNotNull(epoch);
324+
Assert.AreEqual(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc), epoch);
325+
}
317326
}
318327
}

src/tests/DataTypeTests/DataTests/StringTests.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ public void TrimIfNotNullEmptyValueTest()
240240
var inpValue = " test ".TrimIfNotNull();
241241

242242
Assert.IsTrue(inpValue.Length == 4);
243+
Assert.AreEqual("test", inpValue);
243244
}
244245

245246
[TestMethod]
@@ -248,6 +249,16 @@ public void TrimAndReduceSpaceTest()
248249
var inpValue = " Input test data ".TrimAndReduceSpace();
249250

250251
Assert.IsTrue(inpValue.Length == 15);
252+
Assert.AreEqual("Input test data", inpValue);
253+
}
254+
255+
[TestMethod]
256+
public void TrimAndReduceSpace_ReduceAllSpaces_Test()
257+
{
258+
var inpValue = " Input test data ".TrimAndReduceSpace(true);
259+
260+
Assert.IsTrue(inpValue.Length == 13);
261+
Assert.AreEqual("Inputtestdata", inpValue);
251262
}
252263

253264
[TestMethod]
@@ -416,6 +427,19 @@ public void IsBase64StringTest()
416427
Assert.IsFalse(clearTest);
417428
}
418429

430+
[TestMethod]
431+
public void IsBase32StringTest()
432+
{
433+
var clear = "Clear text test 001?!";
434+
var b32 = "INWGKYLSEB2GK6DUEB2GK43UEAYDAMJ7EE======";
435+
436+
var clearTest = clear.IsBase32String();
437+
var b32Test = b32.IsBase32String();
438+
439+
Assert.IsTrue(b32Test);
440+
Assert.IsFalse(clearTest);
441+
}
442+
419443
[TestMethod]
420444
public void ReplaceSpecialCharactersTest()
421445
{
@@ -579,5 +603,17 @@ public void IfNotContains_Test(string source, string checkValue, string excepted
579603
Assert.IsNotNull(res);
580604
Assert.AreEqual(exceptedResult, res);
581605
}
606+
607+
[DataRow("INWGKYLSEB2GK6DUEB2GK43UEAYDAMJ7EE======")]
608+
[TestMethod]
609+
public void Base32StringToByteAndBack_Test(string base32String)
610+
{
611+
var byteArray = base32String.Base32ToBytes();
612+
Assert.IsNotNull(byteArray);
613+
614+
var decoded = byteArray.Base32BytesToString();
615+
Assert.IsNotNull(decoded);
616+
Assert.AreEqual(base32String, decoded);
617+
}
582618
}
583619
}

0 commit comments

Comments
 (0)