strLen boosted

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
aGerman
Expert
Posts: 4721
Joined: 22 Jan 2010 18:01
Location: Germany

Re: strLen boosted

#31 Post by aGerman » 06 Jul 2025 14:54

It's really unreadable, and the variable names are, at best, ugly.
Agreed. At least I tried to have it consistently ugly :lol:
However, it's tricky to get reliable time measurements on a modern system, where the CPU frequency fluctuates constantly.
Yeah. To mitigate it I run the tests in realtime priority. This makes sure we get always fed with enough resources. Without it I get results that are occasionally double as high as usual.

FWIW, that's how your code performs on my notebook if I add it to my test code.

Code: Select all

~~~~~~~~~~~~~~~~~~~~
jeb

8191
00:00:01.44
8191
00:00:01.49
8191
00:00:01.49
8191
00:00:01.49

1000
00:00:01.25
1000
00:00:01.28
1000
00:00:01.27
1000
00:00:01.27

200
00:00:01.11
200
00:00:01.15
200
00:00:01.14
200
00:00:01.16

10
00:00:01.11
10
00:00:01.16
10
00:00:01.14
10
00:00:01.14
Drücken Sie eine beliebige Taste . . .
Excluding the code for long strings makes processing short strings as quick as expected.

Code: Select all

%$lib.macrodefine.free% set strLen=for %%# in (1 2) do if %%#==2 ( %$\n%
    for /f "tokens=1,2" %%1 in ("%%!args%%!") do ( %$\n%
        set L=0 %$\n%
        if defined %%~1 ( %$\n%
            if "" neq "%%!%%~1:~255%%!" ( %$\n%
                for %%# in (4095 2047 1023 511 255) do ( %$\n%
                    set /a t=L+%%# %$\n%
                    for %%T in (%%!t%%!) do ( %$\n%
                        if "" neq "%%!%%~1:~%%T%%!" set /a L=%%T+1 %$\n%
                    ) %$\n%
                ) %$\n%
            ) %$\n%
            for %%# in (%%!L%%!) do ( %$\n%
                set ^"$=%%!%%~1:~%%#%%!^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCC^
BBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAA99999999999999998888888888888888^
7777777777777777666666666666666655555555555555554444444444444444^
333333333333333322222222222222221111111111111111^" %$\n%
                set /a L+=0x%%!$:~511,1%%!%%!$:~255,1%%! %$\n%
            ) %$\n%
        ) %$\n%
        for %%# in (%%!L%%!) do ( %$\n%
            endlocal %$\n%
            set %%~2=%%# %$\n%
        ) %$\n%
    ) %$\n%
 ) else setlocal EnableDelayedExpansion ^& set args=

Code: Select all

~~~~~~~~~~~~~~~~~~~~
jeb

8191
00:00:01.51
8191
00:00:01.55
8191
00:00:01.54
8191
00:00:01.53

1000
00:00:01.27
1000
00:00:01.30
1000
00:00:01.30
1000
00:00:01.30

200
00:00:00.69
200
00:00:00.72
200
00:00:00.70
200
00:00:00.71

10
00:00:00.69
10
00:00:00.71
10
00:00:00.72
10
00:00:00.71
Drücken Sie eine beliebige Taste . . .
Steffen

aGerman
Expert
Posts: 4721
Joined: 22 Jan 2010 18:01
Location: Germany

Re: strLen boosted

#32 Post by aGerman » 09 Jul 2025 13:40

I don't feel like continuing to try to squeeze out every last hundredth of a second 🤯
That's it for now:

Code: Select all

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:initStrLen
:: Computes the number of bytes in a string.
:: %strLen% str len
::   str - [ByRef In] Name of the variable containing the string to be measured.
::   len - [ByRef Out] Name of the variable that receives the measured length.
:: Strings of up to 8191 characters are supported.
                                                FOR /F %%! IN ("! ^! ^^^!") DO ^
set strLen=for %%. in (1 2) do if %%.==2 (^
  for /f "tokens=1,2" %%1 in ("%%!args%%!") do (^
    set "L=0"^&^
    (if defined %%~1^
      (if "" neq "%%!%%~1:~255%%!"^
        (if "" neq "%%!%%~1:~4095%%!"^
          (set "S=%%!%%~1:~4096%%!"^&set "L=4096") else set "S=%%!%%~1%%!"^
        )^&^
        (if defined S^
          set ^"scale=^
%%!S:~255,1%%!%%!S:~511,1%%!%%!S:~767,1%%!%%!S:~1023,1%%!%%!S:~1279,1%%!^
%%!S:~1535,1%%!%%!S:~1791,1%%!%%!S:~2047,1%%!%%!S:~2303,1%%!%%!S:~2559,1%%!^
%%!S:~2815,1%%!%%!S:~3071,1%%!%%!S:~3327,1%%!%%!S:~3583,1%%!%%!S:~3839,1%%!^
FEDCBA9876543210^"^&^
          set /a "L+=0x%%!scale:~15,1%%!*256"^
        )^
      )^&^
      for %%# in (%%!L%%!) do set ^"S=%%!%%~1:~%%#%%!^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCC^
BBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAA99999999999999998888888888888888^
7777777777777777666666666666666655555555555555554444444444444444^
333333333333333322222222222222221111111111111111^"^&^
      set /a "L+=0x%%!S:~511,1%%!%%!S:~255,1%%!"^
    )^&^
    for %%# in (%%!L%%!) do endlocal^&set "%%~2=%%#"^
  )^
) else setlocal EnableDelayedExpansion^&set args=

goto :eof
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
I hope it's also a little more readable now, even if I refrained from defining any additional variables for just a single macro. Indentations are still broken as we can't indent string continuations. Although it's harder to understand, the %%! is beneficial. That's why I used it now instead of the escaped exclamation marks.


Test code:

Code: Select all

:: make sure we are in the spotlight:
@if "%~1"=="" start /realtime conhost "%~f0" 1&exit /b
@echo off &setlocal DisableDelayedExpansion

:: 8191
set str1=^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
:: 1000
set "str2=%str1:~,1000%"
:: 200
set "str3=%str1:~,200%"
:: 10
set "str4=%str1:~,10%"

call :initTimediff

:: uncomment this to get the evidence that it also works with delayed expansion enabled
::setlocal EnableDelayedExpansion

