winim/winstr

This module contains new string types and utilities for dealing with Windows strings. The Windows SDK uses the following types to represent a character or string:

type
  CHAR = char
  WCHAR = uint16
  LPSTR|LPCSTR = ptr CHAR # however, it should be an ANSI string, not a UTF-8 string
  LPWSTR|LPCWSTR = ptr WCHAR
  BSTR = distinct ptr WCHAR # BSTR is not binary compatible with LPWSTR
  (ptr) array[I, CHAR] # sometimes a string is defined as array[1, CHAR], but not necessarily only one character
  (ptr) array[I, WCHAR] # sometimes a string is defined as array[1, WCHAR], but not necessarily only one wide character

By default, Nim's string type uses UTF-8 encoding. However, Windows uses wide-character strings (a.k.a. Unicode strings) or multibyte-character strings (a.k.a. ANSI strings). Therefore, this module introduces the following string types.

type
  string # Nim's built-in string type, UTF-8 encoded by default; can sometimes be an ANSI string.
  cstring # compatible with the type char* in ANSI C
  wstring = distinct string # a new string type to store Unicode strings
  mstring = distinct string # a new string type to store ANSI strings

Some type classes are also defined for convenience when dealing with strings.

type
  SomeChar = byte | char | WCHAR
  SomeString = string | mstring | wstring
  SomeBuffer[I] = ptr SomeChar | array[I, SomeChar] | ptr array[I, SomeChar] |
    ptr UncheckedArray[SomeChar] | openArray[SomeChar] | seq[SomeChar]
  Stringable = SomeChar | SomeString | SomeBuffer | cstring | BSTR

Here is pseudocode for the most useful functions introduced by this module.

proc `&`(s: cstring|string|wstring|mstring): pointer
  # Get address of the first char of a string.
  # For string, it has a similar meaning to cstring(s).

proc `$`(x: Stringable): string
proc `+$`(x: Stringable): wstring
proc `-$`(x: Stringable): mstring
  # Convert any stringable type to string, wstring, or mstring.
  # These operators assume string|cstring|ptr char|openArray[char] are UTF-8-encoded strings.
  # Use `%$` to treat `openArray[SomeChar]` explicitly as a string.

proc `$$`(x: Stringable): string
proc `+$$`(x: Stringable): wstring
proc `-$$`(x: Stringable): mstring
  # Convert any stringable type to string, wstring, or mstring.
  # These operators assume string|cstring|ptr char|openArray[char] are ANSI-encoded strings.
  # For mstring|wstring|LPWSTR etc, these operators are the same as `$`, `+$`, `-$`.

template `<<`(s: SomeString, b: SomeBuffer)
template `<<`(b: SomeBuffer, s: SomeString)
template `<<<`(b: SomeBuffer, s: SomeString)
template `>>`(a: typed, b: typed) = b << a
template `>>>`(a: typed, b: typed) = b <<< a
  # String << Buffer or Buffer >> String: Fill string by buffer.
  # Buffer << String or String >> Buffer: Fill buffer by string.
  # Buffer <<< String or String >>> Buffer: Fill a buffer with a string, including a null terminator.
  
  # These operators don't convert the encoding (they copy byte by byte).
  # Please make sure both sides have the same character size.
  # If the destination does not have length information (e.g. pointer or UncheckedArray),
  # please make sure the buffer size is large enough.

proc nullTerminate(s: var SomeString)
  # Assume a string is null terminated and set the correct length.

proc nullTerminated[T: SomeString](s: T): T
  # Assume a string is null terminated and return the length-corrected string.

template L(s: string): wstring
  # Generate wstring at compile-time if possible.
  # Only a const string or string literal can be converted to a Unicode string at compile time;
  # otherwise it is just `+$`.

template T(s: string): mstring|wstring
  # Generate a wstring or mstring depending on the conditional symbol: useWinAnsi.
  # For example: (this code works under both Unicode and ANSI modes)
    
    MessageBox(0, T"hello, world", T"Nim is Powerful 中文測試", 0)

template T(n: Natural): mstring|wstring
  # Generate a wstring or mstring buffer depending on the conditional symbol: useWinAnsi.
  # Use `&` to get the buffer address and then pass to Windows API.

converter winstrConverter(s: SomeString): SomeBuffer
  # With these converters, passing strings to the Windows API is easier.
  #   The following converters don't need encoding conversion:
  #     string => LPSTR|ptr char
  #     mstring => LPSTR|ptr char
  #     wstring => LPWSTR|BSTR
  #     cstring => ptr char
  #     BSTR => LPWSTR
  #
  #   Some converters do need encoding conversion (UTF-8 to Unicode).
  #   A new memory block will be allocated. However, these converters are useful and convenient.
  #     cstring|string => LPWSTR|BSTR

