Przeglądaj źródła

`for in string16`; Support `string16` across core

gingerBill 2 miesięcy temu
rodzic
commit
bb4bc316a4

+ 62 - 0
base/runtime/internal.odin

@@ -781,6 +781,68 @@ string_decode_last_rune :: proc "contextless" (s: string) -> (rune, int) {
 	return r, size
 	return r, size
 }
 }
 
 
+
+string16_decode_rune :: #force_inline proc "contextless" (s: string16) -> (rune, int) {
+	REPLACEMENT_CHAR :: '\ufffd'
+	_surr1           :: 0xd800
+	_surr2           :: 0xdc00
+	_surr3           :: 0xe000
+	_surr_self       :: 0x10000
+
+	r := rune(REPLACEMENT_CHAR)
+
+	if len(s) < 1 {
+		return r, 0
+	}
+
+	w := 1
+	switch c := s[0]; {
+	case c < _surr1, _surr3 <= c:
+		r = rune(c)
+	case _surr1 <= c && c < _surr2 && 1 < len(s) &&
+		_surr2 <= s[1] && s[1] < _surr3:
+		r1, r2 := rune(c), rune(s[1])
+		if _surr1 <= r1 && r1 < _surr2 && _surr2 <= r2 && r2 < _surr3 {
+			r = (r1-_surr1)<<10 | (r2 - _surr2) + _surr_self
+		}
+		w += 1
+	}
+	return r, w
+}
+
+string16_decode_last_rune :: proc "contextless" (s: string16) -> (rune, int) {
+	REPLACEMENT_CHAR :: '\ufffd'
+	_surr1           :: 0xd800
+	_surr2           :: 0xdc00
+	_surr3           :: 0xe000
+	_surr_self       :: 0x10000
+
+	r := rune(REPLACEMENT_CHAR)
+
+	if len(s) < 1 {
+		return r, 0
+	}
+
+	n := len(s)-1
+	c := s[n]
+	w := 1
+	if _surr2 <= c && c < _surr3 {
+		if n >= 1 {
+			r1 := rune(s[n-1])
+			r2 := rune(c)
+			if _surr1 <= r1 && r1 < _surr2 {
+				r = (r1-_surr1)<<10 | (r2 - _surr2) + _surr_self
+			}
+			w = 2
+		}
+	} else if c < _surr1 || _surr3 <= c {
+		r = rune(c)
+	}
+	return r, w
+}
+
+
+
 abs_complex32 :: #force_inline proc "contextless" (x: complex32) -> f16 {
 abs_complex32 :: #force_inline proc "contextless" (x: complex32) -> f16 {
 	p, q := abs(real(x)), abs(imag(x))
 	p, q := abs(real(x)), abs(imag(x))
 	if p < q {
 	if p < q {

+ 8 - 8
core/c/libc/locale.odin

@@ -72,14 +72,14 @@ when ODIN_OS == .Windows {
 		n_sep_by_space:       c.char,
 		n_sep_by_space:       c.char,
 		p_sign_posn:          c.char,
 		p_sign_posn:          c.char,
 		n_sign_posn:          c.char,
 		n_sign_posn:          c.char,
-		_W_decimal_point:     [^]u16 `fmt:"s,0"`,
-		_W_thousands_sep:     [^]u16 `fmt:"s,0"`,
-		_W_int_curr_symbol:   [^]u16 `fmt:"s,0"`,
-		_W_currency_symbol:   [^]u16 `fmt:"s,0"`,
-		_W_mon_decimal_point: [^]u16 `fmt:"s,0"`,
-		_W_mon_thousands_sep: [^]u16 `fmt:"s,0"`,
-		_W_positive_sign:     [^]u16 `fmt:"s,0"`,
-		_W_negative_sign:     [^]u16 `fmt:"s,0"`,
+		_W_decimal_point:     cstring16,
+		_W_thousands_sep:     cstring16,
+		_W_int_curr_symbol:   cstring16,
+		_W_currency_symbol:   cstring16,
+		_W_mon_decimal_point: cstring16,
+		_W_mon_thousands_sep: cstring16,
+		_W_positive_sign:     cstring16,
+		_W_negative_sign:     cstring16,
 	}
 	}
 } else {
 } else {
 	lconv :: struct {
 	lconv :: struct {

+ 1 - 1
core/debug/trace/trace_windows.odin

@@ -54,7 +54,7 @@ _resolve :: proc(ctx: ^Context, frame: Frame, allocator: runtime.Allocator) -> (
 	symbol.SizeOfStruct = size_of(symbol^)
 	symbol.SizeOfStruct = size_of(symbol^)
 	symbol.MaxNameLen = 255
 	symbol.MaxNameLen = 255
 	if win32.SymFromAddrW(ctx.impl.hProcess, win32.DWORD64(frame), &{}, symbol) {
 	if win32.SymFromAddrW(ctx.impl.hProcess, win32.DWORD64(frame), &{}, symbol) {
-		fl.procedure, _ = win32.wstring_to_utf8(&symbol.Name[0], -1, allocator)
+		fl.procedure, _ = win32.wstring_to_utf8(cstring16(&symbol.Name[0]), -1, allocator)
 	} else {
 	} else {
 		fl.procedure = fmt.aprintf("(procedure: 0x%x)", frame, allocator=allocator)
 		fl.procedure = fmt.aprintf("(procedure: 0x%x)", frame, allocator=allocator)
 	}
 	}

+ 1 - 1
core/dynlib/lib_windows.odin

@@ -13,7 +13,7 @@ _LIBRARY_FILE_EXTENSION :: "dll"
 _load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) {
 _load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) {
 	// NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL
 	// NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL
 	wide_path := win32.utf8_to_wstring(path, allocator)
 	wide_path := win32.utf8_to_wstring(path, allocator)
-	defer free(wide_path, allocator)
+	defer free(rawptr(wide_path), allocator)
 	handle := cast(Library)win32.LoadLibraryW(wide_path)
 	handle := cast(Library)win32.LoadLibraryW(wide_path)
 	return handle, handle != nil
 	return handle, handle != nil
 }
 }

+ 1 - 1
core/mem/virtual/virtual_windows.odin

@@ -72,7 +72,7 @@ foreign Kernel32 {
 		flProtect:               u32,
 		flProtect:               u32,
 		dwMaximumSizeHigh:       u32,
 		dwMaximumSizeHigh:       u32,
 		dwMaximumSizeLow:        u32,
 		dwMaximumSizeLow:        u32,
-		lpName:                  [^]u16,
+		lpName:                  cstring16,
 	) -> rawptr ---
 	) -> rawptr ---
 
 
 	MapViewOfFile :: proc(
 	MapViewOfFile :: proc(

+ 1 - 1
core/os/dir_windows.odin

@@ -87,7 +87,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
 	defer delete(path)
 	defer delete(path)
 
 
 	find_data := &win32.WIN32_FIND_DATAW{}
 	find_data := &win32.WIN32_FIND_DATAW{}
-	find_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data)
+	find_handle := win32.FindFirstFileW(cstring16(raw_data(wpath_search)), find_data)
 	if find_handle == win32.INVALID_HANDLE_VALUE {
 	if find_handle == win32.INVALID_HANDLE_VALUE {
 		err = get_last_error()
 		err = get_last_error()
 		return dfi[:], err
 		return dfi[:], err

+ 1 - 1
core/os/os2/file_windows.odin

@@ -619,7 +619,7 @@ _symlink :: proc(old_name, new_name: string) -> Error {
 	return .Unsupported
 	return .Unsupported
 }
 }
 
 
-_open_sym_link :: proc(p: [^]u16) -> (handle: win32.HANDLE, err: Error) {
+_open_sym_link :: proc(p: cstring16) -> (handle: win32.HANDLE, err: Error) {
 	attrs := u32(win32.FILE_FLAG_BACKUP_SEMANTICS)
 	attrs := u32(win32.FILE_FLAG_BACKUP_SEMANTICS)
 	attrs |= win32.FILE_FLAG_OPEN_REPARSE_POINT
 	attrs |= win32.FILE_FLAG_OPEN_REPARSE_POINT
 	handle = win32.CreateFileW(p, 0, 0, nil, win32.OPEN_EXISTING, attrs, nil)
 	handle = win32.CreateFileW(p, 0, 0, nil, win32.OPEN_EXISTING, attrs, nil)

+ 2 - 2
core/os/stat_windows.odin

@@ -17,7 +17,7 @@ full_path_from_name :: proc(name: string, allocator := context.allocator) -> (pa
 	buf := make([dynamic]u16, 100)
 	buf := make([dynamic]u16, 100)
 	defer delete(buf)
 	defer delete(buf)
 	for {
 	for {
-		n := win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil)
+		n := win32.GetFullPathNameW(cstring16(raw_data(p)), u32(len(buf)), cstring16(raw_data(buf)), nil)
 		if n == 0 {
 		if n == 0 {
 			return "", get_last_error()
 			return "", get_last_error()
 		}
 		}
@@ -154,7 +154,7 @@ cleanpath_from_handle_u16 :: proc(fd: Handle, allocator: runtime.Allocator) -> (
 		return nil, get_last_error()
 		return nil, get_last_error()
 	}
 	}
 	buf := make([]u16, max(n, win32.DWORD(260))+1, allocator)
 	buf := make([]u16, max(n, win32.DWORD(260))+1, allocator)
-	buf_len := win32.GetFinalPathNameByHandleW(h, raw_data(buf), n, 0)
+	buf_len := win32.GetFinalPathNameByHandleW(h, cstring16(raw_data(buf)), n, 0)
 	return buf[:buf_len], nil
 	return buf[:buf_len], nil
 }
 }
 @(private, require_results)
 @(private, require_results)

+ 2 - 2
core/path/filepath/path_windows.odin

@@ -61,13 +61,13 @@ temp_full_path :: proc(name: string) -> (path: string, err: os.Error) {
 	}
 	}
 
 
 	p := win32.utf8_to_utf16(name, ta)
 	p := win32.utf8_to_utf16(name, ta)
-	n := win32.GetFullPathNameW(raw_data(p), 0, nil, nil)
+	n := win32.GetFullPathNameW(cstring16(raw_data(p)), 0, nil, nil)
 	if n == 0 {
 	if n == 0 {
 		return "", os.get_last_error()
 		return "", os.get_last_error()
 	}
 	}
 
 
 	buf := make([]u16, n, ta)
 	buf := make([]u16, n, ta)
-	n = win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil)
+	n = win32.GetFullPathNameW(cstring16(raw_data(p)), u32(len(buf)), cstring16(raw_data(buf)), nil)
 	if n == 0 {
 	if n == 0 {
 		delete(buf)
 		delete(buf)
 		return "", os.get_last_error()
 		return "", os.get_last_error()

+ 6 - 6
core/sys/info/platform_windows.odin

@@ -324,8 +324,8 @@ read_reg_string :: proc(hkey: sys.HKEY, subkey, val: string) -> (res: string, ok
 
 
 	status := sys.RegGetValueW(
 	status := sys.RegGetValueW(
 		hkey,
 		hkey,
-		&key_name_wide[0],
-		&val_name_wide[0],
+		cstring16(&key_name_wide[0]),
+		cstring16(&val_name_wide[0]),
 		sys.RRF_RT_REG_SZ,
 		sys.RRF_RT_REG_SZ,
 		nil,
 		nil,
 		raw_data(result_wide[:]),
 		raw_data(result_wide[:]),
@@ -359,8 +359,8 @@ read_reg_i32 :: proc(hkey: sys.HKEY, subkey, val: string) -> (res: i32, ok: bool
 	result_size := sys.DWORD(size_of(i32))
 	result_size := sys.DWORD(size_of(i32))
 	status := sys.RegGetValueW(
 	status := sys.RegGetValueW(
 		hkey,
 		hkey,
-		&key_name_wide[0],
-		&val_name_wide[0],
+		cstring16(&key_name_wide[0]),
+		cstring16(&val_name_wide[0]),
 		sys.RRF_RT_REG_DWORD,
 		sys.RRF_RT_REG_DWORD,
 		nil,
 		nil,
 		&res,
 		&res,
@@ -386,8 +386,8 @@ read_reg_i64 :: proc(hkey: sys.HKEY, subkey, val: string) -> (res: i64, ok: bool
 	result_size := sys.DWORD(size_of(i64))
 	result_size := sys.DWORD(size_of(i64))
 	status := sys.RegGetValueW(
 	status := sys.RegGetValueW(
 		hkey,
 		hkey,
-		&key_name_wide[0],
-		&val_name_wide[0],
+		cstring16(&key_name_wide[0]),
+		cstring16(&val_name_wide[0]),
 		sys.RRF_RT_REG_QWORD,
 		sys.RRF_RT_REG_QWORD,
 		nil,
 		nil,
 		&res,
 		&res,

+ 5 - 5
core/sys/windows/comctl32.odin

@@ -573,10 +573,10 @@ Button_GetTextMargin :: #force_inline proc "system" (hwnd: HWND, pmargin: ^RECT)
 	return cast(BOOL)SendMessageW(hwnd, BCM_GETTEXTMARGIN, 0, cast(LPARAM)uintptr(pmargin))
 	return cast(BOOL)SendMessageW(hwnd, BCM_GETTEXTMARGIN, 0, cast(LPARAM)uintptr(pmargin))
 }
 }
 Button_SetNote :: #force_inline proc "system" (hwnd: HWND, psz: LPCWSTR) -> BOOL {
 Button_SetNote :: #force_inline proc "system" (hwnd: HWND, psz: LPCWSTR) -> BOOL {
-	return cast(BOOL)SendMessageW(hwnd, BCM_SETNOTE, 0, cast(LPARAM)uintptr(psz))
+	return cast(BOOL)SendMessageW(hwnd, BCM_SETNOTE, 0, cast(LPARAM)uintptr(rawptr(psz)))
 }
 }
 Button_GetNote :: #force_inline proc "system" (hwnd: HWND, psz: LPCWSTR, pcc: ^c_int) -> BOOL {
 Button_GetNote :: #force_inline proc "system" (hwnd: HWND, psz: LPCWSTR, pcc: ^c_int) -> BOOL {
-	return cast(BOOL)SendMessageW(hwnd, BCM_GETNOTE, uintptr(pcc), cast(LPARAM)uintptr(psz))
+	return cast(BOOL)SendMessageW(hwnd, BCM_GETNOTE, uintptr(pcc), cast(LPARAM)uintptr(rawptr(psz)))
 }
 }
 Button_GetNoteLength :: #force_inline proc "system" (hwnd: HWND) -> LRESULT {
 Button_GetNoteLength :: #force_inline proc "system" (hwnd: HWND) -> LRESULT {
 	return SendMessageW(hwnd, BCM_GETNOTELENGTH, 0, 0)
 	return SendMessageW(hwnd, BCM_GETNOTELENGTH, 0, 0)
@@ -604,10 +604,10 @@ EDITBALLOONTIP :: struct {
 PEDITBALLOONTIP :: ^EDITBALLOONTIP
 PEDITBALLOONTIP :: ^EDITBALLOONTIP
 
 
 Edit_SetCueBannerText :: #force_inline proc "system" (hwnd: HWND, lpcwText: LPCWSTR) -> BOOL {
 Edit_SetCueBannerText :: #force_inline proc "system" (hwnd: HWND, lpcwText: LPCWSTR) -> BOOL {
-	return cast(BOOL)SendMessageW(hwnd, EM_SETCUEBANNER, 0, cast(LPARAM)uintptr(lpcwText))
+	return cast(BOOL)SendMessageW(hwnd, EM_SETCUEBANNER, 0, cast(LPARAM)uintptr(rawptr(lpcwText)))
 }
 }
 Edit_SetCueBannerTextFocused :: #force_inline proc "system" (hwnd: HWND, lpcwText: LPCWSTR, fDrawFocused: BOOL) -> BOOL {
 Edit_SetCueBannerTextFocused :: #force_inline proc "system" (hwnd: HWND, lpcwText: LPCWSTR, fDrawFocused: BOOL) -> BOOL {
-	return cast(BOOL)SendMessageW(hwnd, EM_SETCUEBANNER, cast(WPARAM)fDrawFocused, cast(LPARAM)uintptr(lpcwText))
+	return cast(BOOL)SendMessageW(hwnd, EM_SETCUEBANNER, cast(WPARAM)fDrawFocused, cast(LPARAM)uintptr(rawptr(lpcwText)))
 }
 }
 Edit_GetCueBannerText :: #force_inline proc "system" (hwnd: HWND, lpwText: LPWSTR, cchText: LONG) -> BOOL {
 Edit_GetCueBannerText :: #force_inline proc "system" (hwnd: HWND, lpwText: LPWSTR, cchText: LONG) -> BOOL {
 	return cast(BOOL)SendMessageW(hwnd, EM_GETCUEBANNER, uintptr(lpwText), cast(LPARAM)cchText)
 	return cast(BOOL)SendMessageW(hwnd, EM_GETCUEBANNER, uintptr(lpwText), cast(LPARAM)cchText)
@@ -1197,7 +1197,7 @@ ListView_GetItemPosition :: #force_inline proc "system" (hwnd: HWND, i: c_int, p
 	return cast(BOOL)SendMessageW(hwnd, LVM_GETITEMPOSITION, cast(WPARAM)i, cast(LPARAM)uintptr(ppt))
 	return cast(BOOL)SendMessageW(hwnd, LVM_GETITEMPOSITION, cast(WPARAM)i, cast(LPARAM)uintptr(ppt))
 }
 }
 ListView_GetStringWidth :: #force_inline proc "system" (hwndLV: HWND, psz: LPCWSTR) -> c_int {
 ListView_GetStringWidth :: #force_inline proc "system" (hwndLV: HWND, psz: LPCWSTR) -> c_int {
-	return cast(c_int)SendMessageW(hwndLV, LVM_GETSTRINGWIDTHW, 0, cast(LPARAM)uintptr(psz))
+	return cast(c_int)SendMessageW(hwndLV, LVM_GETSTRINGWIDTHW, 0, cast(LPARAM)uintptr(rawptr(psz)))
 }
 }
 ListView_HitTest :: #force_inline proc "system" (hwndLV: HWND, pinfo: ^LV_HITTESTINFO) -> c_int {
 ListView_HitTest :: #force_inline proc "system" (hwndLV: HWND, pinfo: ^LV_HITTESTINFO) -> c_int {
 	return cast(c_int)SendMessageW(hwndLV, LVM_HITTEST, 0, cast(LPARAM)uintptr(pinfo))
 	return cast(c_int)SendMessageW(hwndLV, LVM_HITTEST, 0, cast(LPARAM)uintptr(pinfo))

