Bout de code en C

Vous débutez et vous avez besoin d'aide ? N'hésitez pas à poser vos questions
brossden
Messages : 821
Inscription : lun. 26/janv./2004 14:37

Bout de code en C

Message par brossden »

Est ce que quelqu'un aurait une idée pour traduire le bout de code ci-après ecrit en "C" en PureBasic ?

Code : Tout sélectionner

void cTP::DecodeTp(const unsigned char *in, unsigned char *out)
{
  unsigned int var2=*((unsigned int *)in);
  for(int i=0; i<13; i++) {
    in+=4; out+=4;
    var2=(var2<<3) | (var2>>(32-3));
    unsigned int var1=*((unsigned int *)in) ^ var2;
    *((unsigned int *)out)=(var1<<(i+2)) | (var1>>(32-(i+2)));
    }
}
RegisLG
Messages : 154
Inscription : mer. 22/juin/2005 2:32

Message par RegisLG »

J'ai plus l'impression qu'il s'agit de C++ que de C, c'est la définition de la méthode DecodeTp de la classe cTP, non ?
Backup
Messages : 14526
Inscription : lun. 26/avr./2004 0:40

Message par Backup »

j'y connais rien, mais a vu de nez ce pourrai etre ça 8O 8O

ou un truc dans le genre !!
en fait je sait pas si "*" correspond a un pointeur en C :?

Code : Tout sélectionner

Procedure DecodeTp( *in,  *out)
var2= *in ;
For i=0 To 13
    in=in+4
    out=out+4
    var2=(var2<<3) | (var2>>(32-3));
    var1=Pow(*in, var2)  ;
    *out=(var1<<(i+2)) | (var1>>(32-(i+2)));
Next i
EndProcedure
linkerstorm
Messages : 20
Inscription : lun. 29/janv./2007 7:13

Message par linkerstorm »

RegisLG a écrit :J'ai plus l'impression qu'il s'agit de C++ que de C, c'est la définition de la méthode DecodeTp de la classe cTP, non ?
Tu as tout à fait raison , c'est bien de C++ qu'il s'agit et c'est bien la définition de la méthode DecodeTp de la classe cTP. :D

Dobro>
Ton code ne semble pas loin d'être le bon. Quelques corrections sont toutefois à apporter.

Mais aussi simple que paraisse cette méthode en C++, sa compréhension nécessite quand même une analyse poussée.
Nous allons en effet découvrir quelques limitations de PureBasic et, surtout, comment les contourner.

1 - Histoire de signes...

Code : Tout sélectionner

