Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -500,18 +500,23 @@ private static void DoCreateFromDirectory(String sourceDirectoryName, String des
if (includeBaseDirectory && di.Parent != null)
basePath = di.Parent.FullName;

// Windows' MaxPath (260) is used as an arbitrary default capacity, as it is likely
// to be greater than the length of typical entry names from the file system, even
// on non-Windows platforms. The capacity will be increased, if needed.
const int DefaultCapacity = 260;
char[] entryNameBuffer = new char[DefaultCapacity];

foreach (FileSystemInfo file in di.EnumerateFileSystemInfos("*", SearchOption.AllDirectories))
{
directoryIsEmpty = false;

Int32 entryNameLength = file.FullName.Length - basePath.Length;
Debug.Assert(entryNameLength > 0);

String entryName = EntryFromPath(file.FullName, basePath.Length, entryNameLength);

if (file is FileInfo)
{
// Create entry for file:
String entryName = EntryFromPath(file.FullName, basePath.Length, entryNameLength, ref entryNameBuffer);
ZipFileExtensions.DoCreateEntryFromFile(archive, file.FullName, entryName, compressionLevel);
}
else
Expand All @@ -522,21 +527,23 @@ private static void DoCreateFromDirectory(String sourceDirectoryName, String des
{
// FullName never returns a directory separator character on the end,
// but Zip archives require it to specify an explicit directory:
archive.CreateEntry(entryName + PathSeparator);
String entryName = EntryFromPath(file.FullName, basePath.Length, entryNameLength, ref entryNameBuffer, appendPathSeparator: true);
archive.CreateEntry(entryName);
}
}
} // foreach

// If no entries create an empty root directory entry:
if (includeBaseDirectory && directoryIsEmpty)
archive.CreateEntry(EntryFromPath(di.Name, 0, di.Name.Length) + PathSeparator);
archive.CreateEntry(EntryFromPath(di.Name, 0, di.Name.Length, ref entryNameBuffer, appendPathSeparator: true));

} // using
} // DoCreateFromDirectory

private static string EntryFromPath(string entry, int offset, int length)
private static string EntryFromPath(string entry, int offset, int length, ref char[] buffer, bool appendPathSeparator = false)
{
Debug.Assert(length <= entry.Length - offset);
Debug.Assert(buffer != null);

// Remove any leading slashes from the entry name:
while (length > 0)
Expand All @@ -550,23 +557,40 @@ private static string EntryFromPath(string entry, int offset, int length)
}

if (length == 0)
return String.Empty;
return appendPathSeparator ? PathSeparator.ToString() : String.Empty;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Presumably the length == 0 && appendPathSeparator case isn't very common?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't been able to even exercise the length == 0 case. I don't think it's possible. Maybe on Unix when sourceDirectoryName is "/", includeBaseDirectory is true, and "/" doesn't contain any sub directories or files, but a root dir without any sub directories or files seems extremely unlikely (if not impossible), and zipping such a directory seems just as improbable.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. Thanks.


// create a mutable copy
char[] chars = entry.ToCharArray(offset, length);
int resultLength = appendPathSeparator ? length + 1 : length;
EnsureCapacity(ref buffer, resultLength);
entry.CopyTo(offset, buffer, 0, length);

// '/' is a more broadly recognized directory separator on all platforms (eg: mac, linux)
// We don't use Path.DirectorySeparatorChar or AltDirectorySeparatorChar because this is
// explicitly trying to standardize to '/'
for(int i = 0; i < chars.Length; i++)
for (int i = 0; i < length; i++)
{
if (chars[i] == Path.DirectorySeparatorChar || chars[i] == Path.AltDirectorySeparatorChar)
chars[i] = PathSeparator;
char ch = buffer[i];
if (ch == Path.DirectorySeparatorChar || ch == Path.AltDirectorySeparatorChar)
buffer[i] = PathSeparator;
}

return new string(chars);
if (appendPathSeparator)
buffer[length] = PathSeparator;

return new string(buffer, 0, resultLength);
}

private static void EnsureCapacity(ref char[] buffer, int min)
{
Debug.Assert(buffer != null);
Debug.Assert(min > 0);

if (buffer.Length < min)
{
int newCapacity = buffer.Length * 2;
if (newCapacity < min) newCapacity = min;
buffer = new char[newCapacity];
}
}

private static Boolean IsDirEmpty(DirectoryInfo possiblyEmptyDir)
{
Expand Down