mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-04 22:48:38 +00:00
Merge branch 'devel' into pr_field
This commit is contained in:
@@ -28,6 +28,15 @@ elif defined(netbsd):
|
||||
EVFILT_PROC* = 4 ## attached to struct proc
|
||||
EVFILT_SIGNAL* = 5 ## attached to struct proc
|
||||
EVFILT_TIMER* = 6 ## timers (in ms)
|
||||
elif defined(haiku):
|
||||
const
|
||||
EVFILT_READ* = -1
|
||||
EVFILT_WRITE* = -2
|
||||
EVFILT_AIO* = -3 ## attached to aio requests
|
||||
EVFILT_VNODE* = -4 ## attached to vnodes
|
||||
EVFILT_PROC* = -5 ## attached to struct proc
|
||||
EVFILT_SIGNAL* = -6 ## attached to struct proc
|
||||
EVFILT_TIMER* = -7 ## timers
|
||||
when defined(macosx):
|
||||
const
|
||||
EVFILT_MACHPORT* = -8 ## Mach portsets
|
||||
|
||||
@@ -121,6 +121,13 @@ template unCheckedInc(x) =
|
||||
inc(x)
|
||||
{.pop.}
|
||||
|
||||
template newSeqForOverwrite(T: typedesc; len: int): untyped =
|
||||
## Allocates a fixed-length seq whose elements will be assigned by index.
|
||||
when supportsCopyMem(T) and declared(newSeqUninit):
|
||||
newSeqUninit[T](len)
|
||||
else: # TODO: use `newSeqUnsafe` when that's available
|
||||
newSeq[T](len)
|
||||
|
||||
func concat*[T](seqs: varargs[seq[T]]): seq[T] =
|
||||
## Takes several sequences' items and returns them inside a new sequence.
|
||||
## All sequences must be of the same type.
|
||||
@@ -1105,7 +1112,7 @@ template mapIt*(s: typed, op: untyped): untyped =
|
||||
evalOnceAs(s2, s, compiles((let _ = s)))
|
||||
|
||||
var i = 0
|
||||
var result = newSeq[OutType](s2.len)
|
||||
var result = newSeqForOverwrite(OutType, s2.len)
|
||||
for it {.inject.} in s2:
|
||||
result[i] = op
|
||||
i += 1
|
||||
@@ -1171,10 +1178,7 @@ template newSeqWith*(len: int, init: untyped): untyped =
|
||||
assert seqRand[0] != seqRand[1]
|
||||
type T = typeof(init)
|
||||
let newLen = len
|
||||
when supportsCopyMem(T) and declared(newSeqUninit):
|
||||
var result = newSeqUninit[T](newLen)
|
||||
else: # TODO: use `newSeqUnsafe` when that's available
|
||||
var result = newSeq[T](newLen)
|
||||
var result = newSeqForOverwrite(T, newLen)
|
||||
for i in 0 ..< newLen:
|
||||
result[i] = init
|
||||
move(result) # refs bug #7295
|
||||
|
||||
@@ -15,12 +15,16 @@
|
||||
## It also provides some fast iterators over lines in text files (or
|
||||
## other "line-like", variable length, delimited records).
|
||||
|
||||
const
|
||||
nimUseFallBack = defined(nintendoswitch) or defined(nimMemfileFallback)
|
||||
|
||||
when defined(windows):
|
||||
import std/winlean
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/widestrs
|
||||
elif defined(posix):
|
||||
import std/posix
|
||||
when not nimUseFallBack:
|
||||
import std/posix
|
||||
else:
|
||||
{.error: "the memfiles module is not supported on your operating system!".}
|
||||
|
||||
@@ -29,45 +33,48 @@ import std/oserrors
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/[syncio, assertions]
|
||||
elif nimUseFallBack:
|
||||
import std/syncio
|
||||
|
||||
from system/ansi_c import c_memchr
|
||||
|
||||
proc newEIO(msg: string): ref IOError =
|
||||
result = (ref IOError)(msg: msg)
|
||||
|
||||
proc setFileSize(fh: FileHandle, newFileSize = -1, oldSize = -1): OSErrorCode =
|
||||
## Set the size of open file pointed to by `fh` to `newFileSize` if != -1,
|
||||
## allocating | freeing space from the file system. This routine returns the
|
||||
## last OSErrorCode found rather than raising to support old rollback/clean-up
|
||||
## code style. [ Should maybe move to std/osfiles. ]
|
||||
result = OSErrorCode(0)
|
||||
if newFileSize < 0 or newFileSize == oldSize:
|
||||
return result
|
||||
when defined(windows):
|
||||
var sizeHigh = int32(newFileSize shr 32)
|
||||
let sizeLow = int32(newFileSize and 0xffffffff)
|
||||
let status = setFilePointer(Handle fh, sizeLow, addr(sizeHigh), FILE_BEGIN)
|
||||
let lastErr = osLastError()
|
||||
if (status == INVALID_SET_FILE_POINTER and lastErr.int32 != NO_ERROR) or
|
||||
setEndOfFile(Handle fh) == 0:
|
||||
result = lastErr
|
||||
else:
|
||||
if newFileSize > oldSize: # grow the file
|
||||
var e: cint = cint(0) # posix_fallocate truncates up when needed.
|
||||
when declared(posix_fallocate):
|
||||
while (e = posix_fallocate(fh, 0, newFileSize); e == EINTR):
|
||||
discard
|
||||
if e == EINVAL or e == EOPNOTSUPP or e == ENOSYS:
|
||||
# fallback arguable; Most portable BUT allows SEGV
|
||||
if ftruncate(fh, newFileSize) == -1:
|
||||
when not nimUseFallBack:
|
||||
proc setFileSize(fh: FileHandle, newFileSize = -1, oldSize = -1): OSErrorCode =
|
||||
## Set the size of open file pointed to by `fh` to `newFileSize` if != -1,
|
||||
## allocating | freeing space from the file system. This routine returns the
|
||||
## last OSErrorCode found rather than raising to support old rollback/clean-up
|
||||
## code style. [ Should maybe move to std/osfiles. ]
|
||||
result = OSErrorCode(0)
|
||||
if newFileSize < 0 or newFileSize == oldSize:
|
||||
return result
|
||||
when defined(windows):
|
||||
var sizeHigh = int32(newFileSize shr 32)
|
||||
let sizeLow = int32(newFileSize and 0xffffffff)
|
||||
let status = setFilePointer(Handle fh, sizeLow, addr(sizeHigh), FILE_BEGIN)
|
||||
let lastErr = osLastError()
|
||||
if (status == INVALID_SET_FILE_POINTER and lastErr.int32 != NO_ERROR) or
|
||||
setEndOfFile(Handle fh) == 0:
|
||||
result = lastErr
|
||||
else:
|
||||
if newFileSize > oldSize: # grow the file
|
||||
var e: cint = cint(0) # posix_fallocate truncates up when needed.
|
||||
when declared(posix_fallocate):
|
||||
while (e = posix_fallocate(fh, 0, newFileSize); e == EINTR):
|
||||
discard
|
||||
if e == EINVAL or e == EOPNOTSUPP or e == ENOSYS:
|
||||
# fallback arguable; Most portable BUT allows SEGV
|
||||
if ftruncate(fh, newFileSize) == -1:
|
||||
result = osLastError()
|
||||
else:
|
||||
discard
|
||||
elif e != 0:
|
||||
result = osLastError()
|
||||
else: # shrink the file
|
||||
if ftruncate(fh.cint, newFileSize) == -1:
|
||||
result = osLastError()
|
||||
else:
|
||||
discard
|
||||
elif e != 0:
|
||||
result = osLastError()
|
||||
else: # shrink the file
|
||||
if ftruncate(fh.cint, newFileSize) == -1:
|
||||
result = osLastError()
|
||||
|
||||
type
|
||||
MemFile* = object ## represents a memory mapped file
|
||||
@@ -84,6 +91,89 @@ type
|
||||
else:
|
||||
handle*: cint ## **Caution**: Posix specific public field.
|
||||
flags: cint ## **Caution**: Platform specific private field.
|
||||
when nimUseFallBack:
|
||||
backing: string
|
||||
path: string
|
||||
readonly: bool
|
||||
allowRemap: bool
|
||||
|
||||
when nimUseFallBack:
|
||||
proc fallbackMappedSize(backingLen, mappedSize, offset: int): int =
|
||||
if mappedSize < -1:
|
||||
raise newEIO("mappedSize cannot be less than -1")
|
||||
if offset < 0 or offset > backingLen:
|
||||
raise newEIO("offset out of bounds")
|
||||
if mappedSize == -1:
|
||||
result = backingLen - offset
|
||||
else:
|
||||
result = min(mappedSize, backingLen - offset)
|
||||
|
||||
proc setFallbackView(m: var MemFile, mappedSize, offset: int) =
|
||||
m.size = fallbackMappedSize(m.backing.len, mappedSize, offset)
|
||||
if m.size > 0:
|
||||
m.mem = cast[pointer](addr m.backing[offset])
|
||||
else:
|
||||
m.mem = nil
|
||||
|
||||
proc openFallbackMemFile(filename: string, mode: FileMode, mappedSize,
|
||||
offset, newFileSize: int,
|
||||
allowRemap: bool): MemFile =
|
||||
result = MemFile(
|
||||
handle: -1,
|
||||
flags: 0,
|
||||
path: filename,
|
||||
readonly: mode == fmRead,
|
||||
allowRemap: allowRemap
|
||||
)
|
||||
if newFileSize != -1:
|
||||
result.backing = newString(newFileSize)
|
||||
else:
|
||||
result.backing = readFile(filename)
|
||||
setFallbackView(result, mappedSize, offset)
|
||||
|
||||
proc mapMemFallback(m: var MemFile, mode: FileMode,
|
||||
mappedSize, offset: int): pointer =
|
||||
if not m.allowRemap:
|
||||
raise newException(IOError,
|
||||
"Cannot remap MemFile opened with allowRemap=false")
|
||||
if mode != fmRead and m.readonly:
|
||||
raise newEIO("cannot write to read-only mapping")
|
||||
let size = fallbackMappedSize(m.backing.len, mappedSize, offset)
|
||||
if size > 0:
|
||||
result = cast[pointer](addr m.backing[offset])
|
||||
else:
|
||||
result = nil
|
||||
|
||||
proc flushFallback(m: var MemFile) =
|
||||
if m.readonly or m.path.len == 0:
|
||||
return
|
||||
writeFile(m.path, m.backing)
|
||||
|
||||
proc resizeFallback(m: var MemFile, newFileSize: int) =
|
||||
if m.readonly:
|
||||
raise newException(IOError, "Cannot resize read-only MemFile")
|
||||
if not m.allowRemap:
|
||||
raise newException(IOError,
|
||||
"Cannot resize MemFile opened with allowRemap=false")
|
||||
if m.size != m.backing.len:
|
||||
raise newException(IOError, "Cannot resize partial MemFile")
|
||||
let oldLen = m.backing.len
|
||||
m.backing.setLen(newFileSize)
|
||||
for i in oldLen ..< newFileSize:
|
||||
m.backing[i] = '\0'
|
||||
setFallbackView(m, newFileSize, 0)
|
||||
|
||||
proc closeFallback(m: var MemFile) =
|
||||
if not m.readonly:
|
||||
flushFallback(m)
|
||||
m.mem = nil
|
||||
m.size = 0
|
||||
m.handle = -1
|
||||
m.flags = 0
|
||||
m.backing = ""
|
||||
m.path = ""
|
||||
m.readonly = false
|
||||
m.allowRemap = false
|
||||
|
||||
proc mapMem*(m: var MemFile, mode: FileMode = fmRead,
|
||||
mappedSize = -1, offset = 0, mapFlags = cint(-1)): pointer =
|
||||
@@ -94,7 +184,7 @@ proc mapMem*(m: var MemFile, mode: FileMode = fmRead,
|
||||
if mode == fmAppend:
|
||||
raise newEIO("The append mode is not supported.")
|
||||
|
||||
var readonly = mode == fmRead
|
||||
let readonly = mode == fmRead
|
||||
when defined(windows):
|
||||
result = mapViewOfFileEx(
|
||||
m.mapHandle,
|
||||
@@ -105,6 +195,8 @@ proc mapMem*(m: var MemFile, mode: FileMode = fmRead,
|
||||
nil)
|
||||
if result == nil:
|
||||
raiseOSError(osLastError())
|
||||
elif nimUseFallBack:
|
||||
result = mapMemFallback(m, mode, mappedSize, offset)
|
||||
else:
|
||||
assert mappedSize > 0
|
||||
|
||||
@@ -132,6 +224,8 @@ proc unmapMem*(f: var MemFile, p: pointer, size: int) =
|
||||
## via `mapMem`.
|
||||
when defined(windows):
|
||||
if unmapViewOfFile(p) == 0: raiseOSError(osLastError())
|
||||
elif nimUseFallBack:
|
||||
discard
|
||||
else:
|
||||
if munmap(p, size) != 0: raiseOSError(osLastError())
|
||||
|
||||
@@ -178,7 +272,7 @@ proc open*(filename: string, mode: FileMode = fmRead,
|
||||
raise newEIO("The append mode is not supported.")
|
||||
|
||||
assert newFileSize == -1 or mode != fmRead
|
||||
var readonly = mode == fmRead
|
||||
let readonly = mode == fmRead
|
||||
|
||||
template rollback =
|
||||
result.mem = nil
|
||||
@@ -252,7 +346,10 @@ proc open*(filename: string, mode: FileMode = fmRead,
|
||||
if closeHandle(result.fHandle) != 0:
|
||||
result.fHandle = INVALID_HANDLE_VALUE
|
||||
|
||||
else:
|
||||
elif nimUseFallBack:
|
||||
result = openFallbackMemFile(filename, mode, mappedSize, offset,
|
||||
newFileSize, allowRemap)
|
||||
elif defined(posix):
|
||||
template fail(errCode: OSErrorCode, msg: string) =
|
||||
rollback()
|
||||
if result.handle != -1: discard close(result.handle)
|
||||
@@ -309,6 +406,8 @@ proc flush*(f: var MemFile; attempts: Natural = 3) =
|
||||
lastErr = osLastError()
|
||||
if lastErr != ERROR_LOCK_VIOLATION.OSErrorCode:
|
||||
raiseOSError(lastErr)
|
||||
elif nimUseFallBack:
|
||||
flushFallback(f)
|
||||
else:
|
||||
for i in 1..attempts:
|
||||
res = msync(f.mem, f.size, MS_SYNC or MS_INVALIDATE) == 0
|
||||
@@ -318,59 +417,71 @@ proc flush*(f: var MemFile; attempts: Natural = 3) =
|
||||
if lastErr != EBUSY.OSErrorCode:
|
||||
raiseOSError(lastErr, "error flushing mapping")
|
||||
|
||||
proc resize*(f: var MemFile, newFileSize: int) {.raises: [IOError, OSError].} =
|
||||
## Resize & re-map the file underlying an `allowRemap MemFile`. If the OS/FS
|
||||
## supports it, file space is reserved to ensure room for new virtual pages.
|
||||
## Caller should wait often enough for `flush` to finish to limit use of
|
||||
## system RAM for write buffering, perhaps just prior to this call.
|
||||
## **Note**: this assumes the entire file is mapped read-write at offset 0.
|
||||
## Also, the value of `.mem` will probably change.
|
||||
if newFileSize < 1: # Q: include system/bitmasks & use PageSize ?
|
||||
raise newException(IOError, "Cannot resize MemFile to < 1 byte")
|
||||
when defined(windows):
|
||||
if not f.wasOpened:
|
||||
raise newException(IOError, "Cannot resize unopened MemFile")
|
||||
if f.fHandle == INVALID_HANDLE_VALUE:
|
||||
raise newException(IOError,
|
||||
"Cannot resize MemFile opened with allowRemap=false")
|
||||
if unmapViewOfFile(f.mem) == 0 or closeHandle(f.mapHandle) == 0: # Un-do map
|
||||
raiseOSError(osLastError())
|
||||
if newFileSize != f.size: # Seek to size & `setEndOfFile` => allocated.
|
||||
if (let e = setFileSize(f.fHandle.FileHandle, newFileSize);
|
||||
e != 0.OSErrorCode): raiseOSError(e)
|
||||
f.mapHandle = createFileMappingW(f.fHandle, nil, PAGE_READWRITE, 0,0,nil)
|
||||
if f.mapHandle == 0: # Re-do map
|
||||
raiseOSError(osLastError())
|
||||
let m = mapViewOfFileEx(f.mapHandle, FILE_MAP_READ or FILE_MAP_WRITE,
|
||||
0, 0, WinSizeT(newFileSize), nil)
|
||||
if m != nil:
|
||||
f.mem = m
|
||||
when nimUseFallBack:
|
||||
proc resize*(f: var MemFile, newFileSize: int) {.raises: [IOError].} =
|
||||
## Resize & re-map the file underlying an `allowRemap MemFile`. If the OS/FS
|
||||
## supports it, file space is reserved to ensure room for new virtual pages.
|
||||
## Caller should wait often enough for `flush` to finish to limit use of
|
||||
## system RAM for write buffering, perhaps just prior to this call.
|
||||
## **Note**: this assumes the entire file is mapped read-write at offset 0.
|
||||
## Also, the value of `.mem` will probably change.
|
||||
if newFileSize < 1: # Q: include system/bitmasks & use PageSize ?
|
||||
raise newException(IOError, "Cannot resize MemFile to < 1 byte")
|
||||
resizeFallback(f, newFileSize)
|
||||
else:
|
||||
proc resize*(f: var MemFile, newFileSize: int) {.raises: [IOError, OSError].} =
|
||||
## Resize & re-map the file underlying an `allowRemap MemFile`. If the OS/FS
|
||||
## supports it, file space is reserved to ensure room for new virtual pages.
|
||||
## Caller should wait often enough for `flush` to finish to limit use of
|
||||
## system RAM for write buffering, perhaps just prior to this call.
|
||||
## **Note**: this assumes the entire file is mapped read-write at offset 0.
|
||||
## Also, the value of `.mem` will probably change.
|
||||
if newFileSize < 1: # Q: include system/bitmasks & use PageSize ?
|
||||
raise newException(IOError, "Cannot resize MemFile to < 1 byte")
|
||||
when defined(windows):
|
||||
if not f.wasOpened:
|
||||
raise newException(IOError, "Cannot resize unopened MemFile")
|
||||
if f.fHandle == INVALID_HANDLE_VALUE:
|
||||
raise newException(IOError,
|
||||
"Cannot resize MemFile opened with allowRemap=false")
|
||||
if unmapViewOfFile(f.mem) == 0 or closeHandle(f.mapHandle) == 0: # Un-do map
|
||||
raiseOSError(osLastError())
|
||||
if newFileSize != f.size: # Seek to size & `setEndOfFile` => allocated.
|
||||
if (let e = setFileSize(f.fHandle.FileHandle, newFileSize);
|
||||
e != 0.OSErrorCode): raiseOSError(e)
|
||||
f.mapHandle = createFileMappingW(f.fHandle, nil, PAGE_READWRITE, 0,0,nil)
|
||||
if f.mapHandle == 0: # Re-do map
|
||||
raiseOSError(osLastError())
|
||||
let m = mapViewOfFileEx(f.mapHandle, FILE_MAP_READ or FILE_MAP_WRITE,
|
||||
0, 0, WinSizeT(newFileSize), nil)
|
||||
if m != nil:
|
||||
f.mem = m
|
||||
f.size = newFileSize
|
||||
else:
|
||||
raiseOSError(osLastError())
|
||||
elif defined(posix):
|
||||
if f.handle == -1:
|
||||
raise newException(IOError,
|
||||
"Cannot resize MemFile opened with allowRemap=false")
|
||||
if newFileSize != f.size:
|
||||
let e = setFileSize(f.handle.FileHandle, newFileSize, f.size)
|
||||
if e != 0.OSErrorCode: raiseOSError(e)
|
||||
when defined(linux): #Maybe NetBSD, too?
|
||||
# On Linux this can be over 100 times faster than a munmap,mmap cycle.
|
||||
proc mremap(old: pointer; oldSize, newSize: csize_t; flags: cint):
|
||||
pointer {.importc: "mremap", header: "<sys/mman.h>".}
|
||||
let newAddr = mremap(f.mem, csize_t(f.size), csize_t(newFileSize), 1.cint)
|
||||
if newAddr == cast[pointer](MAP_FAILED):
|
||||
raiseOSError(osLastError())
|
||||
else:
|
||||
if munmap(f.mem, f.size) != 0:
|
||||
raiseOSError(osLastError())
|
||||
let newAddr = mmap(nil, newFileSize, PROT_READ or PROT_WRITE,
|
||||
f.flags, f.handle, 0)
|
||||
if newAddr == cast[pointer](MAP_FAILED):
|
||||
raiseOSError(osLastError())
|
||||
f.mem = newAddr
|
||||
f.size = newFileSize
|
||||
else:
|
||||
raiseOSError(osLastError())
|
||||
elif defined(posix):
|
||||
if f.handle == -1:
|
||||
raise newException(IOError,
|
||||
"Cannot resize MemFile opened with allowRemap=false")
|
||||
if newFileSize != f.size:
|
||||
let e = setFileSize(f.handle.FileHandle, newFileSize, f.size)
|
||||
if e != 0.OSErrorCode: raiseOSError(e)
|
||||
when defined(linux): #Maybe NetBSD, too?
|
||||
# On Linux this can be over 100 times faster than a munmap,mmap cycle.
|
||||
proc mremap(old: pointer; oldSize, newSize: csize_t; flags: cint):
|
||||
pointer {.importc: "mremap", header: "<sys/mman.h>".}
|
||||
let newAddr = mremap(f.mem, csize_t(f.size), csize_t(newFileSize), 1.cint)
|
||||
if newAddr == cast[pointer](MAP_FAILED):
|
||||
raiseOSError(osLastError())
|
||||
else:
|
||||
if munmap(f.mem, f.size) != 0:
|
||||
raiseOSError(osLastError())
|
||||
let newAddr = mmap(nil, newFileSize, PROT_READ or PROT_WRITE,
|
||||
f.flags, f.handle, 0)
|
||||
if newAddr == cast[pointer](MAP_FAILED):
|
||||
raiseOSError(osLastError())
|
||||
f.mem = newAddr
|
||||
f.size = newFileSize
|
||||
|
||||
proc close*(f: var MemFile) =
|
||||
## closes the memory mapped file `f`. All changes are written back to the
|
||||
@@ -389,6 +500,8 @@ proc close*(f: var MemFile) =
|
||||
f.fHandle = INVALID_HANDLE_VALUE
|
||||
if error:
|
||||
lastErr = osLastError()
|
||||
elif nimUseFallBack:
|
||||
closeFallback(f)
|
||||
else:
|
||||
error = munmap(f.mem, f.size) != 0
|
||||
lastErr = osLastError()
|
||||
|
||||
@@ -11,17 +11,119 @@ when defined(nimPreviewSlimSystem):
|
||||
when weirdTarget:
|
||||
discard
|
||||
elif defined(windows):
|
||||
import std/[winlean, times]
|
||||
import std/winlean
|
||||
from std/strutils import toHex, toLowerAscii
|
||||
|
||||
const
|
||||
reparseHeaderSize = 8
|
||||
substituteNameOffsetField = 8
|
||||
substituteNameLengthField = 10
|
||||
symlinkFlagsField = 16
|
||||
mountPointPathBufferOffset = 16
|
||||
symlinkPathBufferOffset = 20
|
||||
|
||||
type
|
||||
ReparseBuffer = array[MAXIMUM_REPARSE_DATA_BUFFER_SIZE, byte]
|
||||
|
||||
ReparseLinkInfo = object
|
||||
tag: int32
|
||||
flags: int32
|
||||
pathBufOffset: int
|
||||
flagsField: int
|
||||
substituteNameOffset: int
|
||||
substituteNameLength: int
|
||||
|
||||
template readU16(buf: ReparseBuffer; off: int): uint16 =
|
||||
uint16(buf[off]) or (uint16(buf[off + 1]) shl 8)
|
||||
|
||||
template readI32(buf: ReparseBuffer; off: int): int32 =
|
||||
cast[int32](
|
||||
uint32(buf[off]) or (uint32(buf[off + 1]) shl 8) or
|
||||
(uint32(buf[off + 2]) shl 16) or (uint32(buf[off + 3]) shl 24))
|
||||
|
||||
func startsWithAsciiIgnoreCase(wide: openArray[Utf16Char]; prefix: openArray[char]): bool =
|
||||
## Matches an ASCII prefix against UTF-16 code units.
|
||||
##
|
||||
## This is only correct for ASCII prefixes.
|
||||
## It is not a valid general case-insensitive Unicode comparison
|
||||
## and must not be used for arbitrary UTF-16 text.
|
||||
if prefix.len > wide.len:
|
||||
return false
|
||||
var i = 0
|
||||
while i < prefix.len:
|
||||
let rune = ord(wide[i])
|
||||
if rune > 0x7F or toLowerAscii(char(rune)) != toLowerAscii(prefix[i]):
|
||||
return false
|
||||
inc i
|
||||
true
|
||||
|
||||
proc decodeWinTarget(wide: openArray[Utf16Char]): string =
|
||||
if wide.startsWithAsciiIgnoreCase(r"\??\unc\"):
|
||||
r"\\" & $(wide.toOpenArray(8, wide.len - 1))
|
||||
elif wide.startsWithAsciiIgnoreCase(r"\??\"):
|
||||
$(wide.toOpenArray(4, wide.len - 1))
|
||||
else:
|
||||
$wide
|
||||
|
||||
template invalidReparseData(path, details: string) =
|
||||
raise newException(OSError,
|
||||
"expandSymlink: invalid reparse data for " & path & " (" & details & ")")
|
||||
|
||||
proc parseReparseLinkInfo(buf: ReparseBuffer; bytesReturned: int;
|
||||
symlinkPath: string): ReparseLinkInfo =
|
||||
if bytesReturned < reparseHeaderSize:
|
||||
invalidReparseData(symlinkPath, "truncated header")
|
||||
|
||||
let
|
||||
reparseDataLen = int(readU16(buf, 4))
|
||||
wholeDataLen = reparseHeaderSize + reparseDataLen
|
||||
if wholeDataLen > bytesReturned:
|
||||
invalidReparseData(symlinkPath, "payload exceeds returned size")
|
||||
|
||||
result.tag = readI32(buf, 0)
|
||||
case result.tag
|
||||
of IO_REPARSE_TAG_SYMLINK:
|
||||
result.pathBufOffset = symlinkPathBufferOffset
|
||||
result.flagsField = symlinkFlagsField
|
||||
of IO_REPARSE_TAG_MOUNT_POINT:
|
||||
result.pathBufOffset = mountPointPathBufferOffset
|
||||
result.flagsField = -1
|
||||
else:
|
||||
raise newException(OSError,
|
||||
"expandSymlink: unsupported reparse tag for " & symlinkPath &
|
||||
" (ReparseTag=0x" & toHex(result.tag) & ")")
|
||||
|
||||
if result.pathBufOffset > wholeDataLen:
|
||||
invalidReparseData(symlinkPath, "missing path buffer")
|
||||
|
||||
result.substituteNameOffset = int(readU16(buf, substituteNameOffsetField))
|
||||
result.substituteNameLength = int(readU16(buf, substituteNameLengthField))
|
||||
if result.substituteNameLength <= 0:
|
||||
invalidReparseData(symlinkPath, "empty substitute name")
|
||||
if (result.substituteNameOffset and 1) != 0 or
|
||||
(result.substituteNameLength and 1) != 0:
|
||||
invalidReparseData(symlinkPath, "unaligned UTF-16 substitute name")
|
||||
|
||||
let startByte = result.pathBufOffset + result.substituteNameOffset
|
||||
let endByte = startByte + result.substituteNameLength
|
||||
if startByte < result.pathBufOffset or endByte < startByte or
|
||||
endByte > wholeDataLen:
|
||||
invalidReparseData(symlinkPath, "substitute name out of bounds")
|
||||
|
||||
result.flags =
|
||||
if result.flagsField >= 0:
|
||||
readI32(buf, result.flagsField)
|
||||
else:
|
||||
0
|
||||
|
||||
elif defined(posix):
|
||||
import std/posix
|
||||
|
||||
|
||||
when weirdTarget:
|
||||
{.pragma: noWeirdTarget, error: "this proc is not available on the NimScript/js target".}
|
||||
else:
|
||||
{.pragma: noWeirdTarget.}
|
||||
|
||||
|
||||
when defined(nimscript):
|
||||
# for procs already defined in scriptconfig.nim
|
||||
template noNimJs(body): untyped = discard
|
||||
@@ -56,13 +158,65 @@ proc createSymlink*(src, dest: string) {.noWeirdTarget.} =
|
||||
raiseOSError(osLastError(), $(src, dest))
|
||||
|
||||
proc expandSymlink*(symlinkPath: string): string {.noWeirdTarget.} =
|
||||
## Returns a string representing the path to which the symbolic link points.
|
||||
## Returns the stored target of the symbolic link `symlinkPath`.
|
||||
##
|
||||
## On Windows this is a noop, `symlinkPath` is simply returned.
|
||||
## This expands exactly one level of indirection, like POSIX `readlink`.
|
||||
## If the target is itself a symbolic link, it is returned as-is rather than
|
||||
## being expanded further.
|
||||
##
|
||||
## On POSIX, raises `OSError` if `symlinkPath` is not a symbolic link or if
|
||||
## the target cannot be read.
|
||||
##
|
||||
## On Windows, this supports symbolic links and junctions by reading the
|
||||
## reparse point payload directly. Unsupported reparse tags raise `OSError`.
|
||||
##
|
||||
## On Nintendo Switch this is currently a noop: `symlinkPath` is simply
|
||||
## returned, without checking whether it is actually a symbolic link.
|
||||
##
|
||||
## See also:
|
||||
## * `createSymlink proc`_
|
||||
when defined(windows) or defined(nintendoswitch):
|
||||
when defined(windows):
|
||||
let handle = createFileW(
|
||||
newWideCString(symlinkPath),
|
||||
0'i32,
|
||||
FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
|
||||
nil,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_OPEN_REPARSE_POINT or FILE_FLAG_BACKUP_SEMANTICS,
|
||||
Handle(0)
|
||||
)
|
||||
|
||||
if handle == INVALID_HANDLE_VALUE:
|
||||
raiseOSError(osLastError(), "expandSymlink: cannot open " & symlinkPath)
|
||||
|
||||
defer:
|
||||
discard closeHandle(handle)
|
||||
|
||||
var buf: ReparseBuffer
|
||||
var bytesReturned: DWORD
|
||||
|
||||
if deviceIoControl(
|
||||
handle,
|
||||
FSCTL_GET_REPARSE_POINT,
|
||||
nil, 0'i32,
|
||||
addr buf[0], DWORD(buf.len),
|
||||
bytesReturned,
|
||||
nil
|
||||
) == 0:
|
||||
raiseOSError(osLastError(),
|
||||
"expandSymlink: DeviceIoControl failed for " & symlinkPath)
|
||||
|
||||
let
|
||||
info = parseReparseLinkInfo(buf, int(bytesReturned), symlinkPath)
|
||||
startByte = info.pathBufOffset + info.substituteNameOffset
|
||||
runeLen = info.substituteNameLength shr 1
|
||||
wideSlicePtr = cast[ptr UncheckedArray[Utf16Char]](addr buf[startByte])
|
||||
|
||||
if info.tag == IO_REPARSE_TAG_SYMLINK and
|
||||
(info.flags and SYMLINK_FLAG_RELATIVE) != 0:
|
||||
return $(wideSlicePtr.toOpenArray(0, runeLen - 1))
|
||||
decodeWinTarget(wideSlicePtr.toOpenArray(0, runeLen - 1))
|
||||
elif defined(nintendoswitch):
|
||||
result = symlinkPath
|
||||
else:
|
||||
var bufLen = 1024
|
||||
|
||||
@@ -24,9 +24,20 @@ proc createSymlink*(src, dest: Path) {.inline.} =
|
||||
createSymlink(src.string, dest.string)
|
||||
|
||||
proc expandSymlink*(symlinkPath: Path): Path {.inline.} =
|
||||
## Returns a string representing the path to which the symbolic link points.
|
||||
## Returns the stored target of the symbolic link `symlinkPath`.
|
||||
##
|
||||
## On Windows this is a noop, `symlinkPath` is simply returned.
|
||||
## This expands exactly one level of indirection, like POSIX `readlink`.
|
||||
## If the target is itself a symbolic link, it is returned as-is rather than
|
||||
## being expanded further.
|
||||
##
|
||||
## On POSIX, raises `OSError` if `symlinkPath` is not a symbolic link or if
|
||||
## the target cannot be read.
|
||||
##
|
||||
## On Windows, this supports symbolic links and junctions by reading the
|
||||
## reparse point payload directly. Unsupported reparse tags raise `OSError`.
|
||||
##
|
||||
## On Nintendo Switch this is currently a noop: `symlinkPath` is simply
|
||||
## returned, without checking whether it is actually a symbolic link.
|
||||
##
|
||||
## See also:
|
||||
## * `createSymlink proc`_
|
||||
|
||||
@@ -185,47 +185,88 @@ when not (defined(cpu16) or defined(cpu8)):
|
||||
proc newWideCString*(s: string): WideCStringObj =
|
||||
result = newWideCString(cstring s, s.len)
|
||||
|
||||
proc `$`*(w: WideCString, estimate: int, replacement: int = 0xFFFD): string =
|
||||
result = newStringOfCap(estimate + estimate shr 2)
|
||||
|
||||
iterator decodeUtf16(w: WideCString; replacement: int): int =
|
||||
## Looks for a terminating NUL for length
|
||||
var i = 0
|
||||
while w[i].int16 != 0'i16:
|
||||
var ch = ord(w[i])
|
||||
inc i
|
||||
if ch >= UNI_SUR_HIGH_START and ch <= UNI_SUR_HIGH_END:
|
||||
# If the 16 bits following the high surrogate are in the source buffer...
|
||||
let ch2 = ord(w[i])
|
||||
|
||||
# If it's a low surrogate, convert to UTF32:
|
||||
if ch2 >= UNI_SUR_LOW_START and ch2 <= UNI_SUR_LOW_END:
|
||||
ch = (((ch and halfMask) shl halfShift) + (ch2 and halfMask)) + halfBase
|
||||
inc i
|
||||
# If the 16 bits following the high surrogate are NOT in the source...
|
||||
if w[i].int16 == 0'i16:
|
||||
ch = replacement #invalid UTF-16
|
||||
else:
|
||||
#invalid UTF-16
|
||||
ch = replacement
|
||||
let ch2 = ord(w[i])
|
||||
# If it's a low surrogate, convert to UTF32:
|
||||
if ch2 >= UNI_SUR_LOW_START and ch2 <= UNI_SUR_LOW_END:
|
||||
ch = (((ch and halfMask) shl halfShift) + (ch2 and halfMask)) + halfBase
|
||||
inc i
|
||||
else:
|
||||
ch = replacement #invalid UTF-16
|
||||
elif ch >= UNI_SUR_LOW_START and ch <= UNI_SUR_LOW_END:
|
||||
#invalid UTF-16
|
||||
ch = replacement
|
||||
ch = replacement #invalid UTF-16
|
||||
yield ch
|
||||
|
||||
if ch < 0x80:
|
||||
result.add chr(ch)
|
||||
elif ch < 0x800:
|
||||
result.add chr((ch shr 6) or 0xc0)
|
||||
result.add chr((ch and 0x3f) or 0x80)
|
||||
elif ch < 0x10000:
|
||||
result.add chr((ch shr 12) or 0xe0)
|
||||
result.add chr(((ch shr 6) and 0x3f) or 0x80)
|
||||
result.add chr((ch and 0x3f) or 0x80)
|
||||
elif ch <= 0x10FFFF:
|
||||
result.add chr((ch shr 18) or 0xf0)
|
||||
result.add chr(((ch shr 12) and 0x3f) or 0x80)
|
||||
result.add chr(((ch shr 6) and 0x3f) or 0x80)
|
||||
result.add chr((ch and 0x3f) or 0x80)
|
||||
else:
|
||||
# replacement char(in case user give very large number):
|
||||
result.add chr(0xFFFD shr 12 or 0b1110_0000)
|
||||
result.add chr(0xFFFD shr 6 and ones(6) or 0b10_0000_00)
|
||||
result.add chr(0xFFFD and ones(6) or 0b10_0000_00)
|
||||
iterator decodeUtf16(w: openArray[Utf16Char]; replacement: int): int =
|
||||
## Doesn't look for terminating NUL for length, trusts `w.len`
|
||||
var i = 0
|
||||
while i < w.len:
|
||||
var ch = ord(w[i])
|
||||
inc i
|
||||
if ch >= UNI_SUR_HIGH_START and ch <= UNI_SUR_HIGH_END:
|
||||
# If the 16 bits following the high surrogate are NOT in the source...
|
||||
if i >= w.len:
|
||||
ch = replacement #invalid UTF-16
|
||||
else:
|
||||
let ch2 = ord(w[i])
|
||||
# If it's a low surrogate, convert to UTF32:
|
||||
if ch2 >= UNI_SUR_LOW_START and ch2 <= UNI_SUR_LOW_END:
|
||||
ch = (((ch and halfMask) shl halfShift) + (ch2 and halfMask)) + halfBase
|
||||
inc i
|
||||
else:
|
||||
ch = replacement #invalid UTF-16
|
||||
elif ch >= UNI_SUR_LOW_START and ch <= UNI_SUR_LOW_END:
|
||||
ch = replacement #invalid UTF-16
|
||||
yield ch
|
||||
|
||||
proc addUtf8(dest: var string; rune: int) =
|
||||
if rune < 0x80:
|
||||
dest.add chr(rune)
|
||||
elif rune < 0x800:
|
||||
dest.add chr((rune shr 6) or 0xc0)
|
||||
dest.add chr((rune and 0x3f) or 0x80)
|
||||
elif rune < 0x10000:
|
||||
dest.add chr((rune shr 12) or 0xe0)
|
||||
dest.add chr(((rune shr 6) and 0x3f) or 0x80)
|
||||
dest.add chr((rune and 0x3f) or 0x80)
|
||||
elif rune <= 0x10FFFF:
|
||||
dest.add chr((rune shr 18) or 0xf0)
|
||||
dest.add chr(((rune shr 12) and 0x3f) or 0x80)
|
||||
dest.add chr(((rune shr 6) and 0x3f) or 0x80)
|
||||
dest.add chr((rune and 0x3f) or 0x80)
|
||||
else:
|
||||
# replacement char (in case user give very large number):
|
||||
dest.add chr(0xFFFD shr 12 or 0b1110_0000)
|
||||
dest.add chr(0xFFFD shr 6 and ones(6) or 0b10_0000_00)
|
||||
dest.add chr(0xFFFD and ones(6) or 0b10_0000_00)
|
||||
|
||||
|
||||
proc `$`*(w: openArray[Utf16Char]; replacement: int = 0xFFFD): string =
|
||||
## Decodes a length-delimited UTF-16 slice to UTF-8.
|
||||
##
|
||||
## Unlike the `WideCString` overloads, this preserves the provided length
|
||||
## and does not search for a terminating NUL.
|
||||
if w.len == 0:
|
||||
result = ""
|
||||
else:
|
||||
result = newStringOfCap(w.len + w.len shr 2)
|
||||
for rune in w.decodeUtf16(replacement):
|
||||
result.addUtf8(rune)
|
||||
|
||||
proc `$`*(w: WideCString; estimate: int; replacement: int = 0xFFFD): string =
|
||||
result = newStringOfCap(estimate + estimate shr 2)
|
||||
for rune in w.decodeUtf16(replacement):
|
||||
result.addUtf8(rune)
|
||||
|
||||
proc `$`*(s: WideCString): string =
|
||||
result = s $ 80
|
||||
|
||||
@@ -54,7 +54,12 @@ type
|
||||
typeOfProc, ## Prefer the interpretation that means `x` is a proc call.
|
||||
typeOfIter ## Prefer the interpretation that means `x` is an iterator call.
|
||||
|
||||
proc typeof*(x: untyped; mode = typeOfIter): typedesc {.
|
||||
TypeOfModifiers* = enum ## Modes to handle type modifiers `var`, `sink` and `lent`.
|
||||
CompatibleTypeModifiers, ## Remove or keep type modifiers in the same way as old typeof. That means keep `sink` but remove `var` and `lent`.
|
||||
RemoveTypeModifiers, ## Remove type modifiers.
|
||||
KeepTypeModifiers, ## Keep type modifiers.
|
||||
|
||||
proc typeof*(x: untyped; mode = typeOfIter; modifierMode = CompatibleTypeModifiers): typedesc {.
|
||||
magic: "TypeOf", noSideEffect, compileTime.} =
|
||||
## Builtin `typeof` operation for accessing the type of an expression.
|
||||
## Since version 0.20.0.
|
||||
@@ -76,6 +81,11 @@ proc typeof*(x: untyped; mode = typeOfIter): typedesc {.
|
||||
# since `typeOfProc` expects a typed expression and `myFoo2()` can
|
||||
# only be used in a `for` context.
|
||||
|
||||
proc varParam(x: var int;
|
||||
y: typeof(x, modifierMode = RemoveTypeModifiers);
|
||||
z: typeof(x, modifierMode = KeepTypeModifiers)) = discard
|
||||
doAssert varParam is proc (x: var int; y: int; z: var int) {.nimcall.}
|
||||
|
||||
proc `or`*(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.}
|
||||
## Constructs an `or` meta class.
|
||||
|
||||
@@ -1713,11 +1723,18 @@ when not (notJSnotNims and defined(nimSeqsV2)):
|
||||
let ns = cast[NimString](s)
|
||||
if ns == nil: nil
|
||||
else: cast[ptr UncheckedArray[char]](addr ns.data[start])
|
||||
template readRawDataStable*(s: var string; start = 0): ptr UncheckedArray[char] =
|
||||
## Same as `readRawData` here: the data lives in a heap `NimStringDesc` at a
|
||||
## stable address, so the pointer already survives moves of `s`. Takes `s` by
|
||||
## `var` to match the `--strings:sso` version, so code can prepare for that
|
||||
## upgrade without `when declared` guards.
|
||||
readRawData(s, start)
|
||||
else:
|
||||
# JS/nimscript: callers are guarded by whenNotVmJsNims/when not defined(js)
|
||||
proc beginStore*(s: var string; newLen: int; start = 0): ptr UncheckedArray[char] {.inline, noSideEffect, raises: [], tags: [].} = nil
|
||||
proc endStore*(s: var string) {.inline, noSideEffect, raises: [], tags: [].} = discard
|
||||
template readRawData*(s: string; start = 0): ptr UncheckedArray[char] = nil
|
||||
template readRawDataStable*(s: var string; start = 0): ptr UncheckedArray[char] = nil
|
||||
|
||||
when not defined(js):
|
||||
template newSeqImpl(T, len) =
|
||||
@@ -3121,10 +3138,7 @@ when notJSnotNims:
|
||||
not defined(nuttx) and
|
||||
hostOS != "any"
|
||||
|
||||
proc raiseEIO(msg: string) {.noinline, noreturn.} =
|
||||
raise newException(IOError, msg)
|
||||
|
||||
proc echoBinSafe(args: openArray[string]) {.compilerproc.} =
|
||||
proc echoBinSafe(args: openArray[string]) {.compilerproc, raises: [].} =
|
||||
when defined(androidNDK):
|
||||
# When running nim in android app, stdout goes nowhere, so echo gets ignored
|
||||
# To redirect echo to the android logcat, use -d:androidNDK
|
||||
@@ -3146,7 +3160,7 @@ when notJSnotNims:
|
||||
for s in args:
|
||||
when defined(windows):
|
||||
# equivalent to syncio.writeWindows
|
||||
proc writeWindows(f: CFilePtr; s: string; doRaise = false) =
|
||||
proc writeWindows(f: CFilePtr; s: string) =
|
||||
# Don't ask why but the 'printf' family of function is the only thing
|
||||
# that writes utf-8 strings reliably on Windows. At least on my Win 10
|
||||
# machine. We also enable `setConsoleOutputCP(65001)` now by default.
|
||||
@@ -3157,13 +3171,11 @@ when notJSnotNims:
|
||||
if s[i] == '\0':
|
||||
let w = c_fputc('\0', f)
|
||||
if w != 0:
|
||||
if doRaise: raiseEIO("cannot write string to file")
|
||||
break
|
||||
inc i
|
||||
else:
|
||||
let w = c_fprintf(f, "%s", unsafeAddr s[i])
|
||||
if w <= 0:
|
||||
if doRaise: raiseEIO("cannot write string to file")
|
||||
break
|
||||
inc i, w
|
||||
writeWindows(cstdout, s)
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
when notJSnotNims:
|
||||
proc zeroMem*(p: pointer, size: Natural) {.inline, noSideEffect,
|
||||
tags: [], raises: [], enforceNoRaises.}
|
||||
proc zeroMem*(p: pointer, size: Natural) {.inline, gcsafe,
|
||||
tags: [], raises: [], enforceNoRaises, noSideEffect.}
|
||||
## Overwrites the contents of the memory at `p` with the value 0.
|
||||
##
|
||||
## Exactly `size` bytes will be overwritten. Like any procedure
|
||||
## dealing with raw memory this is **unsafe**.
|
||||
|
||||
proc copyMem*(dest, source: pointer, size: Natural) {.inline, gcsafe,
|
||||
tags: [], raises: [], enforceNoRaises.}
|
||||
tags: [], raises: [], enforceNoRaises, noSideEffect.}
|
||||
## Copies the contents from the memory at `source` to the memory
|
||||
## at `dest`.
|
||||
## Exactly `size` bytes will be copied. The memory
|
||||
@@ -15,7 +15,7 @@ when notJSnotNims:
|
||||
## memory this is **unsafe**.
|
||||
|
||||
proc moveMem*(dest, source: pointer, size: Natural) {.inline, gcsafe,
|
||||
tags: [], raises: [], enforceNoRaises.}
|
||||
tags: [], raises: [], enforceNoRaises, noSideEffect.}
|
||||
## Copies the contents from the memory at `source` to the memory
|
||||
## at `dest`.
|
||||
##
|
||||
@@ -24,8 +24,8 @@ when notJSnotNims:
|
||||
## and is thus somewhat more safe than `copyMem`. Like any procedure
|
||||
## dealing with raw memory this is still **unsafe**, though.
|
||||
|
||||
proc equalMem*(a, b: pointer, size: Natural): bool {.inline, noSideEffect,
|
||||
tags: [], raises: [], enforceNoRaises.}
|
||||
proc equalMem*(a, b: pointer, size: Natural): bool {.inline, gcsafe,
|
||||
tags: [], raises: [], enforceNoRaises, noSideEffect.}
|
||||
## Compares the memory blocks `a` and `b`. `size` bytes will
|
||||
## be compared.
|
||||
##
|
||||
@@ -33,8 +33,8 @@ when notJSnotNims:
|
||||
## otherwise. Like any procedure dealing with raw memory this is
|
||||
## **unsafe**.
|
||||
|
||||
proc cmpMem*(a, b: pointer, size: Natural): int {.inline, noSideEffect,
|
||||
tags: [], raises: [], enforceNoRaises.}
|
||||
proc cmpMem*(a, b: pointer, size: Natural): int {.inline, gcsafe,
|
||||
tags: [], raises: [], enforceNoRaises, noSideEffect.}
|
||||
## Compares the memory blocks `a` and `b`. `size` bytes will
|
||||
## be compared.
|
||||
##
|
||||
|
||||
@@ -91,7 +91,10 @@ else:
|
||||
elif defined(gcMarkAndSweep):
|
||||
# XXX use 'compileOption' here
|
||||
include "system/gc_ms"
|
||||
else:
|
||||
elif not (defined(nimV2) or usesDestructors):
|
||||
# equivalent to a plain `else` here, but spelled out so that the IC
|
||||
# dependency scanner (which sees `else` imports/includes unguarded)
|
||||
# doesn't schedule system/gc's transitive imports under --mm:orc
|
||||
include "system/gc"
|
||||
|
||||
when not declared(nimNewSeqOfCap) and not defined(nimSeqsV2):
|
||||
|
||||
@@ -89,7 +89,7 @@ elif defined(emscripten) and not defined(StandaloneHeapSize):
|
||||
|
||||
var mmapDescrPos = cast[int](result) -% sizeof(EmscriptenMMapBlock)
|
||||
|
||||
var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos)
|
||||
var mmapDescr = cast[PEmscriptenMMapBlock](mmapDescrPos)
|
||||
mmapDescr.realSize = realSize
|
||||
mmapDescr.realPointer = realPointer
|
||||
|
||||
@@ -99,7 +99,7 @@ elif defined(emscripten) and not defined(StandaloneHeapSize):
|
||||
|
||||
proc osDeallocPages(p: pointer, size: int) {.inline.} =
|
||||
var mmapDescrPos = cast[int](p) -% sizeof(EmscriptenMMapBlock)
|
||||
var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos)
|
||||
var mmapDescr = cast[PEmscriptenMMapBlock](mmapDescrPos)
|
||||
munmap(mmapDescr.realPointer, mmapDescr.realSize)
|
||||
|
||||
elif defined(genode) and not defined(StandaloneHeapSize):
|
||||
|
||||
@@ -40,7 +40,8 @@ type
|
||||
wasm32, ## WASM, 32-bit
|
||||
e2k, ## MCST Elbrus 2000
|
||||
loongarch64, ## LoongArch 64-bit processor
|
||||
s390x ## IBM Z
|
||||
s390x, ## IBM Z
|
||||
wasm64 ## WASM, 64-bit
|
||||
|
||||
OsPlatform* {.pure.} = enum ## the OS this program will run on.
|
||||
none, dos, windows, os2, linux, morphos, skyos, solaris,
|
||||
@@ -101,5 +102,6 @@ const
|
||||
elif defined(e2k): CpuPlatform.e2k
|
||||
elif defined(loongarch64): CpuPlatform.loongarch64
|
||||
elif defined(s390x): CpuPlatform.s390x
|
||||
elif defined(wasm64): CpuPlatform.wasm64
|
||||
else: CpuPlatform.none
|
||||
## the CPU this program will run on.
|
||||
|
||||
@@ -261,4 +261,14 @@ template readRawData*(s: string; start = 0): ptr UncheckedArray[char] =
|
||||
## Template ensures no copy of `s`; ptr is valid while `s` is alive.
|
||||
rawDataImpl(cast[ptr NimStringV2](unsafeAddr s), start)
|
||||
|
||||
template readRawDataStable*(s: var string; start = 0): ptr UncheckedArray[char] =
|
||||
## Like `readRawData`, but the returned pointer additionally survives moves and
|
||||
## copies of `s` (while `s` stays alive and is not reassigned). For this string
|
||||
## implementation the char data already lives in a heap payload at an address
|
||||
## independent of the `string` value itself, so no promotion is needed and this
|
||||
## is identical to `readRawData`. Takes `s` by `var` to match the `--strings:sso`
|
||||
## version (which promotes a small inline string to the heap), so code written
|
||||
## against `readRawDataStable` compiles unchanged under either implementation.
|
||||
rawDataImpl(cast[ptr NimStringV2](addr s), start)
|
||||
|
||||
{.pop.}
|
||||
|
||||
@@ -770,6 +770,33 @@ template readRawData*(s: string; start = 0): ptr UncheckedArray[char] =
|
||||
## Template ensures no copy of `s` is made; ptr is valid while `s` is alive.
|
||||
rawDataImpl(cast[ptr SmallString](unsafeAddr s), start)
|
||||
|
||||
proc readRawDataStable*(s: var string; start = 0): ptr UncheckedArray[char] {.inline.} =
|
||||
## Like `readRawData`, but the returned pointer stays valid across moves and
|
||||
## copies of `s` (as long as `s` stays alive and is not reassigned). A
|
||||
## short/medium string keeps its chars *inline* in the string object, so a
|
||||
## plain `readRawData` pointer dangles the moment the object is moved; this
|
||||
## promotes `s` to its heap (long) representation first, whose payload address
|
||||
## is independent of where the string object itself lives. Use this whenever an
|
||||
## interior pointer must outlive the current scope of the owning string (e.g.
|
||||
## a cursor cached alongside the buffer it points into).
|
||||
let ss = cast[ptr SmallString](addr s)
|
||||
let slen = ssLen(ss[])
|
||||
if slen > 0 and slen <= PayloadSize:
|
||||
# Promote inline/medium to a long heap block so the payload lives at a
|
||||
# stable address. Mirrors the short/medium -> long transition in `add`.
|
||||
let newCap = max(slen, resize(slen))
|
||||
let p = cast[ptr LongString](alloc(LongStringDataOffset + newCap + 1))
|
||||
p.rc = 1
|
||||
p.fullLen = slen
|
||||
p.capImpl = newCap
|
||||
copyMem(addr p.data[0], inlinePtr(ss[]), slen)
|
||||
p.data[slen] = '\0'
|
||||
ss[].more = p
|
||||
setSSLen(ss[], HeapSlen)
|
||||
# Hot-prefix cache (bytes 1..AlwaysAvail) already mirrors data[0..AlwaysAvail-1]
|
||||
# because setSSLen only rewrote byte 0; the inline chars are untouched.
|
||||
rawDataImpl(ss, start)
|
||||
|
||||
# These take `string` (tyString) so the codegen uses them directly, bypassing
|
||||
# strmantle.nim's versions which go through nimStrLen/nimStrAtMutV3 compilerproc calls.
|
||||
proc cmpStrings(a, b: string): int {.compilerproc, inline.} =
|
||||
|
||||
@@ -261,6 +261,11 @@ const
|
||||
FILE_ATTRIBUTE_OFFLINE* = 0x00001000'i32
|
||||
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED* = 0x00002000'i32
|
||||
|
||||
IO_REPARSE_TAG_MOUNT_POINT* = 0xA0000003'i32
|
||||
IO_REPARSE_TAG_SYMLINK* = 0xA000000C'i32
|
||||
MAXIMUM_REPARSE_DATA_BUFFER_SIZE* = 16 * 1024
|
||||
SYMLINK_FLAG_RELATIVE* = 0x1'i32
|
||||
|
||||
FILE_FLAG_FIRST_PIPE_INSTANCE* = 0x00080000'i32
|
||||
FILE_FLAG_OPEN_NO_RECALL* = 0x00100000'i32
|
||||
FILE_FLAG_OPEN_REPARSE_POINT* = 0x00200000'i32
|
||||
@@ -282,6 +287,10 @@ const
|
||||
MOVEFILE_REPLACE_EXISTING* = 0x1'i32
|
||||
MOVEFILE_WRITE_THROUGH* = 0x8'i32
|
||||
|
||||
# CTL_CODE(FILE_DEVICE_FILE_SYSTEM = 9, func = 42, METHOD_BUFFERED = 0,
|
||||
# FILE_ANY_ACCESS = 0)
|
||||
FSCTL_GET_REPARSE_POINT* = 0x000900A8'i32
|
||||
|
||||
type
|
||||
WIN32_FIND_DATA* {.pure.} = object
|
||||
dwFileAttributes*: int32
|
||||
@@ -654,6 +663,12 @@ proc createFileW*(lpFileName: WideCString, dwDesiredAccess, dwShareMode: DWORD,
|
||||
dwCreationDisposition, dwFlagsAndAttributes: DWORD,
|
||||
hTemplateFile: Handle): Handle {.
|
||||
stdcall, dynlib: "kernel32", importc: "CreateFileW".}
|
||||
proc deviceIoControl*(hDevice: Handle, dwIoControlCode: DWORD,
|
||||
lpInBuffer: pointer, nInBufferSize: DWORD,
|
||||
lpOutBuffer: pointer, nOutBufferSize: DWORD,
|
||||
lpBytesReturned: var DWORD,
|
||||
lpOverlapped: pointer): WINBOOL {.
|
||||
stdcall, dynlib: "kernel32", importc: "DeviceIoControl".}
|
||||
proc deleteFileW*(pathName: WideCString): int32 {.
|
||||
importc: "DeleteFileW", dynlib: "kernel32", stdcall.}
|
||||
proc createFileA*(lpFileName: cstring, dwDesiredAccess, dwShareMode: DWORD,
|
||||
|
||||
Reference in New Issue
Block a user