OVH Cloud OVH Cloud

Chaine vers tableau de byte

19 réponses
Avatar
Jean-Marc
Bonjour chers collègues,

j'ai un besoin précis d'optimisation: Je dois convertir
une chaine en un table de Bytes.

J'ai écrit cette fonction, qui fonctionne très bien:

' s : une chaine
' szt : la longuue de cette chaine
' t() : le tableau de byte à remplir
Function CvrtStr2ByteArray(ByVal s As String, _
ByVal szt As Long, _
ByRef t() As Byte) As Boolean
Dim i As Long
Dim c As String

For i = 1 To szt
c = Mid$(s, i, 1)
If LenB(c) = 0 Then
t(i - 1) = CByte(0)
Else
t(i - 1) = CByte(Asc(c))
End If
Next i
CvrtStr2ByteArray = True
End Function

Connaitriez vous un moyen de faire cela, mais plus rapidement
que ne le fait cette implémentation naïve.
J'ai ici un vrai besoin d'optimisation, car cette fonction est
un facteur limitant dans un de mes programmes (Rabbit)
Note: la chaine en paramètre peut être très longeur (jusqu'à
plusieurs centaines de K) et j'appelle parfois cette fonction
plusieurs milliers de fois consécutivement.
Si vous aviez un moyen efficace, je suis preneur, sinon je ferais
une dll en C, mais je voulais éviter cela pour ce projet en
particulier.

Pour établir une comparaison, j'ai mesuré les performances de
ma fonction: 10 mesures de 100 appels avec une chaine aléatoire
de 65535 caractères.

Résultat: 37 millisecondes par appel de fonction.

Si on peut faire en dessous de 10 millisecondes, je prends :-)
Sinon, je fais une Dll :-(

--
Jean-marc
Tester mon serveur (VB6) => http://myjmnhome.dyndns.org
"There are only 10 kind of people
those who understand binary and those who don't."
mailto: remove '_no_spam_' ; _no_spam_jean_marc_n2@yahoo.fr

10 réponses

1 2
Avatar
Jacques93
Bonour Jean-Marc,
Jean-Marc a écrit :
Bonjour chers collègues,

j'ai un besoin précis d'optimisation: Je dois convertir
une chaine en un table de Bytes.

J'ai écrit cette fonction, qui fonctionne très bien:

' s : une chaine
' szt : la longuue de cette chaine
' t() : le tableau de byte à remplir
Function CvrtStr2ByteArray(ByVal s As String, _
ByVal szt As Long, _
ByRef t() As Byte) As Boolean
Dim i As Long
Dim c As String

For i = 1 To szt
c = Mid$(s, i, 1)
If LenB(c) = 0 Then
t(i - 1) = CByte(0)
Else
t(i - 1) = CByte(Asc(c))
End If
Next i
CvrtStr2ByteArray = True
End Function

Connaitriez vous un moyen de faire cela, mais plus rapidement
que ne le fait cette implémentation naïve.
J'ai ici un vrai besoin d'optimisation, car cette fonction est
un facteur limitant dans un de mes programmes (Rabbit)
Note: la chaine en paramètre peut être très longeur (jusqu'à
plusieurs centaines de K) et j'appelle parfois cette fonction
plusieurs milliers de fois consécutivement.
Si vous aviez un moyen efficace, je suis preneur, sinon je ferais
une dll en C, mais je voulais éviter cela pour ce projet en
particulier.

Pour établir une comparaison, j'ai mesuré les performances de
ma fonction: 10 mesures de 100 appels avec une chaine aléatoire
de 65535 caractères.

Résultat: 37 millisecondes par appel de fonction.

Si on peut faire en dessous de 10 millisecondes, je prends :-)
Sinon, je fais une Dll :-(




Y a t-il une contrainte qui t'empêche d'utiliser l'API CopyMemory ?


--
Cordialement,

Jacques.
Avatar
Jean-Marc
"Jacques93" a écrit dans le message de
news:%
Bonour Jean-Marc,
Jean-Marc a écrit :
> Bonjour chers collègues,



> ' s : une chaine
> ' szt : la longuue de cette chaine
> ' t() : le tableau de byte à remplir
> Function CvrtStr2ByteArray(ByVal s As String, _
> ByVal szt As Long, _
> ByRef t() As Byte) As Boolean
> Dim i As Long
> Dim c As String
>
> For i = 1 To szt
> c = Mid$(s, i, 1)
> If LenB(c) = 0 Then
> t(i - 1) = CByte(0)
> Else
> t(i - 1) = CByte(Asc(c))
> End If
> Next i
> CvrtStr2ByteArray = True
> End Function



> Pour établir une comparaison, j'ai mesuré les performances de
> ma fonction: 10 mesures de 100 appels avec une chaine aléatoire
> de 65535 caractères.
>
> Résultat: 37 millisecondes par appel de fonction.
>
> Si on peut faire en dessous de 10 millisecondes, je prends :-)
> Sinon, je fais une Dll :-(




Hello Jacques,

Y a t-il une contrainte qui t'empêche d'utiliser l'API CopyMemory ?



Non, pas du tout. Comment utiliser CopyMemory dans ce cas?
Je connais ceci:
CopyMemory ByVal StrPtr(dest), ByVal StrPtr(src), LenB(dest)

Mais comment faire avec mon tableau de Byte?
Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDst As
Any, pSrc As Any, ByVal ByteLen As Long)