echo(
echo ~~~~~~~~~~~~~~~~~~~~
echo Steffen
call :initStrLenSteffen
call :test str1
call :test str2
call :test str3
call :test str4

echo(
echo ~~~~~~~~~~~~~~~~~~~~
echo Francesco
call :initStrLenFrancesco
call :test str1
call :test str2
call :test str3
call :test str4

echo(
echo ~~~~~~~~~~~~~~~~~~~~
echo jeb
call :initStrLenJeb
call :test str1
call :test str2
call :test str3
call :test str4

echo(
echo ~~~~~~~~~~~~~~~~~~~~
echo final combination
call :initStrLen
call :test str1
call :test str2
call :test str3
call :test str4

pause
exit /b

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:initStrLenSteffen
setlocal DisableDelayedExpansion

:: Computes the number of bytes in a string.
:: %strLen% str len
::   str - [ByRef In] Name of the variable containing the string to be measured.
::   len - [ByRef Out] Name of the variable that receives the measured length.
:: Strings of up to 8191 characters are supported.
set strLen=for %%- in (1 2) do if %%-==2 (^
setlocal EnableDelayedExpansion^&for /f "tokens=1,2" %%. in ("^^!`^^!") do ^
set #=0^&(if defined %%~. (if ` neq ^^!%%~.:~4095^^!` ^
(set $=^^!%%~.:~4096^^!F^&set /a #+=4096) else set $=^^!%%~.^^!F)^&^
(for %%# in (2048 1024 512 256) do if ` neq ^^!$:~%%#^^!` ^
set $=^^!$:~%%#^^!^&set /a #+=%%#)^&set $=^^!$^^!^
EDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCC^
BBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAA99999999999999998888888888888888^
7777777777777777666666666666666655555555555555554444444444444444^
333333333333333322222222222222221111111111111111^&^
set /a #+=0x^^!$:~511,1^^!^^!$:~255,1^^!)^&^
for %%# in (^^!#^^!) do endlocal^&set %%~/=%%#) else set `=

endlocal&set "strLen=%strLen%"
if !!# neq # set "strLen=%strLen:^^=%"
goto :eof
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:initStrLenFrancesco
setlocal DisableDelayedExpansion

set strLen=for %%- in (1 2) do if %%-==2 (^
setlocal EnableDelayedExpansion^&for /f "tokens=1,2" %%. in ("^^!`^^!") do ^
set #=0^&(if defined %%~. (if ` neq ^^!%%~.:~4095^^!` ^
(set $=^^!%%~.:~4096^^!^&set #=4096) else set $=^^!%%~.^^!)^&^
(if defined $ if "^^!$:~255,1^^!" neq "" ^
set "t=^^!$:~3839,1^^!^^!$:~3583,1^^!^^!$:~3327,1^^!^^!$:~3071,1^^!^^!$:~2815,1^^!^^!$:~2559,1^^!^^!$:~2303,1^^!^^!$:~2047,1^^!^^!$:~1791,1^^!^^!$:~1535,1^^!^^!$:~1279,1^^!^^!$:~1023,1^^!^^!$:~767,1^^!^^!$:~511,1^^!^^!$:~255,1^^!FEDCBA9876543210"^&^
set /a "#+=t=0x^^!t:~15,1^^!*256"^&for %%# in (^^!t^^!) do set "$=^^!$:~%%#^^!")^&^
if defined $ ^
set "t=^^!$:~239,1^^!^^!$:~223,1^^!^^!$:~207,1^^!^^!$:~191,1^^!^^!$:~175,1^^!^^!$:~159,1^^!^^!$:~143,1^^!^^!$:~127,1^^!^^!$:~111,1^^!^^^!$:~95,1^^!^^!$:~79,1^^!^^!$:~63,1^^!^^!$:~47,1^^!^^!$:~31,1^^!^^!$:~15,1^^!FEDCBA9876543210"^&^
set /a "t=0x^^!t:~15,1^^!*16"^&for %%# in (^^!t^^!) do ^
set "$=^^!$:~%%#^^!FEDCBA9876543210"^&set /a "#+=t+0x^^!$:~15,1^^!")^&^
for %%# in (^^!#^^!) do endlocal^&set %%~/=%%#) else set `=

endlocal&set "strLen=%strLen%"
if !!# neq # set "strLen=%strLen:^^=%"
goto :eof
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:initStrLenJeb
::: Macro to define macros without knowing the delayed expansion mode
::: Usage:
::: %$lib.macrodefine.free% set my_macro=...
:::
::: Reserved FOR variables:
::: %%! - contains one !
::: %%^ - results into one caret, attention read the other comments!
:::
::: While defining a new macro:
::: - Each bang ! has to be changed to %%!   regex-replace: (?<!%%)! -> %%!

::: - Each caret ^ has to be changed to %%^^ regex-replace: (?<!%%)\^ -> %%^^
::: - If carets are inside quotes replace them only with %%^  .
::: - But be careful only replace carets that should be placed in the macro itself
::: - Carets can also be used for escaping special chars in the definition phase, like ^&, ^<, ^|, ^"
FOR /F "tokens=1 delims== " %%! in ("!=! ^^^!") DO ^
FOR /F %%^^ in ("^ ^^^^%%!=%%!") DO ^
set ^"$lib.macrodefine.free=@FOR /F "tokens=1 delims== " %%%%! in ("%%!=%%! %%^%%^%%^%%!") DO ^
@FOR /F %%%%^^%%^^ in ("%%^ %%^%%^%%^%%^%%^%%!=%%^%%!") DO @"

REM *** Defining a line feed for usage while defining macros
(set ^"$\n=^^^
%=empty=%
)
@REM %$lib.macrodefine.free% echo "Test1: %%! and %%^ --"
@REM %$lib.macrodefine.free% echo  Test2: %%! and %%^^ --

:: Computes the number of bytes in a string.
:: %strLen% str len
::   str - [ByRef In] Name of the variable containing the string to be measured.
::   len - [ByRef Out] Name of the variable that receives the measured length.
:: Strings of up to 8191 characters are supported.
%$lib.macrodefine.free% set strLen=for %%# in (1 2) do if %%#==2 ( %$\n%
    for /f "tokens=1,2" %%1 in ("%%!args%%!") do ( %$\n%
        set L=0 %$\n%
        if defined %%~1 ( %$\n%
            if "" neq "%%!%%~1:~255%%!" ( %$\n%
                for %%# in (4095 2047 1023 511 255) do ( %$\n%
                    set /a t=L+%%# %$\n%
                    for %%T in (%%!t%%!) do ( %$\n%
                        if "" neq "%%!%%~1:~%%T%%!" set /a L=%%T+1 %$\n%
                    ) %$\n%
                ) %$\n%
            ) %$\n%
            for %%# in (%%!L%%!) do ( %$\n%
                set ^"$=%%!%%~1:~%%#%%!^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCC^
BBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAA99999999999999998888888888888888^
7777777777777777666666666666666655555555555555554444444444444444^
333333333333333322222222222222221111111111111111^" %$\n%
                set /a L+=0x%%!$:~511,1%%!%%!$:~255,1%%! %$\n%
            ) %$\n%
        ) %$\n%
        for %%# in (%%!L%%!) do ( %$\n%
            endlocal %$\n%
            set %%~2=%%# %$\n%
        ) %$\n%
    ) %$\n%
 ) else setlocal EnableDelayedExpansion ^& set args=

goto :eof
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:initStrLen
:: Computes the number of bytes in a string.
:: %strLen% str len
::   str - [ByRef In] Name of the variable containing the string to be measured.
::   len - [ByRef Out] Name of the variable that receives the measured length.
:: Strings of up to 8191 characters are supported.
                                                FOR /F %%! IN ("! ^! ^^^!") DO ^
set strLen=for %%. in (1 2) do if %%.==2 (^
  for /f "tokens=1,2" %%1 in ("%%!args%%!") do (^
    set "L=0"^&^
    (if defined %%~1^
      (if "" neq "%%!%%~1:~255%%!"^
        (if "" neq "%%!%%~1:~4095%%!"^
          (set "S=%%!%%~1:~4096%%!"^&set "L=4096") else set "S=%%!%%~1%%!"^
        )^&^
        (if defined S^
          set ^"scale=^
%%!S:~255,1%%!%%!S:~511,1%%!%%!S:~767,1%%!%%!S:~1023,1%%!%%!S:~1279,1%%!^
%%!S:~1535,1%%!%%!S:~1791,1%%!%%!S:~2047,1%%!%%!S:~2303,1%%!%%!S:~2559,1%%!^
%%!S:~2815,1%%!%%!S:~3071,1%%!%%!S:~3327,1%%!%%!S:~3583,1%%!%%!S:~3839,1%%!^
FEDCBA9876543210^"^&^
          set /a "L+=0x%%!scale:~15,1%%!*256"^
        )^
      )^&^
      for %%# in (%%!L%%!) do set ^"S=%%!%%~1:~%%#%%!^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCC^
BBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAA99999999999999998888888888888888^
7777777777777777666666666666666655555555555555554444444444444444^
333333333333333322222222222222221111111111111111^"^&^
      set /a "L+=0x%%!S:~511,1%%!%%!S:~255,1%%!"^
    )^&^
    for %%# in (%%!L%%!) do endlocal^&set "%%~2=%%#"^
  )^
) else setlocal EnableDelayedExpansion^&set args=

goto :eof
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

:initTimediff
for /f %%! in ("! ^! ^^^!") do ^
set timediff=for /l %%# in (1 1 2) do if %%#==2 for /f "tokens=2" %%$ in ("%%!%%! 1 0") do ((if 1==%%$ setlocal EnableDelayedExpansion)^&for /f "tokens=1-3" %%- in ("%%!_i_%%!") do (set "_t1_=%%!%%~-: =0%%!"^&set "_t2_=%%!%%~.: =0%%!"^&^
set /a "_d_=(8640000+(((1%%!_t2_:~,2%%!*60+1%%!_t2_:~3,2%%!)*60+1%%!_t2_:~6,2%%!)*100+1%%!_t2_:~-2%%!-36610100)-(((1%%!_t1_:~,2%%!*60+1%%!_t1_:~3,2%%!)*60+1%%!_t1_:~6,2%%!)*100+1%%!_t1_:~-2%%!-36610100))%%8640000,_o_=100000000+(_d_%%100),_d_/=100,_o_+=(_d_%%60)*100,_d_/=60,_o_+=(_d_%%60)*10000+_d_/60*1000000"^&^
set "_o_=%%!_o_:~1,2%%!:%%!_o_:~3,2%%!:%%!_o_:~5,2%%!.%%!_o_:~-2%%!"^&for /f %%' in ("%%!_o_%%!") do ((if 1==%%$ endlocal)^&if "%%~/"=="" (echo %%') else set "%%~/=%%'"))) else set _i_=
goto :eof

:test
echo(
setlocal EnableDelayedExpansion
for /l %%i in (1 1 4) do (
  set t1=!time!
  for /l %%i in (1 1 1000) do %strLen% %1 len
  set t2=!time!
  echo !len!
  %timediff% t1 t2 diff
  echo !diff!
)
endlocal
goto :eof
... and it's results (the "final combination" block contains the times of the latest macro):

Code: Select all


~~~~~~~~~~~~~~~~~~~~
Steffen

8191
00:00:01.66
8191
00:00:01.54
8191
00:00:01.49
8191
00:00:01.48

1000
00:00:01.02
1000
00:00:01.01
1000
00:00:01.03
1000
00:00:01.03

200
00:00:00.76
200
00:00:00.79
200
00:00:00.79
200
00:00:00.78

10
00:00:00.78
10
00:00:00.77
10
00:00:00.78
10
00:00:00.78

~~~~~~~~~~~~~~~~~~~~
Francesco

8191
00:00:01.40
8191
00:00:01.44
8191
00:00:01.46
8191
00:00:01.43

1000
00:00:01.20
1000
00:00:01.24
1000
00:00:01.25
1000
00:00:01.21

200
00:00:00.92
200
00:00:00.94
200
00:00:00.95
200
00:00:00.94

10
00:00:00.91
10
00:00:00.94
10
00:00:00.92
10
00:00:00.94

~~~~~~~~~~~~~~~~~~~~
jeb

8191
00:00:01.52
8191
00:00:01.54
8191
00:00:01.54
8191
00:00:01.54

1000
00:00:01.26
1000
00:00:01.29
1000
00:00:01.29
1000
00:00:01.30

200
00:00:00.69
200
00:00:00.72
200
00:00:00.73
200
00:00:00.70

10
00:00:00.70
10
00:00:00.71
10
00:00:00.71
10
00:00:00.71

~~~~~~~~~~~~~~~~~~~~
final combination

8191
00:00:01.20
8191
00:00:01.23
8191
00:00:01.27
8191
00:00:01.22

1000
00:00:00.99
1000
00:00:01.00
1000
00:00:01.00
1000
00:00:01.00

200
00:00:00.75
200
00:00:00.76
200
00:00:00.76
200
00:00:00.76

10
00:00:00.76
10
00:00:00.76
10
00:00:00.76
10
00:00:00.76
Drücken Sie eine beliebige Taste . . .
Average of 5 runs (=20 time values each):

Code: Select all

      |Steffen|Francesco|  jeb  | final
------+-------+---------+-------+-------
 8191 | 01.49 |  01.43  | 01.54 | 01.21
 1000 | 01.03 |  01.21  | 01.30 | 01.01
  200 | 00.79 |  00.93  | 00.71 | 00.76
   10 | 00.78 |  00.93  | 00.70 | 00.76
Steffen

aGerman
Expert
Posts: 4721
Joined: 22 Jan 2010 18:01
Location: Germany

Re: strLen boosted

#33 Post by aGerman » 12 Jul 2025 05:57

Addendum: Of course we could call setlocal/endlocal conditionally, depending on whether delayed expansion is already enabled. I changed variable names slightly to mitigate the risk of name clashes.

Code: Select all

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:initStrLen
:: Computes the number of bytes in a string.
:: %strLen% str len
::   str - [ByRef In] Name of the variable containing the string to be measured.
::   len - [ByRef Out] Name of the variable that receives the measured length.
:: Strings of up to 8191 characters are supported.
                                                FOR /F %%! IN ("! ^! ^^^!") DO ^
set strLen=^
for /f "tokens=2" %%? in ("%%!%%! D E") do for %%. in (1 2) do if %%.==2 (^
  for /f "tokens=1,2" %%1 in ("%%!$args%%!") do (^
    set "$L=0"^&^
    (if defined %%~1^
      (if "" neq "%%!%%~1:~255%%!"^
        (if "" neq "%%!%%~1:~4095%%!"^
          (set "$=%%!%%~1:~4096%%!"^&set "$L=4096") else set "$=%%!%%~1%%!"^
        )^&^
        (if defined $^
          set ^"$scale=^
%%!$:~255,1%%!%%!$:~511,1%%!%%!$:~767,1%%!%%!$:~1023,1%%!%%!$:~1279,1%%!^
%%!$:~1535,1%%!%%!$:~1791,1%%!%%!$:~2047,1%%!%%!$:~2303,1%%!%%!$:~2559,1%%!^
%%!$:~2815,1%%!%%!$:~3071,1%%!%%!$:~3327,1%%!%%!$:~3583,1%%!%%!$:~3839,1%%!^
FEDCBA9876543210^"^&^
          set /a "$L+=0x%%!$scale:~15,1%%!*256"^
        )^
      )^&^
      for %%# in (%%!$L%%!) do set ^"$=%%!%%~1:~%%#%%!^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCC^
BBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAA99999999999999998888888888888888^
7777777777777777666666666666666655555555555555554444444444444444^
333333333333333322222222222222221111111111111111^"^&^
      set /a "$L+=0x%%!$:~511,1%%!%%!$:~255,1%%!"^
    )^&^
    for %%# in (%%!$L%%!) do (if %%?==D endlocal)^&set "%%~2=%%#"^
  )^
) else (if %%?==D setlocal EnableDelayedExpansion)^&set $args=

goto :eof
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
The additional check has only a negligibly low impact.
Running the test code (with unrolled loop in the :test routine to get rid of the always enabled delayed expansion) results in these average values:

Code: Select all

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
disabled delayed expansion
      |Steffen|Francesco|  jeb  | final
------+-------+---------+-------+-------
 8191 | 01.47 |  01.41  | 01.51 | 01.24
 1000 | 01.02 |  01.19  | 01.29 | 01.04
  200 | 00.78 |  00.92  | 00.71 | 00.80
   10 | 00.77 |  00.91  | 00.71 | 00.79
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
enabled delayed expansion
      |Steffen|Francesco|  jeb  | final
------+-------+---------+-------+-------
 8191 | 01.47 |  01.41  | 01.52 | 00.94
 1000 | 01.02 |  01.19  | 01.28 | 00.74
  200 | 00.78 |  00.94  | 00.71 | 00.52
   10 | 00.77 |  00.92  | 00.71 | 00.51
Only the code for the final strLen macro was updated. I believe we can safely assume that the additional check would generate the same absolute runtime differences in the other macros.

Steffen

einstein1969
Expert
Posts: 969
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: strLen boosted

#34 Post by einstein1969 » 15 Jul 2025 11:45

It's been a year since the version I had on my PC. Yours seems faster. I haven't had time to study it. I added it to the old tests and I think the added checks and setlocal slow it down a bit. Now I'm studying it to see if there's anything I can improve on in terms of speed.


Code: Select all

@echo off
setlocal EnableDelayedExpansion

rem on 16-7-2024

rem Rif: https://www.dostips.com/forum/viewtopic.php?f=3&t=1429&start=15

rem definition

set "@getlen=for %%# in (1 2) do if %%#==2 ( for /f %%1 in ("^^!args^^!") do (set "$=A^^!%%1^^!" & set "$len=" &  ( for %%] in (4096 2048 1024 512 256 128 64 32 16) do if "^^!$:~%%]^^!" NEQ "" set /a "$len+=%%]" & set "$=^^!$:~%%]^^!" ) & set "$=^^!$:~1^^!FEDCBA9876543210" & set /a $len+=0x^!$:~15,1^! ) ) else set args="

