1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
extern crate winapi;

use std::{
    cmp::min,
    fs::File,
    io,
    io::{Seek, SeekFrom, Write},
    mem,
    os::windows::{fs::FileExt, io::AsRawHandle},
    ptr,
};

use self::winapi::{
    shared::{
        basetsd::SIZE_T,
        minwindef::{BOOL, DWORD},
    },
    um::{
        handleapi::CloseHandle,
        memoryapi::{CreateFileMappingW, MapViewOfFile, UnmapViewOfFile, FILE_MAP_READ},
        sysinfoapi::GetSystemInfo,
        winnt::{HANDLE, PAGE_READONLY},
    },
};
use super::{ReadAt, WriteAt};

fn result(e: BOOL) -> io::Result<()> {
    if e == 0 {
        Err(io::Error::last_os_error())
    } else {
        Ok(())
    }
}

fn allocation_granularity() -> usize {
    unsafe {
        let mut info = mem::zeroed();
        GetSystemInfo(&mut info);
        info.dwAllocationGranularity as usize
    }
}

impl ReadAt for File {
    fn read_at(&self, pos: u64, buf: &mut [u8]) -> io::Result<usize> {
        let file_len = self.metadata()?.len();

        if buf.is_empty() || pos >= file_len {
            return Ok(0);
        }

        let len = min(file_len - pos, buf.len() as u64) as usize;

        unsafe {
            let alignment = pos % allocation_granularity() as u64;
            let aligned_pos = pos - alignment;
            let aligned_len = len + alignment as usize;

            let mapping = CreateFileMappingW(
                self.as_raw_handle() as HANDLE,
                ptr::null_mut(),
                PAGE_READONLY,
                0,
                0,
                ptr::null(),
            );

            if mapping.is_null() {
                return Err(io::Error::last_os_error());
            }

            let aligned_ptr = MapViewOfFile(
                mapping,
                FILE_MAP_READ,
                (aligned_pos >> 32) as DWORD,
                (aligned_pos & 0xffff_ffff) as DWORD,
                aligned_len as SIZE_T,
            );

            CloseHandle(mapping);

            if aligned_ptr.is_null() {
                return Err(io::Error::last_os_error());
            }

            let ptr = (aligned_ptr as *const u8).offset(alignment as isize);
            ptr::copy_nonoverlapping(ptr, buf.as_mut_ptr(), len);

            result(UnmapViewOfFile(aligned_ptr))?;
        }

        Ok(len)
    }
}

impl WriteAt for File {
    fn write_at(&mut self, pos: u64, buf: &[u8]) -> io::Result<usize> {
        let cursor = self.seek(SeekFrom::Current(0))?;
        let result = self.seek_write(buf, pos)?;
        self.seek(SeekFrom::Start(cursor))?;
        Ok(result)
    }

    fn flush(&mut self) -> io::Result<()> {
        Write::flush(self)
    }
}