--
Jean-marc
Tester mon serveur (VB6) => http://myjmnhome.dyndns.org
"There are only 10 kind of people
those who understand binary and those who don't."
mailto: remove '_no_spam_' ;
Avatar
Jean-Marc
"Jean-Marc" a écrit dans le message de
news:44254536$0$14127$
"Jacques93" a écrit dans le message de
news:%



> Y a t-il une contrainte qui t'empêche d'utiliser l'API CopyMemory ?




Arf!!!!
J'ai trouvé :-)
C'était si simple!!
CopyMemory t(0), ByVal StrPtr(s), l

Résultat des courses: C'est si rapide que je ne peux même pas mesurer,
mais
en tout cas c'est inférieur à 0.1 millisecondes par appel, c'est donc
environ
400 fois plus rapide ....

Merci infiniment Jacques, et bon après-midi :-)

--
Jean-marc
Tester mon serveur (VB6) => http://myjmnhome.dyndns.org
"There are only 10 kind of people
those who understand binary and those who don't."
mailto: remove '_no_spam_' ;
Avatar
Jean-Marc
J'ai parlé trop vite :-(

Les 2 fonctions ne sont pas équivalentes, mais peut être est du à mon
passage de paramètre.
Avec copyMemory, je récupère en plus un 0 tous les 2 bytes dans le
tableau.

voici pour être plus clair:

tba() est rempli avec ma fonction

for i=0 to 10:print tba(i); " ";:next i
72 84 84 80 47 49 46 48 32 50 48

tbb() est rempli avec copyMemory

for i=0 to 10:print tbb(i); " ";:next i
72 0 84 0 84 0 80 0 47 0 49


Comment faire ?

--
Jean-marc
Tester mon serveur (VB6) => http://myjmnhome.dyndns.org
"There are only 10 kind of people
those who understand binary and those who don't."
mailto: remove '_no_spam_' ;
Avatar
Picalausa François
Hello,