(set \n=^^^
%= This creates an escaped Line Feed - DO NOT ALTER =%
)

set "dipstick=FEDCBA9876543210"
for /L %%$ in (0,1,15) do (
	set "$=!dipstick:~%%$,1!" & set "long_dipstick=!long_dipstick!!$!!$!!$!!$!!$!!$!!$!!$!!$!!$!!$!!$!!$!!$!!$!!$!"
	set "repeat_dipstick=!repeat_dipstick!!dipstick!
)
set $strlen=for %%# in (1 2) do if %%#==2 ( for /f "tokens=1" %%1 in ("^!args^!") do (%\n%
    set "$=A^!%%1^!"%\n%
    if "^!$:~255,1^!" neq "" (%\n%
	if "^!$:~4095,1^!" neq "" (set "$=^!$:~4095^!" ^& set "$len=4095") else set "$len=0"%\n%
	set "$t=^!$:~3839,1^!^!$:~3583,1^!^!$:~3327,1^!^!$:~3071,1^!^!$:~2815,1^!^!$:~2559,1^!^!$:~2303,1^!^!$:~2047,1^!^!$:~1791,1^!^!$:~1535,1^!^!$:~1279,1^!^!$:~1023,1^!^!$:~767,1^!^!$:~511,1^!^!$:~255,1^!%dipstick%"%\n%
	set /A "$t=0x^!$t:~15,1^!*256"%\n%
	for %%$ in (^^!$t^^!) do set "$=^!$:~%%$^!%repeat_dipstick%%long_dipstick%"%\n%
	set /A "$len+=$t+0x^!$:~511,1^!^!$:~255,1^!-1"%\n%
	set "$="%\n%
    ) else (%\n%
	set "$=^!$^!%repeat_dipstick%%long_dipstick%"%\n%
	set /A "$len=0x^!$:~511,1^!^!$:~255,1^!-1"%\n%
	set "$="%\n%
    )%\n%
)) else set args=
set "dipstick="
set "long_dipstick="
set "repeat_dipstick="

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:initStrLen
:: Computes the number of bytes in a string.
:: %strLen% str len
::   str - [ByRef In] Name of the variable containing the string to be measured.
::   len - [ByRef Out] Name of the variable that receives the measured length.
:: Strings of up to 8191 characters are supported.
                                                FOR /F %%! IN ("! ^! ^^^!") DO ^
