Month: February 2015

Il meccanismo di “string interning” nel .Net Micro Framework

Consideriamo la seguente semplicissima applicazione console in C# …

 static void Main(string[] args)
 {
   string s1 = "Hello world";
   string s2 = "Hello world";

   Console.WriteLine(s1.Equals(s2));
   Console.WriteLine(ReferenceEquals(s1, s2));
   Console.WriteLine(s1 == s2);

   string s3 = String.Intern(Console.ReadLine());

   Console.WriteLine(s1.Equals(s3));
   Console.WriteLine(ReferenceEquals(s1, s3));
   Console.WriteLine(s1 == s3);

   Console.ReadKey();
 }

Il cui output è il seguente …

string_intern_net

Come sappiamo il metodo Equals() e l’operatore “==” verificano che se le due stringhe contengono lo stesso valore pur avendo un reference diverso (s1 ed s2), per cui è ovvio il risultato “True”. Ma come mai la ReferenceEquals() continua a fornire “True” e non “False” considerando che i due riferimenti sono diversi ?

Bene ! Essi non sono diversi in realtà !

Il .Net Framework fornisce un meccanismo di “string interning” ossia un pool all’interno del quale gestisce le stringhe che hanno il medesimo valore evitando di allocare inutilmente ulteriore memoria. Per questo motivo, s1 ed s2 sono in realtà il medesimo riferimento !

E’ ovvio che, quando una stringa viene acquisita dall’esterno ciò non accade ed è quindi necessario chiedere esplicitamente al framework di cercare la stringa acquisita prima nel pool interno ed eventualmente allocarne una nuova se essa non esiste … questo è l’obiettivo dell’istruzione String.Intern().

Proviamo ora ad eseguire il medesimo codice (a meno di alcune modifiche) sul .Net Micro Framework …

 public static void Main()
 {
    string s1 = "Hello world";
    string s2 = "Hello world";
 
    Debug.Print(s1.Equals(s2).ToString());
    Debug.Print(ReferenceEquals(s1, s2).ToString());
    Debug.Print((s1 == s2).ToString());

    string s3 = String.Intern("Hello world");
    
    Debug.Print(s1.Equals(s3).ToString());
    Debug.Print(ReferenceEquals(s1, s3).ToString());
    Debug.Print((s1 == s3).ToString());
 }

con il seguente risultato …

string_intern_netmf

In questo caso il ReferenceEquals() ci conferma che i due reference sono diversi nel primo caso ma anche utilizzando String.Intern() … come mai ?

Il motivo è semplice … il .Net Micro Framework non supporta il meccanismo di “string interning” e non fornisce un pool interno di stringhe e la risposta è come sempre nell’implementazione nativa. Guardando nel file String.cs, troviamo :

 public static String Intern(String str)
 {
   // We don't support "interning" of strings. So simply return the string.
   return str;
 }

 public static String IsInterned(String str)
 {
   // We don't support "interning" of strings. So simply return the string.
   return str;
 }

Un esplicito commento ci dice che questa funzionalità non è supportata !

Creare il primo flow con Node-RED su HTML.it

html_nodered_primo_flow

Questa volta iniziamo a fare sul serio …

Il nuovo capitolo della guida su Node-RED ci mostra come realizzare il nostro primo semplice flow attraverso l’utilizzo dei primi nodi : inject, function e debug.

Il risultato non è esaltante (un tipico “Hello World”) ma inizia a rendere l’idea di quelle che sono le potenzialità dello strumento che abbiamo tra le nostre mani.

Ovviamente è solo l’inizio !

Regex portabile fra .Net e .Net Micro Framework … fate attenzione !

Sviluppando su tutte le versioni del .Net Framework (full, compact e micro) mi capita sempre di dover affrontare il problema della portabilità, evitando di utilizzare metodi che non siano disponibili in uno dei tre framework oppure utilizzando le direttive di precompilazione.

Non mi è mai capitato, però, un caso in cui il metodo esista in tutti i framework ma i parametri abbiano un significato diverso … siano addirittura invertiti !!

Il metodo in questione è l’overload di Regex.Split(String, String, RegexOptions) !

La firma è praticamente la stessa confrontando il .Net Framework ed il .Net Micro Framework ma …. con una notevole differenza nei primi due parametri.

Dalla documentazione MSDN del .Net Framework si legge che il primo parametro è la stringa da splittare mentre il secondo è il pattern da applicare.

01

Viceversa, nella documentazione MSDN del .Net Micro Framework si legge che il primo è il pattern ed il secondo è la stringa di input !!

02

Per assicurarmi che l’errore non fosse nella documentazione, ho cercato il codice sorgente del .Net Micro Framework in cui è implementato questo metodo ma purtroppo è arrivata la conferma !

 /// <summary>
 /// Splits the input string at the positions defined by a specified regular expression pattern. Specified options modify the matching operation.
 /// </summary>
 /// <param name="pattern">The pattern to match against</param>
 /// <param name="split">The string to split</param>
 /// <param name="options">The options to utilize during matching</param>
 /// <returns>The result of splitting the input string against the pattern</returns>
 public static string[] Split(string pattern, string split, RegexOptions options)
 {
   return new Regex(pattern, options).Split(split);
 }

