strLen boosted

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
einstein1969
Expert
Posts: 973
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: strLen boosted

#46 Post by einstein1969 » 20 Jul 2025 06:29

The last one I made isn't the fastest, but a compressed version that uses the previous fast one.

Here's the fast one (it took me a few days to create the macro on one line like you did; I'm going crazy.)

Code: Select all

:: make sure we are in the spotlight:
:: run as administrator for realtime priority!!!
:: /affinity 0x3  = 2 core/thread
@if "%~1"=="" start /realtime conhost "%~f0" 1&exit /b
@echo off & setlocal DisableDelayedExpansion

:: 8190
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^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxX

:: 6000
set "str2=%str1:~,6000%"
:: 4000
set "str3=%str1:~,4000%"
:: 2000
set "str4=%str1:~,2000%"
:: 1000
set "str5=%str1:~,1000%"
:: 200
set "str6=%str1:~,200%"
:: 10
set "str7=%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
echo Functional test
call :func_test
rem call :test str1
rem call :test str2
rem call :test str3
rem call :test str4
rem call :test str5
rem call :test str6
rem call :test str7

echo(
echo ~~~~~~~~~~~~~~~~~~~~
echo Francesco
call :initStrLenFrancesco
echo Functional test
call :func_test
rem call :test str1
rem call :test str2
rem call :test str3
rem call :test str4
rem call :test str5
rem call :test str6
rem call :test str7

echo(
echo ~~~~~~~~~~~~~~~~~~~~
echo jeb
echo Functional test
call :func_test
rem call :test str1
rem call :test str2
rem call :test str3
rem call :test str4
rem call :test str5
rem call :test str6
rem call :test str7

echo(
echo ~~~~~~~~~~~~~~~~~~~~
echo final
call :initStrLen
echo Functional test
call :func_test
call :test str1
call :test str2
call :test str3
call :test str4
call :test str5
call :test str6
call :test str7

echo(
echo ~~~~~~~~~~~~~~~~~~~~
echo final 2
call :initStrLen2
echo Functional test
call :func_test
call :test str1
call :test str2
call :test str3
call :test str4
call :test str5
call :test str6
call :test str7


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 Final
:: 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

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:initStrLen2 final 2
:: 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 (^
    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:~%%#%%!^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
FFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCC^
BBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAA99999999999999998888888888888888^
7777777777777777666666666666666655555555555555554444444444444444^
333333333333333322222222222222221111111111111111^"^&^
      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
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

: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
set len=
for /l %%i in (1 1 4) do (
  set t1=!time!
  for /l %%i in (1 1 5000) do %strLen% %1 len
  set t2=!time!
  %timediff% t1 t2 diff
  echo !diff!
)
echo Check: len=!len!
endlocal
goto :eof
:::::::::::::::::::::::::::::::::::::::::::::::::...
:func_test
	setlocal EnableDelayedExpansion
	set "str="
	for /L %%L in (0,1,8185) do (
		%strLen% str len
		set "str=!str!X"
		title %%L !len!
		if !len! neq %%L pause
	)
goto :eof
result:

Code: Select all

~~~~~~~~~~~~~~~~~~~~
final
Functional test
OK.

00:00:04.46
00:00:04.51
00:00:04.51
00:00:04.52
Check: len=8190

00:00:04.12
00:00:04.18
00:00:04.17
00:00:04.27
Check: len=6000

00:00:03.89
00:00:03.95
00:00:03.95
00:00:03.95
Check: len=4500

00:00:03.82
00:00:04.00
00:00:04.06
00:00:03.89
Check: len=2000

00:00:03.73
00:00:03.76
00:00:03.76
00:00:03.75
Check: len=1000

00:00:02.63
00:00:02.68
00:00:02.68
00:00:02.68
Check: len=200

00:00:02.60
00:00:02.68
00:00:02.68
00:00:02.67
Check: len=10

~~~~~~~~~~~~~~~~~~~~
final 2
Functional test
OK.

00:00:03.97
00:00:04.03
00:00:04.03
00:00:04.03
Check: len=8190

00:00:03.65
00:00:03.72
00:00:03.70
00:00:03.71
Check: len=6000

00:00:03.40
00:00:03.47
00:00:03.45
00:00:03.46
Check: len=4500

00:00:03.54
00:00:03.59
00:00:03.66
00:00:03.59
Check: len=2000

00:00:03.39
00:00:03.45
00:00:03.47
00:00:03.46
Check: len=1000

00:00:02.55
00:00:02.61
00:00:02.61
00:00:02.61
Check: len=200

00:00:02.55
00:00:02.60
00:00:02.60
00:00:02.60
Check: len=10
Premere un tasto per continuare . . .

Code: Select all

5000 cycles
::      | final  |  final2 |
::------+--------+---------+
:: 8190 | 04.51  |  04.03  |
:: 6000 | 04.17  |  03.71  |
:: 4500 | 03.95  |  03.46  |
:: 2000 | 04.00  |  03.60  |
:: 1000 | 03.76  |  03.46  |
::  200 | 02.68  |  02.61  |
::   10 | 02.68  |  02.60  |

Code: Select all

10000 cycles
::      | final  |  final2 |
::------+--------+---------+
:: 8190 | 09.02  |  08.06  |
:: 6000 | 08.27  |  07.40  |
:: 4500 | 07.81  |  06.95  |
:: 2000 | 07.70  |  07.21  |
:: 1000 | 07.40  |  06.94  |
::  200 | 05.34  |  05.26  |
::   10 | 05.31  |  05.24  |
PS. For the "Realtime" prioticity I used the "run as administrator" otherwise it gives you "high"

I also wanted to ask you what difference there is between calling "cmd" or "conhost".

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

Re: strLen boosted

#47 Post by aGerman » 20 Jul 2025 07:34

Cool :D If I take the latest code I posted, it comes pretty much down to the same results. Your's is still slightly faster but lacks the 8191 upper limit. Not really important though.
PS. For the "Realtime" prioticity I used the "run as administrator" otherwise it gives you "high"
You're right. "High" does still sufficiently avoid high value deviations. That's actually the point why I use a higher priority. It doesn't reflect the absolute values that would be measured with normal priority. However, absolute values can't be compared between different computers anyway.
I also wanted to ask you what difference there is between calling "cmd" or "conhost".
I tried to save some resources. I've set Terminal the default to run console apps on my machine. Explicitly running conhost avoids some overhead, taking into consideration that we want to run it with higher priority. Conhost spawns the cmd.exe process for us (not exactly what actually happens, only to keep it short). If you have conhost set the default, it just works the other way around, i.e. cmd spawns the conhost process. There isn't one without the other. Even Terminal runs a hidden conhost window (ConPTY) for our cmd process behind the scenes.
All that doesn't influence the measured values in my tests though.

Steffen

Aacini
Expert
Posts: 1930
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: strLen boosted

#48 Post by Aacini » 21 Jul 2025 09:16

May I suggest a simple modification that may speed up the process when there are setlocal/endlocal and frequent environment updates? Just define a "filler" variable at end of the environment before the setlocal and then, inside the routine that update the environment, release such a variable:

Code: Select all

rem Define a filler variable at environment end:
set "zzzzzz=Enough space for environment movements"

rem Do the setlocal
setlocal

rem Free the filler space
set "zzzzzz="

rem Do environment movements...
set "var=..."

endlocal
The filler variable may have the same name of the first variable used in the routine (so it is not necesary to release it), but it is important that such a variable be defined at end of the environment...

Antonio

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

Re: strLen boosted

#49 Post by aGerman » 21 Jul 2025 17:00

Nice idea, Antonio. So, what you suggest could look like so for testing purposes?

Code: Select all

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

set str=^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

call :initTimediff

for /l %%n in (1 1 20) do (
  set "foo="     &call :test 1
  set "foo=!str!"&call :test 2
)

pause
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
setlocal
set "foo="
set t1=%time%
for /l %%i in (1 1 20000) do set "x%1=%%i%%i%%i%%i%%i%%i%%i%%i%%i%%i%%i%%i%%i%%i%%i%%i%%i%%i%%i%%i" 
set t2=%time%
%timediff% t1 t2 diff
if %1==1 (echo %diff%) else (echo              %diff%)
endlocal
goto :eof
Result:

Code: Select all

00:00:00.79
             00:00:00.70
00:00:00.70
             00:00:00.69
00:00:00.70
             00:00:00.70
00:00:00.70
             00:00:00.72
00:00:00.70
             00:00:00.70
00:00:00.70
             00:00:00.70
00:00:00.70
             00:00:00.72
00:00:00.70
             00:00:00.69
00:00:00.70
             00:00:00.70
00:00:00.69
             00:00:00.70
00:00:00.70
             00:00:00.70
00:00:00.70
             00:00:00.69
00:00:00.72
             00:00:00.70
00:00:00.70
             00:00:00.69
00:00:00.69
             00:00:00.71
00:00:00.70
             00:00:00.70
00:00:00.68
             00:00:00.72
00:00:00.70
             00:00:00.70
00:00:00.69
             00:00:00.69
00:00:00.71
             00:00:00.70
Drücken Sie eine beliebige Taste . . .
The indented durations are those with a long "foo" variable set that is removed after setlocal. There doesn't seem any noticeable difference.
(The first test run takes always slightly longer, also if I change the order and call :test 2 before :test 1. Probably because of caching.)

I guess the assumption you made is that the memory allocated once doesn't shrink. That might be true. ^1)
However, my understanding is that always all environment variables (the entire multi-string that I mentioned previously) will be rewritten, regardless of the kind of the update (no matter if a variable is added or removed, no matter if a value is updated keeping the same length or having a different length, and no matter at what position the variable occurs in the list). I certainly have to do some more internet research to fully understand it though.

Steffen

EDIT:
^1) Yes, this is true. I wrote a little C app to see what happens. You can even look at the results in a live compilation on Compiler Explorer:
https://godbolt.org/z/4Tz9oEha3
As you can see, only after adding the ZZZ variable a new pointer is allocated. If the variable value is shortened or if a variable is removed, it is not necessary to allocate new memory and the pointer remains the same.

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

Re: strLen boosted

#50 Post by einstein1969 » 23 Jul 2025 01:23

Yes, what Antonio mentioned was used in that thread testing the performance of the set command, and in some cases there was a difference.

In the meantime, I added the limit to 8191. It lost a bit in the lower range, but I don't understand why; further testing is needed. There was also a bug with the $ variable that I had to fix. Now it seems good and fast.

Code: Select all

:: make sure we are in the spotlight:
:: run as administrator for realtime priorities, else you will get "high" priorities
:: /affinity 0x3  = 2 core/thread
@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

:: 6000
set "str2=%str1:~,6000%"
:: 4000
set "str3=%str1:~,4500%"
:: 2000
set "str4=%str1:~,2000%"
:: 1000
set "str5=%str1:~,1000%"
:: 200
set "str6=%str1:~,200%"
:: 10
set "str7=%str1:~,10%"

call :initTimediff

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


call :initStrLenFinal
call :initStrLenFinal2


%strlen% str1 len
echo final : Check 8191 string =^> %len%
%strlen2% str1 len
echo final 2 : Check 8191 string =^> %len%

call :test-funz

:prest

call :test str1
call :test str2
call :test str3
call :test str4
call :test str5
call :test str6
call :test str7

pause

goto :eof


::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:initStrLenFinal2 final 2 readable
:: 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 strLen2=^
for /f "tokens=2" %%? in ("%%!%%! D E") do for %%. in (1 2) do if %%.==2 (^
%=   =% for /f "tokens=1,2" %%1 in ("%%!$args%%!") do for %%3 in (%%~1) do (^
%=			=% if defined %%3 (^
%=     				=% (^
%=					=% if "" neq "%%!%%3:~255%%!" (^
%=						=% if "" neq "%%!%%3:~4095%%!" (set "$=%%!%%3:~4096%%!") else set "$=%%!%%3%%!"^
%=						=% ) ^& (^
%=							=% if defined $ (^
%=         							=% set ^"$Scale=^
%=								=%%%!$:~255,1%%!%%!$:~511,1%%!%%!$:~767,1%%!%%!$:~1023,1%%!%%!$:~1279,1%%!^
%=		This zone is empty				=%%%!$:~1535,1%%!%%!$:~1791,1%%!%%!$:~2047,1%%!%%!$:~2303,1%%!%%!$:~2559,1%%!^
%=								=%%%!$:~2815,1%%!%%!$:~3071,1%%!%%!$:~3327,1%%!%%!$:~3583,1%%!%%!$:~3839,1%%!^
%=								=%FEDCBA9876543210^"^&^
%=								=% if "" neq "%%!%%3:~4095%%!" (^
%=									=% set /a "$L=0x%%!$Scale:~15,1%%!*256+4096"^
%=								=% ) else set /a "$L=0x%%!$Scale:~15,1%%!*256"^
%=							=% ) else if "" neq "%%!%%3:~4095%%!" set "$L=4096"^
%=       				=% ) else set "$L=0"^
%=				=% )^&^
%=				=% for %%# in (%%!$L%%!) do set ^"$=%%!%%3:~%%#%%!^
%=				=%FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
%=				=%FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
%=				=%FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
%=				=%FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
%=				=%FFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCC^
%=				=%BBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAA99999999999999998888888888888888^
%=				=%7777777777777777666666666666666655555555555555554444444444444444^
%=				=%333333333333333322222222222222221111111111111111^"^&^
%=				=% 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


::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:initStrLenFinal
:: 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

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:test

echo(
echo +--------+---------+----------+
echo I  len   I  final  I  final2  I
echo +--------+---------+----------+

setlocal EnableDelayedExpansion

set i=0
:loop
	set len=

	set t1=!time!
	for /l %%i in (1 1 15000) do %strLen% %1 len
	set t2=!time!
	%timediff% t1 t2 diff
	set/p.="I  !len!	 I  !diff:~6!  "<nul

	set t1=!time!
	for /l %%i in (1 1 15000) do %strLen2% %1 len
	set t2=!time!
	%timediff% t1 t2 diff
	echo I  !diff:~6!   I

	set /a i+=1
	if !i! lss 4 goto :loop

echo +--------+---------+----------+

endlocal
goto :eof

rem for /l %%i in (1 1 4) do (
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

:test-funz

setlocal EnableDelayedExpansion

echo(
echo Functional test
echo(
set /p ".=final :"<nul

set "str="
for /L %%L in (0,1,8185) do (
		set len=
		%strLen% str len
		set "str=!str!X"
		title "%%L" "!len!"
		if "!len!" neq "%%L" echo error & pause
)
echo OK.

echo(
set /p ".=final 2:"<nul

set "str="
for /L %%L in (0,1,8185) do (
		set len=
		%strLen2% str len
		set "str=!str!X"
		title "%%L" "!len!"
		if "!len!" neq "%%L" echo error & pause
)
echo OK.

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
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
edit : correct some errors

Code: Select all

final : Check 8191 string => 8191
final 2 : Check 8191 string => 8191

Functional test

final :OK.

final 2:OK.


--------+-------+--------+
len     | final | final2 |
--------+-------+--------+
8191    | 13.62 | 11.76  |
8191    | 13.62 | 11.72  |
8191    | 13.61 | 11.73  |
8191    | 13.62 | 11.74  |
--------+-------+--------+

--------+-------+--------+
len     | final | final2 |
--------+-------+--------+
6000    | 12.51 | 10.75  |
6000    | 12.56 | 10.68  |
6000    | 12.58 | 10.70  |
6000    | 12.57 | 10.65  |
--------+-------+--------+

--------+-------+--------+
len     | final | final2 |
--------+-------+--------+
4500    | 11.76 | 09.94  |
4500    | 11.90 | 09.97  |
4500    | 11.90 | 09.94  |
4500    | 11.93 | 09.89  |
--------+-------+--------+

--------+-------+--------+
len     | final | final2 |
--------+-------+--------+
2000    | 11.56 | 10.43  |
2000    | 11.61 | 10.43  |
2000    | 11.60 | 10.39  |
2000    | 11.59 | 10.38  |
--------+-------+--------+

--------+-------+--------+
len     | final | final2 |
--------+-------+--------+
1000    | 11.10 | 09.92  |
1000    | 11.17 | 09.98  |
1000    | 11.19 | 09.93  |
1000    | 11.13 | 09.99  |
--------+-------+--------+

--------+-------+--------+
len     | final | final2 |
--------+-------+--------+
200     | 07.92 | 07.41  |
200     | 07.98 | 07.45  |
200     | 07.98 | 07.41  |
200     | 08.00 | 07.44  |
--------+-------+--------+

--------+-------+--------+
len     | final | final2 |
--------+-------+--------+
10      | 07.86 | 07.36  |
10      | 07.91 | 07.34  |
10      | 07.92 | 07.35  |
10      | 07.94 | 07.39  |
--------+-------+--------+
Premere un tasto per continuare . . .
This is the non-macro version, which is even more readable. It should mirror the macro.

Code: Select all

:sub-strlen
	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:~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"

								if "" neq "!%%~1:~4095!" (set /a "$L=0x!$scale:~15,1!*256+4096") else set /a "$L=0x!$scale:~15,1!*256"

							) else if "" neq "!%%~1:~4095!" (set "$L=4096") 

						)) 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"
 		) 
 	)

goto :eof
Edit: I have a question

Code: Select all

set ^"$
Why is there a need to escape the double quote here while in other cases there is no need?

Francesco

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

Re: strLen boosted

#51 Post by aGerman » 23 Jul 2025 12:55

Definitely the fastest version we have so far. Well done :!:
May I suggest to consistently use spaces rather than tabs? The displayed tab width depends on editor settings and mixing them makes a mess with indentations.

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 %%H IN (FEDCBA9876543210) DO 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 for %%3 in (%%~1) do (^
%=    =% if defined %%3 (^
%=      =% (^
%=        =% if "" neq "%%!%%3:~255%%!" (^
%=          =% if "" neq "%%!%%3:~4095%%!" (^
%=            =% set "$=%%!%%3:~4096%%!"^
%=          =% ) else set "$=%%!%%3%%!"^
%=        =% ) ^& (^
%=          =% 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%%!%%H^"^&^
%=            =% if "" neq "%%!%%3:~4095%%!" (^
%=              =% set /a "$L=0x%%!$Scale:~15,1%%!*256+4096"^
%=            =% ) else set /a "$L=0x%%!$Scale:~15,1%%!*256"^
%=          =% ) else if "" neq "%%!%%3:~4095%%!" set "$L=4096"^
%=        =% ) else set "$L=0"^
%=      =% )^&^
%=      =% for %%# in (%%!$L%%!) do set ^"$=%%!%%3:~%%#%%!^
%=       =%%%H%%H%%H%%H%%H%%H%%H%%H%%H%%H%%H%%H%%H%%H%%H%%H^
%=       =%FFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCC^
%=       =%BBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAA99999999999999998888888888888888^
%=       =%7777777777777777666666666666666655555555555555554444444444444444^
%=       =%333333333333333322222222222222221111111111111111^"^&^
%=      =% 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
Why is there a need to escape the double quote here while in other cases there is no need?
These carets will not be part of the macro. However, we need them for the parser to make sure line continuations work. This is because when viewed line by line, the quotes are not balanced, which would result in the caret at the end of the line belonging to a string literal.

Works because the closing quote is in the same line:
set "foo=bar"^&^
rem more code here

Doesn't work because carets are considered to be belonging to a string literal:
set "foo=^
bar"^&^
rem more code here

Works again:
set ^"foo=^
bar^"^&^
rem more code here

The parts with quotation semantics applied by the parser are marked in red.

Steffen

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

Re: strLen boosted

#52 Post by einstein1969 » 24 Jul 2025 07:46

Thanks for the detailed and understandable explanation of escaping double quotes.

The rewritten code with spaces is a really good idea; I'll keep it in mind.

Francesco

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

Re: strLen boosted

#53 Post by einstein1969 » 02 Aug 2025 11:06

I add this variant made by T3rror, because it introduces intelligent ideas without losing speed.

Indeed it should be faster because it supposes the non -existence of double quotes around the variables
Hi Einst - I was looking over the final StrLen macro version you posted on dostips and noticed an easy little tweak to drop a redundant for loop.

%= =% for /f "tokens=1,2" %%1 in ("%%!$args%%!") do for %%3 in (%%~1) do (^
%= =% if defined %%3 (^

can be changed out to:

%= =% for /f "tokens=1,2" %%1 in ("%%!$args%%! $len") do if not "%%~2" == "" (^
%= =% if defined %%~1 (^


(using your editors replace function) also swap out instances of %%3 for %%~1.

The above also:

assumes a default return var $len if none is provided
fails silently if no arguments are provided

I know dropping the for %%3 in (%%~1) do loop will likely never be noticable in practical usage - especially with the work already put into optimizing this Macro, but I thought I'd share the suggestion all the same.
It was then corrected for use without double quotes and this is its version. Inserted an intelligent functionality and a little speed:

Code: Select all

@Echo off

Call :init.strlen

rem usage example
%strLen% computername
Set $len
Set computername
Endlocal & goto:eof

:init.strlen Macro
::  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. Dafualts to $len if unsupplied
:: Strings of up to 8191 characters are supported.
   rem See: https://www.dostips.com/forum/viewtopic.php?p=71415#p71415
   rem optimized for best performance averaged across supported string lengths.

                                                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 delims= " %%1 in ("%%!$args%%! $len") do if not "%%~2" == "" (^
%=                        =% if defined %%1 (^
%=                                     =% (^
%=                                        =% if "" neq "%%!%%1:~255%%!" (^
%=                                                =% if "" neq "%%!%%1:~4095%%!" (set "$=%%!%%1:~4096%%!") else set "$=%%!%%1%%!"^
%=                                                =% ) ^& (^
%=                                                        =% if defined $ (^
%=                                                                 =% set ^"$Scale=^
%=                                                                =%%%!$:~255,1%%!%%!$:~511,1%%!%%!$:~767,1%%!%%!$:~1023,1%%!%%!$:~1279,1%%!^
%=                This zone is empty                                =%%%!$:~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+4096"^
%=                                                                =% ) else set /a "$L=0x%%!$Scale:~15,1%%!*256"^
%=                                                        =% ) else if "" neq "%%!%%1:~4095%%!" set "$L=4096"^
%=                                       =% ) else set "$L=0"^
%=                                =% )^&^
%=                                =% for %%# in (%%!$L%%!) do set ^"$=%%!%%~1:~%%#%%!^
%= Leading space not supported    =%FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
%=                                =%FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
%=                                =%FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
%=                                =%FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210^
%=                                =%FFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCC^
%=                                =%BBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAA99999999999999998888888888888888^
%=                                =%7777777777777777666666666666666655555555555555554444444444444444^
%=                                =%333333333333333322222222222222221111111111111111^"^&^
%=                                =% 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

Post Reply