llvm.org GIT mirror llvm / 1ebd25e
[PathV2] Add mapped_file_region. Implementation for Windows and POSIX. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@161976 91177308-0d34-0410-b5e6-96231b3b80d8 Michael J. Spencer 8 years ago
4 changed file(s) with 425 addition(s) and 30 deletion(s). Raw diff Collapse all Expand all
2727 #define LLVM_SUPPORT_FILE_SYSTEM_H
2828
2929 #include "llvm/ADT/IntrusiveRefCntPtr.h"
30 #include "llvm/ADT/OwningPtr.h"
3031 #include "llvm/ADT/SmallString.h"
3132 #include "llvm/ADT/Twine.h"
3233 #include "llvm/Support/DataTypes.h"
575576 error_code GetMainExecutable(const char *argv0, void *MainAddr,
576577 SmallVectorImpl &result);
577578
579 /// This class represents a memory mapped file. It is based on
580 /// boost::iostreams::mapped_file.
581 class mapped_file_region {
582 mapped_file_region() LLVM_DELETED_FUNCTION;
583 mapped_file_region(mapped_file_region&) LLVM_DELETED_FUNCTION;
584 mapped_file_region &operator =(mapped_file_region&) LLVM_DELETED_FUNCTION;
585
586 public:
587 enum mapmode {
588 readonly, //< May only access map via const_data as read only.
589 readwrite, //< May access map via data and modify it. Written to path.
590 priv //< May modify via data, but changes are lost on destruction.
591 };
592
593 private:
594 /// Platform specific mapping state.
595 mapmode Mode;
596 uint64_t Size;
597 void *Mapping;
598 #if LLVM_ON_WIN32
599 int FileDescriptor;
600 void *FileHandle;
601 void *FileMappingHandle;
602 #endif
603
604 error_code init(int FD, uint64_t Offset);
605
606 public:
607 typedef char char_type;
608
609 #ifdef LLVM_USE_RVALUE_REFERENCES
610 mapped_file_region(mapped_file_region&&);
611 mapped_file_region &operator =(mapped_file_region&&);
612 #endif
613
614 /// Construct a mapped_file_region at \a path starting at \a offset of length
615 /// \a length and with access \a mode.
616 ///
617 /// \param path Path to the file to map. If it does not exist it will be
618 /// created.
619 /// \param mode How to map the memory.
620 /// \param length Number of bytes to map in starting at \a offset. If the file
621 /// is shorter than this, it will be extended. If \a length is
622 /// 0, the entire file will be mapped.
623 /// \param offset Byte offset from the beginning of the file where the map
624 /// should begin. Must be a multiple of
625 /// mapped_file_region::alignment().
626 /// \param ec This is set to errc::success if the map was constructed
627 /// sucessfully. Otherwise it is set to a platform dependent error.
628 mapped_file_region(const Twine &path,
629 mapmode mode,
630 uint64_t length,
631 uint64_t offset,
632 error_code &ec);
633
634 /// \param fd An open file descriptor to map. mapped_file_region takes
635 /// ownership. It must have been opended in the correct mode.
636 mapped_file_region(int fd,
637 mapmode mode,
638 uint64_t length,
639 uint64_t offset,
640 error_code &ec);
641
642 ~mapped_file_region();
643
644 mapmode flags() const;
645 uint64_t size() const;
646 char *data() const;
647
648 /// Get a const view of the data. Modifying this memory has undefined
649 /// behaivor.
650 const char *const_data() const;
651
652 /// \returns The minimum alignment offset must be.
653 static int alignment();
654 };
578655
579656 /// @brief Memory maps the contents of a file
580657 ///
464464 return error_code::success();
465465 }
466466
467 error_code mapped_file_region::init(int fd, uint64_t offset) {
468 AutoFD FD(fd);
469
470 // Figure out how large the file is.
471 struct stat FileInfo;
472 if (fstat(fd, &FileInfo) == -1)
473 return error_code(errno, system_category());
474 uint64_t FileSize = FileInfo.st_size;
475
476 if (Size == 0)
477 Size = FileSize;
478 else if (FileSize < Size) {
479 // We need to grow the file.
480 if (ftruncate(fd, Size) == -1)
481 return error_code(errno, system_category());
482 }
483
484 int flags = (Mode == readwrite) ? MAP_SHARED : MAP_PRIVATE;
485 int prot = (Mode == readonly) ? PROT_READ : (PROT_READ | PROT_WRITE);
486 #ifdef MAP_FILE
487 flags |= MAP_FILE;
488 #endif
489 Mapping = ::mmap(0, Size, prot, flags, fd, offset);
490 if (Mapping == MAP_FAILED)
491 return error_code(errno, system_category());
492 return error_code::success();
493 }
494
495 mapped_file_region::mapped_file_region(const Twine &path,
496 mapmode mode,
497 uint64_t length,
498 uint64_t offset,
499 error_code &ec)
500 : Mode(mode)
501 , Size(length)
502 , Mapping() {
503 // Make sure that the requested size fits within SIZE_T.
504 if (length > std::numeric_limits::max()) {
505 ec = make_error_code(errc::invalid_argument);
506 return;
507 }
508
509 SmallString<128> path_storage;
510 StringRef name = path.toNullTerminatedStringRef(path_storage);
511 int oflags = (mode == readonly) ? O_RDONLY : O_RDWR;
512 int ofd = ::open(name.begin(), oflags);
513 if (ofd == -1) {
514 ec = error_code(errno, system_category());
515 return;
516 }
517
518 ec = init(ofd, offset);
519 if (ec)
520 Mapping = 0;
521 }
522
523 mapped_file_region::mapped_file_region(int fd,
524 mapmode mode,
525 uint64_t length,
526 uint64_t offset,
527 error_code &ec)
528 : Mode(mode)
529 , Size(length)
530 , Mapping() {
531 // Make sure that the requested size fits within SIZE_T.
532 if (length > std::numeric_limits::max()) {
533 ec = make_error_code(errc::invalid_argument);
534 return;
535 }
536
537 ec = init(fd, offset);
538 if (ec)
539 Mapping = 0;
540 }
541
542 mapped_file_region::~mapped_file_region() {
543 if (Mapping)
544 ::munmap(Mapping, Size);
545 }
546
547 #ifdef LLVM_USE_RVALUE_REFERENCES
548 mapped_file_region::mapped_file_region(mapped_file_region &&other)
549 : Mode(other.Mode), Size(other.Size), Mapping(other.Mapping) {
550 other.Mapping = 0;
551 }
552 #endif
553
554 mapped_file_region::mapmode mapped_file_region::flags() const {
555 assert(Mapping && "Mapping failed but used anyway!");
556 return Mode;
557 }
558
559 uint64_t mapped_file_region::size() const {
560 assert(Mapping && "Mapping failed but used anyway!");
561 return Size;
562 }
563
564 char *mapped_file_region::data() const {
565 assert(Mapping && "Mapping failed but used anyway!");
566 assert(Mode != readonly && "Cannot get non const data for readonly mapping!");
567 return reinterpret_cast(Mapping);
568 }
569
570 const char *mapped_file_region::const_data() const {
571 assert(Mapping && "Mapping failed but used anyway!");
572 return reinterpret_cast(Mapping);
573 }
574
575 int mapped_file_region::alignment() {
576 return Process::GetPageSize();
577 }
578
467579 error_code detail::directory_iterator_construct(detail::DirIterState &it,
468580 StringRef path){
469581 SmallString<128> path_null(path);
2020 #include
2121 #include
2222 #include
23
24 #undef max
2325
2426 // MinGW doesn't define this.
2527 #ifndef _ERRNO_T_DEFINED
702704 return error_code::success();
703705 }
704706
707 error_code mapped_file_region::init(int FD, uint64_t Offset) {
708 FileDescriptor = FD;
709 // Make sure that the requested size fits within SIZE_T.
710 if (Size > std::numeric_limits::max()) {
711 if (FileDescriptor)
712 _close(FileDescriptor);
713 else
714 ::CloseHandle(FileHandle);
715 return make_error_code(errc::invalid_argument);
716 }
717
718 DWORD flprotect;
719 switch (Mode) {
720 case readonly: flprotect = PAGE_READONLY; break;
721 case readwrite: flprotect = PAGE_READWRITE; break;
722 case priv: flprotect = PAGE_WRITECOPY; break;
723 default: llvm_unreachable("invalid mapping mode");
724 }
725
726 FileMappingHandle = ::CreateFileMapping(FileHandle,
727 0,
728 flprotect,
729 Size >> 32,
730 Size & 0xffffffff,
731 0);
732 if (FileMappingHandle == NULL) {
733 error_code ec = windows_error(GetLastError());
734 if (FileDescriptor)
735 _close(FileDescriptor);
736 else
737 ::CloseHandle(FileHandle);
738 return ec;
739 }
740
741 DWORD dwDesiredAccess;
742 switch (Mode) {
743 case readonly: dwDesiredAccess = FILE_MAP_READ; break;
744 case readwrite: dwDesiredAccess = FILE_MAP_WRITE; break;
745 case priv: dwDesiredAccess = FILE_MAP_COPY; break;
746 default: llvm_unreachable("invalid mapping mode");
747 }
748 Mapping = ::MapViewOfFile(FileMappingHandle,
749 dwDesiredAccess,
750 Offset >> 32,
751 Offset & 0xffffffff,
752 Size);
753 if (Mapping == NULL) {
754 error_code ec = windows_error(GetLastError());
755 ::CloseHandle(FileMappingHandle);
756 if (FileDescriptor)
757 _close(FileDescriptor);
758 else
759 ::CloseHandle(FileHandle);
760 return ec;
761 }
762
763 if (Size == 0) {
764 MEMORY_BASIC_INFORMATION mbi;
765 SIZE_T Result = VirtualQuery(Mapping, &mbi, sizeof(mbi));
766 if (Result == 0) {
767 error_code ec = windows_error(GetLastError());
768 ::UnmapViewOfFile(Mapping);
769 ::CloseHandle(FileMappingHandle);
770 if (FileDescriptor)
771 _close(FileDescriptor);
772 else
773 ::CloseHandle(FileHandle);
774 return ec;
775 }
776 Size = mbi.RegionSize;
777 }
778 return error_code::success();
779 }
780
781 mapped_file_region::mapped_file_region(const Twine &path,
782 mapmode mode,
783 uint64_t length,
784 uint64_t offset,
785 error_code &ec)
786 : Mode(mode)
787 , Size(length)
788 , Mapping()
789 , FileDescriptor()
790 , FileHandle(INVALID_HANDLE_VALUE)
791 , FileMappingHandle() {
792 SmallString<128> path_storage;
793 SmallVector path_utf16;
794
795 // Convert path to UTF-16.
796 if (ec = UTF8ToUTF16(path.toStringRef(path_storage), path_utf16))
797 return;
798
799 // Get file handle for creating a file mapping.
800 FileHandle = ::CreateFileW(c_str(path_utf16),
801 Mode == readonly ? GENERIC_READ
802 : GENERIC_READ | GENERIC_WRITE,
803 Mode == readonly ? FILE_SHARE_READ
804 : 0,
805 0,
806 Mode == readonly ? OPEN_EXISTING
807 : OPEN_ALWAYS,
808 Mode == readonly ? FILE_ATTRIBUTE_READONLY
809 : FILE_ATTRIBUTE_NORMAL,
810 0);
811 if (FileHandle == INVALID_HANDLE_VALUE) {
812 ec = windows_error(::GetLastError());
813 return;
814 }
815
816 FileDescriptor = 0;
817 ec = init(FileDescriptor, offset);
818 if (ec) {
819 Mapping = FileMappingHandle = 0;
820 FileHandle = INVALID_HANDLE_VALUE;
821 FileDescriptor = 0;
822 }
823 }
824
825 mapped_file_region::mapped_file_region(int fd,
826 mapmode mode,
827 uint64_t length,
828 uint64_t offset,
829 error_code &ec)
830 : Mode(mode)
831 , Size(length)
832 , Mapping()
833 , FileDescriptor(fd)
834 , FileHandle(INVALID_HANDLE_VALUE)
835 , FileMappingHandle() {
836 FileHandle = reinterpret_cast(_get_osfhandle(fd));
837 if (FileHandle == INVALID_HANDLE_VALUE) {
838 _close(FileDescriptor);
839 FileDescriptor = 0;
840 ec = make_error_code(errc::bad_file_descriptor);
841 return;
842 }
843
844 ec = init(FileDescriptor, offset);
845 if (ec) {
846 Mapping = FileMappingHandle = 0;
847 FileHandle = INVALID_HANDLE_VALUE;
848 FileDescriptor = 0;
849 }
850 }
851
852 mapped_file_region::~mapped_file_region() {
853 if (Mapping)
854 ::UnmapViewOfFile(Mapping);
855 if (FileMappingHandle)
856 ::CloseHandle(FileMappingHandle);
857 if (FileDescriptor)
858 _close(FileDescriptor);
859 else if (FileHandle != INVALID_HANDLE_VALUE)
860 ::CloseHandle(FileHandle);
861 }
862
863 #ifdef LLVM_USE_RVALUE_REFERENCES
864 mapped_file_region::mapped_file_region(mapped_file_region &&other)
865 : Mode(other.Mode)
866 , Size(other.Size)
867 , Mapping(other.Mapping)
868 , FileDescriptor(other.FileDescriptor)
869 , FileHandle(other.FileHandle)
870 , FileMappingHandle(other.FileMappingHandle) {
871 other.Mapping = other.FileMappingHandle = 0;
872 other.FileHandle = INVALID_HANDLE_VALUE;
873 other.FileDescriptor = 0;
874 }
875 #endif
876
877 mapped_file_region::mapmode mapped_file_region::flags() const {
878 assert(Mapping && "Mapping failed but used anyway!");
879 return Mode;
880 }
881
882 uint64_t mapped_file_region::size() const {
883 assert(Mapping && "Mapping failed but used anyway!");
884 return Size;
885 }
886
887 char *mapped_file_region::data() const {
888 assert(Mode != readonly && "Cannot get non const data for readonly mapping!");
889 assert(Mapping && "Mapping failed but used anyway!");
890 return reinterpret_cast(Mapping);
891 }
892
893 const char *mapped_file_region::const_data() const {
894 assert(Mapping && "Mapping failed but used anyway!");
895 return reinterpret_cast(Mapping);
896 }
897
898 int mapped_file_region::alignment() {
899 SYSTEM_INFO SysInfo;
900 ::GetSystemInfo(&SysInfo);
901 return SysInfo.dwAllocationGranularity;
902 }
903
705904 error_code detail::directory_iterator_construct(detail::DirIterState &it,
706905 StringRef path){
707906 SmallVector path_utf16;
339339 }
340340 #endif
341341
342 #if !defined(_WIN32) // FIXME: temporary suppressed.
343342 TEST_F(FileSystemTest, FileMapping) {
344343 // Create a temp file.
345344 int FileDescriptor;
346345 SmallString<64> TempPath;
347346 ASSERT_NO_ERROR(
348347 fs::unique_file("%%-%%-%%-%%.temp", FileDescriptor, TempPath));
349
350 // Grow temp file to be 4096 bytes
351 ASSERT_NO_ERROR(sys::fs::resize_file(Twine(TempPath), 4096));
352
353348 // Map in temp file and add some content
354 void* MappedMemory;
355 ASSERT_NO_ERROR(fs::map_file_pages(Twine(TempPath), 0, 4096,
356 true /*writable*/, MappedMemory));
357 char* Memory = reinterpret_cast(MappedMemory);
358 strcpy(Memory, "hello there");
349 error_code EC;
350 StringRef Val("hello there");
351 {
352 fs::mapped_file_region mfr(FileDescriptor,
353 fs::mapped_file_region::readwrite,
354 4096,
355 0,
356 EC);
357 ASSERT_NO_ERROR(EC);
358 std::copy(Val.begin(), Val.end(), mfr.data());
359 // Explicitly add a 0.
360 mfr.data()[Val.size()] = 0;
361 // Unmap temp file
362 }
363
364 // Map it back in read-only
365 fs::mapped_file_region mfr(Twine(TempPath),
366 fs::mapped_file_region::readonly,
367 0,
368 0,
369 EC);
370 ASSERT_NO_ERROR(EC);
371
372 // Verify content
373 EXPECT_EQ(StringRef(mfr.const_data()), Val);
359374
360375 // Unmap temp file
361 ASSERT_NO_ERROR(fs::unmap_file_pages(MappedMemory, 4096));
362 MappedMemory = NULL;
363 Memory = NULL;
364
365 // Map it back in read-only
366 ASSERT_NO_ERROR(fs::map_file_pages(Twine(TempPath), 0, 4096,
367 false /*read-only*/, MappedMemory));
368
369 // Verify content
370 Memory = reinterpret_cast(MappedMemory);
371 bool SAME = (strcmp(Memory, "hello there") == 0);
372 EXPECT_TRUE(SAME);
373
374 // Unmap temp file
375 ASSERT_NO_ERROR(fs::unmap_file_pages(MappedMemory, 4096));
376 MappedMemory = NULL;
377 Memory = NULL;
378 }
376
377 #ifdef LLVM_USE_RVALUE_REFERENCES
378 fs::mapped_file_region m(Twine(TempPath),
379 fs::mapped_file_region::readonly,
380 0,
381 0,
382 EC);
383 ASSERT_NO_ERROR(EC);
384 const char *Data = m.const_data();
385 fs::mapped_file_region mfrrv(llvm_move(m));
386 EXPECT_EQ(mfrrv.const_data(), Data);
379387 #endif
380
381
388 }
382389 } // anonymous namespace