diff --git a/csharp-basic-topics/Base64EncodingAndDecoding/Base64EncodingAndDecoding/Base64EncodingAndDecoding.csproj b/csharp-basic-topics/Base64EncodingAndDecoding/Base64EncodingAndDecoding/Base64EncodingAndDecoding.csproj
index 74abf5c976..dfb40caafc 100644
--- a/csharp-basic-topics/Base64EncodingAndDecoding/Base64EncodingAndDecoding/Base64EncodingAndDecoding.csproj
+++ b/csharp-basic-topics/Base64EncodingAndDecoding/Base64EncodingAndDecoding/Base64EncodingAndDecoding.csproj
@@ -2,7 +2,7 @@
Exe
- net6.0
+ net10.0
enable
enable
diff --git a/csharp-basic-topics/Base64EncodingAndDecoding/Base64EncodingAndDecoding/Base64Operations.cs b/csharp-basic-topics/Base64EncodingAndDecoding/Base64EncodingAndDecoding/Base64Operations.cs
index d08151204e..18d29d14f4 100644
--- a/csharp-basic-topics/Base64EncodingAndDecoding/Base64EncodingAndDecoding/Base64Operations.cs
+++ b/csharp-basic-topics/Base64EncodingAndDecoding/Base64EncodingAndDecoding/Base64Operations.cs
@@ -1,13 +1,15 @@
-using System.Text;
+using System.Buffers.Text;
+using System.Security.Cryptography;
+using System.Text;
namespace Base64EncodingAndDecoding;
public class Base64Operations
{
- public string Base64Encoding(string text, bool AddLineBreaks = false)
+ public string Base64Encoding(string text, bool addLineBreaks = false)
{
var textBytes = Encoding.UTF8.GetBytes(text);
- return !AddLineBreaks ? Convert.ToBase64String(textBytes) :
+ return !addLineBreaks ? Convert.ToBase64String(textBytes) :
Convert.ToBase64String(textBytes, Base64FormattingOptions.InsertLineBreaks);
}
@@ -22,5 +24,56 @@ public string Base64Decoding(string base64EncodedText)
var base64EncodedBytes = Convert.FromBase64String(base64EncodedText);
return Encoding.UTF8.GetString(base64EncodedBytes);
}
-}
+ public string EncodeFile(string path)
+ {
+ return Convert.ToBase64String(File.ReadAllBytes(path));
+ }
+
+ public void DecodeToFile(string base64EncodedText, string path)
+ {
+ File.WriteAllBytes(path, Convert.FromBase64String(base64EncodedText));
+ }
+
+ public void EncodeStream(Stream input, Stream output)
+ {
+ using var transform = new ToBase64Transform();
+ using var cryptoStream = new CryptoStream(output, transform, CryptoStreamMode.Write, leaveOpen: true);
+
+ input.CopyTo(cryptoStream);
+ }
+
+ public void DecodeStream(Stream input, Stream output)
+ {
+ using var transform = new FromBase64Transform();
+ using var cryptoStream = new CryptoStream(output, transform, CryptoStreamMode.Write, leaveOpen: true);
+
+ input.CopyTo(cryptoStream);
+ }
+
+ public bool TryDecode(string base64EncodedText, out byte[] bytes)
+ {
+ var buffer = new byte[base64EncodedText.Length];
+
+ if (Convert.TryFromBase64String(base64EncodedText, buffer, out var bytesWritten))
+ {
+ bytes = buffer[..bytesWritten];
+
+ return true;
+ }
+
+ bytes = [];
+
+ return false;
+ }
+
+ public string Base64UrlEncoding(string text)
+ {
+ return Base64Url.EncodeToString(Encoding.UTF8.GetBytes(text));
+ }
+
+ public string Base64UrlDecoding(string base64UrlEncodedText)
+ {
+ return Encoding.UTF8.GetString(Base64Url.DecodeFromChars(base64UrlEncodedText));
+ }
+}
diff --git a/csharp-basic-topics/Base64EncodingAndDecoding/Base64EncodingAndDecoding/Program.cs b/csharp-basic-topics/Base64EncodingAndDecoding/Base64EncodingAndDecoding/Program.cs
index 7bcbe97f87..097b4fe60e 100644
--- a/csharp-basic-topics/Base64EncodingAndDecoding/Base64EncodingAndDecoding/Program.cs
+++ b/csharp-basic-topics/Base64EncodingAndDecoding/Base64EncodingAndDecoding/Program.cs
@@ -1,26 +1,27 @@
-
+
using Base64EncodingAndDecoding;
Run();
void Run()
{
- var _base64Operations = new Base64Operations();
- var sampletext = "To what length can the human lifespan be extended?";
+ var base64Operations = new Base64Operations();
+ var sampletext = "To what length can the human lifespan be extended?";
Console.WriteLine($"{sampletext} \n");
- var base64String = _base64Operations.Base64Encoding(sampletext);
- Console.WriteLine(string.Format("Base64 string is '{0}' ", base64String));
- Console.WriteLine(string.Format("Decoded value is '{0}' \n", _base64Operations.Base64Decoding(base64String)));
+ var base64String = base64Operations.Base64Encoding(sampletext);
+ Console.WriteLine($"Base64 string is '{base64String}' ");
+ Console.WriteLine($"Decoded value is '{base64Operations.Base64Decoding(base64String)}' \n");
- base64String = _base64Operations.Base64Encoding(sampletext, 3, 4);
- Console.WriteLine(string.Format("Base64 string is '{0}' ", base64String));
- Console.WriteLine(string.Format("Decoded value is '{0}' \n", _base64Operations.Base64Decoding(base64String)));
+ base64String = base64Operations.Base64Encoding(sampletext, 3, 4);
+ Console.WriteLine($"Base64 string is '{base64String}' ");
+ Console.WriteLine($"Decoded value is '{base64Operations.Base64Decoding(base64String)}' \n");
Console.WriteLine();
sampletext = "The great crocodile of Queensland has been known to attain a length of 30 feet;";
- base64String = _base64Operations.Base64Encoding(sampletext, true);
- Console.WriteLine(string.Format("Base64 string is '{0}' ", base64String));
- Console.WriteLine(string.Format("Decoded value is '{0}' \n", _base64Operations.Base64Decoding(base64String)));
- Console.ReadLine();
-}
\ No newline at end of file
+ base64String = base64Operations.Base64Encoding(sampletext, true);
+ Console.WriteLine($"Base64 string is '{base64String}' ");
+ Console.WriteLine($"Decoded value is '{base64Operations.Base64Decoding(base64String)}' \n");
+
+ Console.WriteLine($"Base64Url string is '{base64Operations.Base64UrlEncoding(sampletext)}' ");
+}
diff --git a/csharp-basic-topics/Base64EncodingAndDecoding/Tests/Tests/Tests.cs b/csharp-basic-topics/Base64EncodingAndDecoding/Tests/Tests/Tests.cs
index 18a74a1ee6..0e9173e46c 100644
--- a/csharp-basic-topics/Base64EncodingAndDecoding/Tests/Tests/Tests.cs
+++ b/csharp-basic-topics/Base64EncodingAndDecoding/Tests/Tests/Tests.cs
@@ -1,5 +1,8 @@
using Base64EncodingAndDecoding;
using System;
+using System.Buffers.Text;
+using System.IO;
+using System.Text;
using Xunit;
namespace Tests;
@@ -7,7 +10,7 @@ namespace Tests;
public class Tests
{
private Base64Operations _base64Operations = new Base64Operations();
- private string _shortString = "To what length can the human lifespan be extended?";
+ private string _shortString = "To what length can the human lifespan be extended?";
private string _longString = "The great crocodile of Queensland has been known to attain a length of 30 ft.; there is a smaller one about 6 ft.";
[Fact]
@@ -16,7 +19,7 @@ public void GivenAText_WhenEncoding_ThenReturnStringOutput()
var base64String = _base64Operations.Base64Encoding(_shortString);
var decodedString = _base64Operations.Base64Decoding(base64String);
- Assert.Equal(decodedString, _shortString);
+ Assert.Equal(_shortString, decodedString);
}
[Fact]
@@ -25,7 +28,7 @@ public void GivenAText_WhenEncodingWithOffset_ThenReturnStringOutput()
var base64String = _base64Operations.Base64Encoding(_shortString, 2, 9);
var decodedString = _base64Operations.Base64Decoding(base64String);
- Assert.Equal(decodedString, _shortString.Substring(2, 9));
+ Assert.Equal(_shortString.Substring(2, 9), decodedString);
}
[Fact]
@@ -33,7 +36,7 @@ public void GivenATextLessThan76Length_WhenEncodingWithLineBreaks_ThenReturnStri
{
var base64String = _base64Operations.Base64Encoding("Test", true);
- Assert.Equal(base64String, _base64Operations.Base64Encoding("Test"));
+ Assert.Equal(_base64Operations.Base64Encoding("Test"), base64String);
Assert.DoesNotContain(Environment.NewLine, base64String);
}
@@ -42,7 +45,108 @@ public void GivenATextWithMoreThan76Length_WhenEncodingWithLineBreaks_ThenReturn
{
var base64String = _base64Operations.Base64Encoding(_longString, true);
- Assert.NotEqual(base64String, _base64Operations.Base64Encoding(_longString));
+ Assert.NotEqual(_base64Operations.Base64Encoding(_longString), base64String);
Assert.Contains(Environment.NewLine, base64String);
}
-}
\ No newline at end of file
+
+ [Fact]
+ public void GivenTextEncodedAsUtf8_WhenDecodedAsUtf16_ThenReturnsMangledTextWithoutThrowing()
+ {
+ var utf8Bytes = Encoding.UTF8.GetBytes("Hello world!");
+
+ var mangled = Encoding.Unicode.GetString(utf8Bytes);
+
+ Assert.NotEqual("Hello world!", mangled);
+ Assert.Equal(6, mangled.Length);
+ Assert.DoesNotContain('\uFFFD', mangled);
+ }
+
+ [Fact]
+ public void GivenAFile_WhenEncodingAndDecodingBack_ThenFileContentSurvives()
+ {
+ var sourcePath = Path.GetTempFileName();
+ var targetPath = Path.GetTempFileName();
+
+ try
+ {
+ var content = new byte[] { 0x25, 0x50, 0x44, 0x46, 0x2D, 0x31, 0x2E, 0x37, 0x0A, 0xFF, 0x00, 0x80 };
+ File.WriteAllBytes(sourcePath, content);
+
+ var base64String = _base64Operations.EncodeFile(sourcePath);
+ _base64Operations.DecodeToFile(base64String, targetPath);
+
+ Assert.Equal(Convert.ToBase64String(content), base64String);
+ Assert.Equal(content, File.ReadAllBytes(targetPath));
+ }
+ finally
+ {
+ File.Delete(sourcePath);
+ File.Delete(targetPath);
+ }
+ }
+
+ [Fact]
+ public void GivenAStream_WhenEncoding_ThenOutputMatchesConvertToBase64String()
+ {
+ var content = Encoding.UTF8.GetBytes(_longString);
+
+ using var input = new MemoryStream(content);
+ using var output = new MemoryStream();
+
+ _base64Operations.EncodeStream(input, output);
+
+ Assert.Equal(Convert.ToBase64String(content), Encoding.ASCII.GetString(output.ToArray()));
+ }
+
+ [Fact]
+ public void GivenABase64Stream_WhenDecoding_ThenOriginalBytesComeBack()
+ {
+ var content = Encoding.UTF8.GetBytes(_longString);
+ var base64Bytes = Encoding.ASCII.GetBytes(Convert.ToBase64String(content));
+
+ using var input = new MemoryStream(base64Bytes);
+ using var output = new MemoryStream();
+
+ _base64Operations.DecodeStream(input, output);
+
+ Assert.Equal(content, output.ToArray());
+ }
+
+ [Fact]
+ public void GivenValidBase64_WhenTryDecoding_ThenReturnsTrueAndTheBytes()
+ {
+ var base64String = Convert.ToBase64String(Encoding.UTF8.GetBytes(_shortString));
+
+ var succeeded = _base64Operations.TryDecode(base64String, out var bytes);
+
+ Assert.True(succeeded);
+ Assert.Equal(_shortString, Encoding.UTF8.GetString(bytes));
+ }
+
+ [Fact]
+ public void GivenInvalidBase64_WhenTryDecoding_ThenReturnsFalseInsteadOfThrowing()
+ {
+ var succeeded = _base64Operations.TryDecode("SGVsbG8gd29ybGQ!", out var bytes);
+
+ Assert.False(succeeded);
+ Assert.Empty(bytes);
+ }
+
+ [Fact]
+ public void GivenText_WhenBase64UrlEncoding_ThenRoundTripsAndOmitsPadding()
+ {
+ var base64UrlString = _base64Operations.Base64UrlEncoding(_shortString);
+
+ Assert.DoesNotContain("=", base64UrlString);
+ Assert.Equal(_shortString, _base64Operations.Base64UrlDecoding(base64UrlString));
+ }
+
+ [Fact]
+ public void GivenBytesThatUsePlusAndSlash_WhenBase64UrlEncoding_ThenTheAlphabetIsSwapped()
+ {
+ var bytes = new byte[] { 0xFB, 0xFF, 0xFE };
+
+ Assert.Equal("+//+", Convert.ToBase64String(bytes));
+ Assert.Equal("-__-", Base64Url.EncodeToString(bytes));
+ }
+}
diff --git a/csharp-basic-topics/Base64EncodingAndDecoding/Tests/Tests/Tests.csproj b/csharp-basic-topics/Base64EncodingAndDecoding/Tests/Tests/Tests.csproj
index 4bd37c5279..74dc81693d 100644
--- a/csharp-basic-topics/Base64EncodingAndDecoding/Tests/Tests/Tests.csproj
+++ b/csharp-basic-topics/Base64EncodingAndDecoding/Tests/Tests/Tests.csproj
@@ -1,20 +1,20 @@
- net6.0
+ net10.0
enable
false
-
-
-
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all