%BITXOR
OU exclusif bit à bit
Compare deux valeurs bit par bit et met à 1 les bits qui diffèrent : sert à basculer un indicateur, à repérer ce qui a changé entre deux états ou à masquer une donnée de façon réversible.
Syntaxe
%BITXOR(expression1 : expression2)
expression1, expression2- Les deux valeurs à comparer : des entiers ou des zones caractère.
- Résultat
- Pour des entiers : le nombre dont les bits sont à 1 quand ils diffèrent entre les deux opérandes. Pour des zones caractère : une chaîne de la longueur du plus long opérande, calculée octet par octet.
À quoi ça sert
Le OU exclusif a deux usages courants. D'abord basculer un bit : appliquer le même masque deux fois remet la valeur d'origine, comme un interrupteur. Ensuite comparer : le résultat vaut 0 si les deux valeurs sont identiques, et ses bits à 1 indiquent exactement ce qui a changé.
Il sert aussi à un masquage simple et réversible d'une donnée avec une clé : c'est de l'offuscation, pas du chiffrement.
Exemple 1 : Basculer un droit et repérer les différences
**free
// %BITXOR : inverser un droit (interrupteur) et trouver ce qui differe entre deux mots.
ctl-opt dftactgrp(*no) actgrp(*new);
dcl-c ECRITURE 2;
dcl-s droits uns(10) inz(3); // lecture + ecriture
dcl-s avant uns(10) inz(3);
dcl-s apres uns(10) inz(6); // ecriture + suppression
snd-msg 'Depart : [' + %char(droits) + ']';
droits = %bitxor(droits : ECRITURE);
snd-msg 'Bascule ecriture: [' + %char(droits) + ']';
droits = %bitxor(droits : ECRITURE);
snd-msg 'Bascule encore : [' + %char(droits) + ']';
// Les bits qui ont change entre deux etats : 3 -> 6
snd-msg 'Differences : [' + %char(%bitxor(avant : apres)) + ']';
// Deux valeurs identiques donnent zero
snd-msg 'Identiques : [' + %char(%bitxor(apres : apres)) + ']';
*inlr = *on;
Résultat réel, compilé et exécuté sur IBM i 7.5 :
Depart : [3]
Bascule ecriture: [1]
Bascule encore : [3]
Differences : [5]
Identiques : [0]
Le mot de droits vaut 3 (lecture et écriture). Un premier %bitxor avec 2 retire l'écriture (1), un second la remet (3). Entre les états 3 et 6, le résultat 5 indique que les bits de valeur 1 et 4 ont changé. Deux valeurs identiques donnent 0.
Exemple 2 : Masquage réversible d'une zone
**free
// %BITXOR sur des zones caracteres : OU exclusif octet par octet.
ctl-opt dftactgrp(*no) actgrp(*new);
// API C standard : convertit des octets en texte hexadecimal
dcl-pr cvthc extproc('cvthc');
cible pointer value;
source pointer value;
longueur int(10) value;
end-pr;
dcl-s message char(4) inz('ABCD');
dcl-s cle char(4) inz(x'5A5A5A5A');
dcl-s codee char(4);
dcl-s clair char(4);
dcl-s res3 char(3);
// Masquage reversible : appliquer deux fois la meme cle redonne l'original
codee = %bitxor(message : cle);
snd-msg 'Codee (hex) : ' + hex(%addr(codee) : 4);
clair = %bitxor(codee : cle);
snd-msg 'Redecodee : [' + clair + ']';
// Longueurs differentes
res3 = %bitxor(x'FFFFFF' : x'0F');
snd-msg 'FFFFFF ^ 0F : ' + hex(%addr(res3) : 3);
*inlr = *on;
dcl-proc hex;
dcl-pi *n varchar(16);
adresse pointer value;
longueur int(10) value;
end-pi;
dcl-s texte char(16);
cvthc(%addr(texte) : adresse : longueur * 2);
return %subst(texte : 1 : longueur * 2);
end-proc;
Résultat réel, compilé et exécuté sur IBM i 7.5 :
Codee (hex) : 9B98999E
Redecodee : [ABCD]
FFFFFF ^ 0F : F0FFFF
Le texte ABCD passé par le OU exclusif avec la clé 5A5A5A5A donne des octets illisibles (9B98999E en hexadécimal, les lettres étant codées en EBCDIC). Le même calcul avec la même clé redonne ABCD. La dernière ligne montre que, pour des longueurs différentes, les octets sans vis-à-vis sont recopiés tels quels (FFFFFF et 0F donnent F0FFFF).
Le piège
Les octets en trop ne sont pas remis à zéro. Avec des opérandes de longueurs différentes, on pourrait croire que le plus court est complété par des zéros et que le résultat s'annule au-delà : %bitxor(x'FFFFFF' : x'0F') renvoie F0FFFF, les deux derniers octets de la première valeur sont conservés. Alignez les longueurs si ce n'est pas le comportement voulu.