set strLen=^
for /f "tokens=2" %%? in ("%%!%%! D E") do for %%. in (1 2) do if %%.==2 (^
  for /f "tokens=1,2" %%1 in ("%%!$args%%!") do (^
    set "$L=0"^&^
    (if defined %%~1^
      (if "" neq "%%!%%~1:~255%%!"^
        (if "" neq "%%!%%~1:~4095%%!"^
          (set "$=%%!%%~1:~4096%%!"^&set "$L=4096") else set "$=%%!%%~1%%!"^
        )^&^
        (if defined $^
          set ^"$scale=^
%%!$:~255,1%%!%%!$:~511,1%%!%%!$:~767,1%%!%%!$:~1023,1%%!%%!$:~1279,1%%!^
%%!$:~1535,1%%!%%!$:~1791,1%%!%%!$:~2047,1%%!%%!$:~2303,1%%!%%!$:~2559,1%%!^
%%!$:~2815,1%%!%%!$:~3071,1%%!%%!$:~3327,1%%!%%!$:~3583,1%%!%%!$:~3839,1%%!^
FEDCBA9876543210^"^&^
          set /a "$L+=0x%%!$scale:~15,1%%!*256"^
        )^
      )^&^
      for %%# in (%%!$L%%!) do set ^"$=%%!%%~1:~%%#%%!^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCC^
BBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAA99999999999999998888888888888888^
7777777777777777666666666666666655555555555555554444444444444444^
333333333333333322222222222222221111111111111111^"^&^
      set /a "$L+=0x%%!$:~511,1%%!%%!$:~255,1%%!"^
    )^&^
    for %%# in (%%!$L%%!) do (if %%?==D endlocal)^&set "%%~2=%%#"^
  )^
) else (if %%?==D setlocal EnableDelayedExpansion)^&set $args=


