From 186b3b26e7a2237adac308c525ffe27c1f6aee10 Mon Sep 17 00:00:00 2001 From: Joel <32407840+vexx32@users.noreply.github.com> Date: Thu, 17 Jan 2019 11:45:58 -0500 Subject: [PATCH 01/28] :sparkles: Add better handling for taking mixed-type pipeline input --- .../FormatAndOutput/format-hex/Format-Hex.cs | 107 +++++++++++++++--- 1 file changed, 91 insertions(+), 16 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs index 6bb1f37a9cd..e935dd78325 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs @@ -23,6 +23,23 @@ public sealed class FormatHex : PSCmdlet { private const int BUFFERSIZE = 16; + /// + /// For cases where a homogenous collection of bytes or other items are directly piped in, we collect all the + /// bytes in a List<byte> and then output the formatted result all at once in EndProcessing(). + /// + private List _totalInputBytes; + + /// + /// If the input is determined to be heterogenous piped input or each input object turns out to be a complete + /// array of items, we output each item as we receive it to avoid squashing output together in strange ways. + /// + private bool _isHeterogenousPipedInput; + + /// + /// Keep track of prior input types to determine if we're given a heterogenous collection. + /// + private Type _lastInputType; + #region Parameters /// @@ -98,6 +115,26 @@ protected override void ProcessRecord() } } + /// + /// Implements the EndProcessing method for the FormatHex command. + /// + protected override void EndProcessing() + { + if (_totalInputBytes != null) + { + int offset = Math.Min(_totalInputBytes.Count, Offset < (long)int.MaxValue ? (int)Offset : int.MaxValue); + int count = Math.Min(_totalInputBytes.Count - offset, Count < (long)int.MaxValue ? (int)Count : int.MaxValue); + if (offset != 0 || count != _totalInputBytes.Count) + { + WriteHexadecimal(_totalInputBytes.GetRange(offset, count).ToArray(), null, 0); + } + else + { + WriteHexadecimal(_totalInputBytes.ToArray(), null, 0); + } + } + } + #endregion #region Paths @@ -207,10 +244,10 @@ private void ProcessFileContent(string path) } } } - catch (IOException ioException) + catch (IOException fileException) { // IOException takes care of FileNotFoundException, DirectoryNotFoundException, and PathTooLongException - WriteError(new ErrorRecord(ioException, "FormatHexIOError", ErrorCategory.WriteError, path)); + WriteError(new ErrorRecord(fileException, "FormatHexIOError", ErrorCategory.WriteError, path)); } catch (ArgumentException argException) { @@ -241,6 +278,7 @@ private void ProcessObjectContent(PSObject inputObject) if (obj is System.IO.FileSystemInfo fsi) { + _isHeterogenousPipedInput = true; string[] path = { fsi.FullName }; List pathsToProcess = ResolvePaths(path, true); ProcessPath(pathsToProcess); @@ -249,28 +287,52 @@ private void ProcessObjectContent(PSObject inputObject) byte[] inputBytes = ConvertToByteArray(obj); - if (inputBytes != null) + if (_isHeterogenousPipedInput) { - int offset = Math.Min(inputBytes.Length, Offset < (long)int.MaxValue ? (int)Offset : int.MaxValue); - int count = Math.Min(inputBytes.Length - offset, Count < (long)int.MaxValue ? (int)Count : int.MaxValue); - if (offset != 0 || count != inputBytes.Length) + // Output any previously stored bytes as individual units for consistency of output + if (_totalInputBytes != null) + { + foreach (byte b in inputBytes) + { + WriteHexadecimal(new[] { b }, null, 0); + } + + _totalInputBytes = null; + } + + if (inputBytes != null) { - WriteHexadecimal(inputBytes.AsSpan().Slice(offset, count), null, 0); + int offset = Math.Min(inputBytes.Length, Offset < (long)int.MaxValue ? (int)Offset : int.MaxValue); + int count = Math.Min(inputBytes.Length - offset, Count < (long)int.MaxValue ? (int)Count : int.MaxValue); + if (offset != 0 || count != inputBytes.Length) + { + WriteHexadecimal(inputBytes.AsSpan().Slice(offset, count), null, 0); + } + else + { + WriteHexadecimal(inputBytes, null, 0); + } } else { - WriteHexadecimal(inputBytes, null, 0); + string errorMessage = StringUtil.Format(UtilityCommonStrings.FormatHexTypeNotSupported, obj.GetType()); + ErrorRecord errorRecord = new ErrorRecord(new ArgumentException(errorMessage), + "FormatHexTypeNotSupported", + ErrorCategory.InvalidArgument, + obj.GetType()); + WriteError(errorRecord); } } else { - string errorMessage = StringUtil.Format(UtilityCommonStrings.FormatHexTypeNotSupported, obj.GetType()); - ErrorRecord errorRecord = new ErrorRecord( - new ArgumentException(errorMessage), - "FormatHexTypeNotSupported", - ErrorCategory.InvalidArgument, - obj.GetType()); - WriteError(errorRecord); + if (_totalInputBytes == null) + { + _totalInputBytes = new List(inputBytes); + } + else + { + _totalInputBytes.AddRange(inputBytes); + } } } @@ -284,10 +346,12 @@ private byte[] ConvertToByteArray(object inputObject) { if (inputObject is string str) { + _isHeterogenousPipedInput = true; + _lastInputType = typeof(string); return Encoding.GetBytes(str); } - var baseType = inputObject.GetType(); + Type baseType = inputObject.GetType(); byte[] result = null; int elements = 1; bool isArray = false; @@ -295,6 +359,7 @@ private byte[] ConvertToByteArray(object inputObject) bool isEnum = false; if (baseType.IsArray) { + _isHeterogenousPipedInput = true; baseType = baseType.GetElementType(); dynamic dynamicObject = inputObject; elements = (int)dynamicObject.Length; @@ -309,6 +374,16 @@ private byte[] ConvertToByteArray(object inputObject) if (baseType.IsPrimitive && elements > 0) { + if (!_isHeterogenousPipedInput) + { + if (_lastInputType != null && baseType != _lastInputType) + { + _isHeterogenousPipedInput = true; + } + + _lastInputType = baseType; + } + if (baseType == typeof(bool)) { isBool = true; From caa70c607ed8d42d5cc8993e18c7a01d82129288 Mon Sep 17 00:00:00 2001 From: Joel <32407840+vexx32@users.noreply.github.com> Date: Thu, 17 Jan 2019 11:55:58 -0500 Subject: [PATCH 02/28] :wrench: Use same error for both input modes --- .../FormatAndOutput/format-hex/Format-Hex.cs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs index e935dd78325..b1a58c78293 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs @@ -325,13 +325,25 @@ private void ProcessObjectContent(PSObject inputObject) } else { - if (_totalInputBytes == null) + if (inputBytes != null) { - _totalInputBytes = new List(inputBytes); + if (_totalInputBytes == null) + { + _totalInputBytes = new List(inputBytes); + } + else + { + _totalInputBytes.AddRange(inputBytes); + } } else { - _totalInputBytes.AddRange(inputBytes); + string errorMessage = StringUtil.Format(UtilityCommonStrings.FormatHexTypeNotSupported, obj.GetType()); + ErrorRecord errorRecord = new ErrorRecord(new ArgumentException(errorMessage), + "FormatHexTypeNotSupported", + ErrorCategory.InvalidArgument, + obj.GetType()); + WriteError(errorRecord); } } } From 6e74e267e39e010e85cf4f65ef090b12de59dc46 Mon Sep 17 00:00:00 2001 From: Joel <32407840+vexx32@users.noreply.github.com> Date: Thu, 17 Jan 2019 11:56:33 -0500 Subject: [PATCH 03/28] :white_check_mark: Add tests for new behaviours --- .../Format-Hex.Tests.ps1 | 407 +++++++++--------- 1 file changed, 196 insertions(+), 211 deletions(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-Hex.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-Hex.Tests.ps1 index baba808a436..549bcdc4aa7 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-Hex.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-Hex.Tests.ps1 @@ -6,10 +6,8 @@ <# Purpose: Verify Format-Hex displays the Hexadecimal value for the input data. - Action: Run Format-Hex. - Expected Result: Hexadecimal equivalent of the input data is displayed. #> @@ -34,8 +32,7 @@ Describe "FormatHex" -tags "CI" { $thumbprint = $null $certProviderAvailable = $false - if ($certificateProvider.Count -gt 0) - { + if ($certificateProvider.Count -gt 0) { $thumbprint = $certificateProvider[0].Thumbprint $certProviderAvailable = $true } @@ -44,100 +41,90 @@ Describe "FormatHex" -tags "CI" { } Context "InputObject Paramater" { + BeforeAll { + enum TestEnum { + TestOne = 1; TestTwo = 2; TestThree = 3; TestFour = 4 + } + Add-Type -TypeDefinition @' +public enum TestSByteEnum : sbyte { + One = -1, + Two = -2, + Three = -3, + Four = -4 +} +'@ + } + $testCases = @( @{ - Name = "Can process byte type 'fhx -InputObject [byte]5'" - InputObject = [byte]5 - Count = 1 + Name = "Can process byte type 'fhx -InputObject [byte]5'" + InputObject = [byte]5 + Count = 1 ExpectedResult = "00000000 05" } @{ - Name = "Can process byte[] type 'fhx -InputObject [byte[]](1,2,3,4,5)'" - InputObject = [byte[]](1,2,3,4,5) - Count = 1 + Name = "Can process byte[] type 'fhx -InputObject [byte[]](1,2,3,4,5)'" + InputObject = [byte[]](1, 2, 3, 4, 5) + Count = 1 ExpectedResult = "00000000 01 02 03 04 05 ....." } @{ - Name = "Can process int type 'fhx -InputObject 7'" - InputObject = 7 - Count = 1 + Name = "Can process int type 'fhx -InputObject 7'" + InputObject = 7 + Count = 1 ExpectedResult = "00000000 07 00 00 00 ...." } @{ - Name = "Can process int[] type 'fhx -InputObject [int[]](5,6,7,8)'" - InputObject = [int[]](5,6,7,8) - Count = 1 + Name = "Can process int[] type 'fhx -InputObject [int[]](5,6,7,8)'" + InputObject = [int[]](5, 6, 7, 8) + Count = 1 ExpectedResult = "00000000 05 00 00 00 06 00 00 00 07 00 00 00 08 00 00 00 ................" } @{ - Name = "Can process int32 type 'fhx -InputObject [int32]2032'" - InputObject = [int32]2032 - Count = 1 + Name = "Can process int32 type 'fhx -InputObject [int32]2032'" + InputObject = [int32]2032 + Count = 1 ExpectedResult = "00000000 F0 07 00 00 ð..." } @{ - Name = "Can process int32[] type 'fhx -InputObject [int32[]](2032, 2033, 2034)'" - InputObject = [int32[]](2032, 2033, 2034) - Count = 1 + Name = "Can process int32[] type 'fhx -InputObject [int32[]](2032, 2033, 2034)'" + InputObject = [int32[]](2032, 2033, 2034) + Count = 1 ExpectedResult = "00000000000000000000 F0 07 00 00 F1 07 00 00 F2 07 00 00 ð...ñ...ò..." } @{ - Name = "Can process Int64 type 'fhx -InputObject [Int64]9223372036854775807'" - InputObject = [Int64]9223372036854775807 - Count = 1 + Name = "Can process Int64 type 'fhx -InputObject [Int64]9223372036854775807'" + InputObject = [Int64]9223372036854775807 + Count = 1 ExpectedResult = "00000000000000000000 FF FF FF FF FF FF FF 7F ......." } @{ - Name = "Can process Int64[] type 'fhx -InputObject [Int64[]](9223372036852,9223372036853)'" - InputObject = [Int64[]](9223372036852,9223372036853) - Count = 1 + Name = "Can process Int64[] type 'fhx -InputObject [Int64[]](9223372036852,9223372036853)'" + InputObject = [Int64[]](9223372036852, 9223372036853) + Count = 1 ExpectedResult = "00000000000000000000 F4 5A D0 7B 63 08 00 00 F5 5A D0 7B 63 08 00 00 ôZÐ{c...õZÐ{c..." } @{ - Name = "Can process string type 'fhx -InputObject hello world'" - InputObject = "hello world" - Count = 1 + Name = "Can process string type 'fhx -InputObject hello world'" + InputObject = "hello world" + Count = 1 ExpectedResult = "00000000000000000000 68 65 6C 6C 6F 20 77 6F 72 6C 64 hello world" } @{ - Name = "Can process enum type 'fhx -InputObject ([DisplayHintType]::DateTime)'" - InputObject = [Microsoft.PowerShell.Commands.DisplayHintType]::DateTime - Count = 1 - ExpectedResult = "00000000000000000000 02 00 00 00 ...." - } - @{ - Name = "Can process DisplayHintType[] type 'fhx -InputObject ([DisplayHintType[]](DateTime, Time))'" - InputObject = [Microsoft.PowerShell.Commands.DisplayHintType[]]([Microsoft.PowerShell.Commands.DisplayHintType]::DateTime) - Count = 1 - ExpectedResult = "00000000000000000000 02 00 00 00 ...." - } - @{ - Name = "Can process DisplayHintType[] type 'fhx -InputObject ([DisplayHintType[]](DateTime, Time))'" - InputObject = [Microsoft.PowerShell.Commands.DisplayHintType[]]([Microsoft.PowerShell.Commands.DisplayHintType]::DateTime, [Microsoft.PowerShell.Commands.DisplayHintType]::Time) - Count = 1 - ExpectedResult = "00000000000000000000 02 00 00 00 01 00 00 00 ........" + Name = "Can process PS-native enum array '[TestEnum[]]('TestOne', 'TestTwo', 'TestThree', 'TestFour') | fhx'" + InputObject = [TestEnum[]]('TestOne', 'TestTwo', 'TestThree', 'TestFour') + Count = 1 + ExpectedResult = "00000000000000000000 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 ................" } @{ - Name = "Can process char type 'fhx -InputObject ([char]`'A`')'" - InputObject = [char]'A' - Count = 1 - ExpectedResult = "00000000000000000000 41 A" - } - @{ - Name = "Can process char[] type 'fhx -InputObject ([char[]](`'A`'))'" - InputObject = [char[]]('A') - Count = 1 - ExpectedResult = "00000000000000000000 41 A" - } - @{ - Name = "Can process char[] type 'fhx -InputObject ([char[]](`'A`', 'B'))'" - InputObject = [char[]]('A', 'B') - Count = 1 - ExpectedResult = "00000000000000000000 41 42 AB" + Name = "Can process C#-native sbyte enum array '[TestSByteEnum[]]('One', 'Two', 'Three', 'Four') | fhx'" + InputObject = [TestSByteEnum[]]('One', 'Two', 'Three', 'Four') + Count = 1 + ExpectedResult = "00000000000000000000 FF FE FD FC .þýü" } ) - It "" -TestCase $testCases{ + It "" -TestCase $testCases { param ($Name, $InputObject, $Count, $ExpectedResult) @@ -150,102 +137,93 @@ Describe "FormatHex" -tags "CI" { } Context "InputObject From Pipeline" { + BeforeAll { + enum TestEnum { + TestOne = 1; TestTwo = 2; TestThree = 3; TestFour = 4 + } + Add-Type -TypeDefinition @' +public enum TestSByteEnum : sbyte { + One = -1, + Two = -2, + Three = -3, + Four = -4 +} +'@ + } + $testCases = @( @{ - Name = "Can process byte type '[byte]5 | fhx'" - InputObject = [byte]5 - Count = 1 + Name = "Can process byte type '[byte]5 | fhx'" + InputObject = [byte]5 + Count = 1 ExpectedResult = "00000000000000000000 05" } @{ - Name = "Can process byte[] type '[byte[]](1,2) | fhx'" - InputObject = [byte[]](1,2) - Count = 2 - ExpectedResult = "00000000000000000000 01 ." - ExpectedSecondResult = "00000000000000000000 02 ." + Name = "Can process byte[] type '[byte[]](1,2) | fhx'" + InputObject = [byte[]](1, 2) + Count = 1 + ExpectedResult = "00000000000000000000 01 02 .." } @{ - Name = "Can process int type '7 | fhx'" - InputObject = 7 - Count = 1 + Name = "Can process int type '7 | fhx'" + InputObject = 7 + Count = 1 ExpectedResult = "00000000000000000000 07 00 00 00 ...." } @{ - Name = "Can process int[] type '[int[]](5,6) | fhx'" - InputObject = [int[]](5,6) - Count = 2 - ExpectedResult = "00000000000000000000 05 00 00 00 ...." - ExpectedSecondResult = "00000000000000000000 06 00 00 00 ...." + Name = "Can process int[] type '[int[]](5,6) | fhx'" + InputObject = [int[]](5, 6) + Count = 1 + ExpectedResult = "00000000000000000000 05 00 00 00 06 00 00 00 ........" } @{ - Name = "Can process int32 type '[int32]2032 | fhx'" - InputObject = [int32]2032 - Count = 1 + Name = "Can process int32 type '[int32]2032 | fhx'" + InputObject = [int32]2032 + Count = 1 ExpectedResult = "00000000000000000000 F0 07 00 00 ð..." } @{ - Name = "Can process int32[] type '[int32[]](2032, 2033) | fhx'" - InputObject = [int32[]](2032, 2033) - Count = 2 - ExpectedResult = "00000000000000000000 F0 07 00 00 ð..." - ExpectedSecondResult = "00000000000000000000 F1 07 00 00 ñ..." + Name = "Can process int32[] type '[int32[]](2032, 2033) | fhx'" + InputObject = [int32[]](2032, 2033) + Count = 1 + ExpectedResult = "00000000000000000000 F0 07 00 00 F1 07 00 00 ð...ñ..." } @{ - Name = "Can process Int64 type '[Int64]9223372036854775807 | fhx'" - InputObject = [Int64]9223372036854775807 - Count = 1 + Name = "Can process Int64 type '[Int64]9223372036854775807 | fhx'" + InputObject = [Int64]9223372036854775807 + Count = 1 ExpectedResult = "00000000000000000000 FF FF FF FF FF FF FF 7F ......." } @{ - Name = "Can process Int64[] type '[Int64[]](9223372036852,9223372036853) | fhx'" - InputObject = [Int64[]](9223372036852,9223372036853) - Count = 2 - ExpectedResult = "00000000000000000000 F4 5A D0 7B 63 08 00 00 ôZÐ{c..." - ExpectedSecondResult = "00000000000000000000 F5 5A D0 7B 63 08 00 00 õZÐ{c..." + Name = "Can process Int64[] type '[Int64[]](9223372036852,9223372036853) | fhx'" + InputObject = [Int64[]](9223372036852, 9223372036853) + Count = 1 + ExpectedResult = "00000000000000000000 F4 5A D0 7B 63 08 00 00 F5 5A D0 7B 63 08 00 00 ôZÐ{c...õZÐ{c..." } @{ - Name = "Can process string type 'hello world | fhx'" - InputObject = "hello world" - Count = 1 + Name = "Can process string type 'hello world | fhx'" + InputObject = "hello world" + Count = 1 ExpectedResult = "00000000000000000000 68 65 6C 6C 6F 20 77 6F 72 6C 64 hello world" } @{ - Name = "Can process enum type '[DisplayHintType]::DateTime | fhx" - InputObject = [Microsoft.PowerShell.Commands.DisplayHintType]::DateTime - Count = 1 - ExpectedResult = "00000000000000000000 02 00 00 00 ...." - } - @{ - Name = "Can process DisplayHintType[] type '[DisplayHintType[]](DateTime) | fhx'" - InputObject = [Microsoft.PowerShell.Commands.DisplayHintType[]]([Microsoft.PowerShell.Commands.DisplayHintType]::DateTime) - Count = 1 - ExpectedResult = "00000000000000000000 02 00 00 00 ...." - } - @{ - Name = "Can process DisplayHintType[] type '[DisplayHintType[]](DateTime, Time) | fhx'" - InputObject = [Microsoft.PowerShell.Commands.DisplayHintType[]]([Microsoft.PowerShell.Commands.DisplayHintType]::DateTime, [Microsoft.PowerShell.Commands.DisplayHintType]::Time) - Count = 2 - ExpectedResult = "00000000000000000000 02 00 00 00 ...." - ExpectedSecondResult = "00000000000000000000 01 00 00 00 ...." + Name = "Can process jagged array type '[sbyte[]](-15, 18, 21, -5), [byte[]](1, 2, 3, 4, 5, 6) | fhx'" + InputObject = [sbyte[]](-15, 18, 21, -5), [byte[]](1, 2, 3, 4, 5, 6) + Count = 2 + ExpectedResult = "00000000000000000000 F1 12 15 FB ñ..û" + ExpectedSecondResult = "00000000000000000000 01 02 03 04 05 06 ......" } @{ - Name = "Can process char type '[char]`'A`' | fhx'" - InputObject = [char]'A' - Count = 1 - ExpectedResult = "00000000000000000000 41 A" + Name = "Can process PS-native enum array '[TestEnum[]]('TestOne', 'TestTwo', 'TestThree', 'TestFour') | fhx'" + InputObject = [TestEnum[]]('TestOne', 'TestTwo', 'TestThree', 'TestFour') + Count = 1 + ExpectedResult = "00000000000000000000 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 ................" } @{ - Name = "Can process char[] type '[char[]](`'A`') | fhx'" - InputObject = [char[]]('A') - Count = 1 - ExpectedResult = "00000000000000000000 41 A" - } - @{ - Name = "Can process char[] type '[char[]](`'A`', 'B') | fhx'" - InputObject = [char[]]('A', 'B') - Count = 2 - ExpectedResult = "00000000000000000000 41 A" - ExpectedSecondResult = "00000000000000000000 42 B" + Name = "Can process C#-native sbyte enum array '[TestSByteEnum[]]('One', 'Two', 'Three', 'Four') | fhx'" + InputObject = [TestSByteEnum[]]('One', 'Two', 'Three', 'Four') + Count = 1 + ExpectedResult = "00000000000000000000 FF FE FD FC .þýü" } ) @@ -255,12 +233,11 @@ Describe "FormatHex" -tags "CI" { $result = $InputObject | Format-Hex - $result.count | Should -Be $Count + $result.Count | Should -Be $Count $result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' $result[0].ToString() | Should -MatchExactly $ExpectedResult - if ($result.count -gt 1) - { + if ($result.count -gt 1) { $result[1].ToString() | Should -MatchExactly $ExpectedSecondResult } } @@ -272,39 +249,39 @@ Describe "FormatHex" -tags "CI" { $testCases = @( @{ - Name = "Can process file content from given file path 'fhx -Path `$inputFile1'" - PathCase = $true - Path = $inputFile1 - Count = 1 + Name = "Can process file content from given file path 'fhx -Path `$inputFile1'" + PathCase = $true + Path = $inputFile1 + Count = 1 ExpectedResult = $inputText1 } @{ - Name = "Can process file content from all files in array of file paths 'fhx -Path `$inputFile1, `$inputFile2'" - PathCase = $true - Path = @($inputFile1, $inputFile2) - Count = 2 - ExpectedResult = $inputText1 + Name = "Can process file content from all files in array of file paths 'fhx -Path `$inputFile1, `$inputFile2'" + PathCase = $true + Path = @($inputFile1, $inputFile2) + Count = 2 + ExpectedResult = $inputText1 ExpectedSecondResult = $inputText2 } @{ - Name = "Can process file content from all files when resolved to multiple paths 'fhx -Path '`$testDirectory\SourceFile-*''" - PathCase = $true - Path = "$testDirectory\SourceFile-*" - Count = 2 - ExpectedResult = $inputText1 + Name = "Can process file content from all files when resolved to multiple paths 'fhx -Path '`$testDirectory\SourceFile-*''" + PathCase = $true + Path = "$testDirectory\SourceFile-*" + Count = 2 + ExpectedResult = $inputText1 ExpectedSecondResult = $inputText2 } @{ - Name = "Can process file content from given file path 'fhx -LiteralPath `$inputFile3'" - Path = $inputFile3 - Count = 1 + Name = "Can process file content from given file path 'fhx -LiteralPath `$inputFile3'" + Path = $inputFile3 + Count = 1 ExpectedResult = $inputText3 } @{ - Name = "Can process file content from all files in array of file paths 'fhx -LiteralPath `$inputFile1, `$inputFile3'" - Path = @($inputFile1, $inputFile3) - Count = 2 - ExpectedResult = $inputText1 + Name = "Can process file content from all files in array of file paths 'fhx -LiteralPath `$inputFile1, `$inputFile3'" + Path = @($inputFile1, $inputFile3) + Count = 2 + ExpectedResult = $inputText1 ExpectedSecondResult = $inputText3 } ) @@ -313,61 +290,73 @@ Describe "FormatHex" -tags "CI" { param ($Name, $PathCase, $Path, $ExpectedResult, $ExpectedSecondResult) - if ($PathCase) - { - $result = Format-Hex -Path $Path - } - else # LiteralPath - { + if ($PathCase) { + $result = Format-Hex -Path $Path + } else { + # LiteralPath $result = Format-Hex -LiteralPath $Path } $result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' $result[0].ToString() | Should -MatchExactly $ExpectedResult - if ($result.count -gt 1) - { + if ($result.count -gt 1) { $result[1].ToString() | Should -MatchExactly $ExpectedSecondResult } } + + It 'properly accepts -LiteralPath input from a FileInfo object' { + $FilePath = 'TestDrive:\FHX-LitPathTest.txt' + "Hello World!" | Set-Content -Path $FilePath + $FileObject = Get-Item -Path $FilePath + + $result = $FileObject | Format-Hex + if ($IsWindows) { + $ExpectedResult = "00000000000000000000 48 65 6C 6C 6F 20 57 6F 72 6C 64 21 0D 0A Hello World!.." + } else { + $ExpectedResult = "00000000000000000000 48 65 6C 6C 6F 20 57 6F 72 6C 64 21 0A Hello World!." + } + + $result[0].ToString() | Should -MatchExactly $ExpectedResult + } } Context "Encoding Parameter" { $testCases = @( @{ - Name = "Can process ASCII encoding 'fhx -InputObject 'hello' -Encoding ASCII'" - Encoding = "ASCII" - Count = 1 + Name = "Can process ASCII encoding 'fhx -InputObject 'hello' -Encoding ASCII'" + Encoding = "ASCII" + Count = 1 ExpectedResult = "00000000000000000000 68 65 6C 6C 6F hello" } @{ - Name = "Can process BigEndianUnicode encoding 'fhx -InputObject 'hello' -Encoding BigEndianUnicode'" - Encoding = "BigEndianUnicode" - Count = 1 + Name = "Can process BigEndianUnicode encoding 'fhx -InputObject 'hello' -Encoding BigEndianUnicode'" + Encoding = "BigEndianUnicode" + Count = 1 ExpectedResult = "00000000000000000000 00 68 00 65 00 6C 00 6C 00 6F .h.e.l.l.o" } @{ - Name = "Can process Unicode encoding 'fhx -InputObject 'hello' -Encoding Unicode'" - Encoding = "Unicode" - Count = 1 + Name = "Can process Unicode encoding 'fhx -InputObject 'hello' -Encoding Unicode'" + Encoding = "Unicode" + Count = 1 ExpectedResult = "00000000000000000000 68 00 65 00 6C 00 6C 00 6F 00 h.e.l.l.o." } @{ - Name = "Can process UTF7 encoding 'fhx -InputObject 'hello' -Encoding UTF7'" - Encoding = "UTF7" - Count = 1 + Name = "Can process UTF7 encoding 'fhx -InputObject 'hello' -Encoding UTF7'" + Encoding = "UTF7" + Count = 1 ExpectedResult = "00000000000000000000 68 65 6C 6C 6F hello" } - @{ - Name = "Can process UTF8 encoding 'fhx -InputObject 'hello' -Encoding UTF8'" - Encoding = "UTF8" - Count = 1 + @{ + Name = "Can process UTF8 encoding 'fhx -InputObject 'hello' -Encoding UTF8'" + Encoding = "UTF8" + Count = 1 ExpectedResult = "00000000000000000000 68 65 6C 6C 6F hello" } - @{ - Name = "Can process UTF32 encoding 'fhx -InputObject 'hello' -Encoding UTF32'" - Encoding = "UTF32" - Count = 1 + @{ + Name = "Can process UTF32 encoding 'fhx -InputObject 'hello' -Encoding UTF32'" + Encoding = "UTF32" + Count = 1 ExpectedResult = "00000000000000000000 68 00 00 00 65 00 00 00 6C 00 00 00 6C 00 00 00 h...e...l...l...$($newline)00000000000000000010 6F 00 00 00 o..." } ) @@ -390,16 +379,16 @@ Describe "FormatHex" -tags "CI" { $testCases = @( @{ - Name = "Does not support non-FileSystem Provider paths 'fhx -Path 'Cert:\CurrentUser\My\`$thumbprint' -ErrorAction Stop'" - PathParameterErrorCase = $true - Path = "Cert:\CurrentUser\My\$thumbprint" + Name = "Does not support non-FileSystem Provider paths 'fhx -Path 'Cert:\CurrentUser\My\`$thumbprint' -ErrorAction Stop'" + PathParameterErrorCase = $true + Path = "Cert:\CurrentUser\My\$thumbprint" ExpectedFullyQualifiedErrorId = "FormatHexOnlySupportsFileSystemPaths,Microsoft.PowerShell.Commands.FormatHex" } @{ - Name = "Type Not Supported 'fhx -InputObject @{'hash' = 'table'} -ErrorAction Stop'" - InputObjectErrorCase = $true - Path = $inputFile1 - InputObject = @{ "hash" = "table" } + Name = "Type Not Supported 'fhx -InputObject @{'hash' = 'table'} -ErrorAction Stop'" + InputObjectErrorCase = $true + Path = $inputFile1 + InputObject = @{ "hash" = "table" } ExpectedFullyQualifiedErrorId = "FormatHexTypeNotSupported,Microsoft.PowerShell.Commands.FormatHex" } ) @@ -409,12 +398,10 @@ Describe "FormatHex" -tags "CI" { param ($Name, $PathParameterErrorCase, $Path, $InputObject, $InputObjectErrorCase, $ExpectedFullyQualifiedErrorId) { - if ($PathParameterErrorCase) - { + if ($PathParameterErrorCase) { $result = Format-Hex -Path $Path -ErrorAction Stop } - if ($InputObjectErrorCase) - { + if ($InputObjectErrorCase) { $result = Format-Hex -InputObject $InputObject -ErrorAction Stop } } | Should -Throw -ErrorId $ExpectedFullyQualifiedErrorId @@ -425,20 +412,20 @@ Describe "FormatHex" -tags "CI" { $testCases = @( @{ - Name = "If given invalid path in array, continues to process valid paths 'fhx -Path `$invalidPath, `$inputFile1 -ErrorVariable e -ErrorAction SilentlyContinue'" - PathCase = $true - InvalidPath = "$($inputFile1.DirectoryName)\fakefile8888845345345348709.txt" + Name = "If given invalid path in array, continues to process valid paths 'fhx -Path `$invalidPath, `$inputFile1 -ErrorVariable e -ErrorAction SilentlyContinue'" + PathCase = $true + InvalidPath = "$($inputFile1.DirectoryName)\fakefile8888845345345348709.txt" ExpectedFullyQualifiedErrorId = "FileNotFound,Microsoft.PowerShell.Commands.FormatHex" } @{ - Name = "If given a non FileSystem path in array, continues to process valid paths 'fhx -Path `$invalidPath, `$inputFile1 -ErrorVariable e -ErrorAction SilentlyContinue'" - PathCase = $true - InvalidPath = "Cert:\CurrentUser\My\$thumbprint" + Name = "If given a non FileSystem path in array, continues to process valid paths 'fhx -Path `$invalidPath, `$inputFile1 -ErrorVariable e -ErrorAction SilentlyContinue'" + PathCase = $true + InvalidPath = "Cert:\CurrentUser\My\$thumbprint" ExpectedFullyQualifiedErrorId = "FormatHexOnlySupportsFileSystemPaths,Microsoft.PowerShell.Commands.FormatHex" } @{ - Name = "If given a non FileSystem path in array (with LiteralPath), continues to process valid paths 'fhx -Path `$invalidPath, `$inputFile1 -ErrorVariable e -ErrorAction SilentlyContinue'" - InvalidPath = "Cert:\CurrentUser\My\$thumbprint" + Name = "If given a non FileSystem path in array (with LiteralPath), continues to process valid paths 'fhx -Path `$invalidPath, `$inputFile1 -ErrorVariable e -ErrorAction SilentlyContinue'" + InvalidPath = "Cert:\CurrentUser\My\$thumbprint" ExpectedFullyQualifiedErrorId = "FormatHexOnlySupportsFileSystemPaths,Microsoft.PowerShell.Commands.FormatHex" } ) @@ -450,12 +437,10 @@ Describe "FormatHex" -tags "CI" { $output = $null $errorThrown = $null - if ($PathCase) - { + if ($PathCase) { $output = Format-Hex -Path $InvalidPath, $inputFile1 -ErrorVariable errorThrown -ErrorAction SilentlyContinue - } - else # LiteralPath - { + } else { + # LiteralPath $output = Format-Hex -LiteralPath $InvalidPath, $inputFile1 -ErrorVariable errorThrown -ErrorAction SilentlyContinue } @@ -470,10 +455,10 @@ Describe "FormatHex" -tags "CI" { It "Path is default Parameter Set 'fhx `$inputFile1'" { - $result = Format-Hex $inputFile1 + $result = Format-Hex $inputFile1 $result | Should -Not -BeNullOrEmpty - ,$result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' + , $result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' $actualResult = $result.ToString() $actualResult | Should -MatchExactly $inputText1 } @@ -483,7 +468,7 @@ Describe "FormatHex" -tags "CI" { $result = Get-ChildItem $inputFile1 | Format-Hex $result | Should -Not -BeNullOrEmpty - ,$result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' + , $result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' $actualResult = $result.ToString() $actualResult | Should -MatchExactly $inputText1 } @@ -493,7 +478,7 @@ Describe "FormatHex" -tags "CI" { $result = "a" * 30 | Format-Hex $result | Should -Not -BeNullOrEmpty - ,$result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' + , $result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' $result.ToString() | Should -MatchExactly "00000000000000000000 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa$($newline)00000000000000000010 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaa " } From 09da3d1c7d7b5d5fb02375bb2a5345edc235353a Mon Sep 17 00:00:00 2001 From: Joel <32407840+vexx32@users.noreply.github.com> Date: Thu, 17 Jan 2019 14:32:05 -0500 Subject: [PATCH 04/28] :recycle: Tidy ErrorRecord instantiations to CodeFactor standards --- .../FormatAndOutput/format-hex/Format-Hex.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs index b1a58c78293..76d70cde4c5 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs @@ -316,10 +316,11 @@ private void ProcessObjectContent(PSObject inputObject) else { string errorMessage = StringUtil.Format(UtilityCommonStrings.FormatHexTypeNotSupported, obj.GetType()); - ErrorRecord errorRecord = new ErrorRecord(new ArgumentException(errorMessage), - "FormatHexTypeNotSupported", - ErrorCategory.InvalidArgument, - obj.GetType()); + ErrorRecord errorRecord = new ErrorRecord( + new ArgumentException(errorMessage), + "FormatHexTypeNotSupported", + ErrorCategory.InvalidArgument, + obj.GetType()); WriteError(errorRecord); } } @@ -339,10 +340,11 @@ private void ProcessObjectContent(PSObject inputObject) else { string errorMessage = StringUtil.Format(UtilityCommonStrings.FormatHexTypeNotSupported, obj.GetType()); - ErrorRecord errorRecord = new ErrorRecord(new ArgumentException(errorMessage), - "FormatHexTypeNotSupported", - ErrorCategory.InvalidArgument, - obj.GetType()); + ErrorRecord errorRecord = new ErrorRecord( + new ArgumentException(errorMessage), + "FormatHexTypeNotSupported", + ErrorCategory.InvalidArgument, + obj.GetType()); WriteError(errorRecord); } } From eeac89c0ed00df51e585cdf2e6379a9cf65ba608 Mon Sep 17 00:00:00 2001 From: vexx32 <32407840+vexx32@users.noreply.github.com> Date: Sun, 23 Jun 2019 08:22:48 -0400 Subject: [PATCH 05/28] :white_check_mark: Address James' comment --- .../Format-Hex.Tests.ps1 | 242 +++++++++--------- 1 file changed, 123 insertions(+), 119 deletions(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-Hex.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-Hex.Tests.ps1 index 549bcdc4aa7..912d2910f2c 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-Hex.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-Hex.Tests.ps1 @@ -23,10 +23,10 @@ Describe "FormatHex" -tags "CI" { $inputText2 = 'More text' $inputText3 = 'Literal path' $inputText4 = 'Now is the winter of our discontent' - $inputFile1 = setup -f "FormatHexDataDir/SourceFile-1.txt" -content $inputText1 -pass - $inputFile2 = setup -f "FormatHexDataDir/SourceFile-2.txt" -content $inputText2 -pass - $inputFile3 = setup -f "FormatHexDataDir/SourceFile literal [3].txt" -content $inputText3 -pass - $inputFile4 = setup -f "FormatHexDataDir/SourceFile-4.txt" -content $inputText4 -pass + $inputFile1 = Setup -f "FormatHexDataDir/SourceFile-1.txt" -content $inputText1 -pass + $inputFile2 = Setup -f "FormatHexDataDir/SourceFile-2.txt" -content $inputText2 -pass + $inputFile3 = Setup -f "FormatHexDataDir/SourceFile literal [3].txt" -content $inputText3 -pass + $inputFile4 = Setup -f "FormatHexDataDir/SourceFile-4.txt" -content $inputText4 -pass $certificateProvider = Get-ChildItem Cert:\CurrentUser\My\ -ErrorAction SilentlyContinue $thumbprint = $null @@ -292,7 +292,8 @@ public enum TestSByteEnum : sbyte { if ($PathCase) { $result = Format-Hex -Path $Path - } else { + } + else { # LiteralPath $result = Format-Hex -LiteralPath $Path } @@ -312,12 +313,14 @@ public enum TestSByteEnum : sbyte { $result = $FileObject | Format-Hex if ($IsWindows) { - $ExpectedResult = "00000000000000000000 48 65 6C 6C 6F 20 57 6F 72 6C 64 21 0D 0A Hello World!.." - } else { - $ExpectedResult = "00000000000000000000 48 65 6C 6C 6F 20 57 6F 72 6C 64 21 0A Hello World!." + $Result.Bytes[-1] | Should -Be 0x0A + $Result.Bytes[-2] | Should -Be 0x0D + $Result.Bytes.Length | Should -Be 14 + } + else { + $Result.Bytes[-1] | Should -Be 0x0A + $Result.Bytes.Length | Should -Be 13 } - - $result[0].ToString() | Should -MatchExactly $ExpectedResult } } @@ -405,154 +408,155 @@ public enum TestSByteEnum : sbyte { $result = Format-Hex -InputObject $InputObject -ErrorAction Stop } } | Should -Throw -ErrorId $ExpectedFullyQualifiedErrorId - } } +} - Context "Continues to Process Valid Paths" { +Context "Continues to Process Valid Paths" { - $testCases = @( - @{ - Name = "If given invalid path in array, continues to process valid paths 'fhx -Path `$invalidPath, `$inputFile1 -ErrorVariable e -ErrorAction SilentlyContinue'" - PathCase = $true - InvalidPath = "$($inputFile1.DirectoryName)\fakefile8888845345345348709.txt" - ExpectedFullyQualifiedErrorId = "FileNotFound,Microsoft.PowerShell.Commands.FormatHex" - } - @{ - Name = "If given a non FileSystem path in array, continues to process valid paths 'fhx -Path `$invalidPath, `$inputFile1 -ErrorVariable e -ErrorAction SilentlyContinue'" - PathCase = $true - InvalidPath = "Cert:\CurrentUser\My\$thumbprint" - ExpectedFullyQualifiedErrorId = "FormatHexOnlySupportsFileSystemPaths,Microsoft.PowerShell.Commands.FormatHex" - } - @{ - Name = "If given a non FileSystem path in array (with LiteralPath), continues to process valid paths 'fhx -Path `$invalidPath, `$inputFile1 -ErrorVariable e -ErrorAction SilentlyContinue'" - InvalidPath = "Cert:\CurrentUser\My\$thumbprint" - ExpectedFullyQualifiedErrorId = "FormatHexOnlySupportsFileSystemPaths,Microsoft.PowerShell.Commands.FormatHex" - } - ) + $testCases = @( + @{ + Name = "If given invalid path in array, continues to process valid paths 'fhx -Path `$invalidPath, `$inputFile1 -ErrorVariable e -ErrorAction SilentlyContinue'" + PathCase = $true + InvalidPath = "$($inputFile1.DirectoryName)\fakefile8888845345345348709.txt" + ExpectedFullyQualifiedErrorId = "FileNotFound,Microsoft.PowerShell.Commands.FormatHex" + } + @{ + Name = "If given a non FileSystem path in array, continues to process valid paths 'fhx -Path `$invalidPath, `$inputFile1 -ErrorVariable e -ErrorAction SilentlyContinue'" + PathCase = $true + InvalidPath = "Cert:\CurrentUser\My\$thumbprint" + ExpectedFullyQualifiedErrorId = "FormatHexOnlySupportsFileSystemPaths,Microsoft.PowerShell.Commands.FormatHex" + } + @{ + Name = "If given a non FileSystem path in array (with LiteralPath), continues to process valid paths 'fhx -Path `$invalidPath, `$inputFile1 -ErrorVariable e -ErrorAction SilentlyContinue'" + InvalidPath = "Cert:\CurrentUser\My\$thumbprint" + ExpectedFullyQualifiedErrorId = "FormatHexOnlySupportsFileSystemPaths,Microsoft.PowerShell.Commands.FormatHex" + } + ) - It "" -Skip:$skipTest -TestCase $testCases { + It "" -Skip:$skipTest -TestCase $testCases { - param ($Name, $PathCase, $InvalidPath, $ExpectedFullyQualifiedErrorId) + param ($Name, $PathCase, $InvalidPath, $ExpectedFullyQualifiedErrorId) - $output = $null - $errorThrown = $null + $output = $null + $errorThrown = $null - if ($PathCase) { - $output = Format-Hex -Path $InvalidPath, $inputFile1 -ErrorVariable errorThrown -ErrorAction SilentlyContinue - } else { - # LiteralPath - $output = Format-Hex -LiteralPath $InvalidPath, $inputFile1 -ErrorVariable errorThrown -ErrorAction SilentlyContinue - } + if ($PathCase) { + $output = Format-Hex -Path $InvalidPath, $inputFile1 -ErrorVariable errorThrown -ErrorAction SilentlyContinue + } + else { + # LiteralPath + $output = Format-Hex -LiteralPath $InvalidPath, $inputFile1 -ErrorVariable errorThrown -ErrorAction SilentlyContinue + } - $errorThrown.FullyQualifiedErrorId | Should -MatchExactly $ExpectedFullyQualifiedErrorId + $errorThrown.FullyQualifiedErrorId | Should -MatchExactly $ExpectedFullyQualifiedErrorId - $output.Length | Should -Be 1 - $output[0].ToString() | Should -MatchExactly $inputText1 - } + $output.Length | Should -Be 1 + $output[0].ToString() | Should -MatchExactly $inputText1 } +} - Context "Cmdlet Functionality" { +Context "Cmdlet Functionality" { - It "Path is default Parameter Set 'fhx `$inputFile1'" { + It "Path is default Parameter Set 'fhx `$inputFile1'" { - $result = Format-Hex $inputFile1 + $result = Format-Hex $inputFile1 - $result | Should -Not -BeNullOrEmpty - , $result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' - $actualResult = $result.ToString() - $actualResult | Should -MatchExactly $inputText1 - } + $result | Should -Not -BeNullOrEmpty + , $result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' + $actualResult = $result.ToString() + $actualResult | Should -MatchExactly $inputText1 + } - It "Validate file input from Pipeline 'Get-ChildItem `$inputFile1 | Format-Hex'" { + It "Validate file input from Pipeline 'Get-ChildItem `$inputFile1 | Format-Hex'" { - $result = Get-ChildItem $inputFile1 | Format-Hex + $result = Get-ChildItem $inputFile1 | Format-Hex - $result | Should -Not -BeNullOrEmpty - , $result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' - $actualResult = $result.ToString() - $actualResult | Should -MatchExactly $inputText1 - } + $result | Should -Not -BeNullOrEmpty + , $result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' + $actualResult = $result.ToString() + $actualResult | Should -MatchExactly $inputText1 + } - It "Validate that streamed text does not have buffer underrun problems ''a' * 30 | Format-Hex'" { + It "Validate that streamed text does not have buffer underrun problems ''a' * 30 | Format-Hex'" { - $result = "a" * 30 | Format-Hex + $result = "a" * 30 | Format-Hex - $result | Should -Not -BeNullOrEmpty - , $result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' - $result.ToString() | Should -MatchExactly "00000000000000000000 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa$($newline)00000000000000000010 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaa " - } + $result | Should -Not -BeNullOrEmpty + , $result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' + $result.ToString() | Should -MatchExactly "00000000000000000000 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa$($newline)00000000000000000010 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaa " + } - It "Validate that files do not have buffer underrun problems 'Format-Hex -Path `$InputFile4'" { + It "Validate that files do not have buffer underrun problems 'Format-Hex -Path `$InputFile4'" { - $result = Format-Hex -Path $InputFile4 + $result = Format-Hex -Path $InputFile4 - $result | Should -Not -BeNullOrEmpty - $result.Count | Should -Be 3 - $result[0].ToString() | Should -MatchExactly "00000000000000000000 4E 6F 77 20 69 73 20 74 68 65 20 77 69 6E 74 65 Now is the winte" - $result[1].ToString() | Should -MatchExactly "00000000000000000010 72 20 6F 66 20 6F 75 72 20 64 69 73 63 6F 6E 74 r of our discont" - $result[2].ToString() | Should -MatchExactly "00000000000000000020 65 6E 74 ent " - } + $result | Should -Not -BeNullOrEmpty + $result.Count | Should -Be 3 + $result[0].ToString() | Should -MatchExactly "00000000000000000000 4E 6F 77 20 69 73 20 74 68 65 20 77 69 6E 74 65 Now is the winte" + $result[1].ToString() | Should -MatchExactly "00000000000000000010 72 20 6F 66 20 6F 75 72 20 64 69 73 63 6F 6E 74 r of our discont" + $result[2].ToString() | Should -MatchExactly "00000000000000000020 65 6E 74 ent " } +} - Context "Count and Offset parameters" { - It "Count = length" { +Context "Count and Offset parameters" { + It "Count = length" { - $result = Format-Hex -Path $InputFile4 -Count $inputText4.Length + $result = Format-Hex -Path $InputFile4 -Count $inputText4.Length - $result | Should -Not -BeNullOrEmpty - $result.Count | Should -Be 3 - $result[0].ToString() | Should -MatchExactly "00000000000000000000 4E 6F 77 20 69 73 20 74 68 65 20 77 69 6E 74 65 Now is the winte" - $result[1].ToString() | Should -MatchExactly "00000000000000000010 72 20 6F 66 20 6F 75 72 20 64 69 73 63 6F 6E 74 r of our discont" - $result[2].ToString() | Should -MatchExactly "00000000000000000020 65 6E 74 ent " - } + $result | Should -Not -BeNullOrEmpty + $result.Count | Should -Be 3 + $result[0].ToString() | Should -MatchExactly "00000000000000000000 4E 6F 77 20 69 73 20 74 68 65 20 77 69 6E 74 65 Now is the winte" + $result[1].ToString() | Should -MatchExactly "00000000000000000010 72 20 6F 66 20 6F 75 72 20 64 69 73 63 6F 6E 74 r of our discont" + $result[2].ToString() | Should -MatchExactly "00000000000000000020 65 6E 74 ent " + } - It "Count = 1" { - $result = Format-Hex -Path $inputFile4 -Count 1 - $result.ToString() | Should -MatchExactly "00000000000000000000 4E N " - } + It "Count = 1" { + $result = Format-Hex -Path $inputFile4 -Count 1 + $result.ToString() | Should -MatchExactly "00000000000000000000 4E N " + } - It "Offset = length" { + It "Offset = length" { - $result = Format-Hex -Path $InputFile4 -Offset $inputText4.Length - $result | Should -BeNullOrEmpty + $result = Format-Hex -Path $InputFile4 -Offset $inputText4.Length + $result | Should -BeNullOrEmpty - $result = Format-Hex -InputObject $inputText4 -Offset $inputText4.Length - $result.Bytes | Should -HaveCount 0 - } + $result = Format-Hex -InputObject $inputText4 -Offset $inputText4.Length + $result.Bytes | Should -HaveCount 0 + } - It "Offset = 1" { + It "Offset = 1" { - $result = Format-Hex -Path $InputFile4 -Offset 1 + $result = Format-Hex -Path $InputFile4 -Offset 1 - $result | Should -Not -BeNullOrEmpty - $result.Count | Should -Be 3 - $result[0].ToString() | Should -MatchExactly "00000000000000000001 6F 77 20 69 73 20 74 68 65 20 77 69 6E 74 65 72 ow is the winter" - $result[1].ToString() | Should -MatchExactly "00000000000000000011 20 6F 66 20 6F 75 72 20 64 69 73 63 6F 6E 74 65 of our disconte" - $result[2].ToString() | Should -MatchExactly "00000000000000000021 6E 74 nt " - } + $result | Should -Not -BeNullOrEmpty + $result.Count | Should -Be 3 + $result[0].ToString() | Should -MatchExactly "00000000000000000001 6F 77 20 69 73 20 74 68 65 20 77 69 6E 74 65 72 ow is the winter" + $result[1].ToString() | Should -MatchExactly "00000000000000000011 20 6F 66 20 6F 75 72 20 64 69 73 63 6F 6E 74 65 of our disconte" + $result[2].ToString() | Should -MatchExactly "00000000000000000021 6E 74 nt " + } - It "Count = 1 and Offset = 1" { - $result = Format-Hex -Path $inputFile4 -Count 1 -Offset 1 - $result.ToString() | Should -MatchExactly "00000000000000000001 6F o " - } + It "Count = 1 and Offset = 1" { + $result = Format-Hex -Path $inputFile4 -Count 1 -Offset 1 + $result.ToString() | Should -MatchExactly "00000000000000000001 6F o " + } - It "Count should be > 0" { - { Format-Hex -Path $inputFile4 -Count 0 } | Should -Throw -ErrorId "ParameterArgumentValidationError,Microsoft.PowerShell.Commands.FormatHex" - } + It "Count should be > 0" { + { Format-Hex -Path $inputFile4 -Count 0 } | Should -Throw -ErrorId "ParameterArgumentValidationError,Microsoft.PowerShell.Commands.FormatHex" + } - It "Offset should be >= 0" { - { Format-Hex -Path $inputFile4 -Offset -1 } | Should -Throw -ErrorId "ParameterArgumentValidationError,Microsoft.PowerShell.Commands.FormatHex" - } + It "Offset should be >= 0" { + { Format-Hex -Path $inputFile4 -Offset -1 } | Should -Throw -ErrorId "ParameterArgumentValidationError,Microsoft.PowerShell.Commands.FormatHex" + } - It "Offset = 0" { + It "Offset = 0" { - $result = Format-Hex -Path $InputFile4 -Offset 0 + $result = Format-Hex -Path $InputFile4 -Offset 0 - $result | Should -Not -BeNullOrEmpty - $result.Count | Should -Be 3 - $result[0].ToString() | Should -MatchExactly "00000000000000000000 4E 6F 77 20 69 73 20 74 68 65 20 77 69 6E 74 65 Now is the winte" - $result[1].ToString() | Should -MatchExactly "00000000000000000010 72 20 6F 66 20 6F 75 72 20 64 69 73 63 6F 6E 74 r of our discont" - $result[2].ToString() | Should -MatchExactly "00000000000000000020 65 6E 74 ent " - } + $result | Should -Not -BeNullOrEmpty + $result.Count | Should -Be 3 + $result[0].ToString() | Should -MatchExactly "00000000000000000000 4E 6F 77 20 69 73 20 74 68 65 20 77 69 6E 74 65 Now is the winte" + $result[1].ToString() | Should -MatchExactly "00000000000000000010 72 20 6F 66 20 6F 75 72 20 64 69 73 63 6F 6E 74 r of our discont" + $result[2].ToString() | Should -MatchExactly "00000000000000000020 65 6E 74 ent " } } +} From 94d5c3d3065ee6ef907489370310eee3838a6bf6 Mon Sep 17 00:00:00 2001 From: Joel <32407840+vexx32@users.noreply.github.com> Date: Wed, 17 Jul 2019 12:49:45 -0400 Subject: [PATCH 06/28] :construction: WIP formatview for ByteCollection --- .../PowerShellCore_format_ps1xml.cs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index 70eaf63bc85..2741111ea0d 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -254,6 +254,10 @@ internal static IEnumerable GetFormatData() yield return new ExtendedTypeDefinition( "Microsoft.PowerShell.MarkdownRender.PSMarkdownOptionInfo", ViewsOf_Microsoft_PowerShell_MarkdownRender_MarkdownOptionInfo()); + + yield return new ExtendedTypeDefinition( + "Microsoft.PowerShell.Commands.ByteCollection", + ViewsOf_Microsoft_PowerShell_Commands_ByteCollection()); } private static IEnumerable ViewsOf_System_RuntimeType() @@ -1437,5 +1441,34 @@ private static IEnumerable ViewsOf_Microsoft_PowerShell_Ma .EndEntry() .EndList()); } + + private static IEnumerable ViewsOf_Microsoft_PowerShell_Commands_ByteCollection() + { + yield return new FormatViewDefinition("Microsoft.PowerShell.Commands.ByteCollection", + TableControl.Create() + .AddHeader(Alignment.Right, label: "Offset", width: 16) + .AddHeader(Alignment.Center, label: "Bytes\n00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F", width: 47) + .AddHeader(Alignment.Left, label: "Ascii", width: 16) + .StartRowDefinition() + .AddPropertyColumn("Offset64") + .AddScriptBlockColumn(@" + $sb = [System.Text.StringBuilder]::new() + foreach ($Byte in $_.Bytes) { + $sb.AppendFormat('{0:X2} ', $Byte) + } + + $sb.ToString().Trim() + ") + .AddScriptBlockColumn(@" + $sb = [System.Text.StringBuilder]::new() + foreach ($Byte in $_.Bytes) { + $sb.Append([char]$Byte) + } + + $sb.ToString() + ") + .EndRowDefinition() + .EndTable()); + } } } From f2a90a86349cbd9815e904d87e7e45b3e6623a40 Mon Sep 17 00:00:00 2001 From: Joel <32407840+vexx32@users.noreply.github.com> Date: Mon, 29 Jul 2019 12:45:18 -0400 Subject: [PATCH 07/28] :construction: add ByteCollection helper methods To enable easy reconstruction of the output --- .../commands/utility/UtilityCommon.cs | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs index 6e167f61825..95378239f85 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs @@ -206,6 +206,58 @@ private set /// public string Path { get; private set; } + /// + /// Gets the hexadecimal representation of the value. + /// + public string HexOffset { get => string.Format("{0:X16}", Offset64); } + + private const int BytesPerLine = 16; + + /// + /// Gets a space-delimited string of the in this + /// in hexadecimal format. + /// + public string HexBytes + { + get + { + StringBuilder line = new StringBuilder(BytesPerLine * 3); + + foreach (var currentByte in Bytes) + { + line.AppendFormat("{0:X2} ", currentByte); + } + + return line.ToString().Trim(); + } + } + + /// + /// Gets the ASCII string representation of the in this . + /// + /// + public string AsciiBytes + { + get + { + StringBuilder ascii = new StringBuilder(BytesPerLine); + + foreach (var currentByte in Bytes) + { + if ((currentByte >= 0x20) && (currentByte <= 0xFE)) + { + ascii.Append((char)currentByte); + } + else + { + ascii.Append('.'); + } + } + + return ascii.ToString(); + } + } + /// /// Displays the hexadecimal format of the bytes stored in the collection. /// From 3265993d1eb88ecb0c0bb84a503fbd3d9c2085f9 Mon Sep 17 00:00:00 2001 From: Joel <32407840+vexx32@users.noreply.github.com> Date: Mon, 29 Jul 2019 12:45:52 -0400 Subject: [PATCH 08/28] :art: Add format definition for ByteCollection TODO: Investigate why it's not taking effect --- .../PowerShellCore_format_ps1xml.cs | 20 +++---------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index 2741111ea0d..256753ff4ed 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -1450,23 +1450,9 @@ private static IEnumerable ViewsOf_Microsoft_PowerShell_Co .AddHeader(Alignment.Center, label: "Bytes\n00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F", width: 47) .AddHeader(Alignment.Left, label: "Ascii", width: 16) .StartRowDefinition() - .AddPropertyColumn("Offset64") - .AddScriptBlockColumn(@" - $sb = [System.Text.StringBuilder]::new() - foreach ($Byte in $_.Bytes) { - $sb.AppendFormat('{0:X2} ', $Byte) - } - - $sb.ToString().Trim() - ") - .AddScriptBlockColumn(@" - $sb = [System.Text.StringBuilder]::new() - foreach ($Byte in $_.Bytes) { - $sb.Append([char]$Byte) - } - - $sb.ToString() - ") + .AddPropertyColumn("HexOffset") + .AddPropertyColumn("HexBytes") + .AddPropertyColumn("AsciiBytes") .EndRowDefinition() .EndTable()); } From 045ebd6596b04adde0700d911511a539379724eb Mon Sep 17 00:00:00 2001 From: Joel <32407840+vexx32@users.noreply.github.com> Date: Tue, 30 Jul 2019 12:37:47 -0400 Subject: [PATCH 09/28] :wrench: Address Ilya's review comments Added better control character handling in new methods. Added explicit culture parameters for format calls. --- .../commands/utility/UtilityCommon.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs index 95378239f85..3c5dcca2653 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs @@ -209,7 +209,7 @@ private set /// /// Gets the hexadecimal representation of the value. /// - public string HexOffset { get => string.Format("{0:X16}", Offset64); } + public string HexOffset { get => string.Format(CultureInfo.CurrentCulture, "{0:X16}", Offset64); } private const int BytesPerLine = 16; @@ -225,7 +225,7 @@ public string HexBytes foreach (var currentByte in Bytes) { - line.AppendFormat("{0:X2} ", currentByte); + line.AppendFormat(CultureInfo.CurrentCulture, "{0:X2} ", currentByte); } return line.ToString().Trim(); @@ -244,13 +244,18 @@ public string AsciiBytes foreach (var currentByte in Bytes) { - if ((currentByte >= 0x20) && (currentByte <= 0xFE)) + var currentChar = (char)currentByte; + if (char.IsControl(currentChar)) + { + ascii.Append((char)0xFFFD); + } + else if (currentChar == 0x0) { - ascii.Append((char)currentByte); + ascii.Append(' '); } else { - ascii.Append('.'); + ascii.Append(currentChar); } } From 221250802090b74bad8fc6ece4f28afe1701e29e Mon Sep 17 00:00:00 2001 From: Joel <32407840+vexx32@users.noreply.github.com> Date: Thu, 3 Oct 2019 16:27:06 -0400 Subject: [PATCH 10/28] :art: Fix formatview issues --- .../PowerShellCore_format_ps1xml.cs | 35 ++----------------- 1 file changed, 2 insertions(+), 33 deletions(-) diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index 256753ff4ed..915c0c59533 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -18,22 +18,8 @@ internal static IEnumerable GetFormatData() .EndEntry() .EndControl(); - var ByteCollection_GroupHeader = CustomControl.Create() - .StartEntry() - .StartFrame() - .AddScriptBlockExpressionBinding(@" - $header = "" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F"" - if($_.Path) { $header = "" "" + [Microsoft.PowerShell.Commands.UtilityResources]::FormatHexPathPrefix + $_.Path + ""`r`n`r`n"" + $header } - - $header - ") - .EndFrame() - .EndEntry() - .EndControl(); - var sharedControls = new CustomControl[] { - AvailableModules_GroupingFormat, - ByteCollection_GroupHeader + AvailableModules_GroupingFormat }; yield return new ExtendedTypeDefinition( @@ -167,10 +153,6 @@ internal static IEnumerable GetFormatData() "System.Management.Automation.InformationRecord", ViewsOf_System_Management_Automation_InformationRecord()); - yield return new ExtendedTypeDefinition( - "Microsoft.PowerShell.Commands.ByteCollection", - ViewsOf_Microsoft_PowerShell_Commands_ByteCollection(sharedControls)); - yield return new ExtendedTypeDefinition( "System.Exception", ViewsOf_System_Exception()); @@ -888,19 +870,6 @@ private static IEnumerable ViewsOf_System_Management_Autom .EndControl()); } - private static IEnumerable ViewsOf_Microsoft_PowerShell_Commands_ByteCollection(CustomControl[] sharedControls) - { - yield return new FormatViewDefinition("ByteCollection", - CustomControl.Create() - .GroupByScriptBlock("if($_.Path) { $_.Path } else { $_.GetHashCode() }", customControl: sharedControls[1]) - .StartEntry() - .StartFrame() - .AddScriptBlockExpressionBinding(@"$_.ToString()") - .EndFrame() - .EndEntry() - .EndControl()); - } - private static IEnumerable ViewsOf_System_Exception() { yield return new FormatViewDefinition("Exception", @@ -1447,7 +1416,7 @@ private static IEnumerable ViewsOf_Microsoft_PowerShell_Co yield return new FormatViewDefinition("Microsoft.PowerShell.Commands.ByteCollection", TableControl.Create() .AddHeader(Alignment.Right, label: "Offset", width: 16) - .AddHeader(Alignment.Center, label: "Bytes\n00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F", width: 47) + .AddHeader(Alignment.Left, label: "Bytes\n00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F", width: 47) .AddHeader(Alignment.Left, label: "Ascii", width: 16) .StartRowDefinition() .AddPropertyColumn("HexOffset") From 3956925669b66f4361035e2e3a7ee9b9306de0a6 Mon Sep 17 00:00:00 2001 From: Joel <32407840+vexx32@users.noreply.github.com> Date: Mon, 7 Oct 2019 13:09:09 -0400 Subject: [PATCH 11/28] :construction: Output per-line objects --- .../FormatAndOutput/format-hex/Format-Hex.cs | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs index 76d70cde4c5..22874c04977 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs @@ -453,21 +453,25 @@ private byte[] ConvertToByteArray(object inputObject) #region Output /// - /// Outputs the hexadecimial representation of the input data. + /// Outputs the hexadecimal representation of the input data. /// - /// Bytes for the hexadecimial representation. + /// Bytes for the hexadecimal representation. /// File path. /// Offset in the file. private void WriteHexadecimal(Span inputBytes, string path, long offset) { - ByteCollection byteCollectionObject = new ByteCollection((ulong)offset, inputBytes.ToArray(), path); - WriteObject(byteCollectionObject); - } - - private void WriteHexadecimal(byte[] inputBytes, string path, long offset) - { - ByteCollection byteCollectionObject = new ByteCollection((ulong)offset, inputBytes, path); - WriteObject(byteCollectionObject); + var bytesPerObject = 16; + for (int index = 0; index < inputBytes.Length; index += bytesPerObject) + { + var count = inputBytes.Length - index < bytesPerObject + ? inputBytes.Length - index + : bytesPerObject; + var bytes = inputBytes.Slice(index, count); + WriteObject(new ByteCollection( + (ulong)index + (ulong)offset, + bytes.ToArray(), + path)); + } } #endregion From af5f72ba71b85a91f8f7b6f35d166b18120353fc Mon Sep 17 00:00:00 2001 From: Joel <32407840+vexx32@users.noreply.github.com> Date: Mon, 7 Oct 2019 17:07:16 -0400 Subject: [PATCH 12/28] :construction: refactor output formats and grouping --- .../FormatAndOutput/format-hex/Format-Hex.cs | 56 ++++++++++--------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs index 22874c04977..1870a9de342 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs @@ -27,7 +27,7 @@ public sealed class FormatHex : PSCmdlet /// For cases where a homogenous collection of bytes or other items are directly piped in, we collect all the /// bytes in a List<byte> and then output the formatted result all at once in EndProcessing(). /// - private List _totalInputBytes; + private List _totalInputBytes = new List(); /// /// If the input is determined to be heterogenous piped input or each input object turns out to be a complete @@ -122,8 +122,12 @@ protected override void EndProcessing() { if (_totalInputBytes != null) { - int offset = Math.Min(_totalInputBytes.Count, Offset < (long)int.MaxValue ? (int)Offset : int.MaxValue); - int count = Math.Min(_totalInputBytes.Count - offset, Count < (long)int.MaxValue ? (int)Count : int.MaxValue); + int offset = Math.Min(_totalInputBytes.Count, Offset < int.MaxValue + ? (int)Offset + : int.MaxValue); + int count = Math.Min(_totalInputBytes.Count - offset, Count < int.MaxValue + ? (int)Count + : int.MaxValue); if (offset != 0 || count != _totalInputBytes.Count) { WriteHexadecimal(_totalInputBytes.GetRange(offset, count).ToArray(), null, 0); @@ -278,7 +282,6 @@ private void ProcessObjectContent(PSObject inputObject) if (obj is System.IO.FileSystemInfo fsi) { - _isHeterogenousPipedInput = true; string[] path = { fsi.FullName }; List pathsToProcess = ResolvePaths(path, true); ProcessPath(pathsToProcess); @@ -290,28 +293,35 @@ private void ProcessObjectContent(PSObject inputObject) if (_isHeterogenousPipedInput) { // Output any previously stored bytes as individual units for consistency of output - if (_totalInputBytes != null) + if (_totalInputBytes.Count > 0) { - foreach (byte b in inputBytes) + int offset = Math.Min( + _totalInputBytes.Count, + Offset < int.MaxValue + ? (int)Offset + : int.MaxValue); + int count = Math.Min( + _totalInputBytes.Count - offset, + Count < int.MaxValue + ? (int)Count + : int.MaxValue); + if (offset != 0 || count != _totalInputBytes.Count) + { + WriteHexadecimal(_totalInputBytes.GetRange(offset, count).ToArray(), null, 0); + } + else { - WriteHexadecimal(new[] { b }, null, 0); + WriteHexadecimal(_totalInputBytes.ToArray(), null, 0); } - _totalInputBytes = null; + _totalInputBytes.Clear(); + // Reset flag so we can continue grouping similar types that come in + _isHeterogenousPipedInput = false; } if (inputBytes != null) { - int offset = Math.Min(inputBytes.Length, Offset < (long)int.MaxValue ? (int)Offset : int.MaxValue); - int count = Math.Min(inputBytes.Length - offset, Count < (long)int.MaxValue ? (int)Count : int.MaxValue); - if (offset != 0 || count != inputBytes.Length) - { - WriteHexadecimal(inputBytes.AsSpan().Slice(offset, count), null, 0); - } - else - { - WriteHexadecimal(inputBytes, null, 0); - } + _totalInputBytes.AddRange(inputBytes); } else { @@ -328,14 +338,7 @@ private void ProcessObjectContent(PSObject inputObject) { if (inputBytes != null) { - if (_totalInputBytes == null) - { - _totalInputBytes = new List(inputBytes); - } - else - { - _totalInputBytes.AddRange(inputBytes); - } + _totalInputBytes.AddRange(inputBytes); } else { @@ -361,7 +364,6 @@ private byte[] ConvertToByteArray(object inputObject) if (inputObject is string str) { _isHeterogenousPipedInput = true; - _lastInputType = typeof(string); return Encoding.GetBytes(str); } From 2ff99580834a96e36c2d1c20bcd5e3883eba132c Mon Sep 17 00:00:00 2001 From: Joel <32407840+vexx32@users.noreply.github.com> Date: Tue, 8 Oct 2019 16:54:24 -0400 Subject: [PATCH 13/28] :sparkles: Add SourceType to ByteCollection This is to allow us to group the output correctly. --- .../commands/utility/UtilityCommon.cs | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs index 3c5dcca2653..9650e5889be 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs @@ -114,7 +114,7 @@ public ByteCollection(UInt32 offset, byte[] value, string path) /// The Offset address to be used while displaying the bytes in the collection. /// Underlying bytes stored in the collection. /// Indicates the path of the file whose contents are wrapped in the ByteCollection. - public ByteCollection(UInt64 offset, byte[] value, string path) + public ByteCollection(ulong offset, byte[] value, string path) { if (value == null) { @@ -132,15 +132,9 @@ public ByteCollection(UInt64 offset, byte[] value, string path) /// The Offset address to be used while displaying the bytes in the collection. /// Underlying bytes stored in the collection. [Obsolete("The constructor is deprecated.", true)] - public ByteCollection(UInt32 offset, byte[] value) + public ByteCollection(uint offset, byte[] value) + : this((ulong)offset, value) { - if (value == null) - { - throw PSTraceSource.NewArgumentNullException("value"); - } - - Offset64 = offset; - Bytes = value; } /// @@ -148,7 +142,7 @@ public ByteCollection(UInt32 offset, byte[] value) /// /// The Offset address to be used while displaying the bytes in the collection. /// Underlying bytes stored in the collection. - public ByteCollection(UInt64 offset, byte[] value) + public ByteCollection(ulong offset, byte[] value) { if (value == null) { @@ -159,6 +153,18 @@ public ByteCollection(UInt64 offset, byte[] value) Bytes = value; } + /// + /// ByteCollection constructor. + /// + /// The Offset address to be used while displaying the bytes in the collection. + /// Underlying bytes stored in the collection. + /// Original object type of the converted input. + public ByteCollection(ulong offset, byte[] value, Type sourceType) + : this(offset, value) + { + SourceType = sourceType; + } + /// /// ByteCollection constructor. /// @@ -211,6 +217,12 @@ private set /// public string HexOffset { get => string.Format(CultureInfo.CurrentCulture, "{0:X16}", Offset64); } + + /// + /// Gets the type of the input objects used to create the . + /// + public Type SourceType { get; private set; } + private const int BytesPerLine = 16; /// From b2ff7c798a0a93b1c92215302b604b7f6ec9e3f4 Mon Sep 17 00:00:00 2001 From: Joel <32407840+vexx32@users.noreply.github.com> Date: Tue, 8 Oct 2019 16:59:38 -0400 Subject: [PATCH 14/28] :art: Add GroupBy to std format --- .../DefaultFormatters/PowerShellCore_format_ps1xml.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index 6fff9ffcbdb..e6f2597ec9a 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -1611,6 +1611,7 @@ private static IEnumerable ViewsOf_Microsoft_PowerShell_Co .AddPropertyColumn("HexBytes") .AddPropertyColumn("AsciiBytes") .EndRowDefinition() + .GroupByProperty("SourceType") .EndTable()); } } From 89656bd581819501873eca4974692d2a04e4985a Mon Sep 17 00:00:00 2001 From: Joel <32407840+vexx32@users.noreply.github.com> Date: Tue, 8 Oct 2019 17:21:11 -0400 Subject: [PATCH 15/28] :construction: :art: Finish grouping logic Pending interactive testing. --- .../FormatAndOutput/format-hex/Format-Hex.cs | 42 +++++++++++++++---- .../commands/utility/UtilityCommon.cs | 1 - .../PowerShellCore_format_ps1xml.cs | 13 +++++- 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs index 1870a9de342..1bc9336e9fb 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs @@ -130,11 +130,11 @@ protected override void EndProcessing() : int.MaxValue); if (offset != 0 || count != _totalInputBytes.Count) { - WriteHexadecimal(_totalInputBytes.GetRange(offset, count).ToArray(), null, 0); + WriteHexadecimal(_totalInputBytes.GetRange(offset, count).ToArray(), 0, _lastInputType); } else { - WriteHexadecimal(_totalInputBytes.ToArray(), null, 0); + WriteHexadecimal(_totalInputBytes.ToArray(), 0, _lastInputType); } } } @@ -280,8 +280,10 @@ private void ProcessObjectContent(PSObject inputObject) { object obj = inputObject.BaseObject; - if (obj is System.IO.FileSystemInfo fsi) + if (obj is FileSystemInfo fsi) { + // Clear last input type to prevent anything mistakenly grouping around files + _lastInputType = null; string[] path = { fsi.FullName }; List pathsToProcess = ResolvePaths(path, true); ProcessPath(pathsToProcess); @@ -303,19 +305,19 @@ private void ProcessObjectContent(PSObject inputObject) int count = Math.Min( _totalInputBytes.Count - offset, Count < int.MaxValue - ? (int)Count - : int.MaxValue); + ? (int)Count + : int.MaxValue); if (offset != 0 || count != _totalInputBytes.Count) { - WriteHexadecimal(_totalInputBytes.GetRange(offset, count).ToArray(), null, 0); + WriteHexadecimal(_totalInputBytes.GetRange(offset, count).ToArray(), 0, _lastInputType); } else { - WriteHexadecimal(_totalInputBytes.ToArray(), null, 0); + WriteHexadecimal(_totalInputBytes.ToArray(), 0, _lastInputType); } _totalInputBytes.Clear(); - // Reset flag so we can continue grouping similar types that come in + // Reset flags so we can properly grouping later objects _isHeterogenousPipedInput = false; } @@ -363,6 +365,8 @@ private byte[] ConvertToByteArray(object inputObject) { if (inputObject is string str) { + // Clear last input type to prevent anything mistakenly grouping with strings + _lastInputType = null; _isHeterogenousPipedInput = true; return Encoding.GetBytes(str); } @@ -476,6 +480,28 @@ private void WriteHexadecimal(Span inputBytes, string path, long offset) } } + /// + /// Outputs the hexadecimal representation of the input data. + /// + /// Bytes for the hexadecimal representation. + /// Offset in the file. + /// The type of the original input objects. + private void WriteHexadecimal(Span inputBytes, long offset, Type sourceType) + { + var bytesPerObject = 16; + for (int index = 0; index < inputBytes.Length; index += bytesPerObject) + { + var count = inputBytes.Length - index < bytesPerObject + ? inputBytes.Length - index + : bytesPerObject; + var bytes = inputBytes.Slice(index, count); + WriteObject(new ByteCollection( + (ulong)index + (ulong)offset, + bytes.ToArray(), + sourceType)); + } + } + #endregion } } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs index 9650e5889be..da396473361 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs @@ -217,7 +217,6 @@ private set /// public string HexOffset { get => string.Format(CultureInfo.CurrentCulture, "{0:X16}", Offset64); } - /// /// Gets the type of the input objects used to create the . /// diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index e6f2597ec9a..64f7b47190e 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -1611,7 +1611,18 @@ private static IEnumerable ViewsOf_Microsoft_PowerShell_Co .AddPropertyColumn("HexBytes") .AddPropertyColumn("AsciiBytes") .EndRowDefinition() - .GroupByProperty("SourceType") + .GroupByScriptBlock(@" + if ($_.SourceType) { + $_.SourceType + } + else if ($_.Path) + { + $_.Path + } + else { + $_.GetHashCode() + } + ") .EndTable()); } } From 3c539560c9360bb8d732a85da9dbba44e4c9b6eb Mon Sep 17 00:00:00 2001 From: vexx32 <32407840+vexx32@users.noreply.github.com> Date: Tue, 8 Oct 2019 18:50:59 -0400 Subject: [PATCH 16/28] :construction: Add backing properties No need to rebuild the strings every single time. --- .../commands/utility/UtilityCommon.cs | 52 ++++++++++++------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs index da396473361..6a3805b219c 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs @@ -224,6 +224,7 @@ private set private const int BytesPerLine = 16; + private string _hexBytes; /// /// Gets a space-delimited string of the in this /// in hexadecimal format. @@ -232,17 +233,23 @@ public string HexBytes { get { - StringBuilder line = new StringBuilder(BytesPerLine * 3); - - foreach (var currentByte in Bytes) + if (_hexBytes == null) { - line.AppendFormat(CultureInfo.CurrentCulture, "{0:X2} ", currentByte); + StringBuilder line = new StringBuilder(BytesPerLine * 3); + + foreach (var currentByte in Bytes) + { + line.AppendFormat(CultureInfo.CurrentCulture, "{0:X2} ", currentByte); + } + + _hexBytes = line.ToString().Trim(); } - return line.ToString().Trim(); + return _hexBytes; } } + private string _asciiBytes; /// /// Gets the ASCII string representation of the in this . /// @@ -251,26 +258,31 @@ public string AsciiBytes { get { - StringBuilder ascii = new StringBuilder(BytesPerLine); - - foreach (var currentByte in Bytes) + if (_asciiBytes == null) { - var currentChar = (char)currentByte; - if (char.IsControl(currentChar)) - { - ascii.Append((char)0xFFFD); - } - else if (currentChar == 0x0) - { - ascii.Append(' '); - } - else + StringBuilder ascii = new StringBuilder(BytesPerLine); + + foreach (var currentByte in Bytes) { - ascii.Append(currentChar); + var currentChar = (char)currentByte; + if (char.IsControl(currentChar)) + { + ascii.Append((char)0xFFFD); + } + else if (currentChar == 0x0) + { + ascii.Append(' '); + } + else + { + ascii.Append(currentChar); + } } + + _asciiBytes = ascii.ToString(); } - return ascii.ToString(); + return _asciiBytes; } } From 77caa05f175bf2b3f0bf976e836a51fe001dd72a Mon Sep 17 00:00:00 2001 From: vexx32 <32407840+vexx32@users.noreply.github.com> Date: Tue, 8 Oct 2019 18:52:18 -0400 Subject: [PATCH 17/28] :art: group bytecollection tables naturally - file tables are grouped by their Path - primitives are grouped by their SourceType - strings are "grouped" by the string contents (ungrouped) --- .../DefaultFormatters/PowerShellCore_format_ps1xml.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index 64f7b47190e..8e2bf22f301 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -1613,14 +1613,14 @@ private static IEnumerable ViewsOf_Microsoft_PowerShell_Co .EndRowDefinition() .GroupByScriptBlock(@" if ($_.SourceType) { - $_.SourceType + 'TypeName: {0}' -f $_.SourceType.FullName } else if ($_.Path) { - $_.Path + 'Path: {0}' -f $_.Path } else { - $_.GetHashCode() + 'String: {0}' -f $_.AsciiBytes } ") .EndTable()); From 3d8955cf94f2f5cc7edd54cc9398b8d7a0fa7fbc Mon Sep 17 00:00:00 2001 From: vexx32 <32407840+vexx32@users.noreply.github.com> Date: Tue, 8 Oct 2019 22:54:41 -0400 Subject: [PATCH 18/28] :construction: Overhaul ByteCollection - Refactor constructors to minimise duplicate code - Store hex and ascii strings during first get - Add Label property to facilitate grouping byte collections --- .../commands/utility/UtilityCommon.cs | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs index 6a3805b219c..de474b8ee22 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs @@ -101,11 +101,9 @@ public class ByteCollection /// Underlying bytes stored in the collection. /// Indicates the path of the file whose contents are wrapped in the ByteCollection. [Obsolete("The constructor is deprecated.", true)] - public ByteCollection(UInt32 offset, byte[] value, string path) + public ByteCollection(uint offset, byte[] value, string path) + : this((ulong)offset, value, path) { - Offset64 = offset; - Bytes = value; - Path = path; } /// @@ -124,6 +122,7 @@ public ByteCollection(ulong offset, byte[] value, string path) Offset64 = offset; Bytes = value; Path = path; + Label = path; } /// @@ -158,11 +157,14 @@ public ByteCollection(ulong offset, byte[] value) /// /// The Offset address to be used while displaying the bytes in the collection. /// Underlying bytes stored in the collection. - /// Original object type of the converted input. - public ByteCollection(ulong offset, byte[] value, Type sourceType) + /// + /// The label for the byte group. This may be a file path, a string value, or a + /// formatted identifying string for the group. + /// + public ByteCollection(ulong offset, string label, byte[] value) : this(offset, value) { - SourceType = sourceType; + Label = label; } /// @@ -220,11 +222,11 @@ private set /// /// Gets the type of the input objects used to create the . /// - public Type SourceType { get; private set; } + public string Label { get; private set; } private const int BytesPerLine = 16; - private string _hexBytes; + private string _hexBytes = string.Empty; /// /// Gets a space-delimited string of the in this /// in hexadecimal format. @@ -233,7 +235,7 @@ public string HexBytes { get { - if (_hexBytes == null) + if (_hexBytes == string.Empty) { StringBuilder line = new StringBuilder(BytesPerLine * 3); @@ -249,29 +251,29 @@ public string HexBytes } } - private string _asciiBytes; + private string _ascii = string.Empty; /// /// Gets the ASCII string representation of the in this . /// /// - public string AsciiBytes + public string Ascii { get { - if (_asciiBytes == null) + if (_ascii == string.Empty) { StringBuilder ascii = new StringBuilder(BytesPerLine); foreach (var currentByte in Bytes) { var currentChar = (char)currentByte; - if (char.IsControl(currentChar)) + if (currentChar == 0x0) { - ascii.Append((char)0xFFFD); + ascii.Append(' '); } - else if (currentChar == 0x0) + else if (char.IsControl(currentChar)) { - ascii.Append(' '); + ascii.Append((char)0xFFFD); } else { @@ -279,10 +281,10 @@ public string AsciiBytes } } - _asciiBytes = ascii.ToString(); + _ascii = ascii.ToString(); } - return _asciiBytes; + return _ascii; } } From 116f09685fb47db841cf81ab82f356534b9edd7e Mon Sep 17 00:00:00 2001 From: vexx32 <32407840+vexx32@users.noreply.github.com> Date: Tue, 8 Oct 2019 23:00:12 -0400 Subject: [PATCH 19/28] :art: Update bytecollection formatter --- .../PowerShellCore_format_ps1xml.cs | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index 8e2bf22f301..f83d6957b3b 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -1601,7 +1601,8 @@ private static IEnumerable ViewsOf_Microsoft_PowerShell_Ma private static IEnumerable ViewsOf_Microsoft_PowerShell_Commands_ByteCollection() { - yield return new FormatViewDefinition("Microsoft.PowerShell.Commands.ByteCollection", + yield return new FormatViewDefinition( + "Microsoft.PowerShell.Commands.ByteCollection", TableControl.Create() .AddHeader(Alignment.Right, label: "Offset", width: 16) .AddHeader(Alignment.Left, label: "Bytes\n00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F", width: 47) @@ -1609,20 +1610,9 @@ private static IEnumerable ViewsOf_Microsoft_PowerShell_Co .StartRowDefinition() .AddPropertyColumn("HexOffset") .AddPropertyColumn("HexBytes") - .AddPropertyColumn("AsciiBytes") + .AddPropertyColumn("Ascii") .EndRowDefinition() - .GroupByScriptBlock(@" - if ($_.SourceType) { - 'TypeName: {0}' -f $_.SourceType.FullName - } - else if ($_.Path) - { - 'Path: {0}' -f $_.Path - } - else { - 'String: {0}' -f $_.AsciiBytes - } - ") + .GroupByProperty("Label") .EndTable()); } } From d3563a46ff1df22a4e547331fbed01ee510866a6 Mon Sep 17 00:00:00 2001 From: vexx32 <32407840+vexx32@users.noreply.github.com> Date: Tue, 8 Oct 2019 23:01:58 -0400 Subject: [PATCH 20/28] :recycle: Refactor Format-Hex to permit grouping - New behaviour is to group primitives by default over the pipeline - Arrays are handled as a single unit and confined to their own grouping - Strings and FileInfo input is also handled on a unit basis --- .../FormatAndOutput/format-hex/Format-Hex.cs | 133 +++++++++++------- 1 file changed, 83 insertions(+), 50 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs index 1bc9336e9fb..d7d72aaa16c 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs @@ -27,7 +27,7 @@ public sealed class FormatHex : PSCmdlet /// For cases where a homogenous collection of bytes or other items are directly piped in, we collect all the /// bytes in a List<byte> and then output the formatted result all at once in EndProcessing(). /// - private List _totalInputBytes = new List(); + private List _inputBuffer = new List(); /// /// If the input is determined to be heterogenous piped input or each input object turns out to be a complete @@ -104,7 +104,7 @@ protected override void ProcessRecord() { if (string.Equals(this.ParameterSetName, "ByInputObject", StringComparison.OrdinalIgnoreCase)) { - ProcessObjectContent(InputObject); + ProcessInputObjects(InputObject); } else { @@ -120,21 +120,21 @@ protected override void ProcessRecord() /// protected override void EndProcessing() { - if (_totalInputBytes != null) + if (_inputBuffer.Count > 0) { - int offset = Math.Min(_totalInputBytes.Count, Offset < int.MaxValue + int offset = Math.Min(_inputBuffer.Count, Offset < int.MaxValue ? (int)Offset : int.MaxValue); - int count = Math.Min(_totalInputBytes.Count - offset, Count < int.MaxValue + int count = Math.Min(_inputBuffer.Count - offset, Count < int.MaxValue ? (int)Count : int.MaxValue); - if (offset != 0 || count != _totalInputBytes.Count) + if (offset != 0 || count != _inputBuffer.Count) { - WriteHexadecimal(_totalInputBytes.GetRange(offset, count).ToArray(), 0, _lastInputType); + WriteHexadecimal(_inputBuffer.GetRange(offset, count).ToArray(), 0, GetGroupLabel(_lastInputType)); } else { - WriteHexadecimal(_totalInputBytes.ToArray(), 0, _lastInputType); + WriteHexadecimal(_inputBuffer.ToArray(), 0, GetGroupLabel(_lastInputType)); } } } @@ -271,59 +271,96 @@ private void ProcessFileContent(string path) #region InputObjects + private void ProcessString(string originalString) + { + Span bytes = Encoding.GetBytes(originalString); + + int offset = Math.Min(bytes.Length, Offset < int.MaxValue ? (int)Offset : int.MaxValue); + int count = Math.Min(bytes.Length - offset, Count < int.MaxValue ? (int)Count : int.MaxValue); + + if (offset != 0 || count != bytes.Length) + { + WriteHexadecimal(bytes.Slice(offset, count), offset: 0, GetGroupLabel(typeof(string))); + } + else + { + WriteHexadecimal(bytes, offset: 0, label: GetGroupLabel(typeof(string))); + } + } + + private static readonly Random _idGenerator = new Random(); + private string GetGroupLabel(Type inputType) + { + return string.Format("{0} ({1}) <{2:X}>", inputType.Name, inputType.FullName, _idGenerator.Next()); + } + + private void FlushInputBuffer() + { + if (_inputBuffer.Count == 0) + { + return; + } + + int offset = Math.Min(_inputBuffer.Count, Offset < int.MaxValue ? (int)Offset : int.MaxValue); + int count = Math.Min(_inputBuffer.Count - offset, Count < int.MaxValue ? (int)Count : int.MaxValue); + + if (offset != 0 || count != _inputBuffer.Count) + { + WriteHexadecimal( + _inputBuffer.GetRange(offset, count).ToArray(), + label: GetGroupLabel(_lastInputType), + offset: 0); + } + else + { + WriteHexadecimal( + _inputBuffer.ToArray(), + label: GetGroupLabel(_lastInputType), + offset: 0); + } + + // Reset flags so we can go back to filling up the buffer when needed. + _lastInputType = null; + _isHeterogenousPipedInput = false; + _inputBuffer.Clear(); + } + /// /// Creates a byte array from the object passed to the cmdlet (based on type) and passes /// that array on to the WriteHexadecimal method to output. /// /// The pipeline input object being processed. - private void ProcessObjectContent(PSObject inputObject) + private void ProcessInputObjects(PSObject inputObject) { object obj = inputObject.BaseObject; if (obj is FileSystemInfo fsi) { - // Clear last input type to prevent anything mistakenly grouping around files - _lastInputType = null; + // Output already processed objects first, then process the file input. + FlushInputBuffer(); string[] path = { fsi.FullName }; List pathsToProcess = ResolvePaths(path, true); ProcessPath(pathsToProcess); return; } + if (obj is string str) + { + // Output already processed objects first, then process the string input. + FlushInputBuffer(); + ProcessString(str); + return; + } + byte[] inputBytes = ConvertToByteArray(obj); if (_isHeterogenousPipedInput) { - // Output any previously stored bytes as individual units for consistency of output - if (_totalInputBytes.Count > 0) - { - int offset = Math.Min( - _totalInputBytes.Count, - Offset < int.MaxValue - ? (int)Offset - : int.MaxValue); - int count = Math.Min( - _totalInputBytes.Count - offset, - Count < int.MaxValue - ? (int)Count - : int.MaxValue); - if (offset != 0 || count != _totalInputBytes.Count) - { - WriteHexadecimal(_totalInputBytes.GetRange(offset, count).ToArray(), 0, _lastInputType); - } - else - { - WriteHexadecimal(_totalInputBytes.ToArray(), 0, _lastInputType); - } - - _totalInputBytes.Clear(); - // Reset flags so we can properly grouping later objects - _isHeterogenousPipedInput = false; - } + FlushInputBuffer(); if (inputBytes != null) { - _totalInputBytes.AddRange(inputBytes); + _inputBuffer.AddRange(inputBytes); } else { @@ -340,7 +377,7 @@ private void ProcessObjectContent(PSObject inputObject) { if (inputBytes != null) { - _totalInputBytes.AddRange(inputBytes); + _inputBuffer.AddRange(inputBytes); } else { @@ -363,14 +400,6 @@ private void ProcessObjectContent(PSObject inputObject) /// Returns a byte array of the input values, or null if there is no available conversion path. private byte[] ConvertToByteArray(object inputObject) { - if (inputObject is string str) - { - // Clear last input type to prevent anything mistakenly grouping with strings - _lastInputType = null; - _isHeterogenousPipedInput = true; - return Encoding.GetBytes(str); - } - Type baseType = inputObject.GetType(); byte[] result = null; int elements = 1; @@ -380,6 +409,7 @@ private byte[] ConvertToByteArray(object inputObject) if (baseType.IsArray) { _isHeterogenousPipedInput = true; + _lastInputType = baseType; baseType = baseType.GetElementType(); dynamic dynamicObject = inputObject; elements = (int)dynamicObject.Length; @@ -485,8 +515,11 @@ private void WriteHexadecimal(Span inputBytes, string path, long offset) /// /// Bytes for the hexadecimal representation. /// Offset in the file. - /// The type of the original input objects. - private void WriteHexadecimal(Span inputBytes, long offset, Type sourceType) + /// + /// The label for the byte group. This may be a file path, a string value, or a + /// formatted identifying string for the group. + /// + private void WriteHexadecimal(Span inputBytes, long offset, string label) { var bytesPerObject = 16; for (int index = 0; index < inputBytes.Length; index += bytesPerObject) @@ -498,7 +531,7 @@ private void WriteHexadecimal(Span inputBytes, long offset, Type sourceTyp WriteObject(new ByteCollection( (ulong)index + (ulong)offset, bytes.ToArray(), - sourceType)); + label)); } } From 06793e79dbf98fc3e9f4e5c054d8521fc50bdd1e Mon Sep 17 00:00:00 2001 From: vexx32 <32407840+vexx32@users.noreply.github.com> Date: Tue, 8 Oct 2019 23:46:06 -0400 Subject: [PATCH 21/28] :bug: Fix issue with consecutive arrays over pipe --- .../commands/utility/FormatAndOutput/format-hex/Format-Hex.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs index d7d72aaa16c..c0029cd2cfe 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs @@ -408,9 +408,10 @@ private byte[] ConvertToByteArray(object inputObject) bool isEnum = false; if (baseType.IsArray) { - _isHeterogenousPipedInput = true; + FlushInputBuffer(); _lastInputType = baseType; baseType = baseType.GetElementType(); + _isHeterogenousPipedInput = true; dynamic dynamicObject = inputObject; elements = (int)dynamicObject.Length; isArray = true; From 643bdcf67bef07aae97ad231b2cdf4d38d787328 Mon Sep 17 00:00:00 2001 From: vexx32 <32407840+vexx32@users.noreply.github.com> Date: Wed, 9 Oct 2019 07:19:30 -0400 Subject: [PATCH 22/28] :recycle: Tidy up FormatHexCommand Consolidated duplicate code, renamed grouping flag appropriately. --- .../FormatAndOutput/format-hex/Format-Hex.cs | 138 ++++++++---------- 1 file changed, 60 insertions(+), 78 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs index c0029cd2cfe..1ba4a6bdf57 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs @@ -17,7 +17,7 @@ namespace Microsoft.PowerShell.Commands /// Displays the hexadecimal equivalent of the input data. /// [Cmdlet(VerbsCommon.Format, "Hex", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=526919")] - [OutputType(typeof(Microsoft.PowerShell.Commands.ByteCollection))] + [OutputType(typeof(ByteCollection))] [Alias("fhx")] public sealed class FormatHex : PSCmdlet { @@ -27,13 +27,14 @@ public sealed class FormatHex : PSCmdlet /// For cases where a homogenous collection of bytes or other items are directly piped in, we collect all the /// bytes in a List<byte> and then output the formatted result all at once in EndProcessing(). /// - private List _inputBuffer = new List(); + private readonly List _inputBuffer = new List(); /// - /// If the input is determined to be heterogenous piped input or each input object turns out to be a complete - /// array of items, we output each item as we receive it to avoid squashing output together in strange ways. + /// Expect to group s by default. When receiving input that should not be grouped, + /// e.g., arrays, strings, FileInfo objects, this flag will be disabled until the next groupable + /// is received over the pipeline. /// - private bool _isHeterogenousPipedInput; + private bool _groupInput = true; /// /// Keep track of prior input types to determine if we're given a heterogenous collection. @@ -102,14 +103,15 @@ public sealed class FormatHex : PSCmdlet /// protected override void ProcessRecord() { - if (string.Equals(this.ParameterSetName, "ByInputObject", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(ParameterSetName, "ByInputObject", StringComparison.OrdinalIgnoreCase)) { ProcessInputObjects(InputObject); } else { - List pathsToProcess = string.Equals(this.ParameterSetName, "LiteralPath", StringComparison.OrdinalIgnoreCase) ? - ResolvePaths(LiteralPath, true) : ResolvePaths(Path, false); + List pathsToProcess = string.Equals(ParameterSetName, "LiteralPath", StringComparison.OrdinalIgnoreCase) + ? ResolvePaths(LiteralPath, true) + : ResolvePaths(Path, false); ProcessPath(pathsToProcess); } @@ -122,12 +124,9 @@ protected override void EndProcessing() { if (_inputBuffer.Count > 0) { - int offset = Math.Min(_inputBuffer.Count, Offset < int.MaxValue - ? (int)Offset - : int.MaxValue); - int count = Math.Min(_inputBuffer.Count - offset, Count < int.MaxValue - ? (int)Count - : int.MaxValue); + int offset = Math.Min(_inputBuffer.Count, Offset < int.MaxValue ? (int)Offset : int.MaxValue); + int count = Math.Min(_inputBuffer.Count - offset, Count < int.MaxValue ? (int)Count : int.MaxValue); + if (offset != 0 || count != _inputBuffer.Count) { WriteHexadecimal(_inputBuffer.GetRange(offset, count).ToArray(), 0, GetGroupLabel(_lastInputType)); @@ -155,7 +154,6 @@ private List ResolvePaths(string[] path, bool literalPath) { List pathsToProcess = new List(); ProviderInfo provider = null; - PSDriveInfo drive = null; foreach (string currentPath in path) { @@ -163,7 +161,7 @@ private List ResolvePaths(string[] path, bool literalPath) if (literalPath) { - newPaths.Add(Context.SessionState.Path.GetUnresolvedProviderPathFromPSPath(currentPath, out provider, out drive)); + newPaths.Add(Context.SessionState.Path.GetUnresolvedProviderPathFromPSPath(currentPath, out provider, out _)); } else { @@ -224,28 +222,26 @@ private void ProcessFileContent(string path) try { - using (BinaryReader reader = new BinaryReader(File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))) - { - long offset = Offset; - int bytesRead = 0; - long count = 0; + using var reader = new BinaryReader(File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read)); + long offset = Offset; + int bytesRead = 0; + long count = 0; - reader.BaseStream.Position = Offset; + reader.BaseStream.Position = Offset; - while ((bytesRead = reader.Read(buffer)) > 0) + while ((bytesRead = reader.Read(buffer)) > 0) + { + count += bytesRead; + if (count > Count) { - count += bytesRead; - if (count > Count) - { - bytesRead -= (int)(count - Count); - WriteHexadecimal(buffer.Slice(0, bytesRead), path, offset); - break; - } - + bytesRead -= (int)(count - Count); WriteHexadecimal(buffer.Slice(0, bytesRead), path, offset); - - offset += bytesRead; + break; } + + WriteHexadecimal(buffer.Slice(0, bytesRead), path, offset); + + offset += bytesRead; } } catch (IOException fileException) @@ -259,11 +255,19 @@ private void ProcessFileContent(string path) } catch (NotSupportedException notSupportedException) { - WriteError(new ErrorRecord(notSupportedException, "FormatHexPathRefersToANonFileDevice", ErrorCategory.InvalidArgument, path)); + WriteError(new ErrorRecord( + notSupportedException, + "FormatHexPathRefersToANonFileDevice", + ErrorCategory.InvalidArgument, + path)); } catch (SecurityException securityException) { - WriteError(new ErrorRecord(securityException, "FormatHexUnauthorizedAccessError", ErrorCategory.PermissionDenied, path)); + WriteError(new ErrorRecord( + securityException, + "FormatHexUnauthorizedAccessError", + ErrorCategory.PermissionDenied, + path)); } } @@ -321,7 +325,7 @@ private void FlushInputBuffer() // Reset flags so we can go back to filling up the buffer when needed. _lastInputType = null; - _isHeterogenousPipedInput = false; + _groupInput = true; _inputBuffer.Clear(); } @@ -352,43 +356,26 @@ private void ProcessInputObjects(PSObject inputObject) return; } - byte[] inputBytes = ConvertToByteArray(obj); + byte[] inputBytes = ConvertToBytes(obj); - if (_isHeterogenousPipedInput) + if (!_groupInput) { FlushInputBuffer(); + } - if (inputBytes != null) - { - _inputBuffer.AddRange(inputBytes); - } - else - { - string errorMessage = StringUtil.Format(UtilityCommonStrings.FormatHexTypeNotSupported, obj.GetType()); - ErrorRecord errorRecord = new ErrorRecord( - new ArgumentException(errorMessage), - "FormatHexTypeNotSupported", - ErrorCategory.InvalidArgument, - obj.GetType()); - WriteError(errorRecord); - } + if (inputBytes != null) + { + _inputBuffer.AddRange(inputBytes); } else { - if (inputBytes != null) - { - _inputBuffer.AddRange(inputBytes); - } - else - { - string errorMessage = StringUtil.Format(UtilityCommonStrings.FormatHexTypeNotSupported, obj.GetType()); - ErrorRecord errorRecord = new ErrorRecord( - new ArgumentException(errorMessage), - "FormatHexTypeNotSupported", - ErrorCategory.InvalidArgument, - obj.GetType()); - WriteError(errorRecord); - } + string errorMessage = StringUtil.Format(UtilityCommonStrings.FormatHexTypeNotSupported, obj.GetType()); + ErrorRecord errorRecord = new ErrorRecord( + new ArgumentException(errorMessage), + "FormatHexTypeNotSupported", + ErrorCategory.InvalidArgument, + obj.GetType()); + WriteError(errorRecord); } } @@ -398,7 +385,7 @@ private void ProcessInputObjects(PSObject inputObject) /// /// The object to convert. /// Returns a byte array of the input values, or null if there is no available conversion path. - private byte[] ConvertToByteArray(object inputObject) + private byte[] ConvertToBytes(object inputObject) { Type baseType = inputObject.GetType(); byte[] result = null; @@ -410,8 +397,9 @@ private byte[] ConvertToByteArray(object inputObject) { FlushInputBuffer(); _lastInputType = baseType; + _groupInput = false; + baseType = baseType.GetElementType(); - _isHeterogenousPipedInput = true; dynamic dynamicObject = inputObject; elements = (int)dynamicObject.Length; isArray = true; @@ -425,11 +413,11 @@ private byte[] ConvertToByteArray(object inputObject) if (baseType.IsPrimitive && elements > 0) { - if (!_isHeterogenousPipedInput) + if (_groupInput) { if (_lastInputType != null && baseType != _lastInputType) { - _isHeterogenousPipedInput = true; + _groupInput = false; } _lastInputType = baseType; @@ -504,10 +492,7 @@ private void WriteHexadecimal(Span inputBytes, string path, long offset) ? inputBytes.Length - index : bytesPerObject; var bytes = inputBytes.Slice(index, count); - WriteObject(new ByteCollection( - (ulong)index + (ulong)offset, - bytes.ToArray(), - path)); + WriteObject(new ByteCollection((ulong)index + (ulong)offset, bytes.ToArray(), path)); } } @@ -529,10 +514,7 @@ private void WriteHexadecimal(Span inputBytes, long offset, string label) ? inputBytes.Length - index : bytesPerObject; var bytes = inputBytes.Slice(index, count); - WriteObject(new ByteCollection( - (ulong)index + (ulong)offset, - bytes.ToArray(), - label)); + WriteObject(new ByteCollection((ulong)index + (ulong)offset, bytes.ToArray(), label)); } } From 65f9cbe0fb4c989f3a08f55c42f7dc4d0a2600f4 Mon Sep 17 00:00:00 2001 From: vexx32 <32407840+vexx32@users.noreply.github.com> Date: Wed, 9 Oct 2019 07:35:08 -0400 Subject: [PATCH 23/28] :art: Update the .ToString() for ByteCollection Make it match what we're displaying in the tabular format --- .../commands/utility/UtilityCommon.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs index de474b8ee22..6f679fb20dd 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs @@ -295,7 +295,7 @@ public string Ascii public override string ToString() { const int BytesPerLine = 16; - const string LineFormat = "{0:X20} "; + const string LineFormat = "{0:X16} "; // '20 + 3' comes from format "{0:X20} ". // '20' comes from '[Uint64]::MaxValue.ToString().Length'. @@ -322,13 +322,18 @@ public override string ToString() // If the character is printable, add its ascii representation to // the right-hand side. Otherwise, add a dot to the right hand side. - if ((currentByte >= 0x20) && (currentByte <= 0xFE)) + var currentChar = (char)currentByte; + if (currentChar == 0x0) { - asciiEnd.Append((char)currentByte); + asciiEnd.Append(' '); + } + else if (char.IsControl(currentChar)) + { + asciiEnd.Append((char)0xFFFD); } else { - asciiEnd.Append('.'); + asciiEnd.Append(currentChar); } charCounter++; From 61722d2ea0d5160d04d71c00b38b0eef1172c68f Mon Sep 17 00:00:00 2001 From: vexx32 <32407840+vexx32@users.noreply.github.com> Date: Wed, 9 Oct 2019 08:31:42 -0400 Subject: [PATCH 24/28] :recycle: Cleanup ByteCollection --- .../commands/utility/UtilityCommon.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs index 6f679fb20dd..ee9aa173d08 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs @@ -297,9 +297,9 @@ public override string ToString() const int BytesPerLine = 16; const string LineFormat = "{0:X16} "; - // '20 + 3' comes from format "{0:X20} ". - // '20' comes from '[Uint64]::MaxValue.ToString().Length'. - StringBuilder nextLine = new StringBuilder(20 + 3 + (BytesPerLine * 3)); + // '16 + 3' comes from format "{0:X16} ". + // '16' comes from '[Uint64]::MaxValue.ToString("X").Length'. + StringBuilder nextLine = new StringBuilder(16 + 3 + (BytesPerLine * 3)); StringBuilder asciiEnd = new StringBuilder(BytesPerLine); // '+1' comes from 'result.Append(nextLine.ToString() + " " + asciiEnd.ToString());' below. @@ -307,10 +307,8 @@ public override string ToString() if (Bytes.Length > 0) { - Int64 charCounter = 0; + long charCounter = 0; - // ToString() in invoked thrice by the F&O for the same content. - // Hence making sure that Offset is not getting incremented thrice for the same bytes being displayed. var currentOffset = Offset64; nextLine.AppendFormat(CultureInfo.InvariantCulture, LineFormat, currentOffset); @@ -349,7 +347,7 @@ public override string ToString() nextLine.AppendFormat(CultureInfo.InvariantCulture, LineFormat, currentOffset); // Adding a newline to support long inputs strings flowing through InputObject parameterset. - if ((charCounter <= Bytes.Length) && string.IsNullOrEmpty(this.Path)) + if ((charCounter <= Bytes.Length) && string.IsNullOrEmpty(Path)) { result.AppendLine(); } From f78bfa9ceec483b8fd5e349e60e085a059f7c797 Mon Sep 17 00:00:00 2001 From: vexx32 <32407840+vexx32@users.noreply.github.com> Date: Wed, 9 Oct 2019 08:32:00 -0400 Subject: [PATCH 25/28] :white_check_mark: Finish updating tests --- .../Format-Hex.Tests.ps1 | 353 +++++++++++------- 1 file changed, 209 insertions(+), 144 deletions(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-Hex.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-Hex.Tests.ps1 index 912d2910f2c..875273acac0 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-Hex.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-Hex.Tests.ps1 @@ -19,14 +19,21 @@ Describe "FormatHex" -tags "CI" { $newline = [Environment]::Newline Setup -d FormatHexDataDir + $inputFile1 = New-Item -Path "$TestDrive/SourceFile-1.txt" $inputText1 = 'Hello World' + Set-Content -LiteralPath $inputFile1.FullName -Value $inputText1 -NoNewline + + $inputFile2 = New-Item -Path "$TestDrive/SourceFile-2.txt" $inputText2 = 'More text' + Set-Content -LiteralPath $inputFile2.FullName -Value $inputText2 -NoNewline + + $inputFile3 = New-Item -Path "$TestDrive/SourceFile literal [3].txt" $inputText3 = 'Literal path' + Set-Content -LiteralPath $inputFile3.FullName -Value $inputText3 -NoNewline + + $inputFile4 = New-Item -Path "$TestDrive/SourceFile-4.txt" $inputText4 = 'Now is the winter of our discontent' - $inputFile1 = Setup -f "FormatHexDataDir/SourceFile-1.txt" -content $inputText1 -pass - $inputFile2 = Setup -f "FormatHexDataDir/SourceFile-2.txt" -content $inputText2 -pass - $inputFile3 = Setup -f "FormatHexDataDir/SourceFile literal [3].txt" -content $inputText3 -pass - $inputFile4 = Setup -f "FormatHexDataDir/SourceFile-4.txt" -content $inputText4 -pass + Set-Content -LiteralPath $inputFile4.FullName -Value $inputText4 -NoNewline $certificateProvider = Get-ChildItem Cert:\CurrentUser\My\ -ErrorAction SilentlyContinue $thumbprint = $null @@ -90,37 +97,37 @@ public enum TestSByteEnum : sbyte { Name = "Can process int32[] type 'fhx -InputObject [int32[]](2032, 2033, 2034)'" InputObject = [int32[]](2032, 2033, 2034) Count = 1 - ExpectedResult = "00000000000000000000 F0 07 00 00 F1 07 00 00 F2 07 00 00 ð...ñ...ò..." + ExpectedResult = "0000000000000000 F0 07 00 00 F1 07 00 00 F2 07 00 00 ð...ñ...ò..." } @{ Name = "Can process Int64 type 'fhx -InputObject [Int64]9223372036854775807'" InputObject = [Int64]9223372036854775807 Count = 1 - ExpectedResult = "00000000000000000000 FF FF FF FF FF FF FF 7F ......." + ExpectedResult = "0000000000000000 FF FF FF FF FF FF FF 7F ÿÿÿÿÿÿÿ�" } @{ Name = "Can process Int64[] type 'fhx -InputObject [Int64[]](9223372036852,9223372036853)'" InputObject = [Int64[]](9223372036852, 9223372036853) Count = 1 - ExpectedResult = "00000000000000000000 F4 5A D0 7B 63 08 00 00 F5 5A D0 7B 63 08 00 00 ôZÐ{c...õZÐ{c..." + ExpectedResult = "0000000000000000 F4 5A D0 7B 63 08 00 00 F5 5A D0 7B 63 08 00 00 ôZÐ{c...õZÐ{c..." } @{ Name = "Can process string type 'fhx -InputObject hello world'" InputObject = "hello world" Count = 1 - ExpectedResult = "00000000000000000000 68 65 6C 6C 6F 20 77 6F 72 6C 64 hello world" + ExpectedResult = "0000000000000000 68 65 6C 6C 6F 20 77 6F 72 6C 64 hello world" } @{ Name = "Can process PS-native enum array '[TestEnum[]]('TestOne', 'TestTwo', 'TestThree', 'TestFour') | fhx'" InputObject = [TestEnum[]]('TestOne', 'TestTwo', 'TestThree', 'TestFour') Count = 1 - ExpectedResult = "00000000000000000000 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 ................" + ExpectedResult = "0000000000000000 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 ................" } @{ Name = "Can process C#-native sbyte enum array '[TestSByteEnum[]]('One', 'Two', 'Three', 'Four') | fhx'" InputObject = [TestSByteEnum[]]('One', 'Two', 'Three', 'Four') Count = 1 - ExpectedResult = "00000000000000000000 FF FE FD FC .þýü" + ExpectedResult = "0000000000000000 FF FE FD FC .þýü" } ) @@ -156,78 +163,85 @@ public enum TestSByteEnum : sbyte { Name = "Can process byte type '[byte]5 | fhx'" InputObject = [byte]5 Count = 1 - ExpectedResult = "00000000000000000000 05" + ExpectedResult = "0000000000000000 05" } @{ Name = "Can process byte[] type '[byte[]](1,2) | fhx'" InputObject = [byte[]](1, 2) Count = 1 - ExpectedResult = "00000000000000000000 01 02 .." + ExpectedResult = "0000000000000000 01 02 ��" } @{ Name = "Can process int type '7 | fhx'" InputObject = 7 Count = 1 - ExpectedResult = "00000000000000000000 07 00 00 00 ...." + ExpectedResult = "0000000000000000 07 00 00 00 � " } @{ Name = "Can process int[] type '[int[]](5,6) | fhx'" InputObject = [int[]](5, 6) Count = 1 - ExpectedResult = "00000000000000000000 05 00 00 00 06 00 00 00 ........" + ExpectedResult = "0000000000000000 05 00 00 00 06 00 00 00 � � " } @{ Name = "Can process int32 type '[int32]2032 | fhx'" InputObject = [int32]2032 Count = 1 - ExpectedResult = "00000000000000000000 F0 07 00 00 ð..." + ExpectedResult = "0000000000000000 F0 07 00 00 ð� " } @{ Name = "Can process int32[] type '[int32[]](2032, 2033) | fhx'" InputObject = [int32[]](2032, 2033) Count = 1 - ExpectedResult = "00000000000000000000 F0 07 00 00 F1 07 00 00 ð...ñ..." + ExpectedResult = "0000000000000000 F0 07 00 00 F1 07 00 00 ð� ñ� " } @{ Name = "Can process Int64 type '[Int64]9223372036854775807 | fhx'" InputObject = [Int64]9223372036854775807 Count = 1 - ExpectedResult = "00000000000000000000 FF FF FF FF FF FF FF 7F ......." + ExpectedResult = "0000000000000000 FF FF FF FF FF FF FF 7F ÿÿÿÿÿÿÿ�" } @{ Name = "Can process Int64[] type '[Int64[]](9223372036852,9223372036853) | fhx'" InputObject = [Int64[]](9223372036852, 9223372036853) Count = 1 - ExpectedResult = "00000000000000000000 F4 5A D0 7B 63 08 00 00 F5 5A D0 7B 63 08 00 00 ôZÐ{c...õZÐ{c..." + ExpectedResult = "0000000000000000 F4 5A D0 7B 63 08 00 00 F5 5A D0 7B 63 08 00 00 ôZÐ{c� õZÐ{c� " } @{ Name = "Can process string type 'hello world | fhx'" InputObject = "hello world" Count = 1 - ExpectedResult = "00000000000000000000 68 65 6C 6C 6F 20 77 6F 72 6C 64 hello world" + ExpectedResult = "0000000000000000 68 65 6C 6C 6F 20 77 6F 72 6C 64 hello world" + } + @{ + Name = "Can process string type amidst other types { 1, 2, 3, 'hello world' | fhx }" + InputObject = 1, 2, 3, "hello world" + Count = 2 + ExpectedResult = "0000000000000000 01 00 00 00 02 00 00 00 03 00 00 00 � � � " + ExpectedSecondResult = "0000000000000000 68 65 6C 6C 6F 20 77 6F 72 6C 64 hello world" } @{ Name = "Can process jagged array type '[sbyte[]](-15, 18, 21, -5), [byte[]](1, 2, 3, 4, 5, 6) | fhx'" InputObject = [sbyte[]](-15, 18, 21, -5), [byte[]](1, 2, 3, 4, 5, 6) Count = 2 - ExpectedResult = "00000000000000000000 F1 12 15 FB ñ..û" - ExpectedSecondResult = "00000000000000000000 01 02 03 04 05 06 ......" + ExpectedResult = "0000000000000000 F1 12 15 FB ñ��û" + ExpectedSecondResult = "0000000000000000 01 02 03 04 05 06 ������" } @{ Name = "Can process PS-native enum array '[TestEnum[]]('TestOne', 'TestTwo', 'TestThree', 'TestFour') | fhx'" InputObject = [TestEnum[]]('TestOne', 'TestTwo', 'TestThree', 'TestFour') Count = 1 - ExpectedResult = "00000000000000000000 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 ................" + ExpectedResult = "0000000000000000 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 � � � � " } @{ Name = "Can process C#-native sbyte enum array '[TestSByteEnum[]]('One', 'Two', 'Three', 'Four') | fhx'" InputObject = [TestSByteEnum[]]('One', 'Two', 'Three', 'Four') Count = 1 - ExpectedResult = "00000000000000000000 FF FE FD FC .þýü" + ExpectedResult = "0000000000000000 FF FE FD FC ÿþýü" } ) - It "" -Testcase $testCases { + It "" -TestCases $testCases { param ($Name, $InputObject, $Count, $ExpectedResult, $ExpectedSecondResult) @@ -241,6 +255,56 @@ public enum TestSByteEnum : sbyte { $result[1].ToString() | Should -MatchExactly $ExpectedSecondResult } } + + $heterogenousInputCases = @( + @{ + InputScript = { [sbyte[]](-15, 18, 21, -5), "hello", [byte[]](1..6), 1, 2, 3, 4 } + Count = 4 + ExpectedResults = @( + "0000000000000000 F1 12 15 FB ñ��û" + "0000000000000000 68 65 6C 6C 6F hello" + "0000000000000000 01 02 03 04 05 06 ������" + "0000000000000000 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 � � � � " + ) + ExpectedLabels = @( + "System.SByte[]" + "System.String" + "System.Byte" + "System.Int32" + ).ForEach{ [regex]::Escape($_) } -join '|' + } + @{ + InputScript = { $inputFile1, "Mountains are merely mountains", 1, 4, 5, 3, [ushort[]](1..10) } + Count = 6 + ExpectedResults = @( + "0000000000000000 48 65 6C 6C 6F 20 57 6F 72 6C 64 Hello World" + "0000000000000000 4D 6F 75 6E 74 61 69 6E 73 20 61 72 65 20 6D 65 Mountains are me" + "0000000000000010 72 65 6C 79 20 6D 6F 75 6E 74 61 69 6E 73 rely mountains" + "0000000000000000 01 00 00 00 04 00 00 00 05 00 00 00 03 00 00 00 � � � � " + "0000000000000000 01 00 02 00 03 00 04 00 05 00 06 00 07 00 08 00 � � � � � � � � " + "0000000000000010 09 00 0A 00 � � " + ) + ExpectedLabels = @( + $inputFile1.FullName + "System.String" + "System.Int32" + "System.UInt16[]" + ).ForEach{ [regex]::Escape($_) } -join '|' + } + ) + + It 'can process jagged input: ' -TestCases $heterogenousInputCases { + param($InputScript, $Count, $ExpectedResults, $ExpectedLabels) + + $Results = & $InputScript | Format-Hex + $Results | Should -HaveCount $Count + $ExpectedResults | Should -HaveCount $Count + + for ($Number = 0; $Number -lt $Results.Count; $Number++) { + $Results[$Number] | Should -MatchExactly $ExpectedResults[$Number] + $Results[$Number].Label | Should -MatchExactly $ExpectedLabels + } + } } Context "Path and LiteralPath Parameters" { @@ -330,37 +394,38 @@ public enum TestSByteEnum : sbyte { Name = "Can process ASCII encoding 'fhx -InputObject 'hello' -Encoding ASCII'" Encoding = "ASCII" Count = 1 - ExpectedResult = "00000000000000000000 68 65 6C 6C 6F hello" + ExpectedResult = "0000000000000000 68 65 6C 6C 6F hello" } @{ Name = "Can process BigEndianUnicode encoding 'fhx -InputObject 'hello' -Encoding BigEndianUnicode'" Encoding = "BigEndianUnicode" Count = 1 - ExpectedResult = "00000000000000000000 00 68 00 65 00 6C 00 6C 00 6F .h.e.l.l.o" + ExpectedResult = "0000000000000000 00 68 00 65 00 6C 00 6C 00 6F h e l l o" } @{ Name = "Can process Unicode encoding 'fhx -InputObject 'hello' -Encoding Unicode'" Encoding = "Unicode" Count = 1 - ExpectedResult = "00000000000000000000 68 00 65 00 6C 00 6C 00 6F 00 h.e.l.l.o." + ExpectedResult = "0000000000000000 68 00 65 00 6C 00 6C 00 6F 00 h e l l o " } @{ Name = "Can process UTF7 encoding 'fhx -InputObject 'hello' -Encoding UTF7'" Encoding = "UTF7" Count = 1 - ExpectedResult = "00000000000000000000 68 65 6C 6C 6F hello" + ExpectedResult = "0000000000000000 68 65 6C 6C 6F hello" } @{ Name = "Can process UTF8 encoding 'fhx -InputObject 'hello' -Encoding UTF8'" Encoding = "UTF8" Count = 1 - ExpectedResult = "00000000000000000000 68 65 6C 6C 6F hello" + ExpectedResult = "0000000000000000 68 65 6C 6C 6F hello" } @{ - Name = "Can process UTF32 encoding 'fhx -InputObject 'hello' -Encoding UTF32'" - Encoding = "UTF32" - Count = 1 - ExpectedResult = "00000000000000000000 68 00 00 00 65 00 00 00 6C 00 00 00 6C 00 00 00 h...e...l...l...$($newline)00000000000000000010 6F 00 00 00 o..." + Name = "Can process UTF32 encoding 'fhx -InputObject 'hello' -Encoding UTF32'" + Encoding = "UTF32" + Count = 2 + ExpectedResult = "0000000000000000 68 00 00 00 65 00 00 00 6C 00 00 00 6C 00 00 00 h e l l " + ExpectedSecondResult = "0000000000000010 6F 00 00 00 o " } ) @@ -408,155 +473,155 @@ public enum TestSByteEnum : sbyte { $result = Format-Hex -InputObject $InputObject -ErrorAction Stop } } | Should -Throw -ErrorId $ExpectedFullyQualifiedErrorId + } } -} -Context "Continues to Process Valid Paths" { + Context "Continues to Process Valid Paths" { - $testCases = @( - @{ - Name = "If given invalid path in array, continues to process valid paths 'fhx -Path `$invalidPath, `$inputFile1 -ErrorVariable e -ErrorAction SilentlyContinue'" - PathCase = $true - InvalidPath = "$($inputFile1.DirectoryName)\fakefile8888845345345348709.txt" - ExpectedFullyQualifiedErrorId = "FileNotFound,Microsoft.PowerShell.Commands.FormatHex" - } - @{ - Name = "If given a non FileSystem path in array, continues to process valid paths 'fhx -Path `$invalidPath, `$inputFile1 -ErrorVariable e -ErrorAction SilentlyContinue'" - PathCase = $true - InvalidPath = "Cert:\CurrentUser\My\$thumbprint" - ExpectedFullyQualifiedErrorId = "FormatHexOnlySupportsFileSystemPaths,Microsoft.PowerShell.Commands.FormatHex" - } - @{ - Name = "If given a non FileSystem path in array (with LiteralPath), continues to process valid paths 'fhx -Path `$invalidPath, `$inputFile1 -ErrorVariable e -ErrorAction SilentlyContinue'" - InvalidPath = "Cert:\CurrentUser\My\$thumbprint" - ExpectedFullyQualifiedErrorId = "FormatHexOnlySupportsFileSystemPaths,Microsoft.PowerShell.Commands.FormatHex" - } - ) + $testCases = @( + @{ + Name = "If given invalid path in array, continues to process valid paths 'fhx -Path `$invalidPath, `$inputFile1 -ErrorVariable e -ErrorAction SilentlyContinue'" + PathCase = $true + InvalidPath = "$($inputFile1.DirectoryName)\fakefile8888845345345348709.txt" + ExpectedFullyQualifiedErrorId = "FileNotFound,Microsoft.PowerShell.Commands.FormatHex" + } + @{ + Name = "If given a non FileSystem path in array, continues to process valid paths 'fhx -Path `$invalidPath, `$inputFile1 -ErrorVariable e -ErrorAction SilentlyContinue'" + PathCase = $true + InvalidPath = "Cert:\CurrentUser\My\$thumbprint" + ExpectedFullyQualifiedErrorId = "FormatHexOnlySupportsFileSystemPaths,Microsoft.PowerShell.Commands.FormatHex" + } + @{ + Name = "If given a non FileSystem path in array (with LiteralPath), continues to process valid paths 'fhx -Path `$invalidPath, `$inputFile1 -ErrorVariable e -ErrorAction SilentlyContinue'" + InvalidPath = "Cert:\CurrentUser\My\$thumbprint" + ExpectedFullyQualifiedErrorId = "FormatHexOnlySupportsFileSystemPaths,Microsoft.PowerShell.Commands.FormatHex" + } + ) - It "" -Skip:$skipTest -TestCase $testCases { + It "" -Skip:$skipTest -TestCase $testCases { - param ($Name, $PathCase, $InvalidPath, $ExpectedFullyQualifiedErrorId) + param ($Name, $PathCase, $InvalidPath, $ExpectedFullyQualifiedErrorId) - $output = $null - $errorThrown = $null + $output = $null + $errorThrown = $null - if ($PathCase) { - $output = Format-Hex -Path $InvalidPath, $inputFile1 -ErrorVariable errorThrown -ErrorAction SilentlyContinue - } - else { - # LiteralPath - $output = Format-Hex -LiteralPath $InvalidPath, $inputFile1 -ErrorVariable errorThrown -ErrorAction SilentlyContinue - } + if ($PathCase) { + $output = Format-Hex -Path $InvalidPath, $inputFile1 -ErrorVariable errorThrown -ErrorAction SilentlyContinue + } + else { + # LiteralPath + $output = Format-Hex -LiteralPath $InvalidPath, $inputFile1 -ErrorVariable errorThrown -ErrorAction SilentlyContinue + } - $errorThrown.FullyQualifiedErrorId | Should -MatchExactly $ExpectedFullyQualifiedErrorId + $errorThrown.FullyQualifiedErrorId | Should -MatchExactly $ExpectedFullyQualifiedErrorId - $output.Length | Should -Be 1 - $output[0].ToString() | Should -MatchExactly $inputText1 + $output.Length | Should -Be 1 + $output[0].ToString() | Should -MatchExactly $inputText1 + } } -} -Context "Cmdlet Functionality" { + Context "Cmdlet Functionality" { - It "Path is default Parameter Set 'fhx `$inputFile1'" { + It "Path is default Parameter Set 'fhx `$inputFile1'" { - $result = Format-Hex $inputFile1 + $result = Format-Hex $inputFile1 - $result | Should -Not -BeNullOrEmpty - , $result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' - $actualResult = $result.ToString() - $actualResult | Should -MatchExactly $inputText1 - } + $result | Should -Not -BeNullOrEmpty + , $result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' + $actualResult = $result.ToString() + $actualResult | Should -MatchExactly $inputText1 + } - It "Validate file input from Pipeline 'Get-ChildItem `$inputFile1 | Format-Hex'" { + It "Validate file input from Pipeline 'Get-ChildItem `$inputFile1 | Format-Hex'" { - $result = Get-ChildItem $inputFile1 | Format-Hex + $result = Get-ChildItem $inputFile1 | Format-Hex - $result | Should -Not -BeNullOrEmpty - , $result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' - $actualResult = $result.ToString() - $actualResult | Should -MatchExactly $inputText1 - } + $result | Should -Not -BeNullOrEmpty + , $result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' + $actualResult = $result.ToString() + $actualResult | Should -MatchExactly $inputText1 + } - It "Validate that streamed text does not have buffer underrun problems ''a' * 30 | Format-Hex'" { + It "Validate that streamed text does not have buffer underrun problems ''a' * 30 | Format-Hex'" { - $result = "a" * 30 | Format-Hex + $result = "a" * 30 | Format-Hex - $result | Should -Not -BeNullOrEmpty - , $result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' - $result.ToString() | Should -MatchExactly "00000000000000000000 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa$($newline)00000000000000000010 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaa " - } + $result | Should -Not -BeNullOrEmpty + $result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' + $result[0].ToString() | Should -MatchExactly "0000000000000000 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa" + $result[1].ToString() | Should -MatchExactly "0000000000000010 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaa " + } - It "Validate that files do not have buffer underrun problems 'Format-Hex -Path `$InputFile4'" { + It "Validate that files do not have buffer underrun problems 'Format-Hex -Path `$InputFile4'" { - $result = Format-Hex -Path $InputFile4 + $result = Format-Hex -Path $InputFile4 - $result | Should -Not -BeNullOrEmpty - $result.Count | Should -Be 3 - $result[0].ToString() | Should -MatchExactly "00000000000000000000 4E 6F 77 20 69 73 20 74 68 65 20 77 69 6E 74 65 Now is the winte" - $result[1].ToString() | Should -MatchExactly "00000000000000000010 72 20 6F 66 20 6F 75 72 20 64 69 73 63 6F 6E 74 r of our discont" - $result[2].ToString() | Should -MatchExactly "00000000000000000020 65 6E 74 ent " + $result | Should -Not -BeNullOrEmpty + $result.Count | Should -Be 3 + $result[0].ToString() | Should -MatchExactly "0000000000000000 4E 6F 77 20 69 73 20 74 68 65 20 77 69 6E 74 65 Now is the winte" + $result[1].ToString() | Should -MatchExactly "0000000000000010 72 20 6F 66 20 6F 75 72 20 64 69 73 63 6F 6E 74 r of our discont" + $result[2].ToString() | Should -MatchExactly "0000000000000020 65 6E 74 ent " + } } -} -Context "Count and Offset parameters" { - It "Count = length" { + Context "Count and Offset parameters" { + It "Count = length" { - $result = Format-Hex -Path $InputFile4 -Count $inputText4.Length + $result = Format-Hex -Path $InputFile4 -Count $inputText4.Length - $result | Should -Not -BeNullOrEmpty - $result.Count | Should -Be 3 - $result[0].ToString() | Should -MatchExactly "00000000000000000000 4E 6F 77 20 69 73 20 74 68 65 20 77 69 6E 74 65 Now is the winte" - $result[1].ToString() | Should -MatchExactly "00000000000000000010 72 20 6F 66 20 6F 75 72 20 64 69 73 63 6F 6E 74 r of our discont" - $result[2].ToString() | Should -MatchExactly "00000000000000000020 65 6E 74 ent " - } - - It "Count = 1" { - $result = Format-Hex -Path $inputFile4 -Count 1 - $result.ToString() | Should -MatchExactly "00000000000000000000 4E N " - } + $result | Should -Not -BeNullOrEmpty + $result.Count | Should -Be 3 + $result[0].ToString() | Should -MatchExactly "0000000000000000 4E 6F 77 20 69 73 20 74 68 65 20 77 69 6E 74 65 Now is the winte" + $result[1].ToString() | Should -MatchExactly "0000000000000010 72 20 6F 66 20 6F 75 72 20 64 69 73 63 6F 6E 74 r of our discont" + $result[2].ToString() | Should -MatchExactly "0000000000000020 65 6E 74 ent " + } - It "Offset = length" { + It "Count = 1" { + $result = Format-Hex -Path $inputFile4 -Count 1 + $result.ToString() | Should -MatchExactly "0000000000000000 4E N " + } - $result = Format-Hex -Path $InputFile4 -Offset $inputText4.Length - $result | Should -BeNullOrEmpty + It "Offset = length" { + $result = Format-Hex -Path $InputFile4 -Offset $inputText4.Length + $result | Should -BeNullOrEmpty - $result = Format-Hex -InputObject $inputText4 -Offset $inputText4.Length - $result.Bytes | Should -HaveCount 0 - } + $result = Format-Hex -InputObject $inputText4 -Offset $inputText4.Length + $result | Should -BeNullOrEmpty + } - It "Offset = 1" { + It "Offset = 1" { - $result = Format-Hex -Path $InputFile4 -Offset 1 + $result = Format-Hex -Path $InputFile4 -Offset 1 - $result | Should -Not -BeNullOrEmpty - $result.Count | Should -Be 3 - $result[0].ToString() | Should -MatchExactly "00000000000000000001 6F 77 20 69 73 20 74 68 65 20 77 69 6E 74 65 72 ow is the winter" - $result[1].ToString() | Should -MatchExactly "00000000000000000011 20 6F 66 20 6F 75 72 20 64 69 73 63 6F 6E 74 65 of our disconte" - $result[2].ToString() | Should -MatchExactly "00000000000000000021 6E 74 nt " - } + $result | Should -Not -BeNullOrEmpty + $result.Count | Should -Be 3 + $result[0].ToString() | Should -MatchExactly "0000000000000001 6F 77 20 69 73 20 74 68 65 20 77 69 6E 74 65 72 ow is the winter" + $result[1].ToString() | Should -MatchExactly "0000000000000011 20 6F 66 20 6F 75 72 20 64 69 73 63 6F 6E 74 65 of our disconte" + $result[2].ToString() | Should -MatchExactly "0000000000000021 6E 74 nt " + } - It "Count = 1 and Offset = 1" { - $result = Format-Hex -Path $inputFile4 -Count 1 -Offset 1 - $result.ToString() | Should -MatchExactly "00000000000000000001 6F o " - } + It "Count = 1 and Offset = 1" { + $result = Format-Hex -Path $inputFile4 -Count 1 -Offset 1 + $result.ToString() | Should -MatchExactly "0000000000000001 6F o " + } - It "Count should be > 0" { - { Format-Hex -Path $inputFile4 -Count 0 } | Should -Throw -ErrorId "ParameterArgumentValidationError,Microsoft.PowerShell.Commands.FormatHex" - } + It "Count should be > 0" { + { Format-Hex -Path $inputFile4 -Count 0 } | Should -Throw -ErrorId "ParameterArgumentValidationError,Microsoft.PowerShell.Commands.FormatHex" + } - It "Offset should be >= 0" { - { Format-Hex -Path $inputFile4 -Offset -1 } | Should -Throw -ErrorId "ParameterArgumentValidationError,Microsoft.PowerShell.Commands.FormatHex" - } + It "Offset should be >= 0" { + { Format-Hex -Path $inputFile4 -Offset -1 } | Should -Throw -ErrorId "ParameterArgumentValidationError,Microsoft.PowerShell.Commands.FormatHex" + } - It "Offset = 0" { + It "Offset = 0" { - $result = Format-Hex -Path $InputFile4 -Offset 0 + $result = Format-Hex -Path $InputFile4 -Offset 0 - $result | Should -Not -BeNullOrEmpty - $result.Count | Should -Be 3 - $result[0].ToString() | Should -MatchExactly "00000000000000000000 4E 6F 77 20 69 73 20 74 68 65 20 77 69 6E 74 65 Now is the winte" - $result[1].ToString() | Should -MatchExactly "00000000000000000010 72 20 6F 66 20 6F 75 72 20 64 69 73 63 6F 6E 74 r of our discont" - $result[2].ToString() | Should -MatchExactly "00000000000000000020 65 6E 74 ent " + $result | Should -Not -BeNullOrEmpty + $result.Count | Should -Be 3 + $result[0].ToString() | Should -MatchExactly "0000000000000000 4E 6F 77 20 69 73 20 74 68 65 20 77 69 6E 74 65 Now is the winte" + $result[1].ToString() | Should -MatchExactly "0000000000000010 72 20 6F 66 20 6F 75 72 20 64 69 73 63 6F 6E 74 r of our discont" + $result[2].ToString() | Should -MatchExactly "0000000000000020 65 6E 74 ent " + } } } -} From e12caad8c8fe2cd2ebc365e96e49ae4ff79d9c9a Mon Sep 17 00:00:00 2001 From: vexx32 <32407840+vexx32@users.noreply.github.com> Date: Wed, 9 Oct 2019 21:22:58 -0400 Subject: [PATCH 26/28] :art: Set fixed width for the random ID --- .../commands/utility/FormatAndOutput/format-hex/Format-Hex.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs index 1ba4a6bdf57..25c1a94b05c 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs @@ -295,7 +295,7 @@ private void ProcessString(string originalString) private static readonly Random _idGenerator = new Random(); private string GetGroupLabel(Type inputType) { - return string.Format("{0} ({1}) <{2:X}>", inputType.Name, inputType.FullName, _idGenerator.Next()); + return string.Format("{0} ({1}) <{2:X8}>", inputType.Name, inputType.FullName, _idGenerator.Next()); } private void FlushInputBuffer() From feb5d57666f35767edb24085c2a6dc36441501f3 Mon Sep 17 00:00:00 2001 From: vexx32 <32407840+vexx32@users.noreply.github.com> Date: Thu, 10 Oct 2019 00:05:43 -0400 Subject: [PATCH 27/28] :art: Codacy/CodeFactor fixes --- .../FormatAndOutput/format-hex/Format-Hex.cs | 6 ++---- .../commands/utility/UtilityCommon.cs | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs index 25c1a94b05c..1ec136f0923 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs @@ -293,10 +293,8 @@ private void ProcessString(string originalString) } private static readonly Random _idGenerator = new Random(); - private string GetGroupLabel(Type inputType) - { - return string.Format("{0} ({1}) <{2:X8}>", inputType.Name, inputType.FullName, _idGenerator.Next()); - } + private static string GetGroupLabel(Type inputType) + => string.Format("{0} ({1}) <{2:X8}>", inputType.Name, inputType.FullName, _idGenerator.Next()); private void FlushInputBuffer() { diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs index ee9aa173d08..f3c9d86a8e9 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs @@ -95,7 +95,7 @@ public static class UtilityResources public class ByteCollection { /// - /// ByteCollection constructor. + /// Initializes a new instance of the class. /// /// The Offset address to be used while displaying the bytes in the collection. /// Underlying bytes stored in the collection. @@ -107,7 +107,7 @@ public ByteCollection(uint offset, byte[] value, string path) } /// - /// Initializes a new instance of ByteCollection. + /// Initializes a new instance of the class. /// /// The Offset address to be used while displaying the bytes in the collection. /// Underlying bytes stored in the collection. @@ -126,7 +126,7 @@ public ByteCollection(ulong offset, byte[] value, string path) } /// - /// ByteCollection constructor. + /// Initializes a new instance of the class. /// /// The Offset address to be used while displaying the bytes in the collection. /// Underlying bytes stored in the collection. @@ -137,7 +137,7 @@ public ByteCollection(uint offset, byte[] value) } /// - /// ByteCollection constructor. + /// Initializes a new instance of the class. /// /// The Offset address to be used while displaying the bytes in the collection. /// Underlying bytes stored in the collection. @@ -153,14 +153,13 @@ public ByteCollection(ulong offset, byte[] value) } /// - /// ByteCollection constructor. + /// Initializes a new instance of the class. /// /// The Offset address to be used while displaying the bytes in the collection. - /// Underlying bytes stored in the collection. /// - /// The label for the byte group. This may be a file path, a string value, or a - /// formatted identifying string for the group. + /// The label for the byte group. This may be a file path or a formatted identifying string for the group. /// + /// Underlying bytes stored in the collection. public ByteCollection(ulong offset, string label, byte[] value) : this(offset, value) { @@ -168,7 +167,7 @@ public ByteCollection(ulong offset, string label, byte[] value) } /// - /// ByteCollection constructor. + /// Initializes a new instance of the class. /// /// Underlying bytes stored in the collection. public ByteCollection(byte[] value) @@ -227,6 +226,7 @@ private set private const int BytesPerLine = 16; private string _hexBytes = string.Empty; + /// /// Gets a space-delimited string of the in this /// in hexadecimal format. @@ -252,6 +252,7 @@ public string HexBytes } private string _ascii = string.Empty; + /// /// Gets the ASCII string representation of the in this . /// From a2ae1684bd8e523fb3c2e17d273d75b16ced8059 Mon Sep 17 00:00:00 2001 From: vexx32 <32407840+vexx32@users.noreply.github.com> Date: Sat, 12 Oct 2019 00:45:56 -0400 Subject: [PATCH 28/28] :art: Use consistent param naming --- .../FormatAndOutput/format-hex/Format-Hex.cs | 27 +++++-------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs index 1ec136f0923..810534a9ffd 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs @@ -122,20 +122,7 @@ protected override void ProcessRecord() /// protected override void EndProcessing() { - if (_inputBuffer.Count > 0) - { - int offset = Math.Min(_inputBuffer.Count, Offset < int.MaxValue ? (int)Offset : int.MaxValue); - int count = Math.Min(_inputBuffer.Count - offset, Count < int.MaxValue ? (int)Count : int.MaxValue); - - if (offset != 0 || count != _inputBuffer.Count) - { - WriteHexadecimal(_inputBuffer.GetRange(offset, count).ToArray(), 0, GetGroupLabel(_lastInputType)); - } - else - { - WriteHexadecimal(_inputBuffer.ToArray(), 0, GetGroupLabel(_lastInputType)); - } - } + FlushInputBuffer(); } #endregion @@ -284,7 +271,7 @@ private void ProcessString(string originalString) if (offset != 0 || count != bytes.Length) { - WriteHexadecimal(bytes.Slice(offset, count), offset: 0, GetGroupLabel(typeof(string))); + WriteHexadecimal(bytes.Slice(offset, count), offset: 0, label: GetGroupLabel(typeof(string))); } else { @@ -310,15 +297,15 @@ private void FlushInputBuffer() { WriteHexadecimal( _inputBuffer.GetRange(offset, count).ToArray(), - label: GetGroupLabel(_lastInputType), - offset: 0); + offset: 0, + label: GetGroupLabel(_lastInputType)); } else { WriteHexadecimal( _inputBuffer.ToArray(), - label: GetGroupLabel(_lastInputType), - offset: 0); + offset: 0, + label: GetGroupLabel(_lastInputType)); } // Reset flags so we can go back to filling up the buffer when needed. @@ -512,7 +499,7 @@ private void WriteHexadecimal(Span inputBytes, long offset, string label) ? inputBytes.Length - index : bytesPerObject; var bytes = inputBytes.Slice(index, count); - WriteObject(new ByteCollection((ulong)index + (ulong)offset, bytes.ToArray(), label)); + WriteObject(new ByteCollection((ulong)index + (ulong)offset, label, bytes.ToArray())); } }