winim/winstr

    Dark Mode

This module contains new string types and utilities to deal with strings in Windows. Windows SDK use following types to represent a char or a string:

type
  CHAR = char
  WCHAR = uint16
  LPSTR|LPCSTR = ptr CHAR # however, it should be ansi string, not utf8 string
  LPWSTR|LPCWSTR = ptr WCHAR
  BSTR = distinct ptr WCHAR # BSTR is not binary compatible with LPWSTR
  (ptr) array[I, CHAR] # sometimes string defined as array[1, CHAR] but not only one char
  (ptr) array[I, WCHAR] # sometimes string defined as array[1, WCHAR] but not only one widechar

By default, Nim's string type is utf8 encoding. However, Windows use wide character string (aka. unicode string) or multibyte character string (aka. ansi string). So, this module introduce following string types.

type
  string # nim built-in string type, utf8 encoding by default, can be ansi string sometimes.
  cstring # compatible to the type char* in Ansi C
  wstring = distinct string # new string type to store unicode string
  mstring = distinct string # new string type to store ansi string

Some type classes are also defined for convenience to deal 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 are the pseudocode for 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 utf8 encoding.
  # setOpenArrayStringable() can be used to switch the behavior of `$` operator.

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 encoding.
  # 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 buffer by string, include a null.
  
  # These operators don't convert the encoding (copy byte by byte).
  # Please make sure both side have the same character size.
  # If destination don't have the 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 const string or string literal can be converted to unicode string at compile-time,
  # otherwise it is just `+$`.

template T(s: string): mstring|wstring
  # Generate wstring or mstring depend on conditional symbol: useWinAnsi.
  # For example: (this code works under both unicode and ansi mode)
    
    MessageBox(0, T"hello, world", T"Nim is Powerful 中文測試", 0)

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

converter winstrConverter(s: SomeString): SomeBuffer
  # With these converters, passing string to Windows API is more easy.
  #   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 (utf8 to unicode).
  #   New memory block will be allocated. However, they are useful and convenience.
  #     cstring|string => LPWSTR|BSTR

There are also new string functions to deal with wstring and mstring just like 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 don't use built-in WideCString, but still support it.

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

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

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

Types

mIndex = distinct int
Use mIndex in substr, [] or []= for mstring means index by MBCS characters, not by bytes.
mstring = distinct string
New string type to store multibyte character string (aka. ansi string).
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 depend on conditional symbol: useWinAnsi.
wstring = distinct string
New string type to store wide character string (aka. unicode string).

Procs