rem functional test
title functional test...
set "str="
for /L %%i in (0,1,8185) do (
	%$strlen% Str
	echo length=!$len!  %%i
	if !$len! neq %%i pause
	set "str=!str!X"
)
timeout /t 5

rem prestational test
title prestational test...
cls
for %%V in (8000 6000 4000 2000 1000 500 250 120 60 30 10) do (

set "str="
for /L %%i in (1,1,%%V) do set "str=!str!X"

for /f "tokens=1-4 delims=:.," %%a in ("!time: =0!") do set /a "t1=(((1%%a*60)+1%%b)*60+1%%c)*100+1%%d-36610100"

for /l %%i in (1,1,4000) do %@getlen% str

for /f "tokens=1-4 delims=:.," %%a in ("!time: =0!") do set /a "t2=(((1%%a*60)+1%%b)*60+1%%c)*100+1%%d-36610100, tDiff=t2-t1"
if !tDiff! lss 0 set /a tDiff+=24*60*60*100

echo getlen !tDiff!cs/4000 len=!$len! (previus strlen^)

for /f "tokens=1-4 delims=:.," %%a in ("!time: =0!") do set /a "t1=(((1%%a*60)+1%%b)*60+1%%c)*100+1%%d-36610100"

for /l %%i in (1,1,4000) do %$strlen% str

for /f "tokens=1-4 delims=:.," %%a in ("!time: =0!") do set /a "t2=(((1%%a*60)+1%%b)*60+1%%c)*100+1%%d-36610100, tDiff=t2-t1"
if !tDiff! lss 0 set /a tDiff+=24*60*60*100

echo $strlen !tDiff!cs/4000 len=!$len!


for /f "tokens=1-4 delims=:.," %%a in ("!time: =0!") do set /a "t1=(((1%%a*60)+1%%b)*60+1%%c)*100+1%%d-36610100"

for /l %%i in (1,1,4000) do %strlen% str len

for /f "tokens=1-4 delims=:.," %%a in ("!time: =0!") do set /a "t2=(((1%%a*60)+1%%b)*60+1%%c)*100+1%%d-36610100, tDiff=t2-t1"
if !tDiff! lss 0 set /a tDiff+=24*60*60*100

echo strlen !tDiff!cs/4000 len=!$len!




echo.
)

pause
exit

aGerman
Expert
Posts: 4721
Joined: 22 Jan 2010 18:01
Location: Germany

Re: strLen boosted

#35 Post by aGerman » 15 Jul 2025 13:15

The $strlen macro that you have in your test code now is pretty much the same as the latest strLen macro I posted :lol: It's just that yours always expects that delayed expansion is enabled and that $len is the predefined name for the output. Besides of that, yours also handles the 255 characters for long strings in the if "^!$:~255,1^!" neq "" branch and separately in the else branch while I just "fall through" into the 255-character processing. I don't know if either one of them would be preferable in terms of speed though.

