-
Notifications
You must be signed in to change notification settings - Fork 980
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
Add support for DEFLATE64 algorithm when extracting from zip archive #818
Open
elitzamarinova
wants to merge
1
commit into
icsharpcode:master
Choose a base branch
from
elitzamarinova:master
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
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 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
157 changes: 157 additions & 0 deletions
157
src/ICSharpCode.SharpZipLib/Zip/Deflate64/Deflate64OutputWindow.cs
This file contains 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 |
---|---|---|
@@ -0,0 +1,157 @@ | ||
// The content of the class is borrowed from DEFLATE64 support implementation for DotNetZip | ||
// which on its part contains modified code from the .NET Core Libraries (CoreFX and System.IO.Compression/DeflateManaged) | ||
// where deflate64 decompression is implemented. | ||
// https://github.com/haf/DotNetZip.Semverd/blob/master/src/Zip.Shared/Deflate64/OutputWindow.cs | ||
|
||
using System; | ||
using System.Diagnostics; | ||
|
||
namespace ICSharpCode.SharpZipLib.Zip.Deflate64 | ||
{ | ||
/// <summary> | ||
/// This class maintains a window for decompressed output. | ||
/// We need to keep this because the decompressed information can be | ||
/// a literal or a length/distance pair. For length/distance pair, | ||
/// we need to look back in the output window and copy bytes from there. | ||
/// We use a byte array of WindowSize circularly. | ||
/// </summary> | ||
internal sealed class Deflate64OutputWindow | ||
{ | ||
// With Deflate64 we can have up to a 65536 length as well as up to a 65538 distance. This means we need a Window that is at | ||
// least 131074 bytes long so we have space to retrieve up to a full 64kb in lookback and place it in our buffer without | ||
// overwriting existing data. Deflate64OutputWindow requires that the WindowSize be an exponent of 2, so we round up to 2^18. | ||
private const int WindowSize = 262144; | ||
private const int WindowMask = 262143; | ||
|
||
private readonly byte[] _window = new byte[WindowSize]; // The window is 2^18 bytes | ||
private int _end; // this is the position to where we should write next byte | ||
private int _bytesUsed; // The number of bytes in the output window which is not consumed. | ||
|
||
internal void ClearBytesUsed() | ||
{ | ||
_bytesUsed = 0; | ||
} | ||
|
||
/// <summary>Add a byte to output window.</summary> | ||
public void Write(byte b) | ||
{ | ||
Debug.Assert(_bytesUsed < WindowSize, "Can't add byte when window is full!"); | ||
_window[_end++] = b; | ||
_end &= WindowMask; | ||
++_bytesUsed; | ||
} | ||
|
||
public void WriteLengthDistance(int length, int distance) | ||
{ | ||
Debug.Assert((_bytesUsed + length) <= WindowSize, "No Enough space"); | ||
|
||
// move backwards distance bytes in the output stream, | ||
// and copy length bytes from this position to the output stream. | ||
_bytesUsed += length; | ||
int copyStart = (_end - distance) & WindowMask; // start position for coping. | ||
|
||
int border = WindowSize - length; | ||
if (copyStart <= border && _end < border) | ||
{ | ||
if (length <= distance) | ||
{ | ||
Array.Copy(_window, copyStart, _window, _end, length); | ||
_end += length; | ||
} | ||
else | ||
{ | ||
// The referenced string may overlap the current | ||
// position; for example, if the last 2 bytes decoded have values | ||
// X and Y, a string reference with <length = 5, distance = 2> | ||
// adds X,Y,X,Y,X to the output stream. | ||
while (length-- > 0) | ||
{ | ||
_window[_end++] = _window[copyStart++]; | ||
} | ||
} | ||
} | ||
else | ||
{ | ||
// copy byte by byte | ||
while (length-- > 0) | ||
{ | ||
_window[_end++] = _window[copyStart++]; | ||
_end &= WindowMask; | ||
copyStart &= WindowMask; | ||
} | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Copy up to length of bytes from input directly. | ||
/// This is used for uncompressed block. | ||
/// </summary> | ||
public int CopyFrom(InputBuffer input, int length) | ||
{ | ||
length = Math.Min(Math.Min(length, WindowSize - _bytesUsed), input.AvailableBytes); | ||
int copied; | ||
|
||
// We might need wrap around to copy all bytes. | ||
int tailLen = WindowSize - _end; | ||
if (length > tailLen) | ||
{ | ||
// copy the first part | ||
copied = input.CopyTo(_window, _end, tailLen); | ||
if (copied == tailLen) | ||
{ | ||
// only try to copy the second part if we have enough bytes in input | ||
copied += input.CopyTo(_window, 0, length - tailLen); | ||
} | ||
} | ||
else | ||
{ | ||
// only one copy is needed if there is no wrap around. | ||
copied = input.CopyTo(_window, _end, length); | ||
} | ||
|
||
_end = (_end + copied) & WindowMask; | ||
_bytesUsed += copied; | ||
return copied; | ||
} | ||
|
||
/// <summary>Free space in output window.</summary> | ||
public int FreeBytes => WindowSize - _bytesUsed; | ||
|
||
/// <summary>Bytes not consumed in output window.</summary> | ||
public int AvailableBytes => _bytesUsed; | ||
|
||
/// <summary>Copy the decompressed bytes to output array.</summary> | ||
public int CopyTo(byte[] output, int offset, int length) | ||
{ | ||
int copy_end; | ||
|
||
if (length > _bytesUsed) | ||
{ | ||
// we can copy all the decompressed bytes out | ||
copy_end = _end; | ||
length = _bytesUsed; | ||
} | ||
else | ||
{ | ||
copy_end = (_end - _bytesUsed + length) & WindowMask; // copy length of bytes | ||
} | ||
|
||
int copied = length; | ||
|
||
int tailLen = length - copy_end; | ||
if (tailLen > 0) | ||
{ | ||
// this means we need to copy two parts separately | ||
// copy tailLen bytes from the end of output window | ||
Array.Copy(_window, WindowSize - tailLen, | ||
output, offset, tailLen); | ||
offset += tailLen; | ||
length = copy_end; | ||
} | ||
Array.Copy(_window, copy_end - length, output, offset, length); | ||
_bytesUsed -= copied; | ||
Debug.Assert(_bytesUsed >= 0, "check this function and find why we copied more bytes than we have"); | ||
return copied; | ||
} | ||
} | ||
} |
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.
You are aware that is a costly copy operation?
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.
Yes, I am. But in this particular case I don't think that it is significant.