+ 3 - 3
core/sys/windows/ip_helper.odin

@@ -38,9 +38,9 @@ IP_Adapter_Addresses :: struct {
 	FirstAnycastAddress:    ^IP_ADAPTER_ANYCAST_ADDRESS_XP,
 	FirstAnycastAddress:    ^IP_ADAPTER_ANYCAST_ADDRESS_XP,
 	FirstMulticastAddress:  ^IP_ADAPTER_MULTICAST_ADDRESS_XP,
 	FirstMulticastAddress:  ^IP_ADAPTER_MULTICAST_ADDRESS_XP,
 	FirstDnsServerAddress:  ^IP_ADAPTER_DNS_SERVER_ADDRESS_XP,
 	FirstDnsServerAddress:  ^IP_ADAPTER_DNS_SERVER_ADDRESS_XP,
-	DnsSuffix:              ^u16,
-	Description:            ^u16,
-	FriendlyName:           ^u16,
+	DnsSuffix:              cstring16,
+	Description:            cstring16,
+	FriendlyName:           cstring16,
 	PhysicalAddress:        [8]u8,
 	PhysicalAddress:        [8]u8,
 	PhysicalAddressLength:  u32,
 	PhysicalAddressLength:  u32,
 	Anonymous2:             struct #raw_union {
 	Anonymous2:             struct #raw_union {

+ 2 - 2
core/sys/windows/types.odin

@@ -107,8 +107,8 @@ PDWORD64 :: ^DWORD64
 PDWORD_PTR :: ^DWORD_PTR
 PDWORD_PTR :: ^DWORD_PTR
 ATOM :: distinct WORD
 ATOM :: distinct WORD
 
 
-wstring :: [^]WCHAR
-PWSTR   :: [^]WCHAR
+wstring :: cstring16
+PWSTR   :: cstring16
 
 
 PBYTE :: ^BYTE
 PBYTE :: ^BYTE
 LPBYTE :: ^BYTE
 LPBYTE :: ^BYTE

+ 25 - 25
core/sys/windows/util.odin

@@ -122,14 +122,14 @@ utf8_to_utf16 :: proc{utf8_to_utf16_alloc, utf8_to_utf16_buf}
 
 
 utf8_to_wstring_alloc :: proc(s: string, allocator := context.temp_allocator) -> wstring {
 utf8_to_wstring_alloc :: proc(s: string, allocator := context.temp_allocator) -> wstring {
 	if res := utf8_to_utf16(s, allocator); len(res) > 0 {
 	if res := utf8_to_utf16(s, allocator); len(res) > 0 {
-		return raw_data(res)
+		return wstring(raw_data(res))
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
 utf8_to_wstring_buf :: proc(buf: []u16, s: string) -> wstring {
 utf8_to_wstring_buf :: proc(buf: []u16, s: string) -> wstring {
 	if res := utf8_to_utf16(buf, s); len(res) > 0 {
 	if res := utf8_to_utf16(buf, s); len(res) > 0 {
-		return raw_data(res)
+		return wstring(raw_data(res))
 	}
 	}
 	return nil
 	return nil
 }
 }
@@ -215,7 +215,7 @@ utf16_to_utf8_alloc :: proc(s: []u16, allocator := context.temp_allocator) -> (r
 	if len(s) == 0 {
 	if len(s) == 0 {
 		return "", nil
 		return "", nil
 	}
 	}
-	return wstring_to_utf8(raw_data(s), len(s), allocator)
+	return wstring_to_utf8(wstring(raw_data(s)), len(s), allocator)
 }
 }
 
 
 /*
 /*
@@ -236,7 +236,7 @@ utf16_to_utf8_buf :: proc(buf: []u8, s: []u16) -> (res: string) {
 	if len(s) == 0 {
 	if len(s) == 0 {
 		return
 		return
 	}
 	}
-	return wstring_to_utf8(buf, raw_data(s), len(s))
+	return wstring_to_utf8(buf, wstring(raw_data(s)), len(s))
 }
 }
 
 
 utf16_to_utf8 :: proc{utf16_to_utf8_alloc, utf16_to_utf8_buf}
 utf16_to_utf8 :: proc{utf16_to_utf8_alloc, utf16_to_utf8_buf}
@@ -298,7 +298,7 @@ _add_user :: proc(servername: string, username: string, password: string) -> (ok
 		servername_w = nil
 		servername_w = nil
 	} else {
 	} else {
 		server := utf8_to_utf16(servername, context.temp_allocator)
 		server := utf8_to_utf16(servername, context.temp_allocator)
-		servername_w = &server[0]
+		servername_w = wstring(&server[0])
 	}
 	}
 
 
 	if len(username) == 0 || len(username) > LM20_UNLEN {
 	if len(username) == 0 || len(username) > LM20_UNLEN {
@@ -348,7 +348,7 @@ get_computer_name_and_account_sid :: proc(username: string) -> (computer_name: s
 
 
 	res := LookupAccountNameW(
 	res := LookupAccountNameW(
 		nil, // Look on this computer first
 		nil, // Look on this computer first
-		&username_w[0],
+		wstring(&username_w[0]),
 		&sid,
 		&sid,
 		&cbsid,
 		&cbsid,
 		nil,
 		nil,
@@ -364,10 +364,10 @@ get_computer_name_and_account_sid :: proc(username: string) -> (computer_name: s
 
 
 	res = LookupAccountNameW(
 	res = LookupAccountNameW(
 		nil,
 		nil,
-		&username_w[0],
+		wstring(&username_w[0]),
 		&sid,
 		&sid,
 		&cbsid,
 		&cbsid,
-		&cname_w[0],
+		wstring(&cname_w[0]),
 		&computer_name_size,
 		&computer_name_size,
 		&pe_use,
 		&pe_use,
 	)
 	)
@@ -390,7 +390,7 @@ get_sid :: proc(username: string, sid: ^SID) -> (ok: bool) {
 
 
 	res := LookupAccountNameW(
 	res := LookupAccountNameW(
 		nil, // Look on this computer first
 		nil, // Look on this computer first
-		&username_w[0],
+		wstring(&username_w[0]),
 		sid,
 		sid,
 		&cbsid,
 		&cbsid,
 		nil,
 		nil,
@@ -406,10 +406,10 @@ get_sid :: proc(username: string, sid: ^SID) -> (ok: bool) {
 
 
 	res = LookupAccountNameW(
 	res = LookupAccountNameW(
 		nil,
 		nil,
-		&username_w[0],
+		wstring(&username_w[0]),
 		sid,
 		sid,
 		&cbsid,
 		&cbsid,
-		&cname_w[0],
+		wstring(&cname_w[0]),
 		&computer_name_size,
 		&computer_name_size,
 		&pe_use,
 		&pe_use,
 	)
 	)
@@ -428,7 +428,7 @@ add_user_to_group :: proc(sid: ^SID, group: string) -> (ok: NET_API_STATUS) {
 	group_name := utf8_to_utf16(group, context.temp_allocator)
 	group_name := utf8_to_utf16(group, context.temp_allocator)
 	ok = NetLocalGroupAddMembers(
 	ok = NetLocalGroupAddMembers(
 		nil,
 		nil,
-		&group_name[0],
+		wstring(&group_name[0]),
 		0,
 		0,
 		&group_member,
 		&group_member,
 		1,
 		1,
@@ -443,7 +443,7 @@ add_del_from_group :: proc(sid: ^SID, group: string) -> (ok: NET_API_STATUS) {
 	group_name := utf8_to_utf16(group, context.temp_allocator)
 	group_name := utf8_to_utf16(group, context.temp_allocator)
 	ok = NetLocalGroupDelMembers(
 	ok = NetLocalGroupDelMembers(
 		nil,
 		nil,
-		&group_name[0],
+		cstring16(&group_name[0]),
 		0,
 		0,
 		&group_member,
 		&group_member,
 		1,
 		1,
@@ -465,19 +465,19 @@ add_user_profile :: proc(username: string) -> (ok: bool, profile_path: string) {
 	if res == false {
 	if res == false {
 		return false, ""
 		return false, ""
 	}
 	}
-	defer LocalFree(sb)
+	defer LocalFree(rawptr(sb))
 
 
 	pszProfilePath := make([]u16, 257, context.temp_allocator)
 	pszProfilePath := make([]u16, 257, context.temp_allocator)
 	res2 := CreateProfile(
 	res2 := CreateProfile(
 		sb,
 		sb,
-		&username_w[0],
-		&pszProfilePath[0],
+		cstring16(&username_w[0]),
+		cstring16(&pszProfilePath[0]),
 		257,
 		257,
 	)
 	)
 	if res2 != 0 {
 	if res2 != 0 {
 		return false, ""
 		return false, ""
 	}
 	}
-	profile_path = wstring_to_utf8(&pszProfilePath[0], 257) or_else ""
+	profile_path = wstring_to_utf8(wstring(&pszProfilePath[0]), 257) or_else ""
 
 
 	return true, profile_path
 	return true, profile_path
 }
 }
@@ -495,7 +495,7 @@ delete_user_profile :: proc(username: string) -> (ok: bool) {
 	if res == false {
 	if res == false {
 		return false
 		return false
 	}
 	}
-	defer LocalFree(sb)
+	defer LocalFree(rawptr(sb))
 
 
 	res2 := DeleteProfileW(
 	res2 := DeleteProfileW(
 		sb,
 		sb,
@@ -548,13 +548,13 @@ delete_user :: proc(servername: string, username: string) -> (ok: bool) {
 		servername_w = nil
 		servername_w = nil
 	} else {
 	} else {
 		server := utf8_to_utf16(servername, context.temp_allocator)
 		server := utf8_to_utf16(servername, context.temp_allocator)
-		servername_w = &server[0]
+		servername_w = wstring(&server[0])
 	}
 	}
 	username_w := utf8_to_utf16(username)
 	username_w := utf8_to_utf16(username)
 
 
 	res := NetUserDel(
 	res := NetUserDel(
 		servername_w,
 		servername_w,
-		&username_w[0],
+		wstring(&username_w[0]),
 	)
 	)
 	if res != .Success {
 	if res != .Success {
 		return false
 		return false
@@ -586,9 +586,9 @@ run_as_user :: proc(username, password, application, commandline: string, pi: ^P
 	user_token: HANDLE
 	user_token: HANDLE
 
 
 	ok = bool(LogonUserW(
 	ok = bool(LogonUserW(
-		lpszUsername    = &username_w[0],
-		lpszDomain      = &domain_w[0],
-		lpszPassword    = &password_w[0],
+		lpszUsername    = wstring(&username_w[0]),
+		lpszDomain      = wstring(&domain_w[0]),
+		lpszPassword    = wstring(&password_w[0]),
 		dwLogonType     = .NEW_CREDENTIALS,
 		dwLogonType     = .NEW_CREDENTIALS,
 		dwLogonProvider = .WINNT50,
 		dwLogonProvider = .WINNT50,
 		phToken         = &user_token,
 		phToken         = &user_token,
@@ -605,8 +605,8 @@ run_as_user :: proc(username, password, application, commandline: string, pi: ^P
 
 
 	ok = bool(CreateProcessAsUserW(
 	ok = bool(CreateProcessAsUserW(
 		user_token,
 		user_token,
-		&app_w[0],
-		&commandline_w[0],
+		wstring(&app_w[0]),
+		wstring(&commandline_w[0]),
 		nil,	// lpProcessAttributes,
 		nil,	// lpProcessAttributes,
 		nil,	// lpThreadAttributes,
 		nil,	// lpThreadAttributes,
 		false,	// bInheritHandles,
 		false,	// bInheritHandles,

+ 4 - 4
core/time/timezone/tz_windows.odin

@@ -159,9 +159,9 @@ iana_to_windows_tz :: proc(iana_name: string, allocator := context.allocator) ->
 	status: windows.UError 
 	status: windows.UError 
 
 
 	iana_name_wstr := windows.utf8_to_wstring(iana_name, allocator)
 	iana_name_wstr := windows.utf8_to_wstring(iana_name, allocator)
-	defer free(iana_name_wstr, allocator)
+	defer free(rawptr(iana_name_wstr), allocator)
 
 
-	wintz_name_len := windows.ucal_getWindowsTimeZoneID(iana_name_wstr, -1, raw_data(wintz_name_buffer[:]), len(wintz_name_buffer), &status)
+	wintz_name_len := windows.ucal_getWindowsTimeZoneID(iana_name_wstr, -1, cstring16(raw_data(wintz_name_buffer[:])), len(wintz_name_buffer), &status)
 	if status != .U_ZERO_ERROR {
 	if status != .U_ZERO_ERROR {
 		return
 		return
 	}
 	}
@@ -178,7 +178,7 @@ local_tz_name :: proc(allocator := context.allocator) -> (name: string, success:
 	iana_name_buffer: [128]u16
 	iana_name_buffer: [128]u16
 	status: windows.UError
 	status: windows.UError
 
 
-	zone_str_len := windows.ucal_getDefaultTimeZone(raw_data(iana_name_buffer[:]), len(iana_name_buffer), &status)
+	zone_str_len := windows.ucal_getDefaultTimeZone(cstring16(raw_data(iana_name_buffer[:])), len(iana_name_buffer), &status)
 	if status != .U_ZERO_ERROR {
 	if status != .U_ZERO_ERROR {
 		return
 		return
 	}
 	}
@@ -291,7 +291,7 @@ _region_load :: proc(reg_str: string, allocator := context.allocator) -> (out_re
 	defer delete(tz_key, allocator)
 	defer delete(tz_key, allocator)
 
 
 	tz_key_wstr := windows.utf8_to_wstring(tz_key, allocator)
 	tz_key_wstr := windows.utf8_to_wstring(tz_key, allocator)
-	defer free(tz_key_wstr, allocator)
+	defer free(rawptr(tz_key_wstr), allocator)
 
 
 	key: windows.HKEY
 	key: windows.HKEY
 	res := windows.RegOpenKeyExW(windows.HKEY_LOCAL_MACHINE, tz_key_wstr, 0, windows.KEY_READ, &key)
 	res := windows.RegOpenKeyExW(windows.HKEY_LOCAL_MACHINE, tz_key_wstr, 0, windows.KEY_READ, &key)

+ 31 - 1
core/unicode/utf16/utf16.odin

@@ -126,7 +126,37 @@ decode_rune_in_string :: proc(s: string16) -> (r: rune, width: int) {
 	return
 	return
 }
 }
 
 
-rune_count :: proc(s: []u16) -> (n: int) {
+string_to_runes :: proc "odin" (s: string16, allocator := context.allocator) -> (runes: []rune) {
+	n := rune_count(s)
+
+	runes = make([]rune, n, allocator)
+	i := 0
+	for r in s {
+		runes[i] = r
+		i += 1
+	}
+	return
+}
+
+
+rune_count :: proc{
+	rune_count_in_string,
+	rune_count_in_slice,
+}
+rune_count_in_string :: proc(s: string16) -> (n: int) {
+	for i := 0; i < len(s); i += 1 {
+		c := s[i]
+		if _surr1 <= c && c < _surr2 && i+1 < len(s) &&
+			_surr2 <= s[i+1] && s[i+1] < _surr3 {
+			i += 1
+		}
+		n += 1
+	}
+	return
+}
+
+
+rune_count_in_slice :: proc(s: []u16) -> (n: int) {
 	for i := 0; i < len(s); i += 1 {
 	for i := 0; i < len(s); i += 1 {
 		c := s[i]
 		c := s[i]
 		if _surr1 <= c && c < _surr2 && i+1 < len(s) && 
 		if _surr1 <= c && c < _surr2 && i+1 < len(s) && 

+ 5 - 1
src/check_builtin.cpp

@@ -7179,7 +7179,11 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
 				return false;
 				return false;
 			}
 			}
 			operand->mode = Addressing_Value;
 			operand->mode = Addressing_Value;
-			operand->type = alloc_type_multi_pointer(t_u16);
+			if (type_hint != nullptr && is_type_cstring16(type_hint)) {
+				operand->type = type_hint;
+			} else {
+				operand->type = alloc_type_multi_pointer(t_u16);
+			}
 			operand->value = {};
 			operand->value = {};
 			break;
 			break;
 		}
 		}

+ 1 - 1
src/check_expr.cpp

@@ -3426,7 +3426,7 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
 	if (are_types_identical(src, t_cstring16) && is_type_u16_multi_ptr(dst)) {
 	if (are_types_identical(src, t_cstring16) && is_type_u16_multi_ptr(dst)) {
 		return !is_constant;
 		return !is_constant;
 	}
 	}
-	// cstring -> rawptr
+	// cstring16 -> rawptr
 	if (are_types_identical(src, t_cstring16) && is_type_rawptr(dst)) {
 	if (are_types_identical(src, t_cstring16) && is_type_rawptr(dst)) {
 		return !is_constant;
 		return !is_constant;
 	}
 	}

+ 23 - 3
src/check_stmt.cpp

@@ -974,7 +974,14 @@ gb_internal void check_unroll_range_stmt(CheckerContext *ctx, Ast *node, u32 mod
 			Type *t = base_type(operand.type);
 			Type *t = base_type(operand.type);
 			switch (t->kind) {
 			switch (t->kind) {
 			case Type_Basic:
 			case Type_Basic:
-				if (is_type_string(t) && t->Basic.kind != Basic_cstring) {
+				if (is_type_string16(t) && t->Basic.kind != Basic_cstring) {
+					val0 = t_rune;
+					val1 = t_int;
+					inline_for_depth = exact_value_i64(operand.value.value_string.len);
+					if (unroll_count > 0) {
+						error(node, "#unroll(%lld) does not support strings", cast(long long)unroll_count);
+					}
+				} else if (is_type_string(t) && t->Basic.kind != Basic_cstring) {
 					val0 = t_rune;
 					val0 = t_rune;
 					val1 = t_int;
 					val1 = t_int;
 					inline_for_depth = exact_value_i64(operand.value.value_string.len);
 					inline_for_depth = exact_value_i64(operand.value.value_string.len);
@@ -1236,7 +1243,11 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags
 
 
 				add_to_seen_map(ctx, &seen, upper_op, x, lhs, rhs);
 				add_to_seen_map(ctx, &seen, upper_op, x, lhs, rhs);
 
 
-				if (is_type_string(x.type)) {
+				if (is_type_string16(x.type)) {
+					// NOTE(bill): Force dependency for strings here
+					add_package_dependency(ctx, "runtime", "string16_le");
+					add_package_dependency(ctx, "runtime", "string16_lt");
+				} else if (is_type_string(x.type)) {
 					// NOTE(bill): Force dependency for strings here
 					// NOTE(bill): Force dependency for strings here
 					add_package_dependency(ctx, "runtime", "string_le");
 					add_package_dependency(ctx, "runtime", "string_le");
 					add_package_dependency(ctx, "runtime", "string_lt");
 					add_package_dependency(ctx, "runtime", "string_lt");
@@ -1770,7 +1781,16 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
 
 
 			switch (t->kind) {
 			switch (t->kind) {
 			case Type_Basic:
 			case Type_Basic:
-				if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) {
+				if (t->Basic.kind == Basic_string16) {
+					is_possibly_addressable = false;
+					array_add(&vals, t_rune);
+					array_add(&vals, t_int);
+					if (is_reverse) {
+						add_package_dependency(ctx, "runtime", "string16_decode_last_rune");
+					} else {
+						add_package_dependency(ctx, "runtime", "string16_decode_rune");
+					}
+				} else if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) {
 					is_possibly_addressable = false;
 					is_possibly_addressable = false;
 					array_add(&vals, t_rune);
 					array_add(&vals, t_rune);
 					array_add(&vals, t_int);
 					array_add(&vals, t_int);

+ 120 - 1
src/llvm_backend_stmt.cpp

@@ -622,6 +622,121 @@ gb_internal void lb_build_range_string(lbProcedure *p, lbValue expr, Type *val_t
 	if (done_) *done_ = done;
 	if (done_) *done_ = done;
 }
 }
 
 
+gb_internal void lb_build_range_string16(lbProcedure *p, lbValue expr, Type *val_type,
+                                       lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_,
+                                       bool is_reverse) {
+
+	lbModule *m = p->module;
+	lbValue count = lb_const_int(m, t_int, 0);
+	Type *expr_type = base_type(expr.type);
+	switch (expr_type->kind) {
+	case Type_Basic:
+		count = lb_string_len(p, expr);
+		break;
+	default:
+		GB_PANIC("Cannot do range_string of %s", type_to_string(expr_type));
+		break;
+	}
+
+	lbValue val = {};
+	lbValue idx = {};
+	lbBlock *loop = nullptr;
+	lbBlock *done = nullptr;
+	lbBlock *body = nullptr;
+
+	loop = lb_create_block(p, "for.string16.loop");
+	body = lb_create_block(p, "for.string16.body");
+	done = lb_create_block(p, "for.string16.done");
+
+	lbAddr offset_ = lb_add_local_generated(p, t_int, false);
+	lbValue offset = {};
+	lbValue cond = {};
+
+	if (!is_reverse) {
+		/*
+			for c, offset in str {
+				...
+			}
+
+			offset := 0
+			for offset < len(str) {
+				c, _w := string16_decode_rune(str[offset:])
+				...
+				offset += _w
+			}
+		*/
+		lb_addr_store(p, offset_, lb_const_int(m, t_int, 0));
+
+		lb_emit_jump(p, loop);
+		lb_start_block(p, loop);
+
+
+		offset = lb_addr_load(p, offset_);
+		cond = lb_emit_comp(p, Token_Lt, offset, count);
+	} else {
+		// NOTE(bill): REVERSED LOGIC
+		/*
+			#reverse for c, offset in str {
+				...
+			}
+
+			offset := len(str)
+			for offset > 0 {
+				c, _w := string16_decode_last_rune(str[:offset])
+				offset -= _w
+				...
+			}
+		*/
+		lb_addr_store(p, offset_, count);
+
+		lb_emit_jump(p, loop);
+		lb_start_block(p, loop);
+
+		offset = lb_addr_load(p, offset_);
+		cond = lb_emit_comp(p, Token_Gt, offset, lb_const_int(m, t_int, 0));
+	}
+	lb_emit_if(p, cond, body, done);
+	lb_start_block(p, body);
+
+
+	lbValue rune_and_len = {};
+	if (!is_reverse) {
+		lbValue str_elem = lb_emit_ptr_offset(p, lb_string_elem(p, expr), offset);
+		lbValue str_len  = lb_emit_arith(p, Token_Sub, count, offset, t_int);
+		auto args = array_make<lbValue>(permanent_allocator(), 1);
+		args[0] = lb_emit_string16(p, str_elem, str_len);
+
+		rune_and_len = lb_emit_runtime_call(p, "string16_decode_rune", args);
+		lbValue len  = lb_emit_struct_ev(p, rune_and_len, 1);
+		lb_addr_store(p, offset_, lb_emit_arith(p, Token_Add, offset, len, t_int));
+
+		idx = offset;
+	} else {
+		// NOTE(bill): REVERSED LOGIC
+		lbValue str_elem = lb_string_elem(p, expr);
+		lbValue str_len  = offset;
+		auto args = array_make<lbValue>(permanent_allocator(), 1);
+		args[0] = lb_emit_string16(p, str_elem, str_len);
+
+		rune_and_len = lb_emit_runtime_call(p, "string16_decode_last_rune", args);
+		lbValue len  = lb_emit_struct_ev(p, rune_and_len, 1);
+		lb_addr_store(p, offset_, lb_emit_arith(p, Token_Sub, offset, len, t_int));
+
+		idx = lb_addr_load(p, offset_);
+	}
+
+
+	if (val_type != nullptr) {
+		val = lb_emit_struct_ev(p, rune_and_len, 0);
+	}
+
+	if (val_)  *val_  = val;
+	if (idx_)  *idx_  = idx;
+	if (loop_) *loop_ = loop;
+	if (done_) *done_ = done;
+}
+
+
 
 
 gb_internal Ast *lb_strip_and_prefix(Ast *ident) {
 gb_internal Ast *lb_strip_and_prefix(Ast *ident) {
 	if (ident != nullptr) {
 	if (ident != nullptr) {
@@ -1138,7 +1253,11 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc
 			}
 			}
 			Type *t = base_type(string.type);
 			Type *t = base_type(string.type);
 			GB_ASSERT(!is_type_cstring(t));
 			GB_ASSERT(!is_type_cstring(t));
-			lb_build_range_string(p, string, val0_type, &val, &key, &loop, &done, rs->reverse);
+			if (is_type_string16(t)) {
+				lb_build_range_string16(p, string, val0_type, &val, &key, &loop, &done, rs->reverse);
+			} else {
+				lb_build_range_string(p, string, val0_type, &val, &key, &loop, &done, rs->reverse);
+			}
 			break;
 			break;
 		}
 		}
 		case Type_Tuple:
 		case Type_Tuple:

+ 17 - 0
src/llvm_backend_utility.cpp

@@ -191,6 +191,23 @@ gb_internal lbValue lb_emit_clamp(lbProcedure *p, Type *t, lbValue x, lbValue mi
 	return z;
 	return z;
 }
 }
 
 
+gb_internal lbValue lb_emit_string16(lbProcedure *p, lbValue str_elem, lbValue str_len) {
+	if (false && lb_is_const(str_elem) && lb_is_const(str_len)) {
+		LLVMValueRef values[2] = {
+			str_elem.value,
+			str_len.value,
+		};
+		lbValue res = {};
+		res.type = t_string16;
+		res.value = llvm_const_named_struct(p->module, t_string16, values, gb_count_of(values));
+		return res;
+	} else {
+		lbAddr res = lb_add_local_generated(p, t_string16, false);
+		lb_emit_store(p, lb_emit_struct_ep(p, res.addr, 0), str_elem);
+		lb_emit_store(p, lb_emit_struct_ep(p, res.addr, 1), str_len);
+		return lb_addr_load(p, res);
+	}
+}
 
 
 
 
 gb_internal lbValue lb_emit_string(lbProcedure *p, lbValue str_elem, lbValue str_len) {
 gb_internal lbValue lb_emit_string(lbProcedure *p, lbValue str_elem, lbValue str_len) {