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..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 @@ -54,10 +54,25 @@ 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. + /// + [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. /// - [Parameter(ParameterSetName = "ByInputObject")] + [Parameter(ParameterSetName = "ByInputObject", DontShow = true)] + [Obsolete("Raw parameter is deprecated.", true)] public SwitchParameter Raw { get; set; } #endregion @@ -157,42 +172,36 @@ 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) { - 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) - { - // 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); - } - else + count += bytesRead; + if (count > Count) { - // 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); + bytesRead -= (int)(count - Count); + WriteHexidecimal(buffer.Slice(0, bytesRead), path, offset); + break; } - // Update offset value. - offset += (UInt32)bytesRead; + + WriteHexidecimal(buffer.Slice(0, bytesRead), path, offset); + + offset += bytesRead; } } } @@ -221,85 +230,86 @@ 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) { 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) + switch (obj) { - string inputString = obj.ToString(); - inputBytes = Encoding.GetBytes(inputString); - } + 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[] i32s: + int i32 = 0; + inputBytes = new byte[sizeof(Int32) * i32s.Length]; + Span inputStreamArray32 = inputBytes; + + foreach (Int32 value in i32s) + { + BitConverter.TryWriteBytes(inputStreamArray32.Slice(i32), value); + i32 += sizeof(Int32); + } - else if (obj is byte) - { - inputBytes = new byte[] { (byte)obj }; - } + 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 byte[]) - { - inputBytes = ((byte[])obj); - } - - else if (obj is Int32) - { - inputBytes = BitConverter.GetBytes((Int32)obj); - } + foreach (Int64 value in inputInt64s) + { + BitConverter.TryWriteBytes(inputStreamArray64.Slice(i64), value); + i64 += sizeof(Int64); + } - else if (obj is Int32[]) - { - List inputStreamArray = new List(); - Int32[] inputInts = (Int32[])obj; - foreach (Int32 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(); - } - - else if (obj is Int64) - { - inputBytes = BitConverter.GetBytes((Int64)obj); } - else if (obj is Int64[]) + if (inputBytes != null) { - List inputStreamArray = new List(); - Int64[] inputInts = (Int64[])obj; - foreach (Int64 value in inputInts) + 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) { - byte[] tempBytes = BitConverter.GetBytes(value); - inputStreamArray.AddRange(tempBytes); + WriteHexidecimal(inputBytes.AsSpan().Slice(offset, count), null, 0); + } + else + { + WriteHexidecimal(inputBytes, null, 0); } - 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) - { - ConvertToHexidecimal(inputBytes, null, 0); } } @@ -308,18 +318,21 @@ 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 WriteHexidecimal(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); + } + + private void WriteHexidecimal(byte[] inputBytes, string path, Int64 offset) + { + ByteCollection byteCollectionObject = new ByteCollection((UInt64)offset, inputBytes, 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..d724330d864 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..9dd90a125cf 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,18 +420,80 @@ 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'" { + + $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 "Validate that files do not have buffer underrun problems 'Format-Hex -path `$InputFile4'" { + It "Offset = 0" { - $result = Format-Hex -path $InputFile4 + $result = Format-Hex -Path $InputFile4 -Offset 0 $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 " } } }