PageRenderTime 53ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/pypy/rlib/rmmap.py

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