%BITAND
ET logique bit à bit
Compare deux valeurs (ou plus) bit par bit et ne garde que les bits qui valent 1 dans toutes : idéal pour tester ou isoler des indicateurs codés dans un entier ou dans un octet.
Syntaxe
%BITAND(expression1 : expression2)
%BITAND(expression1 : expression2 : expression3 …)
expression1, expression2…- Les valeurs à combiner : des entiers ou des zones caractère. Il en faut au moins deux ; on peut en donner davantage.
- Résultat
- Pour des entiers : le nombre dont seuls les bits communs à tous les opérandes sont à 1. Pour des zones caractère : une chaîne aussi longue que le plus long opérande, calculée octet par octet.
À quoi ça sert
Beaucoup de programmes anciens rangent plusieurs indicateurs dans un seul entier ou dans un octet : un bit par droit d'accès, par option, par état. %BITAND est l'outil pour lire un de ces bits : on l'applique avec une constante qui n'a que ce bit à 1 (un « masque »).
On le retrouve aussi avec les API système et les zones d'attributs, où l'information est fournie sous forme de bits.
Exemple 1 : Tester des droits codés dans un entier
**free
// %BITAND : tester des droits codes dans un entier (un bit par droit).
ctl-opt dftactgrp(*no) actgrp(*new);
dcl-c LECTURE 1;
dcl-c ECRITURE 2;
dcl-c SUPPRESS 4;
dcl-c ADMIN 8;
dcl-s droits uns(10) inz(11); // 8 + 2 + 1 : admin, ecriture, lecture
dcl-s masque uns(10);
snd-msg 'Droits : [' + %char(droits) + ']';
snd-msg 'Lecture ? : [' + %char(%bitand(droits : LECTURE)) + ']';
snd-msg 'Suppression ? : [' + %char(%bitand(droits : SUPPRESS)) + ']';
snd-msg 'Admin ? : [' + %char(%bitand(droits : ADMIN)) + ']';
if %bitand(droits : ECRITURE) <> 0;
snd-msg 'Ecriture autorisee';
endif;
if %bitand(droits : SUPPRESS) = 0;
snd-msg 'Suppression refusee';
endif;
// Plusieurs arguments : un bit est garde s'il est present dans tous
masque = %bitand(droits : 6 : 14);
snd-msg 'Droits & 6 & 14: [' + %char(masque) + ']';
*inlr = *on;
Résultat réel, compilé et exécuté sur IBM i 7.5 :
Droits : [11]
Lecture ? : [1]
Suppression ? : [0]
Admin ? : [8]
Ecriture autorisee
Suppression refusee
Droits & 6 & 14: [2]
Le mot de droits vaut 11, soit 8 + 2 + 1 : administration, écriture et lecture. Le masque LECTURE (1) renvoie 1, le masque SUPPRESS (4) renvoie 0 : ce droit n'est pas accordé. Le test se fait donc avec <> 0 ou = 0. Regardez la ligne « Admin » : le résultat est 8, pas 1.
Avec trois opérandes, seuls les bits présents dans les trois valeurs survivent : 11, 6 et 14 n'ont en commun que le bit de valeur 2.
Exemple 2 : Masques sur des octets
**free
// %BITAND sur des zones caracteres : masque applique 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 code char(4) inz(x'12345678');
dcl-s masque char(4) inz(x'FF00FF00');
dcl-s octet char(1) inz(x'F5');
dcl-s res4 char(4);
dcl-s res1 char(1);
dcl-s res3 char(3);
res1 = %bitand(octet : x'0F');
snd-msg 'F5 & 0F : ' + hex(%addr(res1) : 1);
res4 = %bitand(code : masque);
snd-msg '12345678 & FF00FF00 : ' + hex(%addr(res4) : 4);
// Longueurs differentes : que devient le resultat ?
res3 = %bitand(x'FFFFFF' : x'0F');
snd-msg 'FFFFFF & 0F : ' + hex(%addr(res3) : 3);
res3 = %bitand(x'AAAAAA' : x'0F');
snd-msg 'AAAAAA & 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 :
F5 & 0F : 05
12345678 & FF00FF00 : 12005600
FFFFFF & 0F : 0FFFFF
AAAAAA & 0F : 0AAAAA
Sur des zones caractère, le calcul se fait octet par octet : F5 avec le masque 0F donne 05, et 12345678 avec FF00FF00 donne 12005600. La procédure hex de l'exemple (basée sur l'API cvthc) sert seulement à afficher les octets en hexadécimal.
Dernières lignes : quand les opérandes n'ont pas la même longueur, le résultat garde la longueur du plus long et les octets qui n'ont pas de vis-à-vis sont recopiés tels quels (AAAAAA et 0F donnent 0AAAAA).
Le piège
Le résultat n'est pas un booléen. Il vaut la valeur du bit testé, pas 1. Avec le masque ADMIN (8), le test renvoie 8 quand le droit est accordé. Écrire if %bitand(droits : ADMIN) = 1 échoue donc pour tout masque autre que 1. Comparez toujours à zéro : <> 0 pour « le bit est présent ».
Autre surprise avec des zones caractère de longueurs différentes : les octets du plus long opérande qui n'ont pas de vis-à-vis passent tels quels dans le résultat, ils ne sont pas mis à zéro.