proc `$$`(s: Stringable): string {.inline.}
Convert any stringable type to string. This operator assume string|cstring|ptr char|openArray[char] is ansi encoding.
proc `$`(s: Stringable): string {.inline.}
Convert any stringable type to string. This operator assume string|cstring|ptr char|openArray[char] is utf8 encoding.
proc `%$`(s: Stringable): string {.inline.}
Convert any stringable type to string. Always treat openArray[SomeChar] as string. This operator assume string|cstring|ptr char|openArray[char] is utf8 encoding.
proc `&`(s, u: wstring): wstring {.inline, ...raises: [], tags: [].}
Concatenates s with u.
proc `&`(s: cstring): ptr char {.inline, ...raises: [], tags: [].}
Get address of the first char of a cstring.
proc `&`(s: mstring): ptr char {.inline, ...raises: [], tags: [].}
Get address of the first char of a mstring.
proc `&`(s: string): ptr char {.inline, ...raises: [], tags: [].}
Get address of the first char of a string.
proc `&`(s: wstring): ptr WCHAR {.inline, ...raises: [], tags: [].}
Get 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: [].}
Concatenates x with y.
proc `&`(x: char; y: mstring): mstring {.borrow, ...raises: [], tags: [].}
Concatenates x with y.
proc `&`(x: mstring; y: char): mstring {.borrow, ...raises: [], tags: [].}
Concatenates x with y.
proc `+$$`(s: Stringable): wstring {.inline.}
Convert any stringable type to wstring. This operator assume string|cstring|ptr char|openArray[char] is ansi encoding.
proc `+$`(s: Stringable): wstring {.inline.}
Convert any stringable type to wstring. This operator assume string|cstring|ptr char|openArray[char] is utf8 encoding.
proc `+$`(s: WideCString): wstring {.inline, ...raises: [], tags: [].}
Converts WideCString to wstring.
proc `-$$`(s: Stringable): mstring {.inline.}
Convert any stringable type to mstring. This operator assume string|cstring|ptr char|openArray[char] is ansi encoding.
proc `-$`(s: Stringable): mstring {.inline.}
Convert any stringable type to mstring. This operator assume string|cstring|ptr char|openArray[char] is utf8 encoding.
proc `<=`(x, y: mstring): bool {.borrow, ...raises: [], tags: [].}
Lexicographic <= operator for mstring.
proc `<=`(x, y: wstring): bool {.borrow, ...raises: [], tags: [].}
Lexicographic <= operator for wstring.
proc `<`(x, y: mstring): bool {.borrow, ...raises: [], tags: [].}
Lexicographic < operator for mstring.
proc `<`(x, y: wstring): bool {.borrow, ...raises: [], tags: [].}
Lexicographic < operator for wstring.
proc `==`(x, y: mstring): bool {.borrow, ...raises: [], tags: [].}
Checks for equality between two mstring.
proc `==`(x, y: wstring): bool {.borrow, ...raises: [], tags: [].}
Checks for equality between two 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: [].}
Index assignment operator for mstring, counting by MBCS characters, and only first MBCS characters 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 mstrings.
proc `[]=`[T, U](s: var wstring; x: HSlice[T, U]; b: wstring)
Slice assignment for wstring.
proc `[]`(s: mstring; i: int): char {.inline, ...raises: [], tags: [].}
Index operator for mstring, counting by bytes.
proc `[]`(s: mstring; i: mIndex): mstring {....raises: [], tags: [].}
Index operator for mstring, counting by MBCS characters.
proc `[]`(s: wstring; i: int): WCHAR {.inline, ...raises: [], tags: [].}
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 add(s: var wstring; c: char | WCHAR)
Appends c to s in place.
proc add(s: var wstring; u: wstring) {....raises: [], tags: [].}
Appends u to s in place.
proc add(x: var mstring; y: byte) {.inline, ...raises: [], tags: [].}
Appends y to x in place.
proc add(x: var mstring; y: char) {.borrow, ...raises: [], tags: [].}
Appends y to x in place.
proc add(x: var mstring; y: mstring) {.borrow, ...raises: [], tags: [].}
Appends y to x in place.
proc add(x: var mstring; y: string) {.borrow, ...raises: [], tags: [].}
Appends y to x in place.
proc cmp(x, y: mstring): int {.borrow, ...raises: [], tags: [].}
Compare proc for mstring (in binary format only).
proc cmp(x, y: wstring): int {.borrow, ...raises: [], tags: [].}
Compare proc for wstring (in binary format only).
proc high(s: mstring): int {.borrow, ...raises: [], tags: [].}
Returns the highest possible index of mstring.
proc high(s: wstring): int {.inline, ...raises: [], tags: [].}
Returns the highest possible index of wstring.
proc len(s: mstring): int {.inline, ...raises: [], tags: [].}
Returns the length of mstring, counting by bytes.
proc len(s: wstring): int {.inline, ...raises: [], tags: [].}
Returns the length of wstring, counting by wide characters.
proc low(s: mstring): int {.borrow, ...raises: [], tags: [].}
Returns the lowest possible index of mstring.
proc low(s: wstring): int {.inline, ...raises: [], tags: [].}
Returns the lowest possible index of wstring.
proc mlen(s: mstring): int {....raises: [], tags: [].}
Returns the length of mstring, counting by MBCS characters.
proc newMString(L: Natural): mstring {....raises: [], tags: [].}
Returns a new mstring of length L, counting by bytes.
proc newMString(s: string | cstring | wstring): mstring {.inline,
    ...deprecated: "use `-$` instead".}
Deprecated: use `-$` instead
Return a new mstring.
proc newMStringOfCap(L: Natural): mstring {....raises: [], tags: [].}
Returns a new mstring of length 0 but with capacity L, counting by bytes.
proc newWideCString(s: wstring): WideCString {.inline, ...raises: [], tags: [].}
Converts wstring to WideCString.
proc newWString(L: Natural): wstring {....raises: [], tags: [].}
Returns a new wstring of length L, counting by wide characters.
proc newWString(s: cstring | string | mstring): wstring {.inline,
    ...deprecated: "use `+$` instead".}
Deprecated: use `+$` instead
Return a new wstring.
proc newWStringOfCap(L: Natural): wstring {....raises: [], tags: [].}
Returns a new wstring of length 0 but with capacity L, counting by wide characters.
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: [], tags: [].}
Returns string representation of mstring.
proc repr(s: wstring): string {....raises: [], tags: [].}
Returns string representation of wstring.
proc setLen(s: var mstring; L: Natural) {.inline, ...raises: [], tags: [].}
Sets the length of mstring s to L, counting by wide bytes.
proc setLen(s: var wstring; L: Natural) {.inline, ...raises: [], tags: [].}
Sets the length of wstring s to L, counting by wide characters.
proc setOpenArrayStringable(flag: bool): bool {.inline, discardable, ...raises: [],
    tags: [].}
