[singlejar] Port mapped_file for Windows

`src/tools/singlejar/mapped_file_*.inc` are now included in `src/tools/singlejar/mapped_file.cc` instead of header file and no longer inline most member functions: these functions are too large for inlining.

/cc @laszlocsomor

Closes #5780.

PiperOrigin-RevId: 207998444
diff --git a/src/tools/singlejar/BUILD b/src/tools/singlejar/BUILD
index 01415cc..2f0164d 100644
--- a/src/tools/singlejar/BUILD
+++ b/src/tools/singlejar/BUILD
@@ -24,6 +24,7 @@
         "diag.h",
         "input_jar.cc",
         "input_jar.h",
+        "mapped_file.cc",
         "mapped_file.h",
         "mapped_file_posix.inc",
         "mapped_file_windows.inc",
@@ -348,13 +349,16 @@
 
 cc_library(
     name = "mapped_file",
-    srcs = select({
+    srcs = ["mapped_file.cc"] + select({
         "//src:windows": ["mapped_file_windows.inc"],
         "//conditions:default": ["mapped_file_posix.inc"],
     }),
     hdrs = ["mapped_file.h"],
     visibility = ["//visibility:private"],
-    deps = [":diag"],
+    deps = [
+        ":diag",
+        ":port",
+    ],
 )
 
 cc_library(
diff --git a/src/tools/singlejar/mapped_file.cc b/src/tools/singlejar/mapped_file.cc
new file mode 100644
index 0000000..98aca74
--- /dev/null
+++ b/src/tools/singlejar/mapped_file.cc
@@ -0,0 +1,21 @@
+// Copyright 2018 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tools/singlejar/mapped_file.h"
+
+#ifdef _WIN32
+#include "src/tools/singlejar/mapped_file_windows.inc"
+#else  // not _WIN32
+#include "src/tools/singlejar/mapped_file_posix.inc"
+#endif  // _WIN32
diff --git a/src/tools/singlejar/mapped_file.h b/src/tools/singlejar/mapped_file.h
index f4ada45..bca19bc 100644
--- a/src/tools/singlejar/mapped_file.h
+++ b/src/tools/singlejar/mapped_file.h
@@ -17,6 +17,8 @@
 
 #include <string>
 
+#include "src/tools/singlejar/port.h"
+
 /*
  * A mapped read-only file with auto closing.
  *
@@ -28,12 +30,6 @@
  */
 class MappedFile {
  public:
-#ifdef _WIN32
-  typedef /* HANDLE = void* */ void *FileHandleType;
-#else   // not _WIN32
-  typedef int FileHandleType;
-#endif  // _WIN32
-
   MappedFile();
 
   ~MappedFile() { Close(); }
@@ -51,23 +47,20 @@
   const unsigned char *address(off_t offset) const {
     return mapped_start_ + offset;
   }
-  off_t offset(const void *address) const {
+  off64_t offset(const void *address) const {
     return reinterpret_cast<const unsigned char *>(address) - mapped_start_;
   }
-  FileHandleType fd() const { return fd_; }
+  int fd() const { return fd_; }
   size_t size() const { return mapped_end_ - mapped_start_; }
-  bool is_open() const;
+  bool is_open() const { return fd_ >= 0; }
 
  private:
   unsigned char *mapped_start_;
   unsigned char *mapped_end_;
-  FileHandleType fd_;
-};
-
+  int fd_;
 #ifdef _WIN32
-#include "src/tools/singlejar/mapped_file_windows.inc"
-#else  // not _WIN32
-#include "src/tools/singlejar/mapped_file_posix.inc"
-#endif  // _WIN32
+  /* HANDLE */ void *hMapFile_;
+#endif
+};
 
 #endif  //  BAZEL_SRC_TOOLS_SINGLEJAR_MAPPED_FILE_H_
diff --git a/src/tools/singlejar/mapped_file_posix.inc b/src/tools/singlejar/mapped_file_posix.inc
index ad6c6a5..637661b 100644
--- a/src/tools/singlejar/mapped_file_posix.inc
+++ b/src/tools/singlejar/mapped_file_posix.inc
@@ -30,10 +30,10 @@
 #error This code for 64 bit Unix.
 #endif
 
-inline MappedFile::MappedFile()
+MappedFile::MappedFile()
     : mapped_start_(nullptr), mapped_end_(nullptr), fd_(-1) {}
 
-inline bool MappedFile::Open(const std::string& path) {
+bool MappedFile::Open(const std::string& path) {
   if (is_open()) {
     diag_errx(1, "%s:%d: This instance is already open", __FILE__, __LINE__);
   }
@@ -60,7 +60,7 @@
   return true;
 }
 
-inline void MappedFile::Close() {
+void MappedFile::Close() {
   if (is_open()) {
     munmap(mapped_start_, mapped_end_ - mapped_start_);
     mapped_start_ = mapped_end_ = nullptr;
@@ -69,6 +69,4 @@
   }
 }
 
-inline bool MappedFile::is_open() const { return fd_ >= 0; }
-
 #endif  // BAZEL_SRC_TOOLS_SINGLEJAR_MAPPED_FILE_POSIX_H_
diff --git a/src/tools/singlejar/mapped_file_windows.inc b/src/tools/singlejar/mapped_file_windows.inc
index 366103f..43e524a 100644
--- a/src/tools/singlejar/mapped_file_windows.inc
+++ b/src/tools/singlejar/mapped_file_windows.inc
@@ -19,30 +19,101 @@
 #error This code is for 64 bit Windows.
 #endif
 
+#include "src/tools/singlejar/diag.h"
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
 #include <windows.h>
 
 #include <string>
 
-typedef HANDLE FileHandleType;
+MappedFile::MappedFile()
+    : mapped_start_(nullptr),
+      mapped_end_(nullptr),
+      fd_(-1),
+      hMapFile_(INVALID_HANDLE_VALUE) {}
 
-inline MappedFile::MappedFile()
-    : mapped_start_(nullptr), mapped_end_(nullptr), fd_(INVALID_HANDLE_VALUE) {}
+bool MappedFile::Open(const std::string& path) {
+  if (is_open()) {
+    diag_errx(1, "%s:%d: This instance is already open", __FILE__, __LINE__);
+  }
+  if ((fd_ = _open(path.c_str(), O_RDONLY | O_BINARY)) < 0) {
+    diag_warn("%s:%d: open %s:", __FILE__, __LINE__, path.c_str());
+    return false;
+  }
 
-inline bool MappedFile::Open(const std::string& path) {
-  // TODO(laszlocsomor): implement as part of
-  // https://github.com/bazelbuild/bazel/issues/2241
-  return false;
+  HANDLE hFile = reinterpret_cast<HANDLE>(_get_osfhandle(fd_));
+  LARGE_INTEGER temp;
+  ::GetFileSizeEx(hFile, &temp);
+  size_t fileSize = temp.QuadPart;
+
+  if (fileSize == 0) {
+    // This is where Windows implementation differs from POSIX's.
+    // CreateFileMapping cannot map empty file:
+    //
+    // From
+    // https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createfilemappinga
+    //
+    // An attempt to map a file with a length of 0 (zero) fails with an error
+    // code of ERROR_FILE_INVALID. Applications should test for files with a
+    // length of 0 (zero) and reject those files.
+    //
+    // Since the test at //src/tools/singlerjar/input_jar_bad_jar_test expects
+    // empty file to fail anyway, returning error here shouldn't be a problem.
+    diag_warn("%s:%d: Windows MappedFile cannot handle empty file (%s)",
+              __FILE__, __LINE__, path.c_str());
+    _close(fd_);
+    fd_ = -1;
+    return false;
+  }
+
+  hMapFile_ = ::CreateFileMapping(
+      hFile,
+      nullptr,                             // default security
+      PAGE_READONLY,                       // read-only permission
+      static_cast<DWORD>(fileSize >> 32),  // size of mapping object, high
+      static_cast<DWORD>(fileSize),        // size of mapping object, low
+      nullptr);                            // name of mapping object
+
+  if (hMapFile_ == nullptr) {
+    diag_warn("%s:%d: CreateFileMapping for %s failed", __FILE__, __LINE__,
+              path.c_str());
+    _close(fd_);
+    fd_ = -1;
+    return false;
+  }
+
+  mapped_start_ = static_cast<unsigned char*>(
+      MapViewOfFile(hMapFile_,
+                    FILE_MAP_READ | FILE_MAP_COPY,  // PROT_READ | MAP_PRIVATE
+                    0,                              // file offset, high
+                    0,                              // file offset, low
+                    fileSize));                     // file size
+  if (mapped_start_ == nullptr) {
+    diag_warn("%s:%d: MapViewOfFile for %s failed", __FILE__, __LINE__,
+              path.c_str());
+    ::CloseHandle(hMapFile_);
+    _close(fd_);
+    hMapFile_ = INVALID_HANDLE_VALUE;
+    fd_ = -1;
+    return false;
+  }
+
+  mapped_end_ = mapped_start_ + fileSize;
+  return true;
 }
 
-inline void MappedFile::Close() {
-  // TODO(laszlocsomor): implement as part of
-  // https://github.com/bazelbuild/bazel/issues/2241
-}
-
-inline bool MappedFile::is_open() const {
-  // TODO(laszlocsomor): implement as part of
-  // https://github.com/bazelbuild/bazel/issues/2241
-  return false;
+void MappedFile::Close() {
+  if (is_open()) {
+    ::UnmapViewOfFile(mapped_start_);
+    ::CloseHandle(hMapFile_);
+    _close(fd_);
+    hMapFile_ = INVALID_HANDLE_VALUE;
+    fd_ = -1;
+    mapped_start_ = mapped_end_ = nullptr;
+  }
 }
 
 #endif  // BAZEL_SRC_TOOLS_SINGLEJAR_MAPPED_FILE_WINDOWS_H_
\ No newline at end of file
diff --git a/src/tools/singlejar/output_jar.cc b/src/tools/singlejar/output_jar.cc
index 5b6d24f..9238946 100644
--- a/src/tools/singlejar/output_jar.cc
+++ b/src/tools/singlejar/output_jar.cc
@@ -24,6 +24,10 @@
 #include <sys/stat.h>
 #include <time.h>
 
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
 #include "src/tools/singlejar/combiners.h"
 #include "src/tools/singlejar/diag.h"
 #include "src/tools/singlejar/input_jar.h"