Skip to content

Commit

Permalink
C# doesn't support Tail Recursion Optimization. So time to bring out F#.
Browse files Browse the repository at this point in the history
  • Loading branch information
HarryCordewener committed Jan 4, 2024
1 parent 6c177ec commit 3bfae1c
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 67 deletions.
49 changes: 49 additions & 0 deletions TelnetNegotiationCore.Functional/MSDPLibrary.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
namespace TelnetNegotiationCore.Functional

open System.Text

type Trigger =
| NULL = 0uy
| MSDP_VAR = 1uy
| MSDP_VAL = 2uy
| MSDP_TABLE_OPEN = 3uy
| MSDP_TABLE_CLOSE = 4uy
| MSDP_ARRAY_OPEN = 5uy
| MSDP_ARRAY_CLOSE = 6uy

module MSDPLibrary =
let rec MSDPScan (root: obj, array: seq<byte>, type_: Trigger, encoding : Encoding) =
if Seq.length(array) = 0 then root
else
match type_ with
| Trigger.MSDP_VAR ->
(root :?> Map<string, obj>).Add(encoding.GetString(array |> Seq.takeWhile(fun x -> x <> byte Trigger.MSDP_VAL) |> Array.ofSeq), MSDPScan(root, array |> Seq.skipWhile(fun x -> x <> byte Trigger.MSDP_VAL) |> Seq.skip(1), Trigger.MSDP_VAL,encoding))
| Trigger.MSDP_VAL ->
let nextType =
try
array |> Seq.find(fun x ->
x = byte Trigger.MSDP_ARRAY_OPEN ||
x = byte Trigger.MSDP_TABLE_OPEN ||
x = byte Trigger.MSDP_ARRAY_CLOSE ||
x = byte Trigger.MSDP_TABLE_CLOSE ||
x = byte Trigger.MSDP_VAL)
with
| :? System.Collections.Generic.KeyNotFoundException -> 0uy

match LanguagePrimitives.EnumOfValue nextType : Trigger with
| Trigger.NULL ->
encoding.GetString(array |> Array.ofSeq)
| Trigger.MSDP_VAL ->
MSDPScan((root :?> List<obj>) @ [encoding.GetString(array |> Seq.takeWhile(fun x -> x <> nextType) |> Array.ofSeq)], array |> Seq.skipWhile(fun x -> x <> byte Trigger.MSDP_VAL) |> Seq.skip(1), Trigger.MSDP_VAL, encoding)
| Trigger.MSDP_TABLE_CLOSE ->
(root :?> List<obj>) @ [encoding.GetString(array |> Seq.takeWhile(fun x -> x <> nextType) |> Array.ofSeq)]
| Trigger.MSDP_ARRAY_OPEN ->
MSDPScan(root, array |> Seq.skipWhile(fun x -> x <> byte Trigger.MSDP_ARRAY_OPEN) |> Seq.skip(1), Trigger.MSDP_ARRAY_OPEN,encoding)
| Trigger.MSDP_TABLE_OPEN ->
MSDPScan(root, array |> Seq.skipWhile(fun x -> x <> byte Trigger.MSDP_TABLE_OPEN) |> Seq.skip(1), Trigger.MSDP_TABLE_OPEN,encoding)
| _ -> root
| Trigger.MSDP_ARRAY_OPEN ->
MSDPScan(List.Empty, array |> Seq.skip(1), Trigger.MSDP_VAL, encoding)
| Trigger.MSDP_TABLE_OPEN ->
MSDPScan(Map<string, obj> [], array |> Seq.skip(1), Trigger.MSDP_VAR, encoding)
| _ -> failwith "Failure in MSDPScan."
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

<ItemGroup>
<Compile Include="MSDPLibrary.fs" />
</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions TelnetNegotiationCore.UnitTests/CreateDotGraph.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Stateless.Graph;
using System.Collections.Generic;
using System.IO;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using TelnetNegotiationCore.Interpreters;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
<TargetFramework>net8.0</TargetFramework>

<IsPackable>false</IsPackable>

