From 82138c239a81d896e834b0b9ee8a75f4dad06b54 Mon Sep 17 00:00:00 2001 From: iSazonov Date: Tue, 25 Sep 2018 09:25:33 +0500 Subject: [PATCH 1/6] Add Count and Offset parameters in Format-Hex --- .../FormatAndOutput/format-hex/Format-Hex.cs | 73 ++++++----- .../commands/utility/UtilityCommon.cs | 124 ++++++++++++++---- .../PowerShellCore_format_ps1xml.cs | 4 +- .../FormatHex.Tests.ps1 | 56 ++++---- 4 files changed, 168 insertions(+), 89 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 3f48a9bd119..2d634cf6454 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 @@ -54,6 +54,20 @@ public sealed class FormatHex : PSCmdlet [ValidateNotNullOrEmpty] public Encoding Encoding { get; set; } = ClrFacade.GetDefaultEncoding(); + /// + /// Gets and sets count of bytes to read from the input stream from. + /// + [Parameter()] + [ValidateRange(ValidateRangeKind.Positive)] + public Int64 Count { get; set; } = Int64.MaxValue; + + /// + /// Gets and sets offset of bytes to start reading the input stream from. + /// + [Parameter()] + [ValidateRange(ValidateRangeKind.NonNegative)] + public Int64 Offset { get; set; } + /// /// This parameter is no-op. /// @@ -162,37 +176,31 @@ private void ProcessPath(List pathsToProcess) /// private void ProcessFileContent(string path) { - byte[] buffer = new byte[BUFFERSIZE]; + Span buffer = stackalloc byte[BUFFERSIZE]; try { - using (BinaryReader reader = new BinaryReader(File.Open(path, FileMode.Open, FileAccess.Read))) + using (BinaryReader reader = new BinaryReader(File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))) { - UInt32 offset = 0; + Int64 offset = Offset; Int32 bytesRead = 0; + Int64 count = 0; - while ((bytesRead = reader.Read(buffer, 0, BUFFERSIZE)) > 0) + reader.BaseStream.Position = Offset; + + while ((bytesRead = reader.Read(buffer)) > 0) { - if (bytesRead == BUFFERSIZE) + count += bytesRead; + if (count > Count) { - // We are reusing the same buffer so if we save the output to a variable, the variable - // will just contain multiple references to the same buffer memory space (containing only the - // last bytes of the file read). Copying the buffer allows us to pass the values on without - // overwriting previous values. - byte[] copyOfBuffer = new byte[16]; - Array.Copy(buffer, 0, copyOfBuffer, 0, bytesRead); - ConvertToHexidecimal(copyOfBuffer, path, offset); + bytesRead -= (int)(count - Count); + ConvertToHexidecimal(buffer.Slice(0, bytesRead), path, offset); + break; } - else - { - // Handle the case of a partial (and probably last) buffer. Copies the bytes read into a new, - // shorter array so we do not have the extra bytes from the previous pass through at the end. - byte[] remainingBytes = new byte[bytesRead]; - Array.Copy(buffer, 0, remainingBytes, 0, bytesRead); - ConvertToHexidecimal(remainingBytes, path, offset); - } - // Update offset value. - offset += (UInt32)bytesRead; + + ConvertToHexidecimal(buffer.Slice(0, bytesRead), path, offset); + + offset += bytesRead; } } } @@ -299,7 +307,9 @@ private void ProcessObjectContent(PSObject inputObject) if (inputBytes != null) { - ConvertToHexidecimal(inputBytes, 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); + ConvertToHexidecimal(inputBytes.AsSpan().Slice(offset, count), null, 0); } } @@ -308,18 +318,15 @@ private void ProcessObjectContent(PSObject inputObject) #region Output /// - /// Outputs the hexadecimial representaion of the of the input data. + /// Outputs the hexadecimial representaion of the input data. /// - /// - /// - /// - private void ConvertToHexidecimal(byte[] inputBytes, string path, UInt32 offset) + /// Bytes for the hexadecimial representaion. + /// File path. + /// Offset in the file. + private void ConvertToHexidecimal(Span inputBytes, string path, Int64 offset) { - if (inputBytes != null) - { - ByteCollection byteCollectionObject = new ByteCollection(offset, inputBytes, path); - WriteObject(byteCollectionObject); - } + ByteCollection byteCollectionObject = new ByteCollection((UInt64)offset, inputBytes.ToArray(), path); + WriteObject(byteCollectionObject); } #endregion diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs index ffa96f4a0a8..a47a2669810 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs @@ -99,12 +99,12 @@ public class ByteCollection /// 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. + [Obsolete("The constructor is deprecated.", true)] public ByteCollection(UInt32 offset, Byte[] value, string path) { - this.Offset = offset; - _initialOffSet = offset; - this.Bytes = value; - this.Path = path; + Offset64 = offset; + Bytes = value; + Path = path; } /// @@ -112,11 +112,50 @@ 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) + { + if (value == null) + { + throw PSTraceSource.NewArgumentNullException("value"); + } + + Offset64 = offset; + Bytes = value; + Path = path; + } + + /// + /// ByteCollection constructor. + /// + /// 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) { - this.Offset = offset; - _initialOffSet = offset; - this.Bytes = value; + if (value == null) + { + throw PSTraceSource.NewArgumentNullException("value"); + } + + Offset64 = offset; + Bytes = value; + } + + /// + /// ByteCollection constructor. + /// + /// 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) + { + if (value == null) + { + throw PSTraceSource.NewArgumentNullException("value"); + } + + Offset64 = offset; + Bytes = value; } /// @@ -125,23 +164,44 @@ public ByteCollection(UInt32 offset, Byte[] value) /// Underlying bytes stored in the collection. public ByteCollection(Byte[] value) { - this.Bytes = value; + if (value == null) + { + throw PSTraceSource.NewArgumentNullException("value"); + } + + Bytes = value; + } + + /// + /// Gets the Offset address to be used while displaying the bytes in the collection. + /// + [Obsolete("The property is deprecated, please use Offset64 instead.", true)] + public UInt32 Offset + { + get + { + return (UInt32)Offset64; + } + + private set + { + Offset64 = value; + } } /// - /// The Offset address to be used while displaying the bytes in the collection. + /// Gets the Offset address to be used while displaying the bytes in the collection. /// - public UInt32 Offset { get; private set; } - private UInt32 _initialOffSet = 0; + public UInt64 Offset64 { get; private set; } /// - /// Underlying bytes stored in the collection. + /// Gets underlying bytes stored in the collection. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public Byte[] Bytes { get; private set; } /// - /// Indicates the path of the file whose contents are wrapped in the ByteCollection. + /// Gets the path of the file whose contents are wrapped in the ByteCollection. /// public string Path { get; private set; } @@ -151,19 +211,27 @@ public ByteCollection(Byte[] value) /// public override string ToString() { - StringBuilder result = new StringBuilder(); - StringBuilder nextLine = new StringBuilder(); - StringBuilder asciiEnd = new StringBuilder(); + const int BytesPerLine = 16; + const string LineFormat = "{0:X20} "; + + // '20 + 3' comes from format "{0:X20} ". + // '20' comes from '[Uint64]::MaxValue.ToString().Length'. + StringBuilder nextLine = new StringBuilder(20 + 3 + BytesPerLine*3); + StringBuilder asciiEnd = new StringBuilder(BytesPerLine); + + // '+1' comes from 'result.Append(nextLine.ToString() + " " + asciiEnd.ToString());' below. + StringBuilder result = new StringBuilder(nextLine.Capacity+asciiEnd.Capacity+1); if (Bytes.Length > 0) { - UInt32 charCounter = 0; + Int64 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. - Offset = _initialOffSet; + var currentOffset = Offset64; + + nextLine.AppendFormat(CultureInfo.InvariantCulture, LineFormat, currentOffset); - nextLine.AppendFormat("{0:X2} ", CultureInfo.InvariantCulture.TextInfo.ToUpper(Convert.ToString(Offset, 16)).PadLeft(8, '0')); foreach (Byte currentByte in Bytes) { // Display each byte, in 2-digit hexadecimal, and add that to the left-hand side. @@ -179,38 +247,40 @@ public override string ToString() { asciiEnd.Append('.'); } + charCounter++; // If we've hit the end of a line, combine the right half with the // left half, and start a new line. - if ((charCounter % 16) == 0) + if ((charCounter % BytesPerLine) == 0) { - result.Append(nextLine.ToString() + " " + asciiEnd.ToString()); + result.Append(nextLine).Append(' ').Append(asciiEnd); nextLine.Clear(); asciiEnd.Clear(); - Offset += 0x10; - nextLine.AppendFormat("{0:X2} ", CultureInfo.InvariantCulture.TextInfo.ToUpper(Convert.ToString(Offset, 16)).PadLeft(8, '0')); + currentOffset += BytesPerLine; + 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)) { - result.Append("\r\n"); + result.AppendLine(); } } } // At the end of the file, we might not have had the chance to output - // the end of the line yet. Only do this if we didn't exit on the 16-byte + // the end of the line yet. Only do this if we didn't exit on the 16-byte // boundary, though. if ((charCounter % 16) != 0) { while ((charCounter % 16) != 0) { - nextLine.Append(" "); + nextLine.Append(' ', 3); asciiEnd.Append(' '); charCounter++; } - result.Append(nextLine.ToString() + " " + asciiEnd.ToString()); + + result.Append(nextLine).Append(' ').Append(asciiEnd); } } 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 12df579f3c0..fd65f060402 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -23,8 +23,8 @@ internal static IEnumerable GetFormatData() .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 = "" 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() diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/FormatHex.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/FormatHex.Tests.ps1 index b1d23abe370..e6d3991e69b 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/FormatHex.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/FormatHex.Tests.ps1 @@ -18,6 +18,8 @@ Describe "FormatHex" -tags "CI" { BeforeAll { + $newline = [Environment]::Newline + Setup -d FormatHexDataDir $inputText1 = 'Hello World' $inputText2 = 'More text' @@ -77,25 +79,25 @@ Describe "FormatHex" -tags "CI" { Name = "Can process int32[] type 'fhx -InputObject [int32[]](2032, 2033, 2034)'" InputObject = [int32[]](2032, 2033, 2034) Count = 1 - ExpectedResult = "00000000 F0 07 00 00 F1 07 00 00 F2 07 00 00 ð...ñ...ò..." + 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 - ExpectedResult = "00000000 FF FF FF FF FF FF FF 7F ......." + 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 - ExpectedResult = "00000000 F4 5A D0 7B 63 08 00 00 F5 5A D0 7B 63 08 00 00 ôZÐ{c...õZÐ{c..." + 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 - ExpectedResult = "00000000 68 65 6C 6C 6F 20 77 6F 72 6C 64 hello world" + ExpectedResult = "00000000000000000000 68 65 6C 6C 6F 20 77 6F 72 6C 64 hello world" } ) @@ -117,59 +119,59 @@ Describe "FormatHex" -tags "CI" { Name = "Can process byte type '[byte]5 | fhx'" InputObject = [byte]5 Count = 1 - ExpectedResult = "00000000 05" + ExpectedResult = "00000000000000000000 05" } @{ Name = "Can process byte[] type '[byte[]](1,2) | fhx'" InputObject = [byte[]](1,2) Count = 2 - ExpectedResult = "00000000 01 ." - ExpectedSecondResult = "00000000 02 ." + ExpectedResult = "00000000000000000000 01 ." + ExpectedSecondResult = "00000000000000000000 02 ." } @{ Name = "Can process int type '7 | fhx'" InputObject = 7 Count = 1 - ExpectedResult = "00000000 07 00 00 00 ...." + ExpectedResult = "00000000000000000000 07 00 00 00 ...." } @{ Name = "Can process int[] type '[int[]](5,6) | fhx'" InputObject = [int[]](5,6) Count = 2 - ExpectedResult = "00000000 05 00 00 00 ...." - ExpectedSecondResult = "00000000 06 00 00 00 ...." + ExpectedResult = "00000000000000000000 05 00 00 00 ...." + ExpectedSecondResult = "00000000000000000000 06 00 00 00 ...." } @{ Name = "Can process int32 type '[int32]2032 | fhx'" InputObject = [int32]2032 Count = 1 - ExpectedResult = "00000000 F0 07 00 00 ð..." + ExpectedResult = "00000000000000000000 F0 07 00 00 ð..." } @{ Name = "Can process int32[] type '[int32[]](2032, 2033) | fhx'" InputObject = [int32[]](2032, 2033) Count = 2 - ExpectedResult = "00000000 F0 07 00 00 ð..." - ExpectedSecondResult = "00000000 F1 07 00 00 ñ..." + ExpectedResult = "00000000000000000000 F0 07 00 00 ð..." + ExpectedSecondResult = "00000000000000000000 F1 07 00 00 ñ..." } @{ Name = "Can process Int64 type '[Int64]9223372036854775807 | fhx'" InputObject = [Int64]9223372036854775807 Count = 1 - ExpectedResult = "00000000 FF FF FF FF FF FF FF 7F ......." + 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 = "00000000 F4 5A D0 7B 63 08 00 00 ôZÐ{c..." - ExpectedSecondResult = "00000000 F5 5A D0 7B 63 08 00 00 õZÐ{c..." + 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 string type 'hello world | fhx'" InputObject = "hello world" Count = 1 - ExpectedResult = "00000000 68 65 6C 6C 6F 20 77 6F 72 6C 64 hello world" + ExpectedResult = "00000000000000000000 68 65 6C 6C 6F 20 77 6F 72 6C 64 hello world" } ) @@ -262,37 +264,37 @@ Describe "FormatHex" -tags "CI" { Name = "Can process ASCII encoding 'fhx -InputObject 'hello' -Encoding ASCII'" Encoding = "ASCII" Count = 1 - ExpectedResult = "00000000 68 65 6C 6C 6F hello" + ExpectedResult = "00000000000000000000 68 65 6C 6C 6F hello" } @{ Name = "Can process BigEndianUnicode encoding 'fhx -InputObject 'hello' -Encoding BigEndianUnicode'" Encoding = "BigEndianUnicode" Count = 1 - ExpectedResult = "00000000 00 68 00 65 00 6C 00 6C 00 6F .h.e.l.l.o" + 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 - ExpectedResult = "00000000 68 00 65 00 6C 00 6C 00 6F 00 h.e.l.l.o." + 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 - ExpectedResult = "00000000 68 65 6C 6C 6F hello" + ExpectedResult = "00000000000000000000 68 65 6C 6C 6F hello" } @{ Name = "Can process UTF8 encoding 'fhx -InputObject 'hello' -Encoding UTF8'" Encoding = "UTF8" Count = 1 - ExpectedResult = "00000000 68 65 6C 6C 6F hello" + ExpectedResult = "00000000000000000000 68 65 6C 6C 6F hello" } @{ Name = "Can process UTF32 encoding 'fhx -InputObject 'hello' -Encoding UTF32'" Encoding = "UTF32" Count = 1 - ExpectedResult = "00000000 68 00 00 00 65 00 00 00 6C 00 00 00 6C 00 00 00 h...e...l...l...`r`n00000010 6F 00 00 00 o..." + 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..." } ) @@ -418,7 +420,7 @@ Describe "FormatHex" -tags "CI" { $result | Should -Not -BeNullOrEmpty ,$result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' - $result.ToString() | Should -MatchExactly "00000000 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa`r`n00000010 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaa " + $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'" { @@ -427,9 +429,9 @@ Describe "FormatHex" -tags "CI" { $result | Should -Not -BeNullOrEmpty $result.Count | Should -Be 3 - $result[0].ToString() | Should -MatchExactly "00000000 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 "00000010 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 "00000020 65 6E 74 ent " + $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 5b20bf8a22ce70301e8a00fcc20f7f9b01c61e1c Mon Sep 17 00:00:00 2001 From: iSazonov Date: Thu, 27 Sep 2018 20:11:43 +0500 Subject: [PATCH 2/6] Reduce allocations --- .../FormatAndOutput/format-hex/Format-Hex.cs | 127 +++++++++--------- 1 file changed, 66 insertions(+), 61 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 2d634cf6454..b7e8ab0a75d 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 @@ -236,80 +236,79 @@ private void ProcessObjectContent(PSObject inputObject) { Object obj = inputObject.BaseObject; byte[] inputBytes = null; - if (obj is System.IO.FileSystemInfo) - { - string[] path = { ((FileSystemInfo)obj).FullName }; - List pathsToProcess = ResolvePaths(path, true); - ProcessPath(pathsToProcess); - } - - else if (obj is string) - { - string inputString = obj.ToString(); - inputBytes = Encoding.GetBytes(inputString); - } - - else if (obj is byte) - { - inputBytes = new byte[] { (byte)obj }; - } - - else if (obj is byte[]) - { - inputBytes = ((byte[])obj); - } - else if (obj is Int32) + switch (obj) { - inputBytes = BitConverter.GetBytes((Int32)obj); - } + case System.IO.FileSystemInfo fsi: + string[] path = { fsi.FullName }; + List pathsToProcess = ResolvePaths(path, true); + ProcessPath(pathsToProcess); + return; + case string str: + inputBytes = Encoding.GetBytes(str); + break; + case byte b: + inputBytes = new byte[] { b }; + break; + case byte[] byteArray: + inputBytes = byteArray; + break; + case Int32 iInt32: + inputBytes = BitConverter.GetBytes(iInt32); + break; + case Int32[] inputInt32s: + int i32 = 0; + inputBytes = new byte[sizeof(Int32) * inputInt32s.Length]; + Span inputStreamArray32 = inputBytes; + + foreach (Int32 value in inputInt32s) + { + BitConverter.TryWriteBytes(inputStreamArray32.Slice(i32), value); + i32 += sizeof(Int32); + } - else if (obj is Int32[]) - { - List inputStreamArray = new List(); - Int32[] inputInts = (Int32[])obj; - foreach (Int32 value in inputInts) - { - byte[] tempBytes = BitConverter.GetBytes(value); - inputStreamArray.AddRange(tempBytes); - } - inputBytes = inputStreamArray.ToArray(); - } + break; + case Int64 iInt64: + inputBytes = BitConverter.GetBytes(iInt64); + break; + case Int64[] inputInt64s: + int i64 = 0; + inputBytes = new byte[sizeof(Int64) * inputInt64s.Length]; + Span inputStreamArray64 = inputBytes; - else if (obj is Int64) - { - inputBytes = BitConverter.GetBytes((Int64)obj); - } + foreach (Int64 value in inputInt64s) + { + BitConverter.TryWriteBytes(inputStreamArray64.Slice(i64), value); + i64 += sizeof(Int64); + } - else if (obj is Int64[]) - { - List inputStreamArray = new List(); - Int64[] inputInts = (Int64[])obj; - foreach (Int64 value in inputInts) + break; + // If the object type is not supported, throw an error. Once Serialization is + // available on CoreCLR, other types will be supported. + default: { - byte[] tempBytes = BitConverter.GetBytes(value); - inputStreamArray.AddRange(tempBytes); + string errorMessage = StringUtil.Format(UtilityCommonStrings.FormatHexTypeNotSupported, obj.GetType()); + ErrorRecord errorRecord = new ErrorRecord(new ArgumentException(errorMessage), + "FormatHexTypeNotSupported", + ErrorCategory.InvalidArgument, + obj.GetType()); + WriteError(errorRecord); + break; } - inputBytes = inputStreamArray.ToArray(); - } - - // If the object type is not supported, throw an error. Once Serialization is - // available on CoreCLR, other types will be supported. - 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) { 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); - ConvertToHexidecimal(inputBytes.AsSpan().Slice(offset, count), null, 0); + if (offset != 0 || count != inputBytes.Length) + { + ConvertToHexidecimal(inputBytes.AsSpan().Slice(offset, count), null, 0); + } + else + { + ConvertToHexidecimal(inputBytes, null, 0); + } } } @@ -329,6 +328,12 @@ private void ConvertToHexidecimal(Span inputBytes, string path, Int64 offs WriteObject(byteCollectionObject); } + private void ConvertToHexidecimal(byte[] inputBytes, string path, Int64 offset) + { + ByteCollection byteCollectionObject = new ByteCollection((UInt64)offset, inputBytes, path); + WriteObject(byteCollectionObject); + } + #endregion } } From 8619cd0c66be99c451decfbc1029626ba779103f Mon Sep 17 00:00:00 2001 From: iSazonov Date: Tue, 25 Sep 2018 20:34:03 +0500 Subject: [PATCH 3/6] Add tests --- .../FormatHex.Tests.ps1 | 66 ++++++++++++++++++- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/FormatHex.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/FormatHex.Tests.ps1 index e6d3991e69b..9dd90a125cf 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/FormatHex.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/FormatHex.Tests.ps1 @@ -423,9 +423,71 @@ Describe "FormatHex" -tags "CI" { $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 " + } + } + + Context "Count and Offset parameters" { + It "Count = 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 " + } + + 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 + } + + It "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 " + } + + 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 "Offset should be >= 0" { + { Format-Hex -Path $inputFile4 -Offset -1 } | Should -Throw -ErrorId "ParameterArgumentValidationError,Microsoft.PowerShell.Commands.FormatHex" + } + + It "Offset = 0" { + + $result = Format-Hex -Path $InputFile4 -Offset 0 $result | Should -Not -BeNullOrEmpty $result.Count | Should -Be 3 From 314b6f59936fedf62128c515487b5b0de7c1658a Mon Sep 17 00:00:00 2001 From: iSazonov Date: Thu, 27 Sep 2018 09:29:55 +0500 Subject: [PATCH 4/6] Hide deprecated Raw parameter --- .../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 b7e8ab0a75d..92a5b490067 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 @@ -71,7 +71,8 @@ public sealed class FormatHex : PSCmdlet /// /// This parameter is no-op. /// - [Parameter(ParameterSetName = "ByInputObject")] + [Parameter(ParameterSetName = "ByInputObject", DontShow = true)] + [Obsolete("Raw parameter is deprecated.", true)] public SwitchParameter Raw { get; set; } #endregion From 578095fd0adf5e8c23f3455eb94d5a8d9f5cfb3d Mon Sep 17 00:00:00 2001 From: iSazonov Date: Fri, 28 Sep 2018 08:16:08 +0500 Subject: [PATCH 5/6] Fix typo in comment. --- .../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 92a5b490067..5c01e386236 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 @@ -55,7 +55,7 @@ public sealed class FormatHex : PSCmdlet public Encoding Encoding { get; set; } = ClrFacade.GetDefaultEncoding(); /// - /// Gets and sets count of bytes to read from the input stream from. + /// Gets and sets count of bytes to read from the input stream. /// [Parameter()] [ValidateRange(ValidateRangeKind.Positive)] From 533f02eba0d1224f08728504944dedcb04142f72 Mon Sep 17 00:00:00 2001 From: iSazonov Date: Mon, 15 Oct 2018 11:19:41 +0500 Subject: [PATCH 6/6] Address feedback --- .../FormatAndOutput/format-hex/Format-Hex.cs | 22 +++++++++---------- .../commands/utility/UtilityCommon.cs | 2 +- 2 files changed, 12 insertions(+), 12 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 5c01e386236..18f370849ea 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 @@ -172,7 +172,7 @@ private void ProcessPath(List pathsToProcess) /// /// Creates a binary reader that reads the file content into a buffer (byte[]) 16 bytes at a time, and - /// passes a copy of that array on to the ConvertToHexidecimal method to output. + /// passes a copy of that array on to the WriteHexidecimal method to output. /// /// private void ProcessFileContent(string path) @@ -195,11 +195,11 @@ private void ProcessFileContent(string path) if (count > Count) { bytesRead -= (int)(count - Count); - ConvertToHexidecimal(buffer.Slice(0, bytesRead), path, offset); + WriteHexidecimal(buffer.Slice(0, bytesRead), path, offset); break; } - ConvertToHexidecimal(buffer.Slice(0, bytesRead), path, offset); + WriteHexidecimal(buffer.Slice(0, bytesRead), path, offset); offset += bytesRead; } @@ -230,7 +230,7 @@ private void ProcessFileContent(string path) /// /// Creates a byte array from the object passed to the cmdlet (based on type) and passes - /// that array on to the ConvertToHexidecimal method to output. + /// that array on to the WriteHexidecimal method to output. /// /// private void ProcessObjectContent(PSObject inputObject) @@ -257,12 +257,12 @@ private void ProcessObjectContent(PSObject inputObject) case Int32 iInt32: inputBytes = BitConverter.GetBytes(iInt32); break; - case Int32[] inputInt32s: + case Int32[] i32s: int i32 = 0; - inputBytes = new byte[sizeof(Int32) * inputInt32s.Length]; + inputBytes = new byte[sizeof(Int32) * i32s.Length]; Span inputStreamArray32 = inputBytes; - foreach (Int32 value in inputInt32s) + foreach (Int32 value in i32s) { BitConverter.TryWriteBytes(inputStreamArray32.Slice(i32), value); i32 += sizeof(Int32); @@ -304,11 +304,11 @@ private void ProcessObjectContent(PSObject inputObject) int count = Math.Min(inputBytes.Length - offset, Count < (long)int.MaxValue ? (int)Count : int.MaxValue); if (offset != 0 || count != inputBytes.Length) { - ConvertToHexidecimal(inputBytes.AsSpan().Slice(offset, count), null, 0); + WriteHexidecimal(inputBytes.AsSpan().Slice(offset, count), null, 0); } else { - ConvertToHexidecimal(inputBytes, null, 0); + WriteHexidecimal(inputBytes, null, 0); } } } @@ -323,13 +323,13 @@ private void ProcessObjectContent(PSObject inputObject) /// Bytes for the hexadecimial representaion. /// File path. /// Offset in the file. - private void ConvertToHexidecimal(Span inputBytes, string path, Int64 offset) + private void WriteHexidecimal(Span inputBytes, string path, Int64 offset) { ByteCollection byteCollectionObject = new ByteCollection((UInt64)offset, inputBytes.ToArray(), path); WriteObject(byteCollectionObject); } - private void ConvertToHexidecimal(byte[] inputBytes, string path, Int64 offset) + private void WriteHexidecimal(byte[] inputBytes, string path, Int64 offset) { ByteCollection byteCollectionObject = new ByteCollection((UInt64)offset, inputBytes, path); WriteObject(byteCollectionObject); diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs index a47a2669810..d724330d864 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs @@ -220,7 +220,7 @@ public override string ToString() StringBuilder asciiEnd = new StringBuilder(BytesPerLine); // '+1' comes from 'result.Append(nextLine.ToString() + " " + asciiEnd.ToString());' below. - StringBuilder result = new StringBuilder(nextLine.Capacity+asciiEnd.Capacity+1); + StringBuilder result = new StringBuilder(nextLine.Capacity+asciiEnd.Capacity + 1); if (Bytes.Length > 0) {