void cTP::DecodeTp(const unsigned char *in, unsigned char *out)
Voyons les forces en présence :
- un paramètre "const unsigned char *in" qui signifie une chaîne de caractères (spécifiquement ici, plutôt un tableau d'octets) non modifiée par la méthode en question,
- un paramètre "unsigned char *out" qui signifie une chaîne de caractères (spécifiquement ici, plutôt un tableau d'octets) qui contiendra le résultat de la méthode en question.

Code : Tout sélectionner

unsigned int var2=*((unsigned int *)in);
La première ligne est déjà sujette à interprétation.

En fait, Dobro, tu as raison : le caracatère '*' signifie pointeur mais dans le contexte de définition seulement; en fait :

Code : Tout sélectionner

int* pvaleur;                    // définit un pointeur sur un entier long signé et
printf("Valeur = %d", *pvaleur); // affiche la valeur pointée par "pvaleur"; "pvaleur" contient l'adresse de la valeur entière longue.
Pour en revenir à notre première ligne, nous avons d'abord un cast (changement de type ou transtypage) du pointeur *in en un pointeur vers un entier long non signé.
Ensuite, toujours sur cette première ligne, ce nouveau pointeur vers un entier long non signé est déréférencé, donc on y lit une valeur entière longue non signée.
Cette valeur lue est stockée dans la variable entière longue non signée "var2".
Ouf ! La première ligne a livré ses secrets.

Arrêtons-nous un moment à ce stade pour faire deux remarques :
- un entier long est codé sur quatre octets et un tableau d'octets est codé par un ensemble d'octets; donc "var2" contient la valeur des quatre premiers octets du tableau d'octets "in"; par conséquent, celui-ci doit au moins contenir quatre octets;
- PB ne gère pas les types non signés, à part pour le type .c (caractère). Ceci est une première limitation et nous verrons comment la contourner.

Code : Tout sélectionner

for(int i=0; i<13; i++) {
Ensuite arrive une boucle. Juste une correction, Dobro : en fait, la valeur finale 13 ne fait partie des valeurs ;)
La bonne écriture en PB est donc:

Code : Tout sélectionner

For i.l = 0 To 12

Code : Tout sélectionner

in+=4; out+=4;
Ensuite, on incrémente chacun des pointeurs ("in" et "out") de quatre octets (soit un entier long) pour pointer sur les 4 octets suivants, c'est-à-dire sur l'entier long suivant.
Par conséquent, il faut que "out" pointe lui-aussi sur un tableau d'octets.

Code : Tout sélectionner

var2=(var2<<3) | (var2>>(32-3));
Ensuite, un calcul est fait sur "var2" et elle est modifiée en conséquence.

2 - X-OR, le shérif, shérif de l'espace !

Code : Tout sélectionner

unsigned int var1=*((unsigned int *)in) ^ var2;
Ensuite, les quatre octets suivants dans "in" sont transformés en un entier long qui XORé avec "var2".
Le résultat est stockée dans l'entier long non signé "var1".

On lève ici une autre limitation de PB : le XOR binaire n'existe pas dans le langage. Nous verrons la solution plus loin.

Code : Tout sélectionner

*((unsigned int *)out)=(var1<<(i+2)) | (var1>>(32-(i+2)));
Enfin, "var1" et "i" sont utilisées dans un calcul dont le résultat est transformé en entier non signé et stocké dans les quatre octets pointés par "out".

Ouf !

Deux remarques post-analytiques :
- les tableaux "in" et "out" doivent comporter un nombre d'octets divisible par 4 (à cause des entiers longs non signés) et être au moins longs de 4 + (4 * 13) = 56 octets,
- à mon humble avis, les quatre premiers octets du tableau "in" constituent une sorte de "clé" de décodage.

3 - Can you translate, please ?

Passons maintenant à la traduction ligne à ligne du code C++ en PureBasic et voyons sur quels écueils nous tombons (déjà signalés plus haut).

Code : Tout sélectionner

;void cTP::DecodeTp(const unsigned char *in, unsigned char *out)
Procedure DecodeTp(*pin.c, *pout.c)
"void" nous indique qu'aucune valeur de retour n'est attendue.
Nous avons vu plus haut que le type .c (caractère) en PB est le seul qui soit non signé; il est par conséquent tout à fait logique de l'utiliser ici.

Code : Tout sélectionner

; unsigned int var2=*((unsigned int *)in);
var2.l = PeekL(*pin)
Lire les 4 premiers octets de "in" et les stocker dans une variable entière signée (pas le choix avec PB).

Code : Tout sélectionner

	; for(int i=0; i<13; i++) {
	For i.l = 0 To 12
Notre boucle, pas trop compliquée à convertir ;)

Code : Tout sélectionner

		; in+=4; out+=4;
		*pin + 4 : *pout + 4
On se positionne 4 octets plus loin dans "in" et "out".
Attention : veillez à bien avoir décochée l'option du compilateur "Activer le support Unicode" sinon, le type .c (caractère) fait deux octets au lieu d'un.

Code : Tout sélectionner

		; var2=(var2<<3) | (var2>>(32-3));
		var2left.l  = var2 << 3

		; Méthode assembleur => plus concise que la méthode PB
;		var2right.l = var2
;  		!SHR dword [p.v_var2right], (32 - 3) ; Car le >> de PB garde le signe, ce que
  		                                     ; nous ne souhaitons pas

		; Méthode PB => on reste en PB, pas besoin d'assembleur
		var2right.l = (var2 >> 1) & %01111111111111111111111111111111 ; Retirer le bit de signe
		var2right >> ((32 - 3) - 1)

		var2 = var2left | var2right
Le >> (décalage de n bits à droite) de PB conserve le signe de notre "var2", ce que nous ne souhaitons pas.
Il existe deux solutions :
- une purement PB : on effectue un premier décalage en conservant le signe, on élimine ensuite ce signe et on effectue les autres décalages,
- une en assembleur : l'instruction assembleur SHR ne conserve pas le signe ce qui nous convient tout à fait.
Pour utiliser l'une ou l'autre des méthodes, vous pouvez commenter/décommenter les lignes correspondantes.

Code : Tout sélectionner

		; unsigned int var1=*((unsigned int *)in) ^ var2;
 		var1.l = PeekL(*pin)
 		!MOV eax, dword [p.v_var2]
 		!XOR dword [p.v_var1], eax ; Car PB n'a pas de OU exclusif binaire
Le XOR binaire n'existe pas en PB, on passe par l'assembleur pour nous aider ;)

Code : Tout sélectionner

		; *((unsigned int *)out)=(var1<<(i+2)) | (var1>>(32-(i+2)));
		var3left.l = var1 << (i + 2)
		decalage.b = (32 - (i + 2))

		; Méthode assembleur => plus concise que la méthode PB
;		var3right.l = var1
;  		!MOV cl, byte [p.v_decalage]
;  		!SHR dword [p.v_var3right], cl ; Car le >> de PB garde le signe, ce que nous ne souhaitons pas

		; Méthode PB => on reste en PB, pas besoin d'assembleur
		var3right.l = (var1 >> 1) & %01111111111111111111111111111111 ; Retirer le bit de signe
		var3right >> (decalage - 1)
		
 		var3.l = var3left | var3right
		PokeL(*pout, var3)
Mêmes remarques que précédemment (pour "var2"), sauf que l'on ajoute ici la variable "décalage" car elle est le résultat d'un calcul sur "i".
On finit ensuite par inscrire la valeur de "var3" dans "out".

Code : Tout sélectionner

	; }
	Next
