PageRenderTime 56ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/rlib/rmmap.py

https://bitbucket.org/pypy/pypy/
Python | 833 lines | 773 code | 37 blank | 23 comment | 26 complexity | 6584df68fe662dc3422e15d29643f57b MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  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. # low might just happen to have the value INVALID_FILE_SIZE
  197. # so we need to check the last error also
  198. INVALID_FILE_SIZE = -1
  199. if low == INVALID_FILE_SIZE:
  200. err = rwin32.GetLastError()
  201. if err:
  202. raise WindowsError(err, "mmap")
  203. return low, high
  204. finally:
  205. lltype.free(high_ref, flavor='raw')
  206. INVALID_HANDLE = INVALID_HANDLE_VALUE
  207. PAGESIZE = _get_page_size()
  208. ALLOCATIONGRANULARITY = _get_allocation_granularity()
  209. NULL = lltype.nullptr(PTR.TO)
  210. NODATA = lltype.nullptr(PTR.TO)
  211. class MMap(object):
  212. def __init__(self, access, offset):
  213. self.size = 0
  214. self.pos = 0
  215. self.access = access
  216. self.offset = offset
  217. if _MS_WINDOWS:
  218. self.map_handle = NULL_HANDLE
  219. self.file_handle = NULL_HANDLE
  220. self.tagname = ""
  221. elif _POSIX:
  222. self.fd = -1
  223. self.closed = False
  224. def check_valid(self):
  225. if _MS_WINDOWS:
  226. to_close = self.map_handle == INVALID_HANDLE
  227. elif _POSIX:
  228. to_close = self.closed
  229. if to_close:
  230. raise RValueError("map closed or invalid")
  231. def check_writeable(self):
  232. if not (self.access != ACCESS_READ):
  233. raise RTypeError("mmap can't modify a readonly memory map.")
  234. def check_resizeable(self):
  235. if not (self.access == ACCESS_WRITE or self.access == _ACCESS_DEFAULT):
  236. raise RTypeError("mmap can't resize a readonly or copy-on-write memory map.")
  237. def setdata(self, data, size):
  238. """Set the internal data and map size from a PTR."""
  239. assert size >= 0
  240. self.data = data
  241. self.size = size
  242. def close(self):
  243. if _MS_WINDOWS:
  244. if self.size > 0:
  245. self.unmapview()
  246. self.setdata(NODATA, 0)
  247. if self.map_handle != INVALID_HANDLE:
  248. rwin32.CloseHandle(self.map_handle)
  249. self.map_handle = INVALID_HANDLE
  250. if self.file_handle != INVALID_HANDLE:
  251. rwin32.CloseHandle(self.file_handle)
  252. self.file_handle = INVALID_HANDLE
  253. elif _POSIX:
  254. self.closed = True
  255. if self.fd != -1:
  256. # XXX this is buggy - raising in an RPython del is not a good
  257. # idea, we should swallow the exception or ignore the
  258. # underlaying close error code
  259. os.close(self.fd)
  260. self.fd = -1
  261. if self.size > 0:
  262. c_munmap_safe(self.getptr(0), self.size)
  263. self.setdata(NODATA, 0)
  264. def __del__(self):
  265. self.close()
  266. def unmapview(self):
  267. UnmapViewOfFile(self.getptr(0))
  268. def read_byte(self):
  269. self.check_valid()
  270. if self.pos < self.size:
  271. value = self.data[self.pos]
  272. self.pos += 1
  273. return value
  274. else:
  275. raise RValueError("read byte out of range")
  276. def readline(self):
  277. self.check_valid()
  278. data = self.data
  279. for pos in xrange(self.pos, self.size):
  280. if data[pos] == '\n':
  281. eol = pos + 1 # we're interested in the position after new line
  282. break
  283. else: # no '\n' found
  284. eol = self.size
  285. res = "".join([data[i] for i in range(self.pos, eol)])
  286. self.pos += len(res)
  287. return res
  288. def read(self, num=-1):
  289. self.check_valid()
  290. if num < 0:
  291. # read all
  292. eol = self.size
  293. else:
  294. eol = self.pos + num
  295. # silently adjust out of range requests
  296. if eol > self.size:
  297. eol = self.size
  298. res = [self.data[i] for i in range(self.pos, eol)]
  299. res = "".join(res)
  300. self.pos += len(res)
  301. return res
  302. def find(self, tofind, start, end, reverse=False):
  303. self.check_valid()
  304. # XXX naive! how can we reuse the rstr algorithm?
  305. if start < 0:
  306. start += self.size
  307. if start < 0:
  308. start = 0
  309. if end < 0:
  310. end += self.size
  311. if end < 0:
  312. end = 0
  313. elif end > self.size:
  314. end = self.size
  315. #
  316. upto = end - len(tofind)
  317. if not reverse:
  318. step = 1
  319. p = start
  320. if p > upto:
  321. return -1 # failure (empty range to search)
  322. else:
  323. step = -1
  324. p = upto
  325. upto = start
  326. if p < upto:
  327. return -1 # failure (empty range to search)
  328. #
  329. data = self.data
  330. while True:
  331. assert p >= 0
  332. for q in range(len(tofind)):
  333. if data[p+q] != tofind[q]:
  334. break # position 'p' is not a match
  335. else:
  336. # full match
  337. return p
  338. #
  339. if p == upto:
  340. return -1 # failure
  341. p += step
  342. def seek(self, pos, whence=0):
  343. self.check_valid()
  344. dist = pos
  345. how = whence
  346. if how == 0: # relative to start
  347. where = dist
  348. elif how == 1: # relative to current position
  349. where = self.pos + dist
  350. elif how == 2: # relative to the end
  351. where = self.size + dist
  352. else:
  353. raise RValueError("unknown seek type")
  354. if not (0 <= where <= self.size):
  355. raise RValueError("seek out of range")
  356. self.pos = where
  357. def tell(self):
  358. self.check_valid()
  359. return self.pos
  360. def file_size(self):
  361. self.check_valid()
  362. size = self.size
  363. if _MS_WINDOWS:
  364. if self.file_handle != INVALID_HANDLE:
  365. low, high = _get_file_size(self.file_handle)
  366. if not high and low <= sys.maxint:
  367. return low
  368. # not so sure if the signed/unsigned strictness is a good idea:
  369. high = rffi.cast(lltype.Unsigned, high)
  370. low = rffi.cast(lltype.Unsigned, low)
  371. size = (high << 32) + low
  372. size = rffi.cast(lltype.Signed, size)
  373. elif _POSIX:
  374. st = os.fstat(self.fd)
  375. size = st[stat.ST_SIZE]
  376. return size
  377. def write(self, data):
  378. self.check_valid()
  379. self.check_writeable()
  380. data_len = len(data)
  381. if self.pos + data_len > self.size:
  382. raise RValueError("data out of range")
  383. internaldata = self.data
  384. start = self.pos
  385. for i in range(data_len):
  386. internaldata[start+i] = data[i]
  387. self.pos = start + data_len
  388. def write_byte(self, byte):
  389. self.check_valid()
  390. if len(byte) != 1:
  391. raise RTypeError("write_byte() argument must be char")
  392. self.check_writeable()
  393. if self.pos >= self.size:
  394. raise RValueError("write byte out of range")
  395. self.data[self.pos] = byte[0]
  396. self.pos += 1
  397. def getptr(self, offset):
  398. return rffi.ptradd(self.data, offset)
  399. def flush(self, offset=0, size=0):
  400. self.check_valid()
  401. if size == 0:
  402. size = self.size
  403. if offset < 0 or size < 0 or offset + size > self.size:
  404. raise RValueError("flush values out of range")
  405. else:
  406. start = self.getptr(offset)
  407. if _MS_WINDOWS:
  408. res = FlushViewOfFile(start, size)
  409. # XXX res == 0 means that an error occurred, but in CPython
  410. # this is not checked
  411. return res
  412. elif _POSIX:
  413. ## XXX why is this code here? There is no equivalent in CPython
  414. ## if _LINUX:
  415. ## # alignment of the address
  416. ## value = cast(self.data, c_void_p).value
  417. ## aligned_value = value & ~(PAGESIZE - 1)
  418. ## # the size should be increased too. otherwise the final
  419. ## # part is not "msynced"
  420. ## new_size = size + value & (PAGESIZE - 1)
  421. res = c_msync(start, size, MS_SYNC)
  422. if res == -1:
  423. errno = rposix.get_errno()
  424. raise OSError(errno, os.strerror(errno))
  425. return 0
  426. def move(self, dest, src, count):
  427. self.check_valid()
  428. self.check_writeable()
  429. # check boundings
  430. if (src < 0 or dest < 0 or count < 0 or
  431. src + count > self.size or dest + count > self.size):
  432. raise RValueError("source or destination out of range")
  433. datasrc = self.getptr(src)
  434. datadest = self.getptr(dest)
  435. c_memmove(datadest, datasrc, count)
  436. def resize(self, newsize):
  437. self.check_valid()
  438. self.check_resizeable()
  439. if _POSIX:
  440. if not has_mremap:
  441. raise RValueError("mmap: resizing not available--no mremap()")
  442. # resize the underlying file first
  443. os.ftruncate(self.fd, self.offset + newsize)
  444. # now resize the mmap
  445. newdata = c_mremap(self.getptr(0), self.size, newsize,
  446. MREMAP_MAYMOVE or 0)
  447. self.setdata(newdata, newsize)
  448. elif _MS_WINDOWS:
  449. # disconnect the mapping
  450. self.unmapview()
  451. rwin32.CloseHandle(self.map_handle)
  452. # move to the desired EOF position
  453. if _64BIT:
  454. newsize_high = (self.offset + newsize) >> 32
  455. newsize_low = (self.offset + newsize) & 0xFFFFFFFF
  456. offset_high = self.offset >> 32
  457. offset_low = self.offset & 0xFFFFFFFF
  458. else:
  459. newsize_high = 0
  460. newsize_low = self.offset + newsize
  461. offset_high = 0
  462. offset_low = self.offset
  463. FILE_BEGIN = 0
  464. high_ref = lltype.malloc(PLONG.TO, 1, flavor='raw')
  465. try:
  466. high_ref[0] = newsize_high
  467. SetFilePointer(self.file_handle, newsize_low, high_ref,
  468. FILE_BEGIN)
  469. finally:
  470. lltype.free(high_ref, flavor='raw')
  471. # resize the file
  472. SetEndOfFile(self.file_handle)
  473. # create another mapping object and remap the file view
  474. res = CreateFileMapping(self.file_handle, NULL, PAGE_READWRITE,
  475. newsize_high, newsize_low, self.tagname)
  476. self.map_handle = res
  477. dwErrCode = 0
  478. if self.map_handle:
  479. data = MapViewOfFile(self.map_handle, FILE_MAP_WRITE,
  480. offset_high, offset_low, newsize)
  481. if data:
  482. # XXX we should have a real LPVOID which must always be casted
  483. charp = rffi.cast(LPCSTR, data)
  484. self.setdata(charp, newsize)
  485. return
  486. winerror = rwin32.lastWindowsError()
  487. if self.map_handle:
  488. rwin32.CloseHandle(self.map_handle)
  489. self.map_handle = INVALID_HANDLE
  490. raise winerror
  491. def len(self):
  492. self.check_valid()
  493. return self.size
  494. def getitem(self, index):
  495. self.check_valid()
  496. # simplified version, for rpython
  497. if index < 0:
  498. index += self.size
  499. return self.data[index]
  500. def setitem(self, index, value):
  501. self.check_valid()
  502. self.check_writeable()
  503. if len(value) != 1:
  504. raise RValueError("mmap assignment must be "
  505. "single-character string")
  506. if index < 0:
  507. index += self.size
  508. self.data[index] = value[0]
  509. def _check_map_size(size):
  510. if size < 0:
  511. raise RTypeError("memory mapped size must be positive")
  512. if _POSIX:
  513. def mmap(fileno, length, flags=MAP_SHARED,
  514. prot=PROT_WRITE | PROT_READ, access=_ACCESS_DEFAULT, offset=0):
  515. fd = fileno
  516. # check access is not there when flags and prot are there
  517. if access != _ACCESS_DEFAULT and ((flags != MAP_SHARED) or\
  518. (prot != (PROT_WRITE | PROT_READ))):
  519. raise RValueError("mmap can't specify both access and flags, prot.")
  520. # check size boundaries
  521. _check_map_size(length)
  522. map_size = length
  523. if offset < 0:
  524. raise RValueError("negative offset")
  525. if access == ACCESS_READ:
  526. flags = MAP_SHARED
  527. prot = PROT_READ
  528. elif access == ACCESS_WRITE:
  529. flags = MAP_SHARED
  530. prot = PROT_READ | PROT_WRITE
  531. elif access == ACCESS_COPY:
  532. flags = MAP_PRIVATE
  533. prot = PROT_READ | PROT_WRITE
  534. elif access == _ACCESS_DEFAULT:
  535. # map prot to access type
  536. if prot & PROT_READ and prot & PROT_WRITE:
  537. pass # _ACCESS_DEFAULT
  538. elif prot & PROT_WRITE:
  539. access = ACCESS_WRITE
  540. else:
  541. access = ACCESS_READ
  542. else:
  543. raise RValueError("mmap invalid access parameter.")
  544. # check file size
  545. try:
  546. st = os.fstat(fd)
  547. except OSError:
  548. pass # ignore errors and trust map_size
  549. else:
  550. mode = st[stat.ST_MODE]
  551. size = st[stat.ST_SIZE]
  552. if stat.S_ISREG(mode):
  553. if map_size == 0:
  554. if offset > size:
  555. raise RValueError(
  556. "mmap offset is greater than file size")
  557. map_size = int(size - offset)
  558. if map_size != size - offset:
  559. raise RValueError("mmap length is too large")
  560. elif offset + map_size > size:
  561. raise RValueError("mmap length is greater than file size")
  562. m = MMap(access, offset)
  563. if fd == -1:
  564. # Assume the caller wants to map anonymous memory.
  565. # This is the same behaviour as Windows. mmap.mmap(-1, size)
  566. # on both Windows and Unix map anonymous memory.
  567. m.fd = -1
  568. flags |= MAP_ANONYMOUS
  569. else:
  570. m.fd = os.dup(fd)
  571. # XXX if we use hintp below in alloc, the NonConstant
  572. # is necessary since we want a general version of c_mmap
  573. # to be annotated with a non-constant pointer.
  574. res = c_mmap(NonConstant(NULL), map_size, prot, flags, fd, offset)
  575. if res == rffi.cast(PTR, -1):
  576. errno = rposix.get_errno()
  577. raise OSError(errno, os.strerror(errno))
  578. m.setdata(res, map_size)
  579. return m
  580. # XXX is this really necessary?
  581. class Hint:
  582. pos = -0x4fff0000 # for reproducible results
  583. hint = Hint()
  584. def alloc(map_size):
  585. """Allocate memory. This is intended to be used by the JIT,
  586. so the memory has the executable bit set and gets allocated
  587. internally in case of a sandboxed process.
  588. """
  589. flags = MAP_PRIVATE | MAP_ANONYMOUS
  590. prot = PROT_EXEC | PROT_READ | PROT_WRITE
  591. hintp = rffi.cast(PTR, hint.pos)
  592. res = c_mmap_safe(hintp, map_size, prot, flags, -1, 0)
  593. if res == rffi.cast(PTR, -1):
  594. # some systems (some versions of OS/X?) complain if they
  595. # are passed a non-zero address. Try again.
  596. hintp = rffi.cast(PTR, 0)
  597. res = c_mmap_safe(hintp, map_size, prot, flags, -1, 0)
  598. if res == rffi.cast(PTR, -1):
  599. raise MemoryError
  600. else:
  601. hint.pos += map_size
  602. return res
  603. alloc._annenforceargs_ = (int,)
  604. free = c_munmap_safe
  605. elif _MS_WINDOWS:
  606. def mmap(fileno, length, tagname="", access=_ACCESS_DEFAULT, offset=0):
  607. # check size boundaries
  608. _check_map_size(length)
  609. map_size = length
  610. if offset < 0:
  611. raise RValueError("negative offset")
  612. flProtect = 0
  613. dwDesiredAccess = 0
  614. fh = NULL_HANDLE
  615. if access == ACCESS_READ:
  616. flProtect = PAGE_READONLY
  617. dwDesiredAccess = FILE_MAP_READ
  618. elif access == _ACCESS_DEFAULT or access == ACCESS_WRITE:
  619. flProtect = PAGE_READWRITE
  620. dwDesiredAccess = FILE_MAP_WRITE
  621. elif access == ACCESS_COPY:
  622. flProtect = PAGE_WRITECOPY
  623. dwDesiredAccess = FILE_MAP_COPY
  624. else:
  625. raise RValueError("mmap invalid access parameter.")
  626. # assume -1 and 0 both mean invalid file descriptor
  627. # to 'anonymously' map memory.
  628. if fileno != -1 and fileno != 0:
  629. fh = rwin32._get_osfhandle(fileno)
  630. if fh == INVALID_HANDLE:
  631. errno = rposix.get_errno()
  632. raise OSError(errno, os.strerror(errno))
  633. # Win9x appears to need us seeked to zero
  634. # SEEK_SET = 0
  635. # libc._lseek(fileno, 0, SEEK_SET)
  636. m = MMap(access, offset)
  637. m.file_handle = INVALID_HANDLE
  638. m.map_handle = INVALID_HANDLE
  639. if fh:
  640. # it is necessary to duplicate the handle, so the
  641. # Python code can close it on us
  642. handle_ref = lltype.malloc(LPHANDLE.TO, 1, flavor='raw')
  643. handle_ref[0] = m.file_handle
  644. try:
  645. res = DuplicateHandle(GetCurrentProcess(), # source process handle
  646. fh, # handle to be duplicated
  647. GetCurrentProcess(), # target process handle
  648. handle_ref, # result
  649. 0, # access - ignored due to options value
  650. False, # inherited by child procs?
  651. DUPLICATE_SAME_ACCESS) # options
  652. if not res:
  653. raise rwin32.lastWindowsError()
  654. m.file_handle = handle_ref[0]
  655. finally:
  656. lltype.free(handle_ref, flavor='raw')
  657. if not map_size:
  658. low, high = _get_file_size(fh)
  659. if _64BIT:
  660. map_size = (low << 32) + 1
  661. else:
  662. if high:
  663. # file is too large to map completely
  664. map_size = -1
  665. else:
  666. map_size = low
  667. if tagname:
  668. m.tagname = tagname
  669. # DWORD is a 4-byte int. If int > 4-byte it must be divided
  670. if _64BIT:
  671. size_hi = (map_size + offset) >> 32
  672. size_lo = (map_size + offset) & 0xFFFFFFFF
  673. offset_hi = offset >> 32
  674. offset_lo = offset & 0xFFFFFFFF
  675. else:
  676. size_hi = 0
  677. size_lo = map_size + offset
  678. offset_hi = 0
  679. offset_lo = offset
  680. m.map_handle = CreateFileMapping(m.file_handle, NULL, flProtect,
  681. size_hi, size_lo, m.tagname)
  682. if m.map_handle:
  683. data = MapViewOfFile(m.map_handle, dwDesiredAccess,
  684. offset_hi, offset_lo, length)
  685. if data:
  686. # XXX we should have a real LPVOID which must always be casted
  687. charp = rffi.cast(LPCSTR, data)
  688. m.setdata(charp, map_size)
  689. return m
  690. winerror = rwin32.lastWindowsError()
  691. if m.map_handle:
  692. rwin32.CloseHandle(m.map_handle)
  693. m.map_handle = INVALID_HANDLE
  694. raise winerror
  695. def alloc(map_size):
  696. """Allocate memory. This is intended to be used by the JIT,
  697. so the memory has the executable bit set.
  698. XXX implement me: it should get allocated internally in
  699. case of a sandboxed process
  700. """
  701. null = lltype.nullptr(rffi.VOIDP.TO)
  702. res = VirtualAlloc(null, map_size, MEM_COMMIT|MEM_RESERVE,
  703. PAGE_EXECUTE_READWRITE)
  704. if not res:
  705. raise MemoryError
  706. arg = lltype.malloc(LPDWORD.TO, 1, zero=True, flavor='raw')
  707. VirtualProtect(res, map_size, PAGE_EXECUTE_READWRITE, arg)
  708. lltype.free(arg, flavor='raw')
  709. # ignore errors, just try
  710. return res
  711. alloc._annenforceargs_ = (int,)
  712. def free(ptr, map_size):
  713. VirtualFree(ptr, 0, MEM_RELEASE)
  714. # register_external here?