FWIW Yes, setlocal/endlocal makes a difference of 0.2 - 0.3 seconds in a thousand iterations on my machine, depending on the test conditions.

Steffen

einstein1969
Expert
Posts: 969
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: strLen boosted

#36 Post by einstein1969 » 16 Jul 2025 13:02

I took the last one you made and optimized it a bit.

I removed a "Set" from everything and removed another set for some lengths. Also in the upper part from 4096 to 8192.

I compared it, and it's faster than the last one.

Of course, I compared the code without the macros. I think you'll have to figure out the macros yourself, otherwise it would take too long.

Code: Select all

	setlocal

	set "args=%1 %2"
	for /f "tokens=1,2" %%1 in ("!args!") do (

		if defined %%~1 (

			if "" neq "!%%~1:~255!" (

				if "" neq "!%%~1:~4095!" (

					set "$=!%%~1:~4095!"
					set "$L=4095"

 				) else 	(

					set "$=!%%~1!"
				)
      			)

			if defined $ (

				set "$scale=!$:~255,1!!$:~511,1!!$:~767,1!!$:~1023,1!!$:~1279,1!!$:~1535,1!!$:~1791,1!!$:~2047,1!!$:~2303,1!!$:~2559,1!!$:~2815,1!!$:~3071,1!!$:~3327,1!!$:~3583,1!!$:~3839,1!FEDCBA9876543210"
				set /a "$L+=0x!$scale:~15,1!*256"

			)  else set "$L=0"

			for %%# in (!$L!) do set "$=!%%~1:~%%#!FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAA999999999999999988888888888888887777777777777777666666666666666655555555555555554444444444444444333333333333333322222222222222221111111111111111"

			for %%# in ("!$L!+0x!$:~511,1!!$:~255,1!") do endlocal & set /A "%%~2=%%#"

		) else endlocal & set "%%~2=0"
	)
EDIT: at 4096 give me an error...

EDIT2: there was an error here there was 4096 instead of 4095. I corrected above

Code: Select all

				if "" neq "!%%~1:~4095!" (

					set "$=!%%~1:~4096!"
					set "$L=4096"

 				) else 	(

					set "$=!%%~1!"
				)

Code: Select all

~~~~~~~~~~~~~~~~~~~~~~~~~~
      | final  |  this   |
------+--------+---------+
 8000 | 1019cs |  912cs  |
 4000 |  871cs |  784cs  |
 2000 |  790cs |  705cs  |
 1000 |  741cs |  660cs  |
  640 |  718cs |  641cs  |
  320 |  702cs |  628cs  |
  160 |  546cs |  508cs  |
   40 |  545cs |  507cs  |
    0 |  448cs |  406cs  |
there is a saving of approximately 10%. I ran 10000 cycles, and stabilized the CPU throttling which is what matters most

It usually takes about 0.5 - 1.0 millisecond on my mid-range machine

ps. I went dumb trying to decode your and jeb's macro.

Francesco
Last edited by einstein1969 on 16 Jul 2025 14:35, edited 13 times in total.

einstein1969
Expert
Posts: 969
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: strLen boosted

#37 Post by einstein1969 » 16 Jul 2025 13:08

I made another version that's even faster, but I discarded it because the macro was too long and the env for Win 10 is a problem.

I haven't tried it on Windows 11, but they told me the set problem related to the env size is no longer there.

Do you know if this is the case?

einstein1969
Expert
Posts: 969
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: strLen boosted

#38 Post by einstein1969 » 16 Jul 2025 15:37

I tried to optimize the upper part even more, this is the code and the times

Code: Select all

	setlocal

	set "args=str len"
	for /f "tokens=1,2" %%1 in ("!args!") do (

		if defined %%~1 (

			if "" neq "!%%~1:~255!" (

				if "" neq "!%%~1:~4095!" (

					set "$=!%%~1:~4095!"

 				) else 	set "$=!%%~1!"


				set "$scale=!$:~255,1!!$:~511,1!!$:~767,1!!$:~1023,1!!$:~1279,1!!$:~1535,1!!$:~1791,1!!$:~2047,1!!$:~2303,1!!$:~2559,1!!$:~2815,1!!$:~3071,1!!$:~3327,1!!$:~3583,1!!$:~3839,1!FEDCBA9876543210"

				if "" neq "!%%~1:~4095!" (

					set /a "$L=0x!$scale:~15,1!*256+4095"

				) else  set /a "$L=0x!$scale:~15,1!*256"

			)  else set "$L=0"

			for %%# in (!$L!) do set "$=!%%~1:~%%#!FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAA999999999999999988888888888888887777777777777777666666666666666655555555555555554444444444444444333333333333333322222222222222221111111111111111"

			for %%# in ("!$L!+0x!$:~511,1!!$:~255,1!") do endlocal & set /A "%%~2=%%#"

		) else endlocal & set "%%~2=0"
	)
Before:

Code: Select all

strlen 407cs  len=0
strlen 509cs  len=40
strlen 510cs  len=80
strlen 516cs  len=160
strlen 631cs  len=320
strlen 650cs  len=640
strlen 668cs  len=1000
strlen 711cs  len=2000
strlen 792cs  len=4000
strlen 760cs  len=4500
strlen 788cs  len=5000
strlen 834cs  len=6000
strlen 880cs  len=7000
strlen 901cs  len=7500
strlen 923cs  len=8000
After:

Code: Select all

strlen 406cs  len=0
strlen 513cs  len=40
strlen 513cs  len=80
strlen 507cs  len=160
strlen 631cs  len=320
strlen 648cs  len=640
strlen 671cs  len=1000
strlen 718cs  len=2000
strlen 805cs  len=4000
strlen 734cs  len=4500
strlen 753cs  len=5000
strlen 802cs  len=6000
strlen 849cs  len=7000
strlen 873cs  len=7500
strlen 894cs  len=8000
Francesco

aGerman
Expert
Posts: 4721
Joined: 22 Jan 2010 18:01
Location: Germany

Re: strLen boosted

#39 Post by aGerman » 16 Jul 2025 16:15

Looks really promising. I'll try to wrap it into a macro tomorrow.