On oublie pas le Next...

Code : Tout sélectionner

; }
EndProcedure
et le EndProcedure !

Voici donc le code complet avec test qui correspondrait à cette analyse, si elle s'avère juste :

Code : Tout sélectionner


;void cTP::DecodeTp(const unsigned char *in, unsigned char *out)
Procedure DecodeTp(*pin.c, *pout.c)

	; =============================================================================
	; Transformer les 4 premiers octets de "in" en entier long (peut-être une clé ?)
	; =============================================================================

	; unsigned int var2=*((unsigned int *)in);
	var2.l = PeekL(*pin)
	
	; =============================================================================
	; Décoder chaque quaduplet de "in" dans "out"
	; =============================================================================

	; for(int i=0; i<13; i++) {
	For i.l = 0 To 12
		
		; =============================================================================
		; Se positionner sur les 4 octets suivants dans "in" et "out"
		; =============================================================================
		
		; in+=4; out+=4;
		*pin + 4 : *pout + 4
		
		; =============================================================================
		; Recalculer "var2"
		; =============================================================================

		; var2=(var2<<3) | (var2>>(32-3));
		var2left.l  = var2 << 3

		; Méthode assembleur => plus concise que la méthode PB
;		var2right.l = var2
;  		!SHR dword [p.v_var2right], (32 - 3) ; Car le >> de PB garde le signe, ce que
  		                                     ; nous ne souhaitons pas

		; Méthode PB => on reste en PB, pas besoin d'assembleur
		var2right.l = (var2 >> 1) & %01111111111111111111111111111111 ; Retirer le bit de signe
		var2right >> ((32 - 3) - 1)

		var2 = var2left | var2right
		
		; =============================================================================
		; Calculer "var1"
		; =============================================================================
		
		; unsigned int var1=*((unsigned int *)in) ^ var2;
 		var1.l = PeekL(*pin)
 		!MOV eax, dword [p.v_var2]
 		!XOR dword [p.v_var1], eax ; Car PB n'a pas de OU exclusif binaire
		
		; =============================================================================
		; Quelques calculs avec "var1" et "i" dont le résultat est stocké dans les
		; quatre octets courants de "out"
		; =============================================================================
		
		; *((unsigned int *)out)=(var1<<(i+2)) | (var1>>(32-(i+2)));
		var3left.l = var1 << (i + 2)
		decalage.b = (32 - (i + 2))

		; Méthode assembleur => plus concise que la méthode PB
;		var3right.l = var1
;  		!MOV cl, byte [p.v_decalage]
;  		!SHR dword [p.v_var3right], cl ; Car le >> de PB garde le signe, ce que nous ne souhaitons pas

		; Méthode PB => on reste en PB, pas besoin d'assembleur
		var3right.l = (var1 >> 1) & %01111111111111111111111111111111 ; Retirer le bit de signe
		var3right >> (decalage - 1)
		
 		var3.l = var3left | var3right
		PokeL(*pout, var3)
	
	; }
	Next
	
; }
EndProcedure

; Tester la procédure
If OpenConsole()
	Dim in.c(55)
	Dim out.c(55)
	
	; Initialiser  et afficher le contenu "in"
	PrintN("Contenu du tableau 'in' : ")
	For i.l = 0 To 55
		in(i) = 255 - i
		Print(RSet(Hex(in(i)), 2, "0") + " ")
	Next
	
	; Décoder
	DecodeTp(in(), out())
	
	; Afficher le contenu "out"
	PrintN("")
	PrintN("")
	PrintN("Contenu du tableau 'out' : ")
	For i.l = 0 To 55
		Print(RSet(Hex(out(i)), 2, "0") + " ")
	Next
	Input()
	CloseConsole()
