Skip to content

Commit 132fb31

Browse files
ericstjcarlossanlop
authored andcommitted
Packaging: Port fix for GetParts from .NETFramework
This ports a fix for the `GetParts` method to improve part URI handling and collision detection. `src/libraries/System.IO.Packaging/src/System/IO/Packaging/Package.cs`: Added sorting of parts array to ensure proper order, introduced a dictionary and list to track parts and detect collisions, and implemented a new method `CopyPartDictionaryToPartList` to copy parts to `_partList`.
1 parent c2891d3 commit 132fb31

File tree

2 files changed

+57
-7
lines changed

2 files changed

+57
-7
lines changed

src/libraries/System.IO.Packaging/src/System.IO.Packaging.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
<TargetFrameworks>$(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum)</TargetFrameworks>
44
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
55
<IsPackable>true</IsPackable>
6+
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
7+
<ServicingVersion>1</ServicingVersion>
68
<PackageDescription>Provides classes that support storage of multiple data objects in a single container.</PackageDescription>
79
</PropertyGroup>
810

src/libraries/System.IO.Packaging/src/System/IO/Packaging/Package.cs

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -401,32 +401,63 @@ public PackagePartCollection GetParts()
401401

402402
PackUriHelper.ValidatedPartUri partUri;
403403

404+
var uriComparer = Comparer<PackUriHelper.ValidatedPartUri>.Default;
405+
406+
//Sorting the parts array which takes O(n log n) time.
407+
Array.Sort(parts, Comparer<PackagePart>.Create((partA, partB) => uriComparer.Compare((PackUriHelper.ValidatedPartUri)partA.Uri, (PackUriHelper.ValidatedPartUri)partB.Uri)));
408+
404409
//We need this dictionary to detect any collisions that might be present in the
405410
//list of parts that was given to us from the underlying physical layer, as more than one
406411
//partnames can be mapped to the same normalized part.
407412
//Note: We cannot use the _partList member variable, as that gets updated incrementally and so its
408413
//not possible to find the collisions using that list.
409414
//PackUriHelper.ValidatedPartUri implements the IComparable interface.
410-
Dictionary<PackUriHelper.ValidatedPartUri, PackagePart> seenPartUris = new Dictionary<PackUriHelper.ValidatedPartUri, PackagePart>(parts.Length);
415+
Dictionary<string, KeyValuePair<PackUriHelper.ValidatedPartUri, PackagePart>> partDictionary = new Dictionary<string, KeyValuePair<PackUriHelper.ValidatedPartUri, PackagePart>>(parts.Length);
416+
List<string> partIndex = new List<string>(parts.Length);
411417

412418
for (int i = 0; i < parts.Length; i++)
413419
{
414420
partUri = (PackUriHelper.ValidatedPartUri)parts[i].Uri;
415421

416-
if (seenPartUris.ContainsKey(partUri))
422+
string normalizedPartName = partUri.NormalizedPartUriString;
423+
424+
if (partDictionary.ContainsKey(normalizedPartName))
425+
{
417426
throw new FileFormatException(SR.BadPackageFormat);
427+
}
418428
else
419429
{
420-
// Add the part to the list of URIs that we have already seen
421-
seenPartUris.Add(partUri, parts[i]);
430+
//since we will arive to this line of code after the parts are already sorted
431+
string? precedingPartName = null;
432+
433+
if (partIndex.Count > 0)
434+
{
435+
precedingPartName = (partIndex[partIndex.Count - 1]);
436+
}
437+
438+
// Add the part to the dictionary
439+
partDictionary.Add(normalizedPartName, new KeyValuePair<PackUriHelper.ValidatedPartUri, PackagePart>(partUri, parts[i]));
422440

423-
if (!_partList.ContainsKey(partUri))
441+
if (precedingPartName != null
442+
&& normalizedPartName.StartsWith(precedingPartName, StringComparison.Ordinal)
443+
&& normalizedPartName.Length > precedingPartName.Length
444+
&& normalizedPartName[precedingPartName.Length] == PackUriHelper.ForwardSlashChar)
424445
{
425-
// Add the part to the _partList if there is no prefix collision
426-
AddIfNoPrefixCollisionDetected(partUri, parts[i]);
446+
//Removing the invalid entry from the _partList.
447+
partDictionary.Remove(normalizedPartName);
448+
449+
throw new InvalidOperationException(SR.PartNamePrefixExists);
427450
}
451+
452+
//adding entry to partIndex to keep track of last element being added.
453+
//since parts are already sorted, last element in partIndex list will point to preceeding element to the current.
454+
partIndex.Add(partUri.NormalizedPartUriString);
428455
}
429456
}
457+
458+
//copying parts from partdictionary to partlist
459+
CopyPartDicitonaryToPartList(partDictionary, partIndex);
460+
430461
_partCollection = new PackagePartCollection(_partList);
431462
}
432463
return _partCollection;
@@ -1173,6 +1204,23 @@ private PackageRelationshipCollection GetRelationshipsHelper(string? filterStrin
11731204
return new PackageRelationshipCollection(_relationships, filterString);
11741205
}
11751206

1207+
private void CopyPartDicitonaryToPartList(Dictionary<string, KeyValuePair<PackUriHelper.ValidatedPartUri, PackagePart>> partDictionary, List<string> partIndex)
1208+
{
1209+
//Clearing _partList before copying in new data. Reassigning the variable, assuming the previous object to be garbage collected.
1210+
//ideally addition to sortedlist takes O(n) but since we have sorted data and also we defined the size, it will take O(log n) per addition
1211+
//total time complexity for this function will be O(n log n)
1212+
_partList = new SortedList<PackUriHelper.ValidatedPartUri, PackagePart>(partDictionary.Count);
1213+
1214+
//Since partIndex is created from a sorted parts array we are sure that partIndex
1215+
//will have items in same order
1216+
foreach (var id in partIndex)
1217+
{
1218+
//retrieving object from partDictionary hashtable
1219+
var keyValue = partDictionary[id];
1220+
_partList.Add(keyValue.Key, keyValue.Value);
1221+
}
1222+
}
1223+
11761224
#endregion Private Methods
11771225

11781226
#region Private Members

0 commit comments

Comments
 (0)