Assolutamente strano … anche questa volta ho deciso di aprire una issue sul sito ufficiale del .Net Micro Framework su CodePlex !

HTML.it : Installare Node-RED

nodered0203

Dopo aver introdotto il concetto di flow-based programming nel corso del primo capitolo, è giunto il momento di installare Node-RED per poter iniziare a sviluppare i primi flussi per l’IoT !

Il secondo capitolo della guida pubblicata su HTML.it ci prende letteralmente per mano partendo dall’installazione di NodeJS per poi proseguire all’installazione di Node-RED fino alla configurazione ed all’avvio del runtime.

Al termine del capitolo abbiamo a disposizione la UI (all’interno del nostro browser) attraverso la quale poter sviluppare i flussi che vedremo nei capitoli successivi.

Codifica Base64 non standard nel .Net Micro Framework

Come sappiamo, per poter accedere ai servizi Azure, in molti casi è prevista la SAS (Shared Access Signature) Authentication mediante l’invio di un token attraverso il quale otteniamo i diritti per poter eseguire specifiche operazioni.

Tale token si ottiene mediante la costruzione di una stringa contenente una serie di informazioni tra cui l’URI a cui accedere e la scadenza (expiry time) sulla quale calcolare un HMAC (Hash Massage Authentication Code) con SHA256. Il risultato di questa operazione di hashing viene codificato in Base64 ed il risultato ottenuto viene inserito all’interno del token (Shared Access Signature) con un opportuno formato.

L’obiettivo di questo post non è quello di descrivere precisamente la procedura per la determinazione del token ma di mettere in guardia chi utilizza le funzioni di conversione in Base64 messe a disposizione dal .Net Micro Framework.

Eseguendo i miei test con una board FEZ Spider per la connessione al Service Bus, ho riscontrato molte volte la condizione di accesso non autorizzato a causa di un token non correttamente calcolato. Eseguendo la stessa procedura sul PC, tutto funzionava correttamente. Come mai ?

Inizialmente ho pensato ad un errore nel calcolo della firma (HMAC) e solo dopo ho capito che c’era qualcosa che non andava nella codifica Base64.

Sono finalmente riuscito ad estrarre un caso di errore a seguito della firma rappresentata attraverso i seguenti bytes :

byte[] hmac = { 0x16, 0x01, 0x70, 0x76, 0xec, 0xc8, 0xdb, 0x01, 0xf0, 0x6a, 0x60, 0x9a, 0x89, 0x68, 0x6f, 0xef, 0x68, 0x9a, 0xad, 0x10, 0xe7, 0x92, 0x9b, 0xef, 0xfa, 0x10, 0x86, 0x24, 0xf1, 0x72, 0xa6, 0x69 };

Se proviamo a codificare in Base64 l’array di bytes suddetto con il metodo  Convert.ToBase64String(hmac) su PC, il risultato è il seguente :

FgFwduzI2wHwamCaiWhv72iarRDnkpvv+hCGJPFypmk=

Se proviamo ad eseguire la medesima operazione con il .Net Micro Framework, la codifica è la seguente :

FgFwduzI2wHwamCaiWhv72iarRDnkpvv!hCGJPFypmk=

In rosso ed in grassetto ho evidenziato la differenza tra le due codifiche ma … qual’è il motivo ?

Come sempre, la risposta è nell’implementazione nativa del .Net Micro Framework e questa volta nel file Convert.cs all’interno del quale si evincono due “alfabeti” per la codifica Base64 : l’alfabeto standard RFC4648 ed un alfabeto non standard.

 /// <summary>
 /// Conversion array from 6 bit of value into base64 encoded character.
 /// </summary>
 static char[] s_rgchBase64EncodingDefault = new char[]
 {
 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', /* 12 */
 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', /* 24 */
 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', /* 36 */
 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', /* 48 */
 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', /* 60 */
 '8', '9', '!', '*' /* 64 */
 };

 static char[] s_rgchBase64EncodingRFC4648 = new char[]
 {
 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', /* 12 */
 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', /* 24 */
 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', /* 36 */
 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', /* 48 */
 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', /* 60 */
 '8', '9', '+', '/' /* 64 */
 };

 static char[] s_rgchBase64Encoding = s_rgchBase64EncodingDefault;

Questi due alfabeti differiscono per gli ultimi due caratteri che sono “+” e “/” nel primo ma “!” e “*” nel secondo. A quanto pare il carattere “!” è relativo alla codifica Base64 nelle regular expressions mentre “*” é relativo alla medesima codifica nel privacy-enhanced mail.

Dal codice si evince che l’alfabeto di default (s_rgchBase64Encoding) che viene utilzzato non è quello standard !!

Come è possibile risolvere il problema ?

Per fortuna, la classe Convert fornisce la proprietà statica UseRFC4648Encoding che va impostata a true e grazie alla quale viene utilizzato l’alfabeto standard RFC4648 per ogni codifica Base64.

La mia opinione è che sarebbe opportuno invertire la logica in modo che la codifica di default sia quella standard e per questo motivo ho già aperto una issue sul sito ufficiale del .Net Micro Framework su CodePlex.

Voi cosa ne pensate ?