Articoli Manifesto Tools Links Canali Libri Contatti ?
Abstract
Tra gli operatori booleani normalmente conosciuti viene a volte tralasciato lo XOR. Questo operatore ben conosciuto da chi si occupa di sicurezza, è un elemento molto più interessante di quel che si può pensare. Proviamo a curiosarci un pò.
Data di stesura: 12/10/2007
Data di pubblicazione: 18/10/2007
Ultima modifica: 18/10/2007
di Stefano Fago Discuti sul forum   Stampa

XOR, operatore booleano

La tabella di verità dello XOR o OR Esclusivo, è nella forma:

T T F
T F T
F T T
F F F

in generale, quindi, quando i due operandi sono uguali si ottiene un FALSE altrimenti un TRUE.

È possibile esprimere lo XOR in funzione degli altri operatori più conosciuti nella forma:

(a OR b) AND (NOT(a AND b))

Usi (quasi) pratici dello XOR

È possibile usare questo operatore in diverse situazioni pratiche anche se spesso è possibile farne a meno, preferendo forme più esplicite e meno efficienti a favore della legibilità.

L'operatore XOR viene può essere usato nelle tecniche di bit masking per attuare il flip di un bit

Ad esempio, supponiamo di avere l'espressione,in pseudo codice:

if ( condition ){
  toggle = NOT toggle;
}

È possibile scrivere la stessa cosa nella forma:

toggle = toggle XOR condition;

Un'altro statement analogo, dati tre element a, b e x, è il seguente:

if(x==a) x=b
else x=a

e può essere tradotto nella formula:

x = a XOR b XOR x

Infatti sostituendo all'elemento x, l'elemento a si ottiene x= a xor a xor b = b; avendo x=b si otterrebbe x = b xor b xor a = a.

Uno uso, in passato, più conosciuto è quello dello Swap tra interi, per cui è possibile attuare lo scambio di due valori numerici senza usare una variabile di appoggio; normalmente infatti avremmo:

opA = 5
opB = 3

tmp = opA
opA = opB
opB = tmp

utilizzando lo XOR, risulterà:

opA = opA xor opB
opB = opA xor opB
opA = opA xor opB

