-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Add new Offset and Count parameters to Format-Hex and refactor the cmdlet #7877
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
82138c2
Add Count and Offset parameters in Format-Hex
iSazonov 5b20bf8
Reduce allocations
iSazonov 8619cd0
Add tests
iSazonov 314b6f5
Hide deprecated Raw parameter
iSazonov 578095f
Fix typo in comment.
iSazonov 533f02e
Address feedback
iSazonov File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -54,10 +54,25 @@ public sealed class FormatHex : PSCmdlet | |
| [ValidateNotNullOrEmpty] | ||
| public Encoding Encoding { get; set; } = ClrFacade.GetDefaultEncoding(); | ||
|
|
||
| /// <summary> | ||
| /// Gets and sets count of bytes to read from the input stream. | ||
| /// </summary> | ||
| [Parameter()] | ||
| [ValidateRange(ValidateRangeKind.Positive)] | ||
| public Int64 Count { get; set; } = Int64.MaxValue; | ||
|
|
||
| /// <summary> | ||
| /// Gets and sets offset of bytes to start reading the input stream from. | ||
| /// </summary> | ||
| [Parameter()] | ||
| [ValidateRange(ValidateRangeKind.NonNegative)] | ||
| public Int64 Offset { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// This parameter is no-op. | ||
| /// </summary> | ||
| [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<string> pathsToProcess) | |
|
|
||
| /// <summary> | ||
| /// 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. | ||
| /// </summary> | ||
| /// <param name="path"></param> | ||
| private void ProcessFileContent(string path) | ||
| { | ||
| byte[] buffer = new byte[BUFFERSIZE]; | ||
| Span<byte> 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) | |
|
|
||
| /// <summary> | ||
| /// 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. | ||
| /// </summary> | ||
| /// <param name="inputObject"></param> | ||
| private void ProcessObjectContent(PSObject inputObject) | ||
| { | ||
| Object obj = inputObject.BaseObject; | ||
| byte[] inputBytes = null; | ||
| if (obj is System.IO.FileSystemInfo) | ||
| { | ||
| string[] path = { ((FileSystemInfo)obj).FullName }; | ||
| List<string> 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<string> 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<byte> 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<byte> 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<byte> inputStreamArray = new List<byte>(); | ||
| 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<byte> inputStreamArray = new List<byte>(); | ||
| 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 | ||
|
|
||
| /// <summary> | ||
| /// Outputs the hexadecimial representaion of the of the input data. | ||
| /// Outputs the hexadecimial representaion of the input data. | ||
| /// </summary> | ||
| /// <param name="inputBytes"></param> | ||
| /// <param name="path"></param> | ||
| /// <param name="offset"></param> | ||
| private void ConvertToHexidecimal(byte[] inputBytes, string path, UInt32 offset) | ||
| /// <param name="inputBytes">Bytes for the hexadecimial representaion.</param> | ||
| /// <param name="path">File path.</param> | ||
| /// <param name="offset">Offset in the file.</param> | ||
| private void WriteHexidecimal(Span<byte> 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 | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!