GC'ed wide strings for windows
This commit is contained in:
parent
a23d418d78
commit
baf89e3d66
7 changed files with 155 additions and 297 deletions
|
|
@ -2868,9 +2868,9 @@ Effect system
|
||||||
Exception tracking
|
Exception tracking
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
Nimrod supports `exception tracking`:idx:. The `raises`:idx: pragma can used to
|
Nimrod supports `exception tracking`:idx:. The `raises`:idx: pragma can be used
|
||||||
explicitly define which exceptions a proc/iterator/method/converter is allowed
|
to explicitly define which exceptions a proc/iterator/method/converter is
|
||||||
to raise. The compiler verifies this:
|
allowed to raise. The compiler verifies this:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nimrod
|
||||||
proc p(what: bool) {.raises: [EIO, EOS].} =
|
proc p(what: bool) {.raises: [EIO, EOS].} =
|
||||||
|
|
@ -2878,7 +2878,7 @@ to raise. The compiler verifies this:
|
||||||
else: raise newException(EOS, "OS")
|
else: raise newException(EOS, "OS")
|
||||||
|
|
||||||
An empty ``raises`` list (``raises: []``) means that no exception may be raised:
|
An empty ``raises`` list (``raises: []``) means that no exception may be raised:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nimrod
|
||||||
proc p(): bool {.raises: [].} =
|
proc p(): bool {.raises: [].} =
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,7 @@ proc OSErrorMsg*(): string {.rtl, extern: "nos$1".} =
|
||||||
if FormatMessageW(0x00000100 or 0x00001000 or 0x00000200,
|
if FormatMessageW(0x00000100 or 0x00001000 or 0x00000200,
|
||||||
nil, err, 0, addr(msgbuf), 0, nil) != 0'i32:
|
nil, err, 0, addr(msgbuf), 0, nil) != 0'i32:
|
||||||
result = $msgbuf
|
result = $msgbuf
|
||||||
if msgbuf != nil: LocalFree(msgbuf)
|
if msgbuf != nil: LocalFree(cast[pointer](msgbuf))
|
||||||
else:
|
else:
|
||||||
var msgbuf: cstring
|
var msgbuf: cstring
|
||||||
if FormatMessageA(0x00000100 or 0x00001000 or 0x00000200,
|
if FormatMessageA(0x00000100 or 0x00001000 or 0x00000200,
|
||||||
|
|
@ -254,20 +254,14 @@ proc UnixToNativePath*(path: string): string {.
|
||||||
|
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
template wrapUnary(varname, winApiProc, arg: expr) {.immediate.} =
|
template wrapUnary(varname, winApiProc, arg: expr) {.immediate.} =
|
||||||
var tmp = allocWideCString(arg)
|
var varname = winApiProc(newWideCString(arg))
|
||||||
var varname = winApiProc(tmp)
|
|
||||||
dealloc tmp
|
|
||||||
|
|
||||||
template wrapBinary(varname, winApiProc, arg, arg2: expr) {.immediate.} =
|
template wrapBinary(varname, winApiProc, arg, arg2: expr) {.immediate.} =
|
||||||
var tmp2 = allocWideCString(arg)
|
var varname = winApiProc(newWideCString(arg), arg2)
|
||||||
var varname = winApiProc(tmp2, arg2)
|
|
||||||
dealloc tmp2
|
|
||||||
|
|
||||||
when useWinUnicode:
|
when useWinUnicode:
|
||||||
proc FindFirstFile(a: string, b: var TWIN32_FIND_DATA): THandle =
|
proc FindFirstFile(a: string, b: var TWIN32_FIND_DATA): THandle =
|
||||||
var aa = allocWideCString(a)
|
result = FindFirstFileW(newWideCString(a), b)
|
||||||
result = FindFirstFileW(aa, b)
|
|
||||||
dealloc aa
|
|
||||||
template FindNextFile(a, b: expr): expr = FindNextFileW(a, b)
|
template FindNextFile(a, b: expr): expr = FindNextFileW(a, b)
|
||||||
template getCommandLine(): expr = getCommandLineW()
|
template getCommandLine(): expr = getCommandLineW()
|
||||||
|
|
||||||
|
|
@ -363,11 +357,10 @@ proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} =
|
||||||
const bufsize = 512 # should be enough
|
const bufsize = 512 # should be enough
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
when useWinUnicode:
|
when useWinUnicode:
|
||||||
var res = cast[wideCString](alloc0(bufsize+1))
|
var res = newWideCString("", bufsize)
|
||||||
var L = GetCurrentDirectoryW(bufsize, res)
|
var L = GetCurrentDirectoryW(bufsize, res)
|
||||||
result = res$L
|
|
||||||
dealloc res
|
|
||||||
if L == 0'i32: OSError()
|
if L == 0'i32: OSError()
|
||||||
|
result = res$L
|
||||||
else:
|
else:
|
||||||
result = newString(bufsize)
|
result = newString(bufsize)
|
||||||
var L = GetCurrentDirectoryA(bufsize, result)
|
var L = GetCurrentDirectoryA(bufsize, result)
|
||||||
|
|
@ -385,10 +378,7 @@ proc setCurrentDir*(newDir: string) {.inline, tags: [].} =
|
||||||
## `newDir` cannot been set.
|
## `newDir` cannot been set.
|
||||||
when defined(Windows):
|
when defined(Windows):
|
||||||
when useWinUnicode:
|
when useWinUnicode:
|
||||||
var x = allocWideCString(newDir)
|
if SetCurrentDirectoryW(newWideCString(newDir)) == 0'i32: OSError()
|
||||||
let res = SetCurrentDirectoryW(x)
|
|
||||||
dealloc x
|
|
||||||
if res == 0'i32: OSError()
|
|
||||||
else:
|
else:
|
||||||
if SetCurrentDirectoryA(newDir) == 0'i32: OSError()
|
if SetCurrentDirectoryA(newDir) == 0'i32: OSError()
|
||||||
else:
|
else:
|
||||||
|
|
@ -579,15 +569,11 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
|
||||||
const bufsize = 3072'i32
|
const bufsize = 3072'i32
|
||||||
when useWinUnicode:
|
when useWinUnicode:
|
||||||
var unused: widecstring
|
var unused: widecstring
|
||||||
var res = cast[widecstring](alloc(bufsize*2+2))
|
var res = newWideCString("", bufsize div 2)
|
||||||
var f = allocWideCString(filename)
|
var L = GetFullPathNameW(newWideCString(filename), bufsize, res, unused)
|
||||||
var L = GetFullPathNameW(f, bufsize, res, unused)
|
|
||||||
dealloc f
|
|
||||||
if L <= 0'i32 or L >= bufsize:
|
if L <= 0'i32 or L >= bufsize:
|
||||||
dealloc res
|
|
||||||
OSError()
|
OSError()
|
||||||
result = res$L
|
result = res$L
|
||||||
dealloc res
|
|
||||||
else:
|
else:
|
||||||
var unused: cstring
|
var unused: cstring
|
||||||
result = newString(bufsize)
|
result = newString(bufsize)
|
||||||
|
|
@ -673,8 +659,8 @@ proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
|
||||||
var success = true
|
var success = true
|
||||||
|
|
||||||
when useWinUnicode:
|
when useWinUnicode:
|
||||||
var p1 = allocWideCString(path1)
|
var p1 = newWideCString(path1)
|
||||||
var p2 = allocWideCString(path2)
|
var p2 = newWideCString(path2)
|
||||||
template OpenHandle(path: expr): expr =
|
template OpenHandle(path: expr): expr =
|
||||||
CreateFileW(path, 0'i32, FILE_SHARE_DELETE or FILE_SHARE_READ or
|
CreateFileW(path, 0'i32, FILE_SHARE_DELETE or FILE_SHARE_READ or
|
||||||
FILE_SHARE_WRITE, nil, OPEN_EXISTING,
|
FILE_SHARE_WRITE, nil, OPEN_EXISTING,
|
||||||
|
|
@ -705,9 +691,6 @@ proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
|
||||||
|
|
||||||
discard CloseHandle(f1)
|
discard CloseHandle(f1)
|
||||||
discard CloseHandle(f2)
|
discard CloseHandle(f2)
|
||||||
when useWinUnicode:
|
|
||||||
dealloc p1
|
|
||||||
dealloc p2
|
|
||||||
|
|
||||||
if not success: OSError()
|
if not success: OSError()
|
||||||
else:
|
else:
|
||||||
|
|
@ -754,12 +737,9 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
|
||||||
## `EOS` is raised.
|
## `EOS` is raised.
|
||||||
when defined(Windows):
|
when defined(Windows):
|
||||||
when useWinUnicode:
|
when useWinUnicode:
|
||||||
var s = allocWideCString(source)
|
let s = newWideCString(source)
|
||||||
var d = allocWideCString(dest)
|
let d = newWideCString(dest)
|
||||||
let res = CopyFileW(s, d, 0'i32)
|
if CopyFileW(s, d, 0'i32) == 0'i32: OSError()
|
||||||
dealloc s
|
|
||||||
dealloc d
|
|
||||||
if res == 0'i32: OSError()
|
|
||||||
else:
|
else:
|
||||||
if CopyFileA(source, dest, 0'i32) == 0'i32: OSError()
|
if CopyFileA(source, dest, 0'i32) == 0'i32: OSError()
|
||||||
else:
|
else:
|
||||||
|
|
@ -794,11 +774,8 @@ when not defined(ENOENT):
|
||||||
var ENOENT {.importc, header: "<errno.h>".}: cint
|
var ENOENT {.importc, header: "<errno.h>".}: cint
|
||||||
|
|
||||||
proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} =
|
proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} =
|
||||||
## Removes the `file`.
|
## Removes the `file`. If this fails, `EOS` is raised. This does not fail
|
||||||
##
|
## if the file never existed in the first place.
|
||||||
## If this fails, `EOS` is raised. This does not fail if the file never
|
|
||||||
## existed in the first place. Despite the name of this proc you can also
|
|
||||||
## call it on directories, but they will only be removed if they are empty.
|
|
||||||
if cremove(file) != 0'i32 and errno != ENOENT: OSError()
|
if cremove(file) != 0'i32 and errno != ENOENT: OSError()
|
||||||
|
|
||||||
proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
|
proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
|
||||||
|
|
@ -934,12 +911,9 @@ proc putEnv*(key, val: string) {.tags: [FWriteEnv].} =
|
||||||
OSError()
|
OSError()
|
||||||
else:
|
else:
|
||||||
when useWinUnicode:
|
when useWinUnicode:
|
||||||
var k = allocWideCString(key)
|
var k = newWideCString(key)
|
||||||
var v = allocWideCString(val)
|
var v = newWideCString(val)
|
||||||
let res = SetEnvironmentVariableW(k, v)
|
if SetEnvironmentVariableW(k, v) == 0'i32: OSError()
|
||||||
dealloc k
|
|
||||||
dealloc v
|
|
||||||
if res == 0'i32: OSError()
|
|
||||||
else:
|
else:
|
||||||
if SetEnvironmentVariableA(key, val) == 0'i32: OSError()
|
if SetEnvironmentVariableA(key, val) == 0'i32: OSError()
|
||||||
|
|
||||||
|
|
@ -1082,9 +1056,7 @@ proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [
|
||||||
## in `dir` (recursively).
|
## in `dir` (recursively).
|
||||||
##
|
##
|
||||||
## If this fails, `EOS` is raised. This does not fail if the directory never
|
## If this fails, `EOS` is raised. This does not fail if the directory never
|
||||||
## existed in the first place. If you need non recursive directory removal
|
## existed in the first place.
|
||||||
## which fails when the directory contains files use the `removeFile` proc
|
|
||||||
## instead.
|
|
||||||
for kind, path in walkDir(dir):
|
for kind, path in walkDir(dir):
|
||||||
case kind
|
case kind
|
||||||
of pcFile, pcLinkToFile, pcLinkToDir: removeFile(path)
|
of pcFile, pcLinkToFile, pcLinkToDir: removeFile(path)
|
||||||
|
|
|
||||||
|
|
@ -361,14 +361,12 @@ when defined(Windows) and not defined(useNimRtl):
|
||||||
if env != nil: e = buildEnv(env)
|
if env != nil: e = buildEnv(env)
|
||||||
if poEchoCmd in options: echo($cmdl)
|
if poEchoCmd in options: echo($cmdl)
|
||||||
when useWinUnicode:
|
when useWinUnicode:
|
||||||
var tmp = allocWideCString(cmdl)
|
var tmp = newWideCString(cmdl)
|
||||||
var ee = allocWideCString(e)
|
var ee = newWideCString(e)
|
||||||
var wwd = allocWideCString(wd)
|
var wwd = newWideCString(wd)
|
||||||
success = winlean.CreateProcessW(nil,
|
success = winlean.CreateProcessW(nil,
|
||||||
tmp, nil, nil, 1, NORMAL_PRIORITY_CLASS, ee, wwd, SI, ProcInfo)
|
tmp, nil, nil, 1, NORMAL_PRIORITY_CLASS or CREATE_UNICODE_ENVIRONMENT,
|
||||||
if tmp != nil: dealloc tmp
|
ee, wwd, SI, ProcInfo)
|
||||||
if ee != nil: dealloc ee
|
|
||||||
if wwd != nil: dealloc wwd
|
|
||||||
else:
|
else:
|
||||||
success = winlean.CreateProcessA(nil,
|
success = winlean.CreateProcessA(nil,
|
||||||
cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, e, wd, SI, ProcInfo)
|
cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, e, wd, SI, ProcInfo)
|
||||||
|
|
@ -445,10 +443,9 @@ when defined(Windows) and not defined(useNimRtl):
|
||||||
SI.hStdInput = GetStdHandle(STD_INPUT_HANDLE)
|
SI.hStdInput = GetStdHandle(STD_INPUT_HANDLE)
|
||||||
SI.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE)
|
SI.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE)
|
||||||
when useWinUnicode:
|
when useWinUnicode:
|
||||||
var c = allocWideCString(command)
|
var c = newWideCString(command)
|
||||||
var res = winlean.CreateProcessW(nil, c, nil, nil, 0,
|
var res = winlean.CreateProcessW(nil, c, nil, nil, 0,
|
||||||
NORMAL_PRIORITY_CLASS, nil, nil, SI, ProcInfo)
|
NORMAL_PRIORITY_CLASS, nil, nil, SI, ProcInfo)
|
||||||
dealloc c
|
|
||||||
else:
|
else:
|
||||||
var res = winlean.CreateProcessA(nil, command, nil, nil, 0,
|
var res = winlean.CreateProcessA(nil, command, nil, nil, 0,
|
||||||
NORMAL_PRIORITY_CLASS, nil, nil, SI, ProcInfo)
|
NORMAL_PRIORITY_CLASS, nil, nil, SI, ProcInfo)
|
||||||
|
|
|
||||||
|
|
@ -168,18 +168,14 @@ when defined(windows) and not defined(useWinAnsi):
|
||||||
importc: "_wfreopen", nodecl.}
|
importc: "_wfreopen", nodecl.}
|
||||||
|
|
||||||
proc fopen(filename, mode: CString): pointer =
|
proc fopen(filename, mode: CString): pointer =
|
||||||
var f = allocWideCString(filename)
|
var f = newWideCString(filename)
|
||||||
var m = allocWideCString(mode)
|
var m = newWideCString(mode)
|
||||||
result = wfopen(f, m)
|
result = wfopen(f, m)
|
||||||
dealloc m
|
|
||||||
dealloc f
|
|
||||||
|
|
||||||
proc freopen(filename, mode: cstring, stream: TFile): TFile =
|
proc freopen(filename, mode: cstring, stream: TFile): TFile =
|
||||||
var f = allocWideCString(filename)
|
var f = newWideCString(filename)
|
||||||
var m = allocWideCString(mode)
|
var m = newWideCString(mode)
|
||||||
result = wfreopen(f, m, stream)
|
result = wfreopen(f, m, stream)
|
||||||
dealloc m
|
|
||||||
dealloc f
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
proc fopen(filename, mode: CString): pointer {.importc: "fopen", noDecl.}
|
proc fopen(filename, mode: CString): pointer {.importc: "fopen", noDecl.}
|
||||||
|
|
|
||||||
|
|
@ -12,249 +12,138 @@
|
||||||
|
|
||||||
type
|
type
|
||||||
TUtf16Char* = distinct int16
|
TUtf16Char* = distinct int16
|
||||||
WideCString* = ptr array[0.. 1_000_000, TUtf16Char]
|
WideCString* = ref array[0.. 1_000_000, TUtf16Char]
|
||||||
|
|
||||||
proc len*(w: WideCString): int =
|
proc len*(w: WideCString): int =
|
||||||
## returns the length of a widestring. This traverses the whole string to
|
## returns the length of a widestring. This traverses the whole string to
|
||||||
## find the binary zero end marker!
|
## find the binary zero end marker!
|
||||||
while int16(w[result]) != 0'i16: inc result
|
while int16(w[result]) != 0'i16: inc result
|
||||||
|
|
||||||
when true:
|
const
|
||||||
const
|
UNI_REPLACEMENT_CHAR = TUtf16Char(0xFFFD'i16)
|
||||||
UNI_REPLACEMENT_CHAR = TUtf16Char(0xFFFD'i16)
|
UNI_MAX_BMP = 0x0000FFFF
|
||||||
UNI_MAX_BMP = 0x0000FFFF
|
UNI_MAX_UTF16 = 0x0010FFFF
|
||||||
UNI_MAX_UTF16 = 0x0010FFFF
|
UNI_MAX_UTF32 = 0x7FFFFFFF
|
||||||
UNI_MAX_UTF32 = 0x7FFFFFFF
|
UNI_MAX_LEGAL_UTF32 = 0x0010FFFF
|
||||||
UNI_MAX_LEGAL_UTF32 = 0x0010FFFF
|
|
||||||
|
|
||||||
halfShift = 10
|
halfShift = 10
|
||||||
halfBase = 0x0010000
|
halfBase = 0x0010000
|
||||||
halfMask = 0x3FF
|
halfMask = 0x3FF
|
||||||
|
|
||||||
UNI_SUR_HIGH_START = 0xD800
|
UNI_SUR_HIGH_START = 0xD800
|
||||||
UNI_SUR_HIGH_END = 0xDBFF
|
UNI_SUR_HIGH_END = 0xDBFF
|
||||||
UNI_SUR_LOW_START = 0xDC00
|
UNI_SUR_LOW_START = 0xDC00
|
||||||
UNI_SUR_LOW_END = 0xDFFF
|
UNI_SUR_LOW_END = 0xDFFF
|
||||||
|
|
||||||
template ones(n: expr): expr = ((1 shl n)-1)
|
template ones(n: expr): expr = ((1 shl n)-1)
|
||||||
|
|
||||||
template fastRuneAt(s: cstring, i: int, result: expr, doInc = true) =
|
template fastRuneAt(s: cstring, i: int, result: expr, doInc = true) =
|
||||||
## Returns the unicode character ``s[i]`` in `result`. If ``doInc == true``
|
## Returns the unicode character ``s[i]`` in `result`. If ``doInc == true``
|
||||||
## `i` is incremented by the number of bytes that have been processed.
|
## `i` is incremented by the number of bytes that have been processed.
|
||||||
bind ones
|
bind ones
|
||||||
|
|
||||||
if ord(s[i]) <=% 127:
|
if ord(s[i]) <=% 127:
|
||||||
result = ord(s[i])
|
result = ord(s[i])
|
||||||
when doInc: inc(i)
|
when doInc: inc(i)
|
||||||
elif ord(s[i]) shr 5 == 0b110:
|
elif ord(s[i]) shr 5 == 0b110:
|
||||||
#assert(ord(s[i+1]) shr 6 == 0b10)
|
#assert(ord(s[i+1]) shr 6 == 0b10)
|
||||||
result = (ord(s[i]) and (ones(5))) shl 6 or (ord(s[i+1]) and ones(6))
|
result = (ord(s[i]) and (ones(5))) shl 6 or (ord(s[i+1]) and ones(6))
|
||||||
when doInc: inc(i, 2)
|
when doInc: inc(i, 2)
|
||||||
elif ord(s[i]) shr 4 == 0b1110:
|
elif ord(s[i]) shr 4 == 0b1110:
|
||||||
#assert(ord(s[i+1]) shr 6 == 0b10)
|
#assert(ord(s[i+1]) shr 6 == 0b10)
|
||||||
#assert(ord(s[i+2]) shr 6 == 0b10)
|
#assert(ord(s[i+2]) shr 6 == 0b10)
|
||||||
result = (ord(s[i]) and ones(4)) shl 12 or
|
result = (ord(s[i]) and ones(4)) shl 12 or
|
||||||
(ord(s[i+1]) and ones(6)) shl 6 or
|
(ord(s[i+1]) and ones(6)) shl 6 or
|
||||||
(ord(s[i+2]) and ones(6))
|
(ord(s[i+2]) and ones(6))
|
||||||
when doInc: inc(i, 3)
|
when doInc: inc(i, 3)
|
||||||
elif ord(s[i]) shr 3 == 0b11110:
|
elif ord(s[i]) shr 3 == 0b11110:
|
||||||
#assert(ord(s[i+1]) shr 6 == 0b10)
|
#assert(ord(s[i+1]) shr 6 == 0b10)
|
||||||
#assert(ord(s[i+2]) shr 6 == 0b10)
|
#assert(ord(s[i+2]) shr 6 == 0b10)
|
||||||
#assert(ord(s[i+3]) shr 6 == 0b10)
|
#assert(ord(s[i+3]) shr 6 == 0b10)
|
||||||
result = (ord(s[i]) and ones(3)) shl 18 or
|
result = (ord(s[i]) and ones(3)) shl 18 or
|
||||||
(ord(s[i+1]) and ones(6)) shl 12 or
|
(ord(s[i+1]) and ones(6)) shl 12 or
|
||||||
(ord(s[i+2]) and ones(6)) shl 6 or
|
(ord(s[i+2]) and ones(6)) shl 6 or
|
||||||
(ord(s[i+3]) and ones(6))
|
(ord(s[i+3]) and ones(6))
|
||||||
when doInc: inc(i, 4)
|
when doInc: inc(i, 4)
|
||||||
else:
|
else:
|
||||||
result = 0xFFFD
|
result = 0xFFFD
|
||||||
when doInc: inc(i)
|
when doInc: inc(i)
|
||||||
|
|
||||||
iterator runes(s: cstring): int =
|
iterator runes(s: cstring): int =
|
||||||
var
|
var
|
||||||
i = 0
|
i = 0
|
||||||
result: int
|
result: int
|
||||||
while s[i] != '\0':
|
while s[i] != '\0':
|
||||||
fastRuneAt(s, i, result, true)
|
fastRuneAt(s, i, result, true)
|
||||||
yield result
|
yield result
|
||||||
|
|
||||||
proc allocWideCString*(source: cstring, L: int): WideCString =
|
proc newWideCString*(source: cstring, L: int): WideCString =
|
||||||
## free after usage with `dealloc`.
|
unsafeNew(result, L * 4 + 2)
|
||||||
result = cast[wideCString](alloc(L * 4 + 2))
|
#result = cast[wideCString](alloc(L * 4 + 2))
|
||||||
var d = 0
|
var d = 0
|
||||||
for ch in runes(source):
|
for ch in runes(source):
|
||||||
if ch <=% UNI_MAX_BMP:
|
if ch <=% UNI_MAX_BMP:
|
||||||
if ch >=% UNI_SUR_HIGH_START and ch <=% UNI_SUR_LOW_END:
|
if ch >=% UNI_SUR_HIGH_START and ch <=% UNI_SUR_LOW_END:
|
||||||
result[d] = UNI_REPLACEMENT_CHAR
|
|
||||||
else:
|
|
||||||
result[d] = TUtf16Char(toU16(ch))
|
|
||||||
elif ch >% UNI_MAX_UTF16:
|
|
||||||
result[d] = UNI_REPLACEMENT_CHAR
|
result[d] = UNI_REPLACEMENT_CHAR
|
||||||
else:
|
else:
|
||||||
let ch = ch -% halfBase
|
result[d] = TUtf16Char(toU16(ch))
|
||||||
result[d] = TUtf16Char(toU16((ch shr halfShift) +% UNI_SUR_HIGH_START))
|
elif ch >% UNI_MAX_UTF16:
|
||||||
inc d
|
result[d] = UNI_REPLACEMENT_CHAR
|
||||||
result[d] = TUtf16Char(toU16((ch and halfMask) +% UNI_SUR_LOW_START))
|
|
||||||
inc d
|
|
||||||
result[d] = TUtf16Char(0'i16)
|
|
||||||
|
|
||||||
proc allocWideCString*(s: cstring): WideCString =
|
|
||||||
## free after usage with `dealloc`.
|
|
||||||
if s.isNil: return nil
|
|
||||||
|
|
||||||
when not defined(c_strlen):
|
|
||||||
proc c_strlen(a: CString): int {.nodecl, noSideEffect, importc: "strlen".}
|
|
||||||
|
|
||||||
let L = cstrlen(s)
|
|
||||||
result = allocWideCString(s, L)
|
|
||||||
|
|
||||||
proc allocWideCString*(s: string): WideCString =
|
|
||||||
## free after usage with `dealloc`.
|
|
||||||
result = allocWideCString(s, s.len)
|
|
||||||
|
|
||||||
proc `$`*(w: wideCString, estimate: int): string =
|
|
||||||
result = newStringOfCap(estimate + estimate shr 2)
|
|
||||||
|
|
||||||
var i = 0
|
|
||||||
while w[i].int16 != 0'i16:
|
|
||||||
var ch = w[i].int
|
|
||||||
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 = w[i].int
|
|
||||||
# If it's a low surrogate, convert to UTF32:
|
|
||||||
if ch2 >=% UNI_SUR_LOW_START and ch2 <=% UNI_SUR_LOW_END:
|
|
||||||
ch = ((ch -% UNI_SUR_HIGH_START) shr halfShift) +%
|
|
||||||
(ch2 -% UNI_SUR_LOW_START) +% halfBase
|
|
||||||
inc i
|
|
||||||
|
|
||||||
if ch <=% 127:
|
|
||||||
result.add chr(ch)
|
|
||||||
elif ch <=% 0x07FF:
|
|
||||||
result.add chr((ch shr 6) or 0b110_00000)
|
|
||||||
result.add chr((ch and ones(6)) or 0b10_000000)
|
|
||||||
elif ch <=% 0xFFFF:
|
|
||||||
result.add chr(ch shr 12 or 0b1110_0000)
|
|
||||||
result.add chr(ch shr 6 and ones(6) or 0b10_0000_00)
|
|
||||||
result.add chr(ch and ones(6) or 0b10_0000_00)
|
|
||||||
elif ch <=% 0x0010FFFF:
|
|
||||||
result.add chr(ch shr 18 or 0b1111_0000)
|
|
||||||
result.add chr(ch shr 12 and ones(6) or 0b10_0000_00)
|
|
||||||
result.add chr(ch shr 6 and ones(6) or 0b10_0000_00)
|
|
||||||
result.add chr(ch and ones(6) or 0b10_0000_00)
|
|
||||||
else:
|
|
||||||
# replacement char:
|
|
||||||
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)
|
|
||||||
|
|
||||||
proc `$`*(s: WideCString): string =
|
|
||||||
result = s $ 80
|
|
||||||
|
|
||||||
else:
|
|
||||||
const
|
|
||||||
utf8Encoding = 65001
|
|
||||||
|
|
||||||
proc MultiByteToWideChar*(
|
|
||||||
CodePage: int32,
|
|
||||||
dwFlags: int32,
|
|
||||||
lpMultiByteStr: cstring,
|
|
||||||
cbMultiByte: cint,
|
|
||||||
lpWideCharStr: WideCString,
|
|
||||||
cchWideChar: cint): cint {.
|
|
||||||
stdcall, importc: "MultiByteToWideChar", dynlib: "kernel32".}
|
|
||||||
|
|
||||||
proc WideCharToMultiByte*(
|
|
||||||
CodePage: int32,
|
|
||||||
dwFlags: int32,
|
|
||||||
lpWideCharStr: WideCString,
|
|
||||||
cchWideChar: cint,
|
|
||||||
lpMultiByteStr: cstring,
|
|
||||||
cbMultiByte: cint,
|
|
||||||
lpDefaultChar: cstring=nil,
|
|
||||||
lpUsedDefaultChar: pointer=nil): cint {.
|
|
||||||
stdcall, importc: "WideCharToMultiByte", dynlib: "kernel32".}
|
|
||||||
|
|
||||||
proc raiseEncodingError() {.noinline, noreturn.} =
|
|
||||||
raise newException(EOS, "error in unicode conversion")
|
|
||||||
|
|
||||||
proc `$`*(s: WideCString, len: int): string =
|
|
||||||
# special case: empty string: needed because MultiByteToWideChar
|
|
||||||
# returns 0 in case of error:
|
|
||||||
if len == 0: return ""
|
|
||||||
|
|
||||||
# educated guess of capacity:
|
|
||||||
var cap = len + len shr 2
|
|
||||||
result = newStringOfCap(cap)
|
|
||||||
|
|
||||||
let m = WideCharToMultiByte(
|
|
||||||
CodePage = utf8Encoding,
|
|
||||||
dwFlags = 0'i32,
|
|
||||||
lpWideCharStr = s,
|
|
||||||
cchWideChar = cint(len),
|
|
||||||
lpMultiByteStr = cstring(result),
|
|
||||||
cbMultiByte = cap)
|
|
||||||
if m == 0:
|
|
||||||
# try again; ask for capacity:
|
|
||||||
cap = WideCharToMultiByte(
|
|
||||||
CodePage = utf8Encoding,
|
|
||||||
dwFlags = 0'i32,
|
|
||||||
lpWideCharStr = s,
|
|
||||||
cchWideChar = cint(len),
|
|
||||||
lpMultiByteStr = nil,
|
|
||||||
cbMultiByte = cint(0))
|
|
||||||
# and do the conversion properly:
|
|
||||||
result = newStringOfCap(cap)
|
|
||||||
let m = WideCharToMultiByte(
|
|
||||||
CodePage = utf8Encoding,
|
|
||||||
dwFlags = 0'i32,
|
|
||||||
lpWideCharStr = s,
|
|
||||||
cchWideChar = cint(len),
|
|
||||||
lpMultiByteStr = cstring(result),
|
|
||||||
cbMultiByte = cap)
|
|
||||||
if m == 0: raiseEncodingError()
|
|
||||||
setLen(result, m)
|
|
||||||
elif m <= cap:
|
|
||||||
setLen(result, m)
|
|
||||||
else:
|
else:
|
||||||
sysAssert(false, "") # cannot happen
|
let ch = ch -% halfBase
|
||||||
|
result[d] = TUtf16Char(toU16((ch shr halfShift) +% UNI_SUR_HIGH_START))
|
||||||
proc `$`*(s: WideCString): string =
|
inc d
|
||||||
result = s $ s.len
|
result[d] = TUtf16Char(toU16((ch and halfMask) +% UNI_SUR_LOW_START))
|
||||||
|
inc d
|
||||||
proc allocWideCString*(s: string): WideCString =
|
result[d] = TUtf16Char(0'i16)
|
||||||
## free after usage with `dealloc`.
|
|
||||||
let cap = s.len+1
|
|
||||||
result = cast[wideCString](alloc0(cap * 2))
|
|
||||||
# special case: empty string: needed because MultiByteToWideChar
|
|
||||||
# return 0 in case of error:
|
|
||||||
if s.len == 0: return
|
|
||||||
# convert to utf-16 LE
|
|
||||||
let m = MultiByteToWideChar(CodePage = utf8Encoding, dwFlags = 0'i32,
|
|
||||||
lpMultiByteStr = cstring(s),
|
|
||||||
cbMultiByte = cint(s.len),
|
|
||||||
lpWideCharStr = result,
|
|
||||||
cchWideChar = cint(cap))
|
|
||||||
if m == 0: raiseEncodingError()
|
|
||||||
|
|
||||||
proc allocWideCString*(s: cstring): WideCString =
|
proc newWideCString*(s: cstring): WideCString =
|
||||||
## free after usage with `dealloc`.
|
if s.isNil: return nil
|
||||||
if s.isNil: return nil
|
|
||||||
|
|
||||||
when not defined(c_strlen):
|
when not defined(c_strlen):
|
||||||
proc c_strlen(a: CString): int {.nodecl, noSideEffect, importc: "strlen".}
|
proc c_strlen(a: CString): int {.nodecl, noSideEffect, importc: "strlen".}
|
||||||
|
|
||||||
let len = cstrlen(s)
|
let L = cstrlen(s)
|
||||||
let cap = len+1
|
result = newWideCString(s, L)
|
||||||
result = cast[wideCString](alloc0(cap * 2))
|
|
||||||
# special case: empty string: needed because MultiByteToWideChar
|
|
||||||
# return 0 in case of error:
|
|
||||||
if s.len == 0: return
|
|
||||||
# convert to utf-16 LE
|
|
||||||
let m = MultiByteToWideChar(CodePage = utf8Encoding, dwFlags = 0'i32,
|
|
||||||
lpMultiByteStr = s,
|
|
||||||
cbMultiByte = cint(len),
|
|
||||||
lpWideCharStr = result,
|
|
||||||
cchWideChar = cint(cap))
|
|
||||||
if m == 0: raiseEncodingError()
|
|
||||||
|
|
||||||
|
proc newWideCString*(s: string): WideCString =
|
||||||
|
result = newWideCString(s, s.len)
|
||||||
|
|
||||||
|
proc `$`*(w: wideCString, estimate: int): string =
|
||||||
|
result = newStringOfCap(estimate + estimate shr 2)
|
||||||
|
|
||||||
|
var i = 0
|
||||||
|
while w[i].int16 != 0'i16:
|
||||||
|
var ch = w[i].int
|
||||||
|
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 = w[i].int
|
||||||
|
# If it's a low surrogate, convert to UTF32:
|
||||||
|
if ch2 >=% UNI_SUR_LOW_START and ch2 <=% UNI_SUR_LOW_END:
|
||||||
|
ch = ((ch -% UNI_SUR_HIGH_START) shr halfShift) +%
|
||||||
|
(ch2 -% UNI_SUR_LOW_START) +% halfBase
|
||||||
|
inc i
|
||||||
|
|
||||||
|
if ch <=% 127:
|
||||||
|
result.add chr(ch)
|
||||||
|
elif ch <=% 0x07FF:
|
||||||
|
result.add chr((ch shr 6) or 0b110_00000)
|
||||||
|
result.add chr((ch and ones(6)) or 0b10_000000)
|
||||||
|
elif ch <=% 0xFFFF:
|
||||||
|
result.add chr(ch shr 12 or 0b1110_0000)
|
||||||
|
result.add chr(ch shr 6 and ones(6) or 0b10_0000_00)
|
||||||
|
result.add chr(ch and ones(6) or 0b10_0000_00)
|
||||||
|
elif ch <=% 0x0010FFFF:
|
||||||
|
result.add chr(ch shr 18 or 0b1111_0000)
|
||||||
|
result.add chr(ch shr 12 and ones(6) or 0b10_0000_00)
|
||||||
|
result.add chr(ch shr 6 and ones(6) or 0b10_0000_00)
|
||||||
|
result.add chr(ch and ones(6) or 0b10_0000_00)
|
||||||
|
else:
|
||||||
|
# replacement char:
|
||||||
|
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)
|
||||||
|
|
||||||
|
proc `$`*(s: WideCString): string =
|
||||||
|
result = s $ 80
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,8 @@ const
|
||||||
|
|
||||||
SW_SHOWNORMAL* = 1'i32
|
SW_SHOWNORMAL* = 1'i32
|
||||||
INVALID_HANDLE_VALUE* = THANDLE(-1)
|
INVALID_HANDLE_VALUE* = THANDLE(-1)
|
||||||
|
|
||||||
|
CREATE_UNICODE_ENVIRONMENT* = 1024'i32
|
||||||
|
|
||||||
proc CloseHandle*(hObject: THANDLE): WINBOOL {.stdcall, dynlib: "kernel32",
|
proc CloseHandle*(hObject: THANDLE): WINBOOL {.stdcall, dynlib: "kernel32",
|
||||||
importc: "CloseHandle".}
|
importc: "CloseHandle".}
|
||||||
|
|
@ -116,7 +118,7 @@ when useWinUnicode:
|
||||||
lpProcessAttributes: ptr TSECURITY_ATTRIBUTES,
|
lpProcessAttributes: ptr TSECURITY_ATTRIBUTES,
|
||||||
lpThreadAttributes: ptr TSECURITY_ATTRIBUTES,
|
lpThreadAttributes: ptr TSECURITY_ATTRIBUTES,
|
||||||
bInheritHandles: WINBOOL, dwCreationFlags: int32,
|
bInheritHandles: WINBOOL, dwCreationFlags: int32,
|
||||||
lpEnvironment: pointer, lpCurrentDirectory: widecstring,
|
lpEnvironment, lpCurrentDirectory: widecstring,
|
||||||
lpStartupInfo: var TSTARTUPINFO,
|
lpStartupInfo: var TSTARTUPINFO,
|
||||||
lpProcessInformation: var TPROCESS_INFORMATION): WINBOOL{.
|
lpProcessInformation: var TPROCESS_INFORMATION): WINBOOL{.
|
||||||
stdcall, dynlib: "kernel32", importc: "CreateProcessW".}
|
stdcall, dynlib: "kernel32", importc: "CreateProcessW".}
|
||||||
|
|
|
||||||
|
|
@ -31,9 +31,9 @@ Bugfixes
|
||||||
floating point values.
|
floating point values.
|
||||||
- SCGI module's performance has been improved greatly, it will no longer block
|
- SCGI module's performance has been improved greatly, it will no longer block
|
||||||
on many concurrent requests.
|
on many concurrent requests.
|
||||||
|
|
||||||
- In total fixed over 70 github issues and merged over 60 pull requests.
|
- In total fixed over 70 github issues and merged over 60 pull requests.
|
||||||
|
|
||||||
|
|
||||||
Library Additions
|
Library Additions
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
|
@ -60,6 +60,8 @@ Changes affecting backwards compatibility
|
||||||
- The expression/statement unification has been implemented. Again this
|
- The expression/statement unification has been implemented. Again this
|
||||||
only affects edge cases and no known real world code.
|
only affects edge cases and no known real world code.
|
||||||
- Changed the async interface of the ``scgi`` module.
|
- Changed the async interface of the ``scgi`` module.
|
||||||
|
- WideStrings are now garbage collected like other string types.
|
||||||
|
|
||||||
|
|
||||||
Compiler Additions
|
Compiler Additions
|
||||||
------------------
|
------------------
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue