PageRenderTime 64ms CodeModel.GetById 19ms app.highlight 38ms RepoModel.GetById 1ms app.codeStats 0ms

/pypy/rlib/rmmap.py

https://bitbucket.org/pypy/pypy/
Python | 833 lines | 773 code | 37 blank | 23 comment | 23 complexity | 6584df68fe662dc3422e15d29643f57b MD5 | raw file
  1
  2from pypy.rpython.tool import rffi_platform
  3from pypy.rpython.lltypesystem import rffi, lltype, llmemory
  4from pypy.rlib import rposix
  5from pypy.translator.tool.cbuild import ExternalCompilationInfo
  6from pypy.rlib.nonconst import NonConstant
  7
  8import sys
  9import os
 10import platform
 11import stat
 12
 13_POSIX = os.name == "posix"
 14_MS_WINDOWS = os.name == "nt"
 15_LINUX = "linux" in sys.platform
 16_64BIT = "64bit" in platform.architecture()[0]
 17
 18class RValueError(Exception):
 19    def __init__(self, message):
 20        self.message = message
 21
 22class RTypeError(Exception):
 23    def __init__(self, message):
 24        self.message = message
 25
 26includes = ["sys/types.h"]
 27if _POSIX:
 28    includes += ['unistd.h', 'sys/mman.h']
 29elif _MS_WINDOWS:
 30    includes += ['winsock2.h','windows.h']
 31
 32class CConfig:
 33    _compilation_info_ = ExternalCompilationInfo(
 34        includes=includes,
 35        #pre_include_bits=['#ifndef _GNU_SOURCE\n' +
 36        #                  '#define _GNU_SOURCE\n' +
 37        #                  '#endif']
 38        # ^^^ _GNU_SOURCE is always defined by the ExternalCompilationInfo now
 39    )
 40    size_t = rffi_platform.SimpleType("size_t", rffi.LONG)
 41    off_t = rffi_platform.SimpleType("off_t", rffi.LONG)
 42
 43constants = {}
 44if _POSIX:
 45    # constants, look in sys/mman.h and platform docs for the meaning
 46    # some constants are linux only so they will be correctly exposed outside
 47    # depending on the OS
 48    constant_names = ['MAP_SHARED', 'MAP_PRIVATE',
 49                      'PROT_READ', 'PROT_WRITE',
 50                      'MS_SYNC']
 51    opt_constant_names = ['MAP_ANON', 'MAP_ANONYMOUS', 'MAP_NORESERVE',
 52                          'PROT_EXEC',
 53                          'MAP_DENYWRITE', 'MAP_EXECUTABLE']
 54    for name in constant_names:
 55        setattr(CConfig, name, rffi_platform.ConstantInteger(name))
 56    for name in opt_constant_names:
 57        setattr(CConfig, name, rffi_platform.DefinedConstantInteger(name))
 58
 59    CConfig.MREMAP_MAYMOVE = (
 60        rffi_platform.DefinedConstantInteger("MREMAP_MAYMOVE"))
 61    CConfig.has_mremap = rffi_platform.Has('mremap(NULL, 0, 0, 0)')
 62    # a dirty hack, this is probably a macro
 63
 64elif _MS_WINDOWS:
 65    constant_names = ['PAGE_READONLY', 'PAGE_READWRITE', 'PAGE_WRITECOPY',
 66                      'FILE_MAP_READ', 'FILE_MAP_WRITE', 'FILE_MAP_COPY',
 67                      'DUPLICATE_SAME_ACCESS', 'MEM_COMMIT', 'MEM_RESERVE',
 68                      'MEM_RELEASE', 'PAGE_EXECUTE_READWRITE', 'PAGE_NOACCESS']
 69    for name in constant_names:
 70        setattr(CConfig, name, rffi_platform.ConstantInteger(name))
 71
 72    from pypy.rlib import rwin32
 73
 74    from pypy.rlib.rwin32 import HANDLE, LPHANDLE
 75    from pypy.rlib.rwin32 import NULL_HANDLE, INVALID_HANDLE_VALUE
 76    from pypy.rlib.rwin32 import DWORD, WORD, DWORD_PTR, LPDWORD
 77    from pypy.rlib.rwin32 import BOOL, LPVOID, LPCSTR, SIZE_T
 78    from pypy.rlib.rwin32 import INT, LONG, PLONG
 79
 80# export the constants inside and outside. see __init__.py
 81cConfig = rffi_platform.configure(CConfig)
 82constants.update(cConfig)
 83
 84if _POSIX:
 85    # MAP_ANONYMOUS is not always present but it's always available at CPython level
 86    if constants["MAP_ANONYMOUS"] is None:
 87        constants["MAP_ANONYMOUS"] = constants["MAP_ANON"]
 88    assert constants["MAP_ANONYMOUS"] is not None
 89    constants["MAP_ANON"] = constants["MAP_ANONYMOUS"]
 90
 91locals().update(constants)
 92
 93_ACCESS_DEFAULT, ACCESS_READ, ACCESS_WRITE, ACCESS_COPY = range(4)
 94
 95if rffi.sizeof(off_t) > rffi.sizeof(lltype.Signed):
 96    HAVE_LARGEFILE_SUPPORT = True
 97else:
 98    HAVE_LARGEFILE_SUPPORT = False
 99
