Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}

Expand All @@ -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));
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
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)}' ");
}
116 changes: 110 additions & 6 deletions csharp-basic-topics/Base64EncodingAndDecoding/Tests/Tests/Tests.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
using Base64EncodingAndDecoding;
using System;
using System.Buffers.Text;
using System.IO;
using System.Text;
using Xunit;

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]
Expand All @@ -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]
Expand All @@ -25,15 +28,15 @@ 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]
public void GivenATextLessThan76Length_WhenEncodingWithLineBreaks_ThenReturnStringOutput()
{
var base64String = _base64Operations.Base64Encoding("Test", true);

Assert.Equal(base64String, _base64Operations.Base64Encoding("Test"));
Assert.Equal(_base64Operations.Base64Encoding("Test"), base64String);
Assert.DoesNotContain(Environment.NewLine, base64String);
}

Expand All @@ -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);
}
}

[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));
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.9.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="4.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.0">
<PackageReference Include="coverlet.collector" Version="10.0.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down
Loading