There are also new string functions for dealing with wstring and mstring like the built-in string type.

proc newWString(len: Natural): wstring
  # Generate wstring buffer
proc newMString(len: Natural): mstring
  # Generate mstring buffer

proc setLen(s: var mstring|wstring, newLen: Natural)
proc substr(s: wstring|mstring, first = 0): wstring|mstring
proc substr(s: wstring|mstring, first, last: int): wstring|mstring
proc len(s: wstring|mstring): int
proc high(s: wstring|mstring): int
proc low(s: wstring|mstring): int
proc repr(s: wstring|mstring): string
proc toHex(s: wstring|mstring): string

proc `[]`(s: wstring|mstring, i: int): WCHAR|mstring
proc `[]=`(s: wstring|mstring, i: int, u: WCHAR|CHAR)
proc `[]=`(s: wstring|mstring, i: int, u: wstring|mstring)
proc `[]`(s: wstring|mstring, x: HSlice)
proc `[]=`(s: var wstring|var mstring, x: HSlice[int], b: wstring|mstring)
proc `==`(x, y: wstring|mstring): bool
proc `<=`(x, y: wstring|mstring): bool
proc `<`(x, y: wstring|mstring): bool
proc cmp(x, y: wstring|mstring): int
proc `&`(s: wstring|mstring, t: wstring|mstring): wstring|mstring

iterator items(s: wstring|mstring): WCHAR|mstring
iterator mitems(s: var wstring): WCHAR
iterator pairs(s: wstring|mstring): tuple[key: int|mIndex, val: WCHAR|mstring]
iterator mpairs(s: var wstring): WCHAR

Winim doesn't use the built-in WideCString, but still supports it.

converter winstrConverter(s: WideCString): LPWSTR
  # WideCString can be sent directly to the Windows API (Unicode only).

proc `+$`(s: WideCString): wstring
  # Converts WideCString to wstring.

proc newWideCString(s: wstring): WideCString
  # Converts wstring to WideCString.

Imports

winimbase, windef

Types

mIndex = distinct int
Using mIndex with substr, [], or []= on an mstring means indexing by MBCS characters, not by bytes.
mstring = distinct string
New string type to store multibyte character strings (a.k.a. ANSI strings).
SomeBuffer[I] = ptr SomeChar | array[I, SomeChar] | ptr array[I, SomeChar] |
    ptr UncheckedArray[SomeChar] |
    openArray[SomeChar] |
    seq[SomeChar]
Type class matching all string buffer types.
SomeChar = byte | char | WCHAR
Type class matching all char types.
SomeString = string | mstring | wstring
Type class matching all string types.
Stringable = SomeChar | SomeString | SomeBuffer | cstring | BSTR
Type class matching all stringable types.
TString = wstring
wstring or mstring depending on the conditional symbol: useWinAnsi.
wstring = distinct string
New string type to store UTF-16 code units for Windows wide strings.

Procs

proc `$`(s: Stringable): string {.inline.}
Converts any stringable type to string. This operator assumes string|cstring|ptr char|openArray[char] are UTF-8-encoded strings.
proc `$$`(s: Stringable): string {.inline.}
Converts any stringable type to string. This operator assumes string|cstring|ptr char|openArray[char] are ANSI-encoded strings.
proc `%$`(s: Stringable): string {.inline.}
Converts any stringable type to string. Always treats openArray[SomeChar] as a string. This operator assumes string|cstring|ptr char|openArray[char] are UTF-8-encoded strings.
proc `&`(s, u: wstring): wstring {.inline, ...raises: [], tags: [], forbids: [].}
Concatenates s with u.
proc `&`(s: cstring): ptr char {.inline, ...raises: [], tags: [], forbids: [].}
Gets the address of the first character of a cstring.
proc `&`(s: mstring): ptr char {.inline, ...raises: [], tags: [], forbids: [].}
Gets the address of the first character of a mstring.
proc `&`(s: string): ptr char {.inline, ...raises: [], tags: [], forbids: [].}
Gets the address of the first character of a string.
proc `&`(s: wstring): ptr WCHAR {.inline, ...raises: [], tags: [], forbids: [].}
Gets the address of the first WCHAR of a wstring.
proc `&`(s: wstring; c: WCHAR | char): wstring {.inline.}
Concatenates s with c.
proc `&`(x, y: mstring): mstring {.borrow, ...raises: [], tags: [], forbids: [].}
Concatenates x with y.
proc `&`(x: char; y: mstring): mstring {.borrow, ...raises: [], tags: [],
    forbids: [].}