<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
Expand Down
6 changes: 6 additions & 0 deletions TelnetNegotiationCore.sln
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TelnetNegotiationCore.UnitT
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TelnetNegotiationCore.TestClient", "TelnetNegotiationCore.TestClient\TelnetNegotiationCore.TestClient.csproj", "{E1A76C78-8246-472F-824E-9F7F9B3778B7}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "TelnetNegotiationCore.Functional", "TelnetNegotiationCore.Functional\TelnetNegotiationCore.Functional.fsproj", "{0307D0B8-AAA7-40C8-9E3E-34BE64F93BB7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -41,6 +43,10 @@ Global
{E1A76C78-8246-472F-824E-9F7F9B3778B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E1A76C78-8246-472F-824E-9F7F9B3778B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E1A76C78-8246-472F-824E-9F7F9B3778B7}.Release|Any CPU.Build.0 = Release|Any CPU
{0307D0B8-AAA7-40C8-9E3E-34BE64F93BB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0307D0B8-AAA7-40C8-9E3E-34BE64F93BB7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0307D0B8-AAA7-40C8-9E3E-34BE64F93BB7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0307D0B8-AAA7-40C8-9E3E-34BE64F93BB7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
72 changes: 5 additions & 67 deletions TelnetNegotiationCore/Interpreters/TelnetMSDPInterpreter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.FSharp.Collections;
using MoreLinq;
using OneOf;
using Stateless;
Expand Down Expand Up @@ -105,76 +106,13 @@ private void CaptureMSDPValue(OneOf<byte, Trigger> b)

private async Task ReadMSDPValues()
{
dynamic root = new ExpandoObject();
var array = _currentMSDPInfo.Skip(1).ToImmutableArray();

MSDPScan(root, array, Trigger.MSDP_VAR);
Functional.MSDPLibrary.MSDPScan(new FSharpMap<string, dynamic>(Enumerable.Empty<Tuple<string,dynamic>>()),
_currentMSDPInfo.Skip(1),
Functional.Trigger.MSDP_VAL,
CurrentEncoding);
await Task.CompletedTask;
}

private dynamic MSDPScan(dynamic root, ImmutableArray<byte> array, Trigger type)
{
if (array.Length == 0) return root;

if (type == Trigger.MSDP_VAR)
{
var variableName = array.TakeUntil(x => x is (byte)Trigger.MSDP_VAL).ToArray();
((IDictionary<string, object>)root).Add(CurrentEncoding.GetString(variableName),
MSDPScan(root, array.SkipUntil(x => x is (byte)Trigger.MSDP_VAL).ToImmutableArray(), Trigger.MSDP_VAL));
}
else if (type == Trigger.MSDP_VAL)
{
var nextType = array.FirstOrDefault(x => x is
(byte)Trigger.MSDP_ARRAY_OPEN or
(byte)Trigger.MSDP_TABLE_OPEN or
(byte)Trigger.MSDP_ARRAY_CLOSE or
(byte)Trigger.MSDP_TABLE_CLOSE or
(byte)Trigger.MSDP_VAL);
dynamic result = root;

if (nextType == default) // We have hit the end.
{
result = CurrentEncoding.GetString(array.ToArray());
}
else if (nextType is (byte)Trigger.MSDP_VAL)
{
var value = array.TakeWhile(x => x != nextType);
var startOfRest = array.SkipUntil(x => x is (byte)Trigger.MSDP_VAL).ToImmutableArray();
result = MSDPScan(((ImmutableList<dynamic>)root).Add(CurrentEncoding.GetString(value.ToArray())), startOfRest, Trigger.MSDP_VAL);
}
else if (nextType is (byte)Trigger.MSDP_ARRAY_OPEN)
{
var startOfArray = array.SkipUntil(x => x is (byte)Trigger.MSDP_ARRAY_OPEN).ToImmutableArray();
result = MSDPScan(root, startOfArray, Trigger.MSDP_ARRAY_OPEN);
}
else if (nextType is (byte)Trigger.MSDP_TABLE_OPEN)
{
var startOfTable = array.SkipUntil(x => x is (byte)Trigger.MSDP_TABLE_OPEN).ToImmutableArray();
result = MSDPScan(root, startOfTable, Trigger.MSDP_ARRAY_OPEN);
}
else if (nextType is (byte)Trigger.MSDP_ARRAY_CLOSE)
{
result = root;
}
else if (nextType is (byte)Trigger.MSDP_TABLE_CLOSE)
{
result = root;
}

return result;
}
else if (type == Trigger.MSDP_ARRAY_OPEN)
{
return MSDPScan(ImmutableList<dynamic>.Empty, array.Skip(1).ToImmutableArray(), Trigger.MSDP_VAL);
}
else if (type == Trigger.MSDP_TABLE_OPEN)
{
return MSDPScan(new ExpandoObject(), array.Skip(1).ToImmutableArray(), Trigger.MSDP_VAR);
}

return root;
}

/// <summary>
/// Announce we do MSDP negotiation to the client.
/// </summary>
Expand Down
4 changes: 4 additions & 0 deletions TelnetNegotiationCore/TelnetNegotiationCore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,8 @@
<PackageReference Include="System.Collections.Immutable" Version="8.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\TelnetNegotiationCore.Functional\TelnetNegotiationCore.Functional.fsproj" />
</ItemGroup>

</Project>

0 comments on commit 3bfae1c

Please sign in to comment.