Nim's system.$ will return array representation for openArray[SomeChar]. After turn this option on, winstr will overwrite the defualt behavior for $ and treats openArray[SomeChar] as string.
proc substr(s: mstring; first = 0): mstring {.borrow, ...raises: [], tags: [].}
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: [].}
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: [].}
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: [].}
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: [].}
Copies a slice of s into a new wstring and returns it.
proc substr(s: wstring; first, last: int): wstring {....raises: [], tags: [].}
Copies a slice of s into a new wstring and returns it.
proc toHex(s: cstring): string {.inline, ...raises: [], tags: [].}
Converts a cstring to its hexadecimal representation. No prefix like 0x is generated.
proc toHex(s: mstring): string {.inline, ...raises: [], tags: [].}
Converts mstring to its hexadecimal representation. No prefix like 0x is generated.
proc toHex(s: wstring): string {.inline, ...raises: [], tags: [].}
Converts wstring to its hexadecimal representation. No prefix like 0x is generated.

Iterators

iterator items(s: mstring): mstring {....raises: [], tags: [].}
Iterates over each MBCS character of mstring.
iterator items(s: wstring): WCHAR {....raises: [], tags: [].}
Iterates over each WCHAR of wstring.
iterator mitems(s: var wstring): var WCHAR {....raises: [], tags: [].}
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: [].}
Iterates over each WCHAR of wstring. Yields (int, var WCHAR) pairs.
iterator pairs(s: mstring): tuple[key: mIndex, val: mstring] {....raises: [],
    tags: [].}
Iterates over each MBCS character of mstring. Yields (mIndex, mstring) pairs.
iterator pairs(s: wstring): tuple[key: int, val: WCHAR] {....raises: [], tags: [].}
Iterates over each WCHAR of wstring. Yields (int, WCHAR) pairs.

Converters

converter winstrConverterBSTRToLPWSTR(x: BSTR): LPWSTR {....raises: [], tags: [].}
Converts BSTR to LPWSTR automatically.
converter winstrConverterCStringToBSTR(x: cstring): BSTR {....raises: [], tags: [].}
Converts cstring to BSTR automatically.
converter winstrConverterCStringToLPWSTR(x: cstring): LPWSTR {....raises: [],
    tags: [].}
Converts cstring to LPWSTR automatically.
converter winstrConverterCStringToPtrChar(x: cstring): ptr char {....raises: [],
    tags: [].}
Converts cstring to ptr char automatically.
converter winstrConverterMStringToLPSTR(x: mstring): LPSTR {....raises: [],
    tags: [].}
Converts mstring to LPSTR automatically.
converter winstrConverterMStringToPtrChar(x: mstring): ptr char {....raises: [],
    tags: [].}
Converts mstring to ptr char automatically.
converter winstrConverterStringToBSTR(x: string): BSTR {....raises: [], tags: [].}
Converts string to BSTR automatically.
converter winstrConverterStringToLPWSTR(x: string): LPWSTR {....raises: [],
    tags: [].}
Converts string to LPWSTR automatically.
converter winstrConverterStringToPtrChar(x: string): ptr char {....raises: [],
    tags: [].}
Converts string to ptr char automatically.
converter winstrConverterWideCStringToLPWSTR(x: WideCString): LPWSTR {.
    ...raises: [], tags: [].}
Converts WideCString to LPWSTR automatically.
converter winstrConverterWStringToBSTR(x: wstring): BSTR {....raises: [], tags: [].}
Converts wstring to BSTR automatically.
converter winstrConverterWStringToLPWSTR(x: wstring): LPWSTR {....raises: [],
    tags: [].}
Converts wstring to LPWSTR automatically.

Templates

template `<<<`[A: SomeBuffer | SomeString](a: A; b: SomeString)
Fill buffer by string, include a null. Please make sure both side have the same character size. If destination don't have the length information (e.g. pointer or UncheckedArray), please make sure the buffer size is large enough.
template `<<`[A: SomeBuffer | SomeString; B: SomeBuffer | SomeString](a: A; b: B)
Fill operator for SomeBuffer and SomeString. Please make sure both side have the same character size. If destination don't have the length information (e.g. pointer or UncheckedArray), please make sure the buffer size is large enough.
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
Generate 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
Generate wstring or mstring buffer depend on conditional symbol: useWinAnsi. Use & to get the buffer address and then pass to Windows API.
template T(x: string): untyped
Generate wstring or mstring depend on conditional symbol: useWinAnsi.