Si tu copymemory(strconv("a",vbFromUnicode)), tu auras la description de
caractère voulue (ANSI plutôt qu'UNICODE).
Il ne faut pas oublier que VB stoque en interne les chaines en UNICODE et
donc la conversion est nécessaire...

--
Picalausa François

"Jean-Marc" a écrit dans le message de news:
44254eef$0$1161$
J'ai parlé trop vite :-(

Les 2 fonctions ne sont pas équivalentes, mais peut être est du à mon
passage de paramètre.
Avec copyMemory, je récupère en plus un 0 tous les 2 bytes dans le
tableau.

voici pour être plus clair:

tba() est rempli avec ma fonction

for i=0 to 10:print tba(i); " ";:next i
72 84 84 80 47 49 46 48 32 50 48

tbb() est rempli avec copyMemory

for i=0 to 10:print tbb(i); " ";:next i
72 0 84 0 84 0 80 0 47 0 49


Comment faire ?

--
Jean-marc
Tester mon serveur (VB6) => http://myjmnhome.dyndns.org
"There are only 10 kind of people
those who understand binary and those who don't."
mailto: remove '_no_spam_' ;



Avatar
Picalausa François
Hello,

Il y a une technique très efficace qui consiste à mapper un SAFEARRAY sur ta
string...
Ceci évite de devoir allouer deux fois la mémoire (donc gain de temps) et de
devoir recopier la chaine à chaque modification (ou d'effectuer les
modification en double) puisqu'on travaille sur la même zone mémoire...

Reste à répéter : "Même en VB, je n'ai pas peur des pointeurs!" deux ou
trois fois... ou pas!

Voici un bout de code illustrant la technique:
Option Explicit

Private Const FADF_HAVEVARTYPE = &H80
Private Const VT_I2 = 2
Private Const VT_UI1 = 17

Private Type SAFEARRAYBOUND
cElements As Long
lLbound As Long
End Type

Private Type SAFEARRAY
cDims As Integer
fFeatures As Integer
cbElements As Long
cLocks As Long
pvData As Long
End Type

Private Declare Function VarPtrArray _
Lib "msvbvm60.dll" _
Alias "VarPtr" _
( _
Var() As Any _
) _
As Long
Private Declare Sub CopyMemory _
Lib "kernel32" _
Alias "RtlMoveMemory" _
( _
ByRef Destination As Any, _
ByRef Source As Any, _
ByVal Length As Long _
)

Private opsa As Long 'pointeur vers la safearray précédente
Private opvdata As Long 'pointeur vers les données précédentes
Private osb As SAFEARRAYBOUND 'bornes précédentes du tableau

Private Function MapSafeArrrayToString(psa As Long, strText As String) As
Boolean
Dim sa As SAFEARRAY, vt As Long
Dim sab As SAFEARRAYBOUND
MapSafeArrrayToString = False

'Vérifie la validité des données en entrée
If ((StrPtr(strText) <> 0) And (psa <> 0)) Then
'Vérifie qu'on a bien démappé précédemment
If (opsa = 0) Then
'déférence le pointer
Call CopyMemory(sa, ByVal psa, LenB(sa))

'Demande une array typée en 1D non lockée
If ((sa.fFeatures And FADF_HAVEVARTYPE) And (sa.cDims = 1) And
(sa.cLocks = 0)) Then
'Demande de plus que cette array soit typée As Byte ou As
Integer
CopyMemory vt, ByVal (psa - 4), LenB(vt)
If (vt = VT_UI1) Then
'Enregistre les données actuelle du tableau
opsa = psa
opvdata = sa.pvData
Call CopyMemory(osb, ByVal psa + LenB(sa), LenB(osb))

'Remap le tableau
sab.cElements = LenB(strText)
sab.lLbound = 0
Call CopyMemory(ByVal psa + LenB(sa), sab, LenB(sab))

sa.pvData = StrPtr(strText)
Call CopyMemory(ByVal psa, sa, LenB(sa))

MapSafeArrrayToString = True
End If
End If
End If
End If
End Function

Private Function UnmapSafeArray() As Boolean
Dim sa As SAFEARRAY, vt As Long
Dim sab As SAFEARRAYBOUND
UnmapSafeArray = False

'Vérifie la validité des données en entrée
If (opsa <> 0) Then
'déférence le pointer
Call CopyMemory(sa, ByVal opsa, LenB(sa))

'Restaure les données
Call CopyMemory(ByVal opsa + LenB(sa), osb, LenB(osb))
sa.pvData = opvdata
Call CopyMemory(ByVal opsa, sa, LenB(sa))

'Oublie tout de ce qui s'est passé
opsa = 0
opvdata = 0
osb.cElements = 0
osb.lLbound = 0
End If
End Function

Private Function GetPsa(PPSA As Long) As Long
Call CopyMemory(GetPsa, ByVal PPSA, LenB(GetPsa))
End Function

Private Sub Form_Load()
Dim Blah As String
Dim byBlah() As Byte
Dim i As Long

ReDim byBlah(0)

Blah = "Hello World!"

'ATTENTION : jusqu'à UnmapSafeArray, il est préférable que
'Blah ne sorte jamais du scope
If MapSafeArrrayToString(GetPsa(VarPtrArray(byBlah)), Blah) Then
For i = LBound(byBlah) To UBound(byBlah)
Debug.Print Chr$(byBlah(i))
Next i

UnmapSafeArray
End If
End Sub

--
Picalausa François

"Jean-Marc" a écrit dans le message de news:
4425260b$0$11427$
Bonjour chers collègues,

j'ai un besoin précis d'optimisation: Je dois convertir
une chaine en un table de Bytes.

J'ai écrit cette fonction, qui fonctionne très bien:

' s : une chaine
' szt : la longuue de cette chaine
' t() : le tableau de byte à remplir
Function CvrtStr2ByteArray(ByVal s As String, _
ByVal szt As Long, _
ByRef t() As Byte) As Boolean
Dim i As Long
Dim c As String

For i = 1 To szt
c = Mid$(s, i, 1)
If LenB(c) = 0 Then
t(i - 1) = CByte(0)
Else
t(i - 1) = CByte(Asc(c))
End If
Next i
CvrtStr2ByteArray = True
End Function

Connaitriez vous un moyen de faire cela, mais plus rapidement
que ne le fait cette implémentation naïve.
J'ai ici un vrai besoin d'optimisation, car cette fonction est
un facteur limitant dans un de mes programmes (Rabbit)
Note: la chaine en paramètre peut être très longeur (jusqu'à
plusieurs centaines de K) et j'appelle parfois cette fonction
plusieurs milliers de fois consécutivement.
Si vous aviez un moyen efficace, je suis preneur, sinon je ferais
une dll en C, mais je voulais éviter cela pour ce projet en
particulier.

Pour établir une comparaison, j'ai mesuré les performances de
ma fonction: 10 mesures de 100 appels avec une chaine aléatoire
de 65535 caractères.

Résultat: 37 millisecondes par appel de fonction.

Si on peut faire en dessous de 10 millisecondes, je prends :-)
Sinon, je fais une Dll :-(

--
Jean-marc
Tester mon serveur (VB6) => http://myjmnhome.dyndns.org
"There are only 10 kind of people
those who understand binary and those who don't."
mailto: remove '_no_spam_' ;



Avatar
Jacques93
Jean-Marc a écrit :
J'ai parlé trop vite :-(

Les 2 fonctions ne sont pas équivalentes, mais peut être est du à mon
passage de paramètre.
Avec copyMemory, je récupère en plus un 0 tous les 2 bytes dans le
tableau.

voici pour être plus clair:

tba() est rempli avec ma fonction

for i=0 to 10:print tba(i); " ";:next i
72 84 84 80 47 49 46 48 32 50 48

tbb() est rempli avec copyMemory

for i=0 to 10:print tbb(i); " ";:next i
72 0 84 0 84 0 80 0 47 0 49


Comment faire ?




En faisant :

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(Destination As Any, _
Source As Any, _
ByVal Length As Long)

Function CvrtStr2ByteArray(ByRef s As String, _
ByVal szt As Long, _
ByRef t() As Byte) As Boolean
CopyMemory t(0), ByVal s, szt

CvrtStr2ByteArray = True
End Function

Pour moi c'est OK.

--
Cordialement,

Jacques.
Avatar
Jacques93
Bonjour Picalausa François,
Picalausa François a écrit :
Hello,

Si tu copymemory(strconv("a",vbFromUnicode)), tu auras la description de
caractère voulue (ANSI plutôt qu'UNICODE).
Il ne faut pas oublier que VB stoque en interne les chaines en UNICODE et
donc la conversion est nécessaire...




Cette conversion n'est elle pas implicite, notamment lors de l'appel aux
API ?

Il me semble que c'est lorsque qu'une API renvoie une chaîne Unicode
qu'il faut faire une conversion explicite.

Il y a peut être aussi la manière de déclarer les paramètres de l'API
qui a son importance :

As Any

ou

As String

--
Cordialement,

Jacques.
Avatar
Picalausa François
"Jacques93" a écrit dans le message de news:
%
Bonjour Picalausa François,
Picalausa François a écrit :
Hello,

Si tu copymemory(strconv("a",vbFromUnicode)), tu auras la description de
caractère voulue (ANSI plutôt qu'UNICODE).
Il ne faut pas oublier que VB stoque en interne les chaines en UNICODE et
donc la conversion est nécessaire...




Cette conversion n'est elle pas implicite, notamment lors de l'appel aux
API ?

Il me semble que c'est lorsque qu'une API renvoie une chaîne Unicode qu'il
faut faire une conversion explicite.

Il y a peut être aussi la manière de déclarer les paramètres de l'API qui
a son importance :

As Any

ou

As String



Hello,

Tu as tout à fait raison, je m'est trompé sur ce coup... je pensais au
passage As Long + StrPtr (qui lui peut poser problème)

--
Picalausa François
Avatar
Jacques93
Picalausa François a écrit :
"Jacques93" a écrit dans le message de news:
%
Bonjour Picalausa François,
Picalausa François a écrit :
Hello,

Si tu copymemory(strconv("a",vbFromUnicode)), tu auras la description de
caractère voulue (ANSI plutôt qu'UNICODE).
Il ne faut pas oublier que VB stoque en interne les chaines en UNICODE et
donc la conversion est nécessaire...



Cette conversion n'est elle pas implicite, notamment lors de l'appel aux
API ?

Il me semble que c'est lorsque qu'une API renvoie une chaîne Unicode qu'il
faut faire une conversion explicite.

Il y a peut être aussi la manière de déclarer les paramètres de l'API qui
a son importance :

As Any

ou

As String



Hello,

Tu as tout à fait raison, je m'est trompé sur ce coup... je pensais au
passage As Long + StrPtr (qui lui peut poser problème)




Sieste trop courte ? fais gaffe, en plus on perd une heure cette nuit.

:-D

--
Cordialement,

Jacques.
1 2