Concatenates x with y.
proc `&`(x: mstring; y: char): mstring {.borrow, ...raises: [], tags: [],
    forbids: [].}
Concatenates x with y.
proc `+$`(s: Stringable): wstring {.inline.}
Converts any stringable type to wstring. This operator assumes string|cstring|ptr char|openArray[char] are UTF-8-encoded strings.
proc `+$`(s: WideCStringObj): wstring {.inline, ...raises: [], tags: [],
                                        forbids: [].}
Converts WideCString to wstring.
proc `+$$`(s: Stringable): wstring {.inline.}
Converts any stringable type to wstring. This operator assumes string|cstring|ptr char|openArray[char] are ANSI-encoded strings.
proc `-$`(s: Stringable): mstring {.inline.}
Converts any stringable type to mstring. This operator assumes string|cstring|ptr char|openArray[char] are UTF-8-encoded strings.
proc `-$$`(s: Stringable): mstring {.inline.}
Converts any stringable type to mstring. This operator assumes string|cstring|ptr char|openArray[char] are ANSI-encoded strings.
proc `<`(x, y: mstring): bool {.borrow, ...raises: [], tags: [], forbids: [].}
Lexicographic < operator for mstring.
proc `<`(x, y: wstring): bool {.inline, ...raises: [], tags: [], forbids: [].}
Lexicographic < operator for wstring.
proc `<=`(x, y: mstring): bool {.borrow, ...raises: [], tags: [], forbids: [].}
Lexicographic <= operator for mstring.
proc `<=`(x, y: wstring): bool {.inline, ...raises: [], tags: [], forbids: [].}
Lexicographic <= operator for wstring.
proc `==`(x, y: mstring): bool {.borrow, ...raises: [], tags: [], forbids: [].}
Checks for equality between two mstring values.
proc `==`(x, y: wstring): bool {....raises: [], tags: [], forbids: [].}
Checks for equality between two wstring values.
proc `[]`(s: mstring; i: int): char {.inline, ...raises: [], tags: [], forbids: [].}
Index operator for mstring, counting by bytes.
proc `[]`(s: mstring; i: mIndex): mstring {....raises: [], tags: [], forbids: [].}
Index operator for mstring, counting by MBCS characters.
proc `[]`(s: wstring; i: int): WCHAR {.inline, ...raises: [], tags: [], forbids: [].}
Index operator for wstring.
proc `[]`[T, U](s: mstring; x: HSlice[T, U]): mstring
Slice operation for mstring.
proc `[]`[T, U](s: wstring; x: HSlice[T, U]): wstring
Slice operation for wstring.
proc `[]=`(s: var mstring; i: int; x: char | byte) {.inline.}
Index assignment operator for mstring, counting by bytes.
proc `[]=`(s: var mstring; i: mIndex; u: mstring) {....raises: [], tags: [],
    forbids: [].}
Index assignment operator for mstring, counting by MBCS characters, and only the first MBCS character of u will be used.
proc `[]=`(s: var wstring; i: int; c: WCHAR | char) {.inline.}
Index assignment operator for wstring.
proc `[]=`[T, U](s: var mstring; x: HSlice[T, U]; u: mstring)
Slice assignment for mstring.
proc `[]=`[T, U](s: var wstring; x: HSlice[T, U]; b: wstring)
Slice assignment for wstring.
proc add(s: var wstring; c: char | WCHAR)
Appends c to s in place.
proc add(s: var wstring; u: wstring) {....raises: [], tags: [], forbids: [].}
Appends u to s in place.
proc add(x: var mstring; y: byte) {.inline, ...raises: [], tags: [], forbids: [].}
Appends y to x in place.
proc add(x: var mstring; y: char) {.borrow, ...raises: [], tags: [], forbids: [].}
Appends y to x in place.
proc add(x: var mstring; y: mstring) {.borrow, ...raises: [], tags: [], forbids: [].}
Appends y to x in place.
proc add(x: var mstring; y: string) {.borrow, ...raises: [], tags: [], forbids: [].}
Appends y to x in place.
proc cmp(x, y: mstring): int {.borrow, ...raises: [], tags: [], forbids: [].}
Comparison proc for mstring (in binary format only).
proc cmp(x, y: wstring): int {....raises: [], tags: [], forbids: [].}
Compares wstring values by ordinal UTF-16 code units.
proc hash(s: wstring): Hash {....raises: [], tags: [], forbids: [].}
proc high(s: mstring): int {.borrow, ...raises: [], tags: [], forbids: [].}
Returns the highest possible index of mstring.
proc high(s: wstring): int {.inline, ...raises: [], tags: [], forbids: [].}
Returns the highest possible index of wstring.
proc len(s: mstring): int {.inline, ...raises: [], tags: [], forbids: [].}
Returns the length of mstring, counting by bytes.
proc len(s: wstring): int {.inline, ...raises: [], tags: [], forbids: [].}
Returns the length of wstring, counting by UTF-16 code units.
proc low(s: mstring): int {.borrow, ...raises: [], tags: [], forbids: [].}
Returns the lowest possible index of mstring.
proc low(s: wstring): int {.inline, ...raises: [], tags: [], forbids: [].}
Returns the lowest possible index of wstring.
proc mlen(s: mstring): int {....raises: [], tags: [], forbids: [].}
Returns the length of mstring, counting by MBCS characters.
proc newMString(L: Natural): mstring {....raises: [], tags: [], forbids: [].}
Returns a new mstring of length L, counting by bytes.
proc newMString(s: string | cstring | wstring): mstring {.inline,
    ...deprecated: "use `-$` instead".}