Just attempting to answer your question
but they told me the set problem related to the env size is no longer there
It depends. The limit in the CMD is still 8191. I just tried to add another x to the str1 variable as in my test code. An "ECHO !str1!" now result in the "ECHO is OFF" message which suggests that str1 was not defined.
However, the length limit for an environment string that the operating system accepts is 32767.

Steffen

einstein1969
Expert
Posts: 969
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: strLen boosted

#40 Post by einstein1969 » 17 Jul 2025 01:34

I was referring to the slowing down of the "set":

Why does SET performance degrade as environment size grows?

This is a version that reduces the macro size while still maintaining almost all of its speed. It's a good fit for me.

Code: Select all

	setlocal

	set "args=%1 %2"
	for /f "tokens=1,2" %%1 in ("!args!") do (

		if defined %%~1 (

			if "" neq "!%%~1:~255!" (

				if "" neq "!%%~1:~4095!" (

					set "$=!%%~1:~4095!"

 				) else 	set "$=!%%~1!"

				set "$scale=!$:~255,1!!$:~511,1!!$:~767,1!!$:~1023,1!!$:~1279,1!!$:~1535,1!!$:~1791,1!!$:~2047,1!!$:~2303,1!!$:~2559,1!!$:~2815,1!!$:~3071,1!!$:~3327,1!!$:~3583,1!!$:~3839,1!FEDCBA9876543210"

				if "" neq "!%%~1:~4095!" (

					set /a "$L=0x!$scale:~15,1!*256+4095"

				) else  set /a "$L=0x!$scale:~15,1!*256"

			)  else set "$L=0"

			for %%# in (!$L!) do for %%. in (FEDCBA9876543210FEDCBA9876543210) do set "$=!%%~1:~%%#!%%.%%.%%.%%.%%.%%.%%.%%.FFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAA999999999999999988888888888888887777777777777777666666666666666655555555555555554444444444444444333333333333333322222222222222221111111111111111"

			for %%# in ("!$L!+0x!$:~511,1!!$:~255,1!") do endlocal & set /A "%%~2=%%#"

		) else endlocal & set "%%~2=0"
	)

F.

einstein1969
Expert
Posts: 969
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: strLen boosted

#41 Post by einstein1969 » 17 Jul 2025 02:07

even shorter.

Code: Select all

	setlocal

	set "args=%1 %2"
	for /f "tokens=1-3" %%1 in ("!args! FEDCBA9876543210") do (

		if defined %%~1 (

			if "" neq "!%%~1:~255!" (

				if "" neq "!%%~1:~4095!" (

					set "$=!%%~1:~4095!"

 				) else 	set "$=!%%~1!"

				set "$scale=!$:~255,1!!$:~511,1!!$:~767,1!!$:~1023,1!!$:~1279,1!!$:~1535,1!!$:~1791,1!!$:~2047,1!!$:~2303,1!!$:~2559,1!!$:~2815,1!!$:~3071,1!!$:~3327,1!!$:~3583,1!!$:~3839,1!%%3"

				if "" neq "!%%~1:~4095!" (

					set /a "$L=0x!$scale:~15,1!*256+4095"

				) else  set /a "$L=0x!$scale:~15,1!*256"

			)  else set "$L=0"

			for %%# in (!$L!) do set "$=!%%~1:~%%#!%%3%%3%%3%%3%%3%%3%%3%%3%%3%%3%%3%%3%%3%%3%%3%%3FFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAA999999999999999988888888888888887777777777777777666666666666666655555555555555554444444444444444333333333333333322222222222222221111111111111111"

			for %%# in ("!$L!+0x!$:~511,1!!$:~255,1!") do endlocal & set /A "%%~2=%%#"

		) else endlocal & set "%%~2=0"
	)

Code: Select all

strlen 400cs  len=0
strlen 516cs  len=40
strlen 516cs  len=80
strlen 515cs  len=160
strlen 639cs  len=320
strlen 652cs  len=640
strlen 680cs  len=1000
strlen 724cs  len=2000
strlen 809cs  len=4000
strlen 734cs  len=4500
strlen 762cs  len=5000
strlen 808cs  len=6000
strlen 859cs  len=7000
strlen 883cs  len=7500
strlen 902cs  len=8000
more throttling stabilization:

Code: Select all

strlen 400cs  len=0
strlen 514cs  len=40
strlen 513cs  len=80
strlen 510cs  len=160
strlen 632cs  len=320
strlen 648cs  len=640
strlen 672cs  len=1000
strlen 717cs  len=2000
strlen 798cs  len=4000
strlen 722cs  len=4500
strlen 748cs  len=5000
strlen 797cs  len=6000
strlen 852cs  len=7000
strlen 869cs  len=7500
strlen 890cs  len=8000
Code used for testing:

Code: Select all

@echo off
setlocal EnableDelayedExpansion

rem on 17-7-2025

rem Rif: https://www.dostips.com/forum/viewtopic.php?f=3&t=1429&start=15


call :Test 0
call :Test 40
call :Test 80
call :Test 160
call :Test 320
call :Test 640
call :Test 1000
call :Test 2000
call :Test 4000
call :Test 4500
call :Test 5000
call :Test 6000
call :Test 7000
call :Test 7500
call :Test 8000


pause

goto :eof

===============================================================================
:Test
===============================================================================

	set "str="
	for /L %%i in (1,1,%1) do set "str=!str!X"
	call :sub_strlen "str" len

goto :eof
===============================================================================


===============================================================================
:sub_strlen
===============================================================================

for /L %%L in (1,1,300000) do rem Cpu Throttling