EndIf
End
Je sais que ce topic est très long mais il soulève des aspects intéressants et surtout des solutions qui sont, somme toute, assez simples.

Pour ceux qui sont arrivés jusqu'ici, bravo ! Parce que je sais que les longs topics peuvents être rébarbatifs ;)
Vous pouvez prendre une aspirine, vous l'avez bien méritée !!!

Pour une question située dans le forum "Débutants", je trouve qu'on est allé bien loin dans la technique de PB !!!
Si un modo trouve judicieux de déplacer ce sujet sur ASM, je comprendrai aisément ;)

N'hésitez pas à me corriger ou me questionner si quelque chose vous paraît faux/erroné ou flou/pas clair.

On est tous ici pour apprendre et/ou enseigner :P

linkerstorm

NB : j'ai vérifié la validité de mon code en créant le code C correspondant ;)
comtois
Messages : 5186
Inscription : mer. 21/janv./2004 17:48
Contact :

Message par comtois »

Le XOR binaire n'existe pas en PB
Il existe, c'est le point d'exclamation

Code : Tout sélectionner

a.w=%1100!%0101
Debug Bin(a)
[EDIT]

Je préfère l'hexa c'est plus facile à lire que le binaire
je remplacerais %01111111111111111111111111111111 par $7FFFFFFF.
http://purebasic.developpez.com/
Je ne réponds à aucune question technique en PV, utilisez le forum, il est fait pour ça, et la réponse peut profiter à tous.
Backup
Messages : 14526
Inscription : lun. 26/avr./2004 0:40

Message par Backup »

ouaip, ben le C c'est pas simple :lol:
linkerstorm
Messages : 20
Inscription : lun. 29/janv./2007 7:13

Message par linkerstorm »

Comtois>
Merci, j'avais pas les yeux en face des trous !

J'avais bien lu la doc mais je suis passé au travers !

Il faut donc corriger la ligne

Code : Tout sélectionner

 		var1.l = PeekL(*pin)
  		!MOV eax, dword [p.v_var2]
  		!XOR dword [p.v_var1], eax ; Car PB n'a pas de OU exclusif binaire
en

Code : Tout sélectionner

 		var1.l = PeekL(*pin) ! var2
Pour l'utilisation de l'hexa, je suis tout à fait d'accord avec toi mais ma démarche étant aussi pédagogique, j'ai souhaité que l'on voie bien que le bit de signe est celui qui se situe à gauche des 32 bits; comme on travaille sur les bits (opérations de décalage et de masquage), il me semblait que c'était plus parlant (au niveau pédagogique), non ?
brossden
Messages : 821
Inscription : lun. 26/janv./2004 14:37

Message par brossden »

Merci à tous !

Un merci tout particulier à linkerstorm qui n'a pas fait dans la demi mesure coté explication mais PureBasic est plus puissant que l'on peut croire !

Comme je n'aime pas NE PAS comprendre ce que je fais, j'ai quelque peu été voir quelques cours de C++ !

J'en ai traduit le précédent code par :

Code : Tout sélectionner

Procedure TpsDecode(*in,*out)
  var2.q = (PeekQ(*in) & $FFFFFFFF) 
  For i = 0 To 12
    *in +4 : *out+4
    var2.q =  ( $FFFFFFFF & var2.q << 3 )  | ((var2 >> (32-3) )& $FFFFFFFF ) 
    var1.q = ((PeekQ(*in) & $FFFFFFFF) ! var2)
    var3.q=(var1.q >> (30-i))
    ret.q =  (var1 << (i+2)) & $FFFFFFFF | var3
    PokeQ(*out,ret)
  Next
EndProcedure
Après test il est exact !
linkerstorm
Messages : 20
Inscription : lun. 29/janv./2007 7:13

Message par linkerstorm »

Ravi de t'avoir aidé.

Pour ma conversion, je n'ai pas voulu utiliser les Quad car :
- ils sont seulement dans la version 4 de PB et
- ils ne reflètent pas les types utilisés dans le source original en C++.

J'ai préféré être très didactique dans mon approche afin que le maximum d'entre nous puisse bien comprendre la différence et la similitude entre ces deux langages (C++ et PB) afin de pouvoir faire des conversions fiables.

Le fait que tu sois allé voir des cours de C/C++ est une excellente chose !
Bientôt, c'est toi qui interviendras pour nous donner des cours de conversion !!!
En plus, avec le C/C++, tu peux programmer sous quasiment n'importe quel OS car c'est un langage très répandu.

Bravo pour cette démarche, d'être autodidacte, que j'encourage ;)
Répondre