@@ -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(" \t compressedSize = %d\n " , compressedSize);
268276 printf(" \t internalAttributes = x%04x\n " , internalAttributes);
269277 printf(" \t externalAttributes = x%08x\n " , externalAttributes);
278+ printf(" \t index = 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