for /f "tokens=1-4 delims=:.," %%a in ("!time: =0!") do set /a "t1=(((1%%a*60)+1%%b)*60+1%%c)*100+1%%d-36610100"
(
for /l %%L in (1,1,10000) do (


	setlocal

	set "args=%1 %2"

	for /f "tokens=1-3" %%1 in ("!args! FEDCBA9876543210") do (

		if defined %%~1 (

			if "" neq "!%%~1:~255!" (

				if "" neq "!%%~1:~4095!" (

					set "$=!%%~1:~4095!"

 				) else 	set "$=!%%~1!"

				set "$scale=!$:~255,1!!$:~511,1!!$:~767,1!!$:~1023,1!!$:~1279,1!!$:~1535,1!!$:~1791,1!!$:~2047,1!!$:~2303,1!!$:~2559,1!!$:~2815,1!!$:~3071,1!!$:~3327,1!!$:~3583,1!!$:~3839,1!%%3"

				if "" neq "!%%~1:~4095!" (

					set /a "$L=0x!$scale:~15,1!*256+4095"

				) else  set /a "$L=0x!$scale:~15,1!*256"

			)  else set "$L=0"

			for %%# in (!$L!) do set "$=!%%~1:~%%#!%%3%%3%%3%%3%%3%%3%%3%%3%%3%%3%%3%%3%%3%%3%%3%%3FFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAA999999999999999988888888888888887777777777777777666666666666666655555555555555554444444444444444333333333333333322222222222222221111111111111111"

			for %%# in ("!$L!+0x!$:~511,1!!$:~255,1!") do endlocal & set /A "%%~2=%%#"

		) else endlocal & set "%%~2=0"
	)

)
)
for /f "tokens=1-4 delims=:.," %%a in ("!time: =0!") do set /a "t2=(((1%%a*60)+1%%b)*60+1%%c)*100+1%%d-36610100, tDiff=t2-t1"
if !tDiff! lss 0 set /a tDiff+=24*60*60*100

echo strlen !tDiff!cs  len=!len!

goto :eof
===============================================================================


aGerman
Expert
Posts: 4721
Joined: 22 Jan 2010 18:01
Location: Germany

Re: strLen boosted

#42 Post by aGerman » 17 Jul 2025 09:48

Re: "Why does SET performance degrade as environment size grows?"

Nothing changed in the way environment variables are handled in the process. So I guess the impact of the size/amount of variables decreased only because we have faster processors, multi-core processors, better branch prediction, more caching, more RAM space, etc. I doubt this has anything to do whith whether you're running on a Win 10 or Win 11 OS.

To give you a little more context - environment variables are stored in the PEB as an unordered flat list of adjacent strings, like so:
name1=value1\0name2=value2\0 ... nameN=valueN\0\0
It could be called a multi-string (similar like the REG_MULTI_SZ type in the registry which has the same structure). Each "name=value" pair is terminated with a NUL character which is also the separator between the pairs. The end of the list is marked with an additional NUL character.
This structure is usually good enough as long as it doesn't need to get altered repeatedly. As you can imagine, you can't magically insert characters without having the memory space at this position. You also can't remove characters and leave a "hole". In other words, any update of just a single variable results in rewriting the whole multi-string. The more characters are in this multi-string, the more have to be rewritten. This makes it comparably slow.

Thinking further... If you call a variable by its name, the only applicable search algorithm in this structure is linear searching (O(n)). The more variables you have in the multi-string, the less it is likely to find a certain variable without iterating a lot of previous variables. The string search algorithm, however, might have been updated to something significantly better than an O(n) character-by-character algorithm.
Worse again, if you want to calculate with variable values, a conversion from string into integer (and vice versa afterwards) has to be performed because the only supported data type for environment variables is a null-terminated string.

So, using environment variables as script variables is a very bad idea and Batch is the only language with this poor concept that I'm aware of. But this stems from ancient times and is very unlikely to get ever changed 🤣

Steffen

aGerman
Expert
Posts: 4721
Joined: 22 Jan 2010 18:01
Location: Germany

Re: strLen boosted

#43 Post by aGerman » 17 Jul 2025 11:02

Your latest wrapped into a macro:

Code: Select all

:initStrLen
:: Computes the number of bytes in a string.
:: %strLen% str len
::   str - [ByRef In] Name of the variable containing the string to be measured.
::   len - [ByRef Out] Name of the variable that receives the measured length.
:: Strings of up to 8190 characters are supported.
                                                FOR /F %%! IN ("! ^! ^^^!") DO ^
set strLen=^
for /f "tokens=2" %%? in ("%%!%%! D E") do for %%. in (1 2) do if %%.==2 (^
  for /f "tokens=1-3" %%1 in ("%%!$args%%! FEDCBA9876543210") do (^
    if defined %%~1 (^
      (if "" neq "%%!%%~1:~255%%!" (^
        (if "" neq "%%!%%~1:~4095%%!" (^
          set "$=%%!%%~1:~4095%%!"^
        ) else   set "$=%%!%%~1%%!")^&^
        set "$scale=%%!$:~255,1%%!%%!$:~511,1%%!%%!$:~767,1%%!%%!$:~1023,1%%!%%!$:~1279,1%%!%%!$:~1535,1%%!%%!$:~1791,1%%!%%!$:~2047,1%%!%%!$:~2303,1%%!%%!$:~2559,1%%!%%!$:~2815,1%%!%%!$:~3071,1%%!%%!$:~3327,1%%!%%!$:~3583,1%%!%%!$:~3839,1%%!%%3"^&^
        (if "" neq "%%!%%~1:~4095%%!" (^
          set /a "$L=0x%%!$scale:~15,1%%!*256+4095"^
        ) else  set /a "$L=0x%%!$scale:~15,1%%!*256")^
      )  else set "$L=0")^&^
      (for %%# in (%%!$L%%!) do set "$=%%!%%~1:~%%#%%!%%3%%3%%3%%3%%3%%3%%3%%3%%3%%3%%3%%3%%3%%3%%3%%3FFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAA999999999999999988888888888888887777777777777777666666666666666655555555555555554444444444444444333333333333333322222222222222221111111111111111")^&^
      (for %%# in ("%%!$L%%!+0x%%!$:~511,1%%!%%!$:~255,1%%!") do (if %%?==D endlocal)^&set /A "%%~2=%%#")^
    ) else (if %%?==D endlocal)^&set "%%~2=0"^
  )^
) else (if %%?==D setlocal EnableDelayedExpansion)^&set $args=

goto :eof
If called from an environment with delayed expansion already enabled:

Code: Select all

8190
00:00:00.87
8190
00:00:00.87
8190
00:00:00.87
8190
00:00:00.87

1000
00:00:00.71
1000
00:00:00.70
1000
00:00:00.69
1000
00:00:00.70

200
00:00:00.52
200
00:00:00.52
200
00:00:00.53
200
00:00:00.52

10
00:00:00.51
10
00:00:00.53
10
00:00:00.52
10
00:00:00.50
There isn't any big difference in my test case. 8190 is now the upper limit though.

Steffen

Post Reply