Skip to content

Commit d691d6a

Browse files
authored
Merge pull request #4637 from dhasenan/zip_order
Allow specifying zip archive member order.
2 parents 4889e48 + 31f9a9e commit d691d6a

File tree

1 file changed

+56
-4
lines changed

1 file changed

+56
-4
lines changed

std/zip.d

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ final class ArchiveMember
122122
private ushort _diskNumber;
123123
private uint _externalAttributes;
124124
private DosFileTime _time;
125+
// by default, no explicit order goes after explicit order
126+
private uint _index = uint.max;
125127

126128
ushort flags; /// Read/Write: normally set to 0
127129
ushort internalAttributes; /// Read/Write
@@ -251,6 +253,12 @@ final class ArchiveMember
251253
_compressionMethod = cm;
252254
}
253255

256+
/**
257+
* The index of this archive member within the archive.
258+
*/
259+
@property uint index() const pure nothrow @nogc { return _index; }
260+
@property uint index(uint value) pure nothrow @nogc { return _index = value; }
261+
254262
debug(print)
255263
{
256264
void print()
@@ -267,6 +275,7 @@ final class ArchiveMember
267275
printf("\tcompressedSize = %d\n", compressedSize);
268276
printf("\tinternalAttributes = x%04x\n", internalAttributes);
269277
printf("\texternalAttributes = x%08x\n", externalAttributes);
278+
printf("\tindex = x%08x\n", index);
270279
}
271280
}
272281
}
@@ -376,7 +385,9 @@ final class ZipArchive
376385
* Returns: array representing the entire archive.
377386
*/
378387
void[] build()
379-
{ uint i;
388+
{
389+
import std.algorithm.sorting : sort;
390+
uint i;
380391
uint directoryOffset;
381392

382393
if (comment.length > 0xFFFF)
@@ -385,7 +396,8 @@ final class ZipArchive
385396
// Compress each member; compute size
386397
uint archiveSize = 0;
387398
uint directorySize = 0;
388-
foreach (ArchiveMember de; _directory)
399+
auto directory = _directory.values().sort!((x, y) => x.index < y.index).release;
400+
foreach (ArchiveMember de; directory)
389401
{
390402
if (!de._compressedData.length)
391403
{
@@ -436,7 +448,7 @@ final class ZipArchive
436448

437449
// Store each archive member
438450
i = 0;
439-
foreach (ArchiveMember de; _directory)
451+
foreach (ArchiveMember de; directory)
440452
{
441453
de.offset = i;
442454
_data[i .. i + 4] = cast(ubyte[])"PK\x03\x04";
@@ -462,7 +474,7 @@ final class ZipArchive
462474
// Write directory
463475
directoryOffset = i;
464476
_numEntries = 0;
465-
foreach (ArchiveMember de; _directory)
477+
foreach (ArchiveMember de; directory)
466478
{
467479
_data[i .. i + 4] = cast(ubyte[])"PK\x01\x02";
468480
putUshort(i + 4, de._madeVersion);
@@ -666,6 +678,7 @@ final class ZipArchive
666678
if (_data[i .. i + 4] != cast(ubyte[])"PK\x01\x02")
667679
throw new ZipException("invalid directory entry 1");
668680
ArchiveMember de = new ArchiveMember();
681+
de._index = n;
669682
de._madeVersion = getUshort(i + 4);
670683
de._extractVersion = getUshort(i + 6);
671684
de.flags = getUshort(i + 8);
@@ -872,6 +885,45 @@ debug(print)
872885
}
873886
}
874887

888+
@system unittest
889+
{
890+
import std.random : Mt19937, randomShuffle;
891+
import std.conv : to;
892+
// Test if packing and unpacking preserves order.
893+
auto rand = Mt19937(15966);
894+
string[] names;
895+
int value = 0;
896+
// Generate a series of unique numbers as filenames.
897+
foreach (i; 0..20)
898+
{
899+
value += 1 + rand.front & 0xFFFF;
900+
rand.popFront;
901+
names ~= value.to!string;
902+
}
903+
// Insert them in a random order.
904+
names.randomShuffle(rand);
905+
auto zip1 = new ZipArchive();
906+
foreach (i, name; names)
907+
{
908+
auto member = new ArchiveMember();
909+
member.name = name;
910+
member.expandedData = cast(ubyte[])name;
911+
member.index = cast(int)i;
912+
zip1.addMember(member);
913+
}
914+
auto data = zip1.build();
915+
916+
// Ensure that they appear in the same order.
917+
auto zip2 = new ZipArchive(data);
918+
foreach (i, name; names)
919+
{
920+
const member = zip2.directory[name];
921+
assert(member.index == i, "member " ~ name ~ " had index " ~
922+
member.index.to!string ~ " but we expected index " ~ i.to!string ~
923+
". The input array was " ~ names.to!string);
924+
}
925+
}
926+
875927
@system unittest
876928
{
877929
import std.zlib;

0 commit comments

Comments
 (0)