Merge "[zip] Save 1 malloc and memset for each added file in ZipWriter"
diff --git a/libziparchive/zip_archive_benchmark.cpp b/libziparchive/zip_archive_benchmark.cpp
index 23ed408..09d3b8a 100644
--- a/libziparchive/zip_archive_benchmark.cpp
+++ b/libziparchive/zip_archive_benchmark.cpp
@@ -58,7 +58,7 @@
   std::string_view name("thisFileNameDoesNotExist");
 
   // Start the benchmark.
-  while (state.KeepRunning()) {
+  for (auto _ : state) {
     OpenArchive(temp_file->path, &handle);
     FindEntry(handle, name, &data);
     CloseArchive(handle);
@@ -73,7 +73,7 @@
   ZipEntry data;
   std::string name;
 
-  while (state.KeepRunning()) {
+  for (auto _ : state) {
     OpenArchive(temp_file->path, &handle);
     StartIteration(handle, &iteration_cookie);
     while (Next(iteration_cookie, &data, &name) == 0) {
@@ -84,4 +84,27 @@
 }
 BENCHMARK(Iterate_all_files);
 
+static void StartAlignedEntry(benchmark::State& state) {
+  TemporaryFile file;
+  FILE* fp = fdopen(file.fd, "w");
+
+  ZipWriter writer(fp);
+
+  auto alignment = uint32_t(state.range(0));
+  std::string name = "name";
+  int counter = 0;
+  for (auto _ : state) {
+    writer.StartAlignedEntry(name + std::to_string(counter++), 0, alignment);
+    state.PauseTiming();
+    writer.WriteBytes("hola", 4);
+    writer.FinishEntry();
+    state.ResumeTiming();
+  }
+
+  writer.Finish();
+  fclose(fp);
+}
+BENCHMARK(StartAlignedEntry)->Arg(2)->Arg(16)->Arg(1024)->Arg(4096);
+
+
 BENCHMARK_MAIN();
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index 3c53209..198154b 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -247,13 +247,24 @@
   ExtractTimeAndDate(time, &file_entry.last_mod_time, &file_entry.last_mod_date);
 
   off_t offset = current_offset_ + sizeof(LocalFileHeader) + file_entry.path.size();
-  std::vector<char> zero_padding;
+  // prepare a pre-zeroed memory page in case when we need to pad some aligned data.
+  static constexpr auto kPageSize = 4096;
+  static constexpr char kSmallZeroPadding[kPageSize] = {};
+  // use this buffer if our preallocated one is too small
+  std::vector<char> zero_padding_big;
+  const char* zero_padding = nullptr;
+
   if (alignment != 0 && (offset & (alignment - 1))) {
     // Pad the extra field so the data will be aligned.
     uint16_t padding = static_cast<uint16_t>(alignment - (offset % alignment));
     file_entry.padding_length = padding;
     offset += padding;
-    zero_padding.resize(padding, 0);
+    if (padding <= std::size(kSmallZeroPadding)) {
+        zero_padding = kSmallZeroPadding;
+    } else {
+        zero_padding_big.resize(padding, 0);
+        zero_padding = zero_padding_big.data();
+    }
   }
 
   LocalFileHeader header = {};
@@ -269,7 +280,7 @@
     return HandleError(kIoError);
   }
 
-  if (file_entry.padding_length != 0 && fwrite(zero_padding.data(), 1, file_entry.padding_length,
+  if (file_entry.padding_length != 0 && fwrite(zero_padding, 1, file_entry.padding_length,
                                                file_) != file_entry.padding_length) {
     return HandleError(kIoError);
   }