﻿id	summary	reporter	owner	description	type	status	milestone	component	version	severity	resolution	keywords	cc
3999	_WinAPI_OemToChar - improved implementation	AspirinJunkie	Jpm	"The current implementation of the function `_WinAPI_OemToChar()` uses ""''str''"" as the string parameters for `DllCall()` as the data type for the strings. This means that the size of the buffer for the output string is fixed at 65536 characters.
With small strings, this leads to unused memory and large strings no longer fit into the buffer.
The second problem was solved in the current implementation by splitting the input string into pieces of 65536 characters, processing them individually and then reassembling them.

The following alternative implementation is now proposed instead:


{{{#!autoit
Func _WinAPI_OemToChar($sStr)
	; input string
	Local $tIn = DllStructCreate(""CHAR["" & StringLen($sStr) + 1 & ""]"")
	DllStructSetData($tIn, 1, $sStr)

	; output buffer
	Local $tOut = DllStructCreate(""CHAR["" & StringLen($sStr) + 1 & ""]"")

	Local $aCall = DllCall(""user32.dll"", ""BOOL"", ""OemToCharA"", ""PTR"", DllStructGetPtr($tIn), ""PTR"", DllStructGetPtr($tOut))
	If @error Or Not $aCall[0] Then Return SetError(@error + 10, @extended, '')
		
	Return DllStructGetData($tOut, 1)
EndFunc   ;==>_WinAPI_OemToChar
}}}


By manually creating the buffer in the correct size, memory wastage is prevented for small strings and multiple executions of DllCall are eliminated for large strings. This results in advantages over the previous implementation in terms of memory efficiency and performance without any recognizable disadvantages.

The following script demonstrates a performance comparison of the two implementations: 
{{{#!autoit
#include <WinAPIConv.au3>
#include <String.au3>

Global Const $aN = [1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8]
Global Const $iRUNS = 10
Global $iT, $iT1, $iT2

Global $sMeasureFormat = ""%15.3f  ms"", $dFormLen = StringLen(StringFormat($sMeasureFormat, 0))

Global $sHeader = StringFormat(""\n% 11s\t% "" & $dFormLen & ""s\t% "" & $dFormLen & ""s% 13s\n"", ""String size"", ""_WinAPI_OemToChar"", ""_WinAPI_OemToChar2"", ""Speedup"")
ConsoleWrite($sHeader & _StringRepeat(""-"", 64))



For $iN In $aN
	
	; put some preparation stuff here
	Global $sString = _StringRepeat(""ß"", $iN)
	ConsoleWrite(StringFormat(""\n% 11d"", StringLen($sString)))

	; the original _WinAPI_OemToChar()
	$iT = TimerInit()
	For $i = 1 To $iRUNS
		_WinAPI_OemToChar($sString)
	Next
	$iT1 = TimerDiff($iT)
	ConsoleWrite(StringFormat(""\t"" & $sMeasureFormat, $iT1 / $iRUNS))

	; the modified _WinAPI_OemToChar
	$iT = TimerInit()
	For $i = 1 To $iRUNS
		_WinAPI_OemToChar2($sString)
	Next
	$iT2 = TimerDiff($iT)
	ConsoleWrite(StringFormat(""\t"" & $sMeasureFormat, $iT2 / $iRUNS))

	ConsoleWrite(StringFormat(""\t%10.1f %%"", (1 - $iT2 / $iT1) * 100))
Next
ConsoleWrite(@CRLF & @CRLF)



Func _WinAPI_OemToChar2($sStr)
	; input string
	Local $tIn = DllStructCreate(""CHAR["" & StringLen($sStr) + 1 & ""]"")
	DllStructSetData($tIn, 1, $sStr)

	; output buffer
	Local $tOut = DllStructCreate(""CHAR["" & StringLen($sStr) + 1 & ""]"")

	Local $aCall = DllCall(""user32.dll"", ""BOOL"", ""OemToCharA"", ""PTR"", DllStructGetPtr($tIn), ""PTR"", DllStructGetPtr($tOut))
	If @error Or Not $aCall[0] Then Return SetError(@error + 10, @extended, '')
		
	Return DllStructGetData($tOut, 1)
EndFunc   ;==>_WinAPI_OemToChar
}}}
"	Feature Request	closed	3.3.17.0	Standard UDFs		None	Completed	_WinAPI_OemToChar	