Possiamo vedere la differenza tra i due approcci con del codice d'esempio

  1. class IntHolder { 
  2.   public int value; 
  3.  
  4.   public IntHolder() { 
  5.     this(0); 
  6.  
  7.   public IntHolder(int value) { 
  8.     this.value = value; 
  9.  
  10.   public String toString() { 
  11.     return String.valueOf(value); 
  12.  
  13. // ... 
  14.  
  15. public static void normalSwap(IntHolder a, IntHolder b) { 
  16.   int valA = a.value; 
  17.   int valB = b.value; 
  18.   int swap = valA; 
  19.  
  20.   valA = valB; 
  21.   valB = swap; 
  22.  
  23.   a.value = valA; 
  24.   b.value = valB; 
  25.  
  26. public static void xorSwap(IntHolder a, IntHolder b) { 
  27.   int valA = a.value; 
  28.   int valB = b.value; 
  29.  
  30.   valA = valA ^ valB; 
  31.   valB = valA ^ valB; 
  32.   valA = valA ^ valB; 
  33.   a.value = valA; 
  34.   b.value = valB; 

XOR e strutture dati

Nell'ambito delle strutture dati è da citarsi la XOR Linked-List. Questa è una particolare variante della Double Linked-List, sviluppata per diminuire lo spazio occupato in memoria, sfruttando l'aritmetica dei puntatori e lo XOR: è possibile infatti memorizzare l'indirizzo dell'elemento precedente e dell'elemento successivo in un unico campo a differenza del normale approccio in cui sono necessari due puntatori. I difetti di questa struttura dati sono molteplici e di fatto la rendono utilizzabile in ambiti ridotti.

In Java è possibile simulare questa struttura ricreando i puntatori con la classe sun.misc.Unsafe come nel codice che segue:

  1. import sun.misc.Unsafe; 
  2. import java.lang.reflect.*; 
  3.  
  4. public class PointerUtils { 
  5.   // 
  6.   private static Unsafe unsafe; 
  7.  
  8.   // 
  9.   private static int fieldOffset; 
  10.  
  11.   // 
  12.   private static PointerUtils instance = new PointerUtils(); 
  13.  
  14.   static { 
  15.     try { 
  16.       Class objectStreamClass = Class.forName("java.io.ObjectStreamClass$FieldReflector"); 
  17.       Field unsafeField = objectStreamClass.getDeclaredField("unsafe"); 
  18.  
  19.       unsafeField.setAccessible(true); 
  20.       unsafe = (Unsafe) unsafeField.get(null); 
  21.       fieldOffset = unsafe.fieldOffset(PointerUtils.class.getDeclaredField("obj")); 
  22.     } catch (Exception ex) { 
  23.       throw new RuntimeException(ex); 
  24.   }; 
  25.  
  26.  
  27.   // 
  28.   private Object obj; 
  29.  
  30.  
  31.   public synchronized static long toAddress(Object o) { 
  32.     instance.obj = o; 
  33.  
  34.     return unsafe.getLong(instance, fieldOffset); 
  35.  
  36.   public synchronized static Object toObject(long address) { 
  37.     unsafe.putLong(instance, fieldOffset, address); 
  38.  
  39.     return instance.obj; 
  40. }// END 

Un Java XORLinkedList può essere la seguente:

  1. import java.util.*; 
  2.  
  3. public class XORLinkedList { 
  4.   // 
  5.   private LLEntry head, tail; 
  6.  
  7.   // 
  8.   private int size = 0; 
  9.  
  10.  
  11.   public Iterator iterateForward() { 
  12.     return new LLIterator(head); 
  13.  
  14.   public Iterator iterateReverse() { 
  15.     return new LLIterator(tail); 
  16.  
  17.   public void addAtTail(Object o) { 
  18.     if (tail == null) { 
  19.       head = tail = new LLEntry(o, 0); 
  20.     } else { 
  21.       LLEntry e = new LLEntry(o, PointerUtils.toAddress(tail)); 
  22.       tail.pointers = tail.pointers ^ PointerUtils.toAddress(e); 
  23.       tail = e; 
  24.  
  25.   public void addAtHead(Object o) { 
  26.     if (head == null) { 
  27.       head = tail = new LLEntry(o, 0); 
  28.     } else { 
  29.       LLEntry e = new LLEntry(o, PointerUtils.toAddress(head)); 
  30.       head.pointers = head.pointers ^ PointerUtils.toAddress(e); 
  31.       head = e; 
  32.  
  33.   public static class LLEntry { 
  34.     public Object data; 
  35.     public long pointers; 
  36.  
  37.     public LLEntry(Object data, long pointers) { 
  38.       this.data = data; 
  39.       this.pointers = pointers; 
  40.  
  41.   public static class LLIterator implements Iterator { 
  42.     private long lastPtr; 
  43.     private LLEntry currentEntry; 
  44.  
  45.     public LLIterator(LLEntry head) { 
  46.       lastPtr = 0; 
  47.       currentEntry = head; 
  48.  
  49.     public boolean hasNext() { 
  50.       return currentEntry != null; 
  51.  
  52.     public Object next() { 
  53.       long nextPtr = currentEntry.pointers ^ lastPtr; 
  54.       lastPtr = PointerUtils.toAddress(currentEntry); 
  55.       Object data = currentEntry.data; 
  56.  
  57.       if (nextPtr == 0) { 
  58.         currentEntry = null; 
  59.       } else { 
  60.         currentEntry = (LLEntry) PointerUtils.toObject(nextPtr); 
  61.  
  62.       return data; 
  63.  
  64.     public void remove() { 
  65.       throw new UnsupportedOperationException(); 
  66.  
  67.  
  68.   public static void main(String[] args) { 
  69.     XORLinkedList l = new XORLinkedList(); 
  70.  
  71.     l.addAtTail("a"); 
  72.     l.addAtTail("b"); 
  73.     l.addAtTail("c"); 
  74.     l.addAtTail("d"); 
  75.  
  76.     System.err.println("forward..."); 
  77.  
  78.     for (Iterator i = l.iterateForward(); i.hasNext();) { 
  79.       System.err.println("got: " + i.next()); 
  80.  
  81.     System.err.println("reverse..."); 
  82.  
  83.     for (Iterator i = l.iterateReverse(); i.hasNext();) { 
  84.       System.err.println("got: " + i.next()); 
  85. }//END 

XOR ed encryption

Lo XOR può essere utilizzato per un semplice meccanismo di encryption, supponendo di avere una chiave formata da elementi effettivamente casuali. Se data[] è un array di byte di lunghezza N e supponiamo di avere una chiave formata da un array key[] della stessa lunghezza e del tutto casuale nella composizione, possiamo utilizzare l'algoritmo che segue:

byte[] e = new byte[data.length];
  
for ( int idx=0; idx<data.length; idx++ ){
  e[idx] = (byte)( a[idx] ^ k[idx] );
}

È ora possibile ottenere i dati originali applicando sempre lo XOR a quanto trasmesso; è evidente la necessità dello scambio della chiave grazie alla quale sarà possibile attuare il procedimento inverso a quello ora esposto. L'algoritmo è nella forma:

byte[] data = new byte[e.length];

for ( int idx=0; idx<e.length; idx++ ){
  data[idx] = (byte)( e[idx] ^ k[idx] );
}

Un soluzione di questo tipo non è adottabile in contesti reali, anche se l'operatore XOR è pesantemente usato in molti algoritmi di crypting tra cui DES.

XOR e numeri casuali

Un utilizzo interessante e con un buon riscontro pratico di questo operatore booleano è nella generazione di numeri casuali. Si deve ad un ricercatore, George Marsaglia, dell'università dello stato della Florida la formulazione di generatori di numeri casuali basati su operazioni binarie e precisamento utilizzando XOR e SHIFT. La velocità di generazione e la sufficiente resistenza alle collisioni di valori rende questa tecnica particolarmente efficace e discapito di tecniche che richiedono molta più memoria e potenza di calcolo. Un esempio di questa tecnica può essere il seguente, partendo da un seed dato dall'attuale time si sistema:

  1. public class XorShift { 
  2.   private static int base = (int) System.currentTimeMillis(); 
  3.   private int x = 0; 
  4.  
  5.   public XorShift() { 
  6.     x =((int) System.currentTimeMillis() + base); 
  7.  
  8.   public int next() { 
  9.     x ^= x << 6; 
  10.     x ^= x >>> 21; 
  11.     x ^= (x << 7); 
  12.  
  13.     return x; 

Come si vede la routine di generazione sfrutta operazioni di shift oltre allo XOR e quindi sfrutta operazioni proprie dei più comuni processori. Si pensi che sulla generazione di 100000000(cento milioni) di numeri non si verifica alcuna collisione, esplicitando una buona casualità.

Informazioni sull'autore

Stefano Fago, classe 1973. Diplomato in ragioneria, ha conseguito il Diploma di Laurea in Informatica con un progetto legato alle interfacce grafiche soft-realtime in Java. Dopo esperienze in Alcatel ed Elea, ha svolto attività di consulenza come Software Developer e Trainer alla ObjectWay S.p.A. sede di Milano. Attualmente Software Designer presso la sezione Innovazione e Attività Progettuali di BPU Banca. Appassionato del linguaggio Java e di tutte le tecnolgie Object Oriented. Polistrumentista dilettante.

È possibile consultare l'elenco degli articoli scritti da Stefano Fago.

Altri articoli sul tema Programmazione.

Discuti sul forum   Stampa

Cosa ne pensi di questo articolo?

Discussioni

Questo articolo o l'argomento ti ha interessato? Parliamone.