Deprecated: use `-$` instead
Returns a new mstring.
proc newMStringOfCap(L: Natural): mstring {....raises: [], tags: [], forbids: [].}
Returns a new mstring of length 0 but with capacity L, counting by bytes.
proc newWideCString(s: wstring): WideCStringObj {.inline, ...raises: [], tags: [],
    forbids: [].}
Converts wstring to WideCString.
proc newWString(L: Natural): wstring {....raises: [], tags: [], forbids: [].}
Returns a new wstring of length L, counted in UTF-16 code units.
proc newWString(s: cstring | string | mstring): wstring {.inline,
    ...deprecated: "use `+$` instead".}
Deprecated: use `+$` instead
Returns a new wstring.
proc newWStringOfCap(L: Natural): wstring {....raises: [], tags: [], forbids: [].}
Returns a new wstring of length 0 but with capacity L, counting by UTF-16 code units.
proc nullTerminate(s: var SomeString) {.inline.}
Assume a string is null terminated and set the correct length.
proc nullTerminated[T: SomeString](s: T): T {.inline.}
Assume a string is null terminated and return the length-corrected string.
proc repr(s: mstring): string {....raises: [Exception], tags: [RootEffect],
                                forbids: [].}
Returns string representation of mstring.
proc repr(s: wstring): string {....raises: [], tags: [], forbids: [].}
Returns string representation of wstring.
proc setLen(s: var mstring; L: Natural) {.inline, ...raises: [], tags: [],
    forbids: [].}
Sets the length of mstring s to L, counting by bytes.
proc setLen(s: var wstring; L: Natural) {.inline, ...raises: [], tags: [],
    forbids: [].}
Sets the length of wstring s to L, counting by UTF-16 code units.
proc substr(s: mstring; first = 0): mstring {.borrow, ...raises: [], tags: [],
    forbids: [].}
Copies a slice of s into a new mstring and returns it, counting by bytes.
proc substr(s: mstring; first, last: int): mstring {.borrow, ...raises: [],
    tags: [], forbids: [].}
Copies a slice of s into a new mstring and returns it, counting by bytes.
proc substr(s: mstring; first, last: mIndex): mstring {....raises: [], tags: [],
    forbids: [].}
Copies a slice of s into a new mstring and returns it, counting by MBCS characters.
proc substr(s: mstring; first: mIndex = 0.mIndex): mstring {....raises: [],
    tags: [], forbids: [].}
Copies a slice of s into a new mstring and returns it, counting by MBCS characters.
proc substr(s: wstring; first = 0): wstring {.inline, ...raises: [], tags: [],
    forbids: [].}
Copies a slice of s into a new wstring and returns it.
proc substr(s: wstring; first, last: int): wstring {....raises: [], tags: [],
    forbids: [].}
Copies a slice of s into a new wstring and returns it.
proc toHex(s: cstring): string {.inline, ...raises: [], tags: [], forbids: [].}
Converts a cstring to its hexadecimal representation. No prefix like 0x is generated.
proc toHex(s: mstring): string {.inline, ...raises: [], tags: [], forbids: [].}
Converts mstring to its hexadecimal representation. No prefix like 0x is generated.
proc toHex(s: wstring): string {.inline, ...raises: [], tags: [], forbids: [].}
Converts wstring to its hexadecimal representation. No prefix like 0x is generated.

Iterators