100def external(name, args, result, **kwargs):
101    unsafe = rffi.llexternal(name, args, result,
102                             compilation_info=CConfig._compilation_info_,
103                             **kwargs)
104    safe = rffi.llexternal(name, args, result,
105                           compilation_info=CConfig._compilation_info_,
106                           sandboxsafe=True, threadsafe=False,
107                           **kwargs)
108    return unsafe, safe
109
110def winexternal(name, args, result, **kwargs):
111    return rffi.llexternal(name, args, result,
112                           compilation_info=CConfig._compilation_info_,
113                           calling_conv='win',
114                           **kwargs)
115
116PTR = rffi.CCHARP
117
118c_memmove, _ = external('memmove', [PTR, PTR, size_t], lltype.Void)
119
120if _POSIX:
121    has_mremap = cConfig['has_mremap']
122    c_mmap, c_mmap_safe = external('mmap', [PTR, size_t, rffi.INT, rffi.INT,
123                               rffi.INT, off_t], PTR, macro=True)
124    # 'mmap' on linux32 is a macro that calls 'mmap64'
125    _, c_munmap_safe = external('munmap', [PTR, size_t], rffi.INT)
126    c_msync, _ = external('msync', [PTR, size_t, rffi.INT], rffi.INT)
127    if has_mremap:
128        c_mremap, _ = external('mremap',
129                               [PTR, size_t, size_t, rffi.ULONG], PTR)
130
131    # this one is always safe
132    _, _get_page_size = external('getpagesize', [], rffi.INT)
133    _get_allocation_granularity = _get_page_size
134
135elif _MS_WINDOWS:
136
137    class ComplexCConfig:
138        _compilation_info_ = CConfig._compilation_info_
139
140        SYSINFO_STRUCT = rffi.CStruct(
141            'SYSINFO_STRUCT',
142                ("wProcessorArchitecture", WORD),
143                ("wReserved", WORD),
144            )
145
146        SYSINFO_UNION = rffi.CStruct(
147            'union SYSINFO_UNION',
148                ("dwOemId", DWORD),
149                ("_struct_", SYSINFO_STRUCT),
150            )
151        # sorry, I can't find a way to insert the above
152        # because the union field has no name
153        SYSTEM_INFO = rffi_platform.Struct(
154            'SYSTEM_INFO', [
155                ## ("_union_", SYSINFO_UNION),
156                ## instead, we put the smaller fields, here
157                ("wProcessorArchitecture", WORD),
158                ("wReserved", WORD),
159                ## should be a union. dwOemId is obsolete, anyway
160                ("dwPageSize", DWORD),
161                ("lpMinimumApplicationAddress", LPVOID),
162                ("lpMaximumApplicationAddress", LPVOID),
163                ("dwActiveProcessorMask", DWORD_PTR),
164                ("dwNumberOfProcessors", DWORD),
165                ("dwProcessorType", DWORD),
166                ("dwAllocationGranularity", DWORD),
167                ("wProcessorLevel", WORD),
168                ("wProcessorRevision", WORD),
169            ])
170
171    config = rffi_platform.configure(ComplexCConfig)
172    SYSTEM_INFO = config['SYSTEM_INFO']
173    SYSTEM_INFO_P = lltype.Ptr(SYSTEM_INFO)
174
175    GetSystemInfo = winexternal('GetSystemInfo', [SYSTEM_INFO_P], lltype.Void)
176    GetFileSize = winexternal('GetFileSize', [HANDLE, LPDWORD], DWORD)
177    GetCurrentProcess = winexternal('GetCurrentProcess', [], HANDLE)
178    DuplicateHandle = winexternal('DuplicateHandle', [HANDLE, HANDLE, HANDLE, LPHANDLE, DWORD, BOOL, DWORD], BOOL)
179    CreateFileMapping = winexternal('CreateFileMappingA', [HANDLE, rwin32.LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCSTR], HANDLE)
180    MapViewOfFile = winexternal('MapViewOfFile', [HANDLE, DWORD, DWORD, DWORD, SIZE_T], LPCSTR)##!!LPVOID)
181    UnmapViewOfFile = winexternal('UnmapViewOfFile', [LPCSTR], BOOL,
182                                  threadsafe=False)
183    FlushViewOfFile = winexternal('FlushViewOfFile', [LPCSTR, SIZE_T], BOOL)
184    SetFilePointer = winexternal('SetFilePointer', [HANDLE, LONG, PLONG, DWORD], DWORD)
185    SetEndOfFile = winexternal('SetEndOfFile', [HANDLE], BOOL)
186    VirtualAlloc = winexternal('VirtualAlloc',
187                               [rffi.VOIDP, rffi.SIZE_T, DWORD, DWORD],
188                               rffi.VOIDP)
189    # VirtualProtect is used in llarena and should not release the GIL
190    _VirtualProtect = winexternal('VirtualProtect',
191                                  [rffi.VOIDP, rffi.SIZE_T, DWORD, LPDWORD],
192                                  BOOL,
193                                  _nowrapper=True)
194    def VirtualProtect(addr, size, mode, oldmode_ptr):
195        return _VirtualProtect(addr,
196                               rffi.cast(rffi.SIZE_T, size),
197                               rffi.cast(DWORD, mode),
198                               oldmode_ptr)
199    VirtualProtect._annspecialcase_ = 'specialize:ll'
200    VirtualFree = winexternal('VirtualFree',
201                              [rffi.VOIDP, rffi.SIZE_T, DWORD], BOOL)
202
203
204    def _get_page_size():
205        try:
206            si = rffi.make(SYSTEM_INFO)
207            GetSystemInfo(si)
208            return int(si.c_dwPageSize)
209        finally:
210            lltype.free(si, flavor="raw")
211
212    def _get_allocation_granularity():
213        try:
214            si = rffi.make(SYSTEM_INFO)
215            GetSystemInfo(si)
216            return int(si.c_dwAllocationGranularity)
217        finally:
218            lltype.free(si, flavor="raw")
219
220    def _get_file_size(handle):
221        # XXX use native Windows types like WORD
222        high_ref = lltype.malloc(LPDWORD.TO, 1, flavor='raw')
223        try:
224            low = GetFileSize(handle, high_ref)
225            low = rffi.cast(lltype.Signed, low)
226            # XXX should be propagate the real type, allowing
227            # for 2*sys.maxint?
228            high = high_ref[0]
229            # low might just happen to have the value INVALID_FILE_SIZE
230            # so we need to check the last error also
231            INVALID_FILE_SIZE = -1
232            if low == INVALID_FILE_SIZE:
233                err = rwin32.GetLastError()
234                if err:
235                    raise WindowsError(err, "mmap")
236            return low, high
237        finally:
238            lltype.free(high_ref, flavor='raw')
239
240    INVALID_HANDLE = INVALID_HANDLE_VALUE
241
242PAGESIZE = _get_page_size()
243ALLOCATIONGRANULARITY = _get_allocation_granularity()
244NULL = lltype.nullptr(PTR.TO)
245NODATA = lltype.nullptr(PTR.TO)
246
247class MMap(object):
248    def __init__(self, access, offset):
249        self.size = 0
250        self.pos = 0
251        self.access = access
252        self.offset = offset
253
254        if _MS_WINDOWS:
255            self.map_handle = NULL_HANDLE
256            self.file_handle = NULL_HANDLE
257            self.tagname = ""
258        elif _POSIX:
259            self.fd = -1
260            self.closed = False
261
262    def check_valid(self):
263        if _MS_WINDOWS:
264            to_close = self.map_handle == INVALID_HANDLE
265        elif _POSIX:
266            to_close = self.closed
267
268        if to_close:
269            raise RValueError("map closed or invalid")
270
271    def check_writeable(self):
272        if not (self.access != ACCESS_READ):
273            raise RTypeError("mmap can't modify a readonly memory map.")
274
275    def check_resizeable(self):
276        if not (self.access == ACCESS_WRITE or self.access == _ACCESS_DEFAULT):
277            raise RTypeError("mmap can't resize a readonly or copy-on-write memory map.")
278
279    def setdata(self, data, size):
280        """Set the internal data and map size from a PTR."""
281        assert size >= 0
282        self.data = data
283        self.size = size
284
285    def close(self):
286        if _MS_WINDOWS:
287            if self.size > 0:
288                self.unmapview()
289                self.setdata(NODATA, 0)
290            if self.map_handle != INVALID_HANDLE:
291                rwin32.CloseHandle(self.map_handle)
292                self.map_handle = INVALID_HANDLE
293            if self.file_handle != INVALID_HANDLE:
294                rwin32.CloseHandle(self.file_handle)
295                self.file_handle = INVALID_HANDLE
296        elif _POSIX:
297            self.closed = True
298            if self.fd != -1:
299                # XXX this is buggy - raising in an RPython del is not a good
300                #     idea, we should swallow the exception or ignore the
301                #     underlaying close error code
302                os.close(self.fd)
303                self.fd = -1
304            if self.size > 0:
305                c_munmap_safe(self.getptr(0), self.size)
306                self.setdata(NODATA, 0)
307
308    def __del__(self):
309        self.close()
310
311    def unmapview(self):
312        UnmapViewOfFile(self.getptr(0))
313
314    def read_byte(self):
315        self.check_valid()
316
317        if self.pos < self.size:
318            value = self.data[self.pos]
319            self.pos += 1
320            return value
321        else:
322            raise RValueError("read byte out of range")
323
324    def readline(self):
325        self.check_valid()
326
327        data = self.data
328        for pos in xrange(self.pos, self.size):
329            if data[pos] == '\n':
330                eol = pos + 1 # we're interested in the position after new line
331                break
332        else: # no '\n' found
333            eol = self.size
334
335        res = "".join([data[i] for i in range(self.pos, eol)])
336        self.pos += len(res)
337        return res
338
339    def read(self, num=-1):
340        self.check_valid()
341
342        if num < 0:
343            # read all
344            eol = self.size
345        else:
346            eol = self.pos + num
347            # silently adjust out of range requests
348            if eol > self.size:
349                eol = self.size
350
351        res = [self.data[i] for i in range(self.pos, eol)]
352        res = "".join(res)
353        self.pos += len(res)
354        return res
355
356    def find(self, tofind, start, end, reverse=False):
357        self.check_valid()
358
359        # XXX naive! how can we reuse the rstr algorithm?
360        if start < 0:
361            start += self.size
362            if start < 0:
363                start = 0
364        if end < 0:
365            end += self.size
366            if end < 0:
367                end = 0
368        elif end > self.size:
369            end = self.size
370        #
371        upto = end - len(tofind)
372        if not reverse:
373            step = 1
374            p = start
375            if p > upto:
376                return -1      # failure (empty range to search)
377        else:
378            step = -1
379            p = upto
380            upto = start
381            if p < upto:
382                return -1      # failure (empty range to search)
383        #
384        data = self.data
385        while True:
386            assert p >= 0
387            for q in range(len(tofind)):
388                if data[p+q] != tofind[q]:
389                    break     # position 'p' is not a match
390            else:
391                # full match
392                return p
393            #
394            if p == upto:
395                return -1   # failure
396            p += step
397
398    def seek(self, pos, whence=0):
399        self.check_valid()
400
401        dist = pos
402        how = whence
403
404        if how == 0: # relative to start
405            where = dist
406        elif how == 1: # relative to current position
407            where = self.pos + dist
408        elif how == 2: # relative to the end
409            where = self.size + dist
410        else:
411            raise RValueError("unknown seek type")
412
413        if not (0 <= where <= self.size):
414            raise RValueError("seek out of range")
415
416        self.pos = where
417
418    def tell(self):
419        self.check_valid()
420        return self.pos
421
422    def file_size(self):
423        self.check_valid()
424
425        size = self.size
426        if _MS_WINDOWS:
427            if self.file_handle != INVALID_HANDLE:
428                low, high = _get_file_size(self.file_handle)
429                if not high and low <= sys.maxint:
430                    return low
431                # not so sure if the signed/unsigned strictness is a good idea:
432                high = rffi.cast(lltype.Unsigned, high)
433                low = rffi.cast(lltype.Unsigned, low)
434                size = (high << 32) + low
435                size = rffi.cast(lltype.Signed, size)
436        elif _POSIX:
437            st = os.fstat(self.fd)
438            size = st[stat.ST_SIZE]
439        return size
440
441    def write(self, data):
442        self.check_valid()
443        self.check_writeable()
444
445        data_len = len(data)
446        if self.pos + data_len > self.size:
447            raise RValueError("data out of range")
448
449        internaldata = self.data
450        start = self.pos
451        for i in range(data_len):
452            internaldata[start+i] = data[i]
453        self.pos = start + data_len
454
455    def write_byte(self, byte):
456        self.check_valid()
457
458        if len(byte) != 1:
459            raise RTypeError("write_byte() argument must be char")
460
461        self.check_writeable()
462        if self.pos >= self.size:
463            raise RValueError("write byte out of range")
464
465        self.data[self.pos] = byte[0]
466        self.pos += 1
467
468    def getptr(self, offset):
469        return rffi.ptradd(self.data, offset)
470
471    def flush(self, offset=0, size=0):
472        self.check_valid()
473
474        if size == 0:
475            size = self.size
476        if offset < 0 or size < 0 or offset + size > self.size:
477            raise RValueError("flush values out of range")
478        else:
479            start = self.getptr(offset)
480            if _MS_WINDOWS:
481                res = FlushViewOfFile(start, size)
482                # XXX res == 0 means that an error occurred, but in CPython
483                # this is not checked
484                return res
485            elif _POSIX:
486##                XXX why is this code here?  There is no equivalent in CPython
487##                if _LINUX:
488##                    # alignment of the address
489##                    value = cast(self.data, c_void_p).value
490##                    aligned_value = value & ~(PAGESIZE - 1)
491##                    # the size should be increased too. otherwise the final
492##                    # part is not "msynced"
493##                    new_size = size + value & (PAGESIZE - 1)
494                res = c_msync(start, size, MS_SYNC)
495                if res == -1:
496                    errno = rposix.get_errno()
497                    raise OSError(errno, os.strerror(errno))
498
499        return 0
500
501    def move(self, dest, src, count):
502        self.check_valid()
503
504        self.check_writeable()
505
506        # check boundings
507        if (src < 0 or dest < 0 or count < 0 or
508            src + count > self.size or dest + count > self.size):
509            raise RValueError("source or destination out of range")
510
511        datasrc = self.getptr(src)
512        datadest = self.getptr(dest)
513        c_memmove(datadest, datasrc, count)
514
515    def resize(self, newsize):
516        self.check_valid()
517
518        self.check_resizeable()
519
520        if _POSIX:
521            if not has_mremap:
522                raise RValueError("mmap: resizing not available--no mremap()")
523
524            # resize the underlying file first
525            os.ftruncate(self.fd, self.offset + newsize)
526
527            # now resize the mmap
528            newdata = c_mremap(self.getptr(0), self.size, newsize,
529                               MREMAP_MAYMOVE or 0)
530            self.setdata(newdata, newsize)
531        elif _MS_WINDOWS:
532            # disconnect the mapping
533            self.unmapview()
534            rwin32.CloseHandle(self.map_handle)
535
536            # move to the desired EOF position
537            if _64BIT:
538                newsize_high = (self.offset + newsize) >> 32
539                newsize_low = (self.offset + newsize) & 0xFFFFFFFF
540                offset_high = self.offset >> 32
541                offset_low = self.offset & 0xFFFFFFFF
542            else:
543                newsize_high = 0
544                newsize_low = self.offset + newsize
545                offset_high = 0
546                offset_low = self.offset
547
548            FILE_BEGIN = 0
549            high_ref = lltype.malloc(PLONG.TO, 1, flavor='raw')
550            try:
551                high_ref[0] = newsize_high
552                SetFilePointer(self.file_handle, newsize_low, high_ref,
553                               FILE_BEGIN)
554            finally:
555                lltype.free(high_ref, flavor='raw')
556            # resize the file
557            SetEndOfFile(self.file_handle)
558            # create another mapping object and remap the file view
559            res = CreateFileMapping(self.file_handle, NULL, PAGE_READWRITE,
560                                 newsize_high, newsize_low, self.tagname)
561            self.map_handle = res
562
563            dwErrCode = 0
564            if self.map_handle:
565                data = MapViewOfFile(self.map_handle, FILE_MAP_WRITE,
566                                     offset_high, offset_low, newsize)
567                if data:
568                    # XXX we should have a real LPVOID which must always be casted
569                    charp = rffi.cast(LPCSTR, data)
570                    self.setdata(charp, newsize)
571                    return
572            winerror = rwin32.lastWindowsError()
573            if self.map_handle:
574                rwin32.CloseHandle(self.map_handle)
575            self.map_handle = INVALID_HANDLE
576            raise winerror
577
578    def len(self):
579        self.check_valid()
580
581        return self.size
582
583    def getitem(self, index):
584        self.check_valid()
585        # simplified version, for rpython
586        if index < 0:
587            index += self.size
588        return self.data[index]
589
590    def setitem(self, index, value):
591        self.check_valid()
592        self.check_writeable()
593
594        if len(value) != 1:
595            raise RValueError("mmap assignment must be "
596                             "single-character string")
597        if index < 0:
598            index += self.size
599        self.data[index] = value[0]
600
601def _check_map_size(size):
602    if size < 0:
603        raise RTypeError("memory mapped size must be positive")
604
605if _POSIX:
606    def mmap(fileno, length, flags=MAP_SHARED,
607        prot=PROT_WRITE | PROT_READ, access=_ACCESS_DEFAULT, offset=0):
608
609        fd = fileno
610
611        # check access is not there when flags and prot are there
612        if access != _ACCESS_DEFAULT and ((flags != MAP_SHARED) or\
613                                          (prot != (PROT_WRITE | PROT_READ))):
614            raise RValueError("mmap can't specify both access and flags, prot.")
615
616        # check size boundaries
617        _check_map_size(length)
618        map_size = length
619        if offset < 0:
620            raise RValueError("negative offset")
621
622        if access == ACCESS_READ:
623            flags = MAP_SHARED
624            prot = PROT_READ
625        elif access == ACCESS_WRITE:
626            flags = MAP_SHARED
627            prot = PROT_READ | PROT_WRITE
628        elif access == ACCESS_COPY:
629            flags = MAP_PRIVATE
630            prot = PROT_READ | PROT_WRITE
631        elif access == _ACCESS_DEFAULT:
632            # map prot to access type
633            if prot & PROT_READ and prot & PROT_WRITE:
634                pass  # _ACCESS_DEFAULT
635            elif prot & PROT_WRITE:
636                access = ACCESS_WRITE
637            else:
638                access = ACCESS_READ
639        else:
640            raise RValueError("mmap invalid access parameter.")
641
642        # check file size
643        try:
644            st = os.fstat(fd)
645        except OSError:
646            pass     # ignore errors and trust map_size
647        else:
648            mode = st[stat.ST_MODE]
649            size = st[stat.ST_SIZE]
650            if stat.S_ISREG(mode):
651                if map_size == 0:
652                    if offset > size:
653                        raise RValueError(
654                            "mmap offset is greater than file size")
655                    map_size = int(size - offset)
656                    if map_size != size - offset:
657                        raise RValueError("mmap length is too large")
658                elif offset + map_size > size:
659                    raise RValueError("mmap length is greater than file size")
660
661        m = MMap(access, offset)
662        if fd == -1:
663            # Assume the caller wants to map anonymous memory.
664            # This is the same behaviour as Windows.  mmap.mmap(-1, size)
665            # on both Windows and Unix map anonymous memory.
666            m.fd = -1
667
668            flags |= MAP_ANONYMOUS
669
670        else:
671            m.fd = os.dup(fd)
672
673        # XXX if we use hintp below in alloc, the NonConstant
674        #     is necessary since we want a general version of c_mmap
675        #     to be annotated with a non-constant pointer.
676        res = c_mmap(NonConstant(NULL), map_size, prot, flags, fd, offset)
677        if res == rffi.cast(PTR, -1):
678            errno = rposix.get_errno()
679            raise OSError(errno, os.strerror(errno))
680
681        m.setdata(res, map_size)
682        return m
683
684    # XXX is this really necessary?
685    class Hint:
686        pos = -0x4fff0000   # for reproducible results
687    hint = Hint()
688
689    def alloc(map_size):
690        """Allocate memory.  This is intended to be used by the JIT,
691        so the memory has the executable bit set and gets allocated
692        internally in case of a sandboxed process.
693        """
694        flags = MAP_PRIVATE | MAP_ANONYMOUS
695        prot = PROT_EXEC | PROT_READ | PROT_WRITE
696        hintp = rffi.cast(PTR, hint.pos)
697        res = c_mmap_safe(hintp, map_size, prot, flags, -1, 0)
698        if res == rffi.cast(PTR, -1):
699            # some systems (some versions of OS/X?) complain if they
700            # are passed a non-zero address.  Try again.
701            hintp = rffi.cast(PTR, 0)
702            res = c_mmap_safe(hintp, map_size, prot, flags, -1, 0)
703            if res == rffi.cast(PTR, -1):
704                raise MemoryError
705        else:
706            hint.pos += map_size
707        return res
708    alloc._annenforceargs_ = (int,)
709
710    free = c_munmap_safe
711
712elif _MS_WINDOWS:
713    def mmap(fileno, length, tagname="", access=_ACCESS_DEFAULT, offset=0):
714        # check size boundaries
715        _check_map_size(length)
716        map_size = length
717        if offset < 0:
718            raise RValueError("negative offset")
719
720        flProtect = 0
721        dwDesiredAccess = 0
722        fh = NULL_HANDLE
723
724        if access == ACCESS_READ:
725            flProtect = PAGE_READONLY
726            dwDesiredAccess = FILE_MAP_READ
727        elif access == _ACCESS_DEFAULT or access == ACCESS_WRITE:
728            flProtect = PAGE_READWRITE
729            dwDesiredAccess = FILE_MAP_WRITE
730        elif access == ACCESS_COPY:
731            flProtect = PAGE_WRITECOPY
732            dwDesiredAccess = FILE_MAP_COPY
733        else:
734            raise RValueError("mmap invalid access parameter.")
735
736        # assume -1 and 0 both mean invalid file descriptor
737        # to 'anonymously' map memory.
738        if fileno != -1 and fileno != 0:
739            fh = rwin32._get_osfhandle(fileno)
740            if fh == INVALID_HANDLE:
741                errno = rposix.get_errno()
742                raise OSError(errno, os.strerror(errno))
743            # Win9x appears to need us seeked to zero
744            # SEEK_SET = 0
745            # libc._lseek(fileno, 0, SEEK_SET)
746
747        m = MMap(access, offset)
748        m.file_handle = INVALID_HANDLE
749        m.map_handle = INVALID_HANDLE
750        if fh:
751            # it is necessary to duplicate the handle, so the
752            # Python code can close it on us
753            handle_ref = lltype.malloc(LPHANDLE.TO, 1, flavor='raw')
754            handle_ref[0] = m.file_handle
755            try:
756                res = DuplicateHandle(GetCurrentProcess(), # source process handle
757                                      fh, # handle to be duplicated
758                                      GetCurrentProcess(), # target process handle
759                                      handle_ref, # result
760                                      0, # access - ignored due to options value
761                                      False, # inherited by child procs?
762                                      DUPLICATE_SAME_ACCESS) # options
763                if not res:
764                    raise rwin32.lastWindowsError()
765                m.file_handle = handle_ref[0]
766            finally:
767                lltype.free(handle_ref, flavor='raw')
768
769            if not map_size:
770                low, high = _get_file_size(fh)
771                if _64BIT:
772                    map_size = (low << 32) + 1
773                else:
774                    if high:
775                        # file is too large to map completely
776                        map_size = -1
777                    else:
778                        map_size = low
779
780        if tagname:
781            m.tagname = tagname
782
783        # DWORD is a 4-byte int. If int > 4-byte it must be divided
784        if _64BIT:
785            size_hi = (map_size + offset) >> 32
786            size_lo = (map_size + offset) & 0xFFFFFFFF
787            offset_hi = offset >> 32
788            offset_lo = offset & 0xFFFFFFFF
789        else:
790            size_hi = 0
791            size_lo = map_size + offset
792            offset_hi = 0
793            offset_lo = offset
794
795        m.map_handle = CreateFileMapping(m.file_handle, NULL, flProtect,
796                                         size_hi, size_lo, m.tagname)
797
798        if m.map_handle:
799            data = MapViewOfFile(m.map_handle, dwDesiredAccess,
800                                 offset_hi, offset_lo, length)
801            if data:
802                # XXX we should have a real LPVOID which must always be casted
803                charp = rffi.cast(LPCSTR, data)
804                m.setdata(charp, map_size)
805                return m
806        winerror = rwin32.lastWindowsError()
807        if m.map_handle:
808            rwin32.CloseHandle(m.map_handle)
809        m.map_handle = INVALID_HANDLE
810        raise winerror
811
812    def alloc(map_size):
813        """Allocate memory.  This is intended to be used by the JIT,
814        so the memory has the executable bit set.
815        XXX implement me: it should get allocated internally in
816        case of a sandboxed process
817        """
818        null = lltype.nullptr(rffi.VOIDP.TO)
819        res = VirtualAlloc(null, map_size, MEM_COMMIT|MEM_RESERVE,
820                           PAGE_EXECUTE_READWRITE)
821        if not res:
822            raise MemoryError
823        arg = lltype.malloc(LPDWORD.TO, 1, zero=True, flavor='raw')
824        VirtualProtect(res, map_size, PAGE_EXECUTE_READWRITE, arg)
825        lltype.free(arg, flavor='raw')
826        # ignore errors, just try
827        return res
828    alloc._annenforceargs_ = (int,)
829
830    def free(ptr, map_size):
831        VirtualFree(ptr, 0, MEM_RELEASE)
832
833# register_external here?