Skip to content

Commit 741b045

Browse files
ericstjcarlossanlop
authored andcommitted
Packaging: Port fix for GetParts from .NETFramework
This addresses a bug in the `GetParts` method by porting a fix from .NETFramework, improving part URI handling and collision detection.. `src/libraries/System.IO.Packaging/src/System/IO/Packaging/Package.cs`: Added sorting of parts array, updated dictionary structure for part URIs, and implemented collision detection and handling. `src/libraries/System.IO.Packaging/src/System.IO.Packaging.csproj`: Enabled package generation on build and updated servicing version.
1 parent 40d60a1 commit 741b045

File tree

2 files changed

+58
-10
lines changed

2 files changed

+58
-10
lines changed

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
<NoWarn>$(NoWarn);CA1847</NoWarn>
88
<IsPackable>true</IsPackable>
99
<!-- If you enable GeneratePackageOnBuild for this package and bump ServicingVersion, make sure to also bump ServicingVersion in Microsoft.Windows.Compatibility.csproj once for the next release. -->
10-
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
11-
<ServicingVersion>0</ServicingVersion>
10+
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
11+
<ServicingVersion>1</ServicingVersion>
1212
<PackageDescription>Provides classes that support storage of multiple data objects in a single container.</PackageDescription>
1313
</PropertyGroup>
1414

@@ -59,4 +59,4 @@
5959
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'">
6060
<Reference Include="WindowsBase" />
6161
</ItemGroup>
62-
</Project>
62+
</Project>

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

+55-7
Original file line numberDiff line numberDiff line change
@@ -403,32 +403,63 @@ public PackagePartCollection GetParts()
403403

404404
PackUriHelper.ValidatedPartUri partUri;
405405

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

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

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

425-
if (!_partList.ContainsKey(partUri))
443+
if (precedingPartName != null
444+
&& normalizedPartName.StartsWith(precedingPartName, StringComparison.Ordinal)
445+
&& normalizedPartName.Length > precedingPartName.Length
446+
&& normalizedPartName[precedingPartName.Length] == PackUriHelper.ForwardSlashChar)
426447
{
427-
// Add the part to the _partList if there is no prefix collision
428-
AddIfNoPrefixCollisionDetected(partUri, parts[i]);
448+
//Removing the invalid entry from the _partList.
449+
partDictionary.Remove(normalizedPartName);
450+
451+
throw new InvalidOperationException(SR.PartNamePrefixExists);
429452
}
453+
454+
//adding entry to partIndex to keep track of last element being added.
455+
//since parts are already sorted, last element in partIndex list will point to preceeding element to the current.
456+
partIndex.Add(partUri.NormalizedPartUriString);
430457
}
431458
}
459+
460+
//copying parts from partdictionary to partlist
461+
CopyPartDicitonaryToPartList(partDictionary, partIndex);
462+
432463
_partCollection = new PackagePartCollection(_partList);
433464
}
434465
return _partCollection;
@@ -1186,6 +1217,23 @@ private PackageRelationshipCollection GetRelationshipsHelper(string? filterStrin
11861217
return new PackageRelationshipCollection(_relationships, filterString);
11871218
}
11881219

1220+
private void CopyPartDicitonaryToPartList(Dictionary<string, KeyValuePair<PackUriHelper.ValidatedPartUri, PackagePart>> partDictionary, List<string> partIndex)
1221+
{
1222+
//Clearing _partList before copying in new data. Reassigning the variable, assuming the previous object to be garbage collected.
1223+
//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
1224+
//total time complexity for this function will be O(n log n)
1225+
_partList = new SortedList<PackUriHelper.ValidatedPartUri, PackagePart>(partDictionary.Count);
1226+
1227+
//Since partIndex is created from a sorted parts array we are sure that partIndex
1228+
//will have items in same order
1229+
foreach (var id in partIndex)
1230+
{
1231+
//retrieving object from partDictionary hashtable
1232+
var keyValue = partDictionary[id];
1233+
_partList.Add(keyValue.Key, keyValue.Value);
1234+
}
1235+
}
1236+
11891237
#endregion Private Methods
11901238

11911239
#region Private Members

0 commit comments

Comments
 (0)