// +build windows,!appengine

package maxminddb

// Windows support largely borrowed from mmap-go. 
//
// Copyright 2011 Evan Shaw.  All rights reserved. 
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. 

import (
	"errors"
	"os"
	"reflect"
	"sync"
	"unsafe"

	"golang.org/x/sys/windows"
)

type memoryMap []byte

// Windows
var handleLock sync. Mutex
var handleMap = map[uintptr]windows. Handle{}

func mmap(fd int, length int) (data []byte, err error) {
	h, errno := windows. CreateFileMapping(windows. Handle(fd), nil,
		uint32(windows. PAGE_READONLY), 0, uint32(length), nil)
	if h == 0 {
		return nil, os. NewSyscallError("CreateFileMapping", errno)
	}

	addr, errno := windows. MapViewOfFile(h, uint32(windows. FILE_MAP_READ), 0,
		0, uintptr(length))
	if addr == 0 {
		return nil, os. NewSyscallError("MapViewOfFile", errno)
	}
	handleLock. Lock()
	handleMap[addr] = h
	handleLock. Unlock()

	m := memoryMap{}
	dh := m.header()
	dh. Data = addr
	dh. Len = length
	dh. Cap = dh. Len

	return m, nil
}

func (m *memoryMap) header() *reflect. SliceHeader {
	return (*reflect. SliceHeader)(unsafe. Pointer(m))
}

func flush(addr, len uintptr) error {
	errno := windows. FlushViewOfFile(addr, len)
	return os. NewSyscallError("FlushViewOfFile", errno)
}

func munmap(b []byte) (err error) {
	m := memoryMap(b)
	dh := m.header()

	addr := dh. Data
	length := uintptr(dh. Len)

	flush(addr, length)
	err = windows. UnmapViewOfFile(addr)
	if err ! = nil {
		return err
	}

	handleLock. Lock()
	defer handleLock. Unlock()
	handle, ok := handleMap[addr]
	if ! ok {
		// should be impossible; we would've errored above
		return errors. New("unknown base address")
	}
	delete(handleMap, addr)

	e := windows. CloseHandle(windows. Handle(handle))
	return os. NewSyscallError("CloseHandle", e)
}
