Modify

Opened 8 weeks ago

Closed 6 weeks ago

#3999 closed Feature Request (Completed)

_WinAPI_OemToChar - improved implementation

Reported by: AspirinJunkie Owned by: Jpm
Milestone: 3.3.17.0 Component: Standard UDFs
Version: Severity: None
Keywords: _WinAPI_OemToChar Cc:

Description

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:

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:

#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

Attachments (0)

Change History (3)

comment:1 Changed 8 weeks ago by TicketCleanup

  • Version 3.3.16.1 deleted

Automatic ticket cleanup.

comment:2 Changed 8 weeks ago by AspirinJunkie

Further optimized version in terms of memory consumption, performance and code size through shared use of the string buffer for input and output:

Func _WinAPI_OemToChar($sStr)
    Local $tString = DllStructCreate("CHAR[" & StringLen($sStr) + 1 & "]")
    DllStructSetData($tString, 1, $sStr)

    Local $aCall = DllCall("user32.dll", 'BOOL', 'OemToCharA', "struct*", $tString, "struct*", $tString)
    If @error Or Not $aCall[0] Then Return SetError(@error + 10, @extended, '')
        
    Return DllStructGetData($tString, 1)
EndFunc   ;==>_WinAPI_OemToChar

comment:3 Changed 6 weeks ago by Jpm

  • Milestone set to 3.3.17.0
  • Owner set to Jpm
  • Resolution set to Completed
  • Status changed from new to closed

Added by revision [13046] in version: 3.3.17.0

Guidelines for posting comments:

  • You cannot re-open a ticket but you may still leave a comment if you have additional information to add.
  • In-depth discussions should take place on the forum.

For more information see the full version of the ticket guidelines here.

Add Comment

Modify Ticket

Action
as closed The owner will remain Jpm.
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.