iterator items(s: mstring): mstring {....raises: [], tags: [], forbids: [].}
Iterates over each MBCS character of mstring.
iterator items(s: wstring): WCHAR {....raises: [], tags: [], forbids: [].}
Iterates over each WCHAR of wstring.
iterator mitems(s: var wstring): var WCHAR {....raises: [], tags: [], forbids: [].}
Iterates over each WCHAR of wstring so that you can modify the yielded value.
iterator mpairs(s: var wstring): tuple[key: int, val: var WCHAR] {....raises: [],
    tags: [], forbids: [].}
Iterates over each WCHAR of wstring. Yields (int, var WCHAR) pairs.
iterator pairs(s: mstring): tuple[key: mIndex, val: mstring] {....raises: [],
    tags: [], forbids: [].}
Iterates over each MBCS character of mstring. Yields (mIndex, mstring) pairs.
iterator pairs(s: wstring): tuple[key: int, val: WCHAR] {....raises: [], tags: [],
    forbids: [].}
Iterates over each WCHAR of wstring. Yields (int, WCHAR) pairs.

Converters

converter winstrConverterBSTRToLPWSTR(x: BSTR): LPWSTR {....raises: [], tags: [],
    forbids: [].}
Borrows the BSTR buffer as LPWSTR for an immediate input call. The callee must not retain, modify, or free it.
converter winstrConverterCStringToBSTR(x: cstring): BSTR {....raises: [], tags: [],
    forbids: [].}
Borrows a BSTR-layout-compatible input for an immediate call. The callee must not retain or free it; a later conversion on this thread may reuse it.
converter winstrConverterCStringToLPWSTR(x: cstring): LPWSTR {....raises: [],
    tags: [], forbids: [].}
Borrows an LPWSTR for an immediate input call. The callee must not retain, modify, or free it; a later conversion on this thread may reuse it.
converter winstrConverterCStringToPtrChar(x: cstring): ptr char {....raises: [],
    tags: [], forbids: [].}
Borrows the cstring buffer as ptr char. Its lifetime is the source lifetime, and the callee must not modify or free it.
converter winstrConverterMStringToLPSTR(x: mstring): LPSTR {....raises: [],
    tags: [], forbids: [].}
Borrows an ANSI/MBCS LPSTR for an immediate input call. The callee must not retain, modify, or free it.
converter winstrConverterMStringToPtrChar(x: mstring): ptr char {....raises: [],
    tags: [], forbids: [].}
Borrows an ANSI/MBCS ptr char for an immediate input call. The callee must not retain, modify, or free it.
converter winstrConverterStringToBSTR(x: string): BSTR {....raises: [], tags: [],
    forbids: [].}
Borrows a BSTR-layout-compatible input for an immediate call. The callee must not retain or free it; a later conversion on this thread may reuse it.
converter winstrConverterStringToLPWSTR(x: string): LPWSTR {....raises: [],
    tags: [], forbids: [].}
Borrows an LPWSTR for an immediate input call. The callee must not retain, modify, or free it; a later conversion on this thread may reuse it.
converter winstrConverterStringToPtrChar(x: string): ptr char {....raises: [],
    tags: [], forbids: [].}
Borrows a UTF-8 ptr char for an immediate input call. The callee must not retain, modify, or free it.
converter winstrConverterWideCStringToLPWSTR(x: WideCStringObj): LPWSTR {.
    ...raises: [], tags: [], forbids: [].}
Borrows the WideCString buffer as LPWSTR; the pointer is valid only while the source remains alive and must not be freed.
converter winstrConverterWStringToBSTR(x: wstring): BSTR {....raises: [], tags: [],
    forbids: [].}
Borrows a BSTR-layout-compatible input for an immediate call. The callee must not retain or free it; a later conversion on this thread may reuse it.
converter winstrConverterWStringToLPWSTR(x: wstring): LPWSTR {....raises: [],
    tags: [], forbids: [].}
Borrows an LPWSTR for an immediate input call. The callee must not retain, modify, or free it.

Macros

macro `<<`(a, b: typed): untyped
macro `<<<`(a, b: typed): untyped

Templates

template `>>`(a: typed; b: typed)
This is the same as b << a.
template `>>>`(a: typed; b: typed)
This is the same as b <<< a.
template L(x: static[string]): wstring
Generates a const wstring from static[string] at compile time.
template L(x: string): wstring
Same as +$ for dynamic string (string at run-time).
template T(x: Natural): untyped
Generates a wstring or mstring buffer depending on the conditional symbol: useWinAnsi. Uses & to get the buffer address and then passes it to the Windows API.
template T(x: string): untyped
Generates a wstring or mstring depending on the conditional symbol: useWinAnsi.