Classe utilitaire pour le traitement des chaînes de caractères
Par -Alexandre LEGOUT aka LAlex- le mercredi, novembre 19 2003, 12:57 - AS2 - Lien permanent
Voici une petit classe que j'ai développé pour y regrouper mes traitements sur les chaînes de caractères. Vous y trouverez les méthodes statiques replace, lTrim, rTrim, trim, stringToArray, arrayToString et reverse ...
Si vous avez des idées de méthodes (ou même des implémentations) à me suggérer, je suis preneur !!! ![]()
/*************************
* StringUtils.as v0.1
* Fonction "utilitaires" de traitement
* des chaînes de caractères
*-------------------
* LAlex
* <a href="http://www.lalex.com/">http://www.lalex.com/</a>
**************************/
class com.lalex.utils.StringUtils {
// Caractères considérés comme caractères d'espacement
private static var SPACE_CHARS:Array = [" ","
","
","\t"];
// StringUtils.replace
// Cherche une suite de caractères dans une chaine
// pour la remplacer par une autre
// ----------
// $str : Chaine de caractêres à traiter
// $search : Chaine de caractère à chercher
// $replace : Chaine de caractère de remplacement
public static function replace($str:String, $search:String, $replace:String):String {
// Debut de la recherche courante
var stsearch:Number = 0;
// Position trouvée
var fnd:Number = -1;
// Retour
var ret:String = "";
// Tant qu'on trouve la chaine recherchée
while ((fnd = $str.indexOf($search,stsearch)) != -1) {
// On rajoute du début de la recherche à la position
// trouvée, puis on rajoute la chaine de remplacement
ret += $str.slice(stsearch,fnd) + $replace;
// On déplace le début de la recherche suivante
stsearch = fnd+$search.length;
}
// On rajoute la fin de la chaine
ret += $str.slice(stsearch,$str.length);
// On retourne le résultat
return ret;
}
// StringUtils.lTrim
// On supprime tous les caractères d'espacement
// au début de la chaine de caractères.
public static function lTrim($str:String):String {
// Boucle infinie (arretée par les return)
while (true) {
// Si la chaine est vide, on la retourne
if ($str == "") {
return $str;
}
// On boucle sur les espaces
var numSpace:Number = SPACE_CHARS.length;
while (--numSpace >= 0) {
// Si un espace est trouvé, on l'enlève de la chaîne
// et on arrête la boucle.
if ($str.charAt(0) == SPACE_CHARS[numSpace]) {
$str = $str.slice(1);
break;
}
}
// Si numSpace est négatif, la boucle entière a étée
// faite, donc aucun espace n'est trouvé.
// On retourne donc la chaine.
if (numSpace < 0) {
return $str;
}
}
}
// StringUtils.rTrim
// On supprime tous les caractères d'espacement
// à la fin de la chaine de caractères.
public static function rTrim($str:String):String {
// On stocke la longueur de la chaine
// pour éviter d'y accéder plusieurs fois
var len:Number = $str.length-1;
// Boucle infinie (arretée par les return)
while(true) {
if ($str == "") {
return $str;
}
// Si la chaine est vide, on la retourne
var numSpace:Number = SPACE_CHARS.length;
while (--numSpace >= 0) {
// Si un espace est trouvé, on l'enlève de la chaîne
// et on arrête la boucle. On retire 1 à la longueur
// de la chaîne.
if ($str.charAt(len) == SPACE_CHARS[numSpace]) {
$str = $str.slice(0,-1);
len--;
break;
}
}
// Si numSpace est négatif, la boucle entière a étée
// faite, donc aucun espace n'est trouvé.
// On retourne donc la chaine.
if (numSpace < 0) {
return $str;
}
}
}
// StringUtils.trim
// On supprime tous les caractères d'espacement
// au début et à la fin de la chaine de caractères.
public static function trim($str:String):String {
return rTrim(lTrim($str));
}
// StringUtils.stringToArray
// Convertit une chaine de caractères en tableau
// contenant un caractère par index.
public static function stringToArray($str:String):Array {
return $str.split("");
}
// StringUtils.arrayToString
// Transforme un tableau en chaine de caractère
// constituée de la concatenation de ses éléments.
public static function arrayToString($ar:Array):String {
return "" + $ar.join("");
}
// StringUtils.reverse
// Inverse l'ordre des caractères d'une chaine
public static function reverse($str:String):String {
var rev:String = "";
var i:Number = $str.length;
while (--i >= 0) {
rev += $str.charAt(i);
}
return rev;
/*
--- Autre implémentation
--- sans doute moins rapide
var ar:Array = $str.split("");
ar.reverse();
return ar.join(""); */
}
}
Commentaires
Salut, qlques idées pour ta classe qui a l'air prométeuse.
count ( compte le nombre de lettres ou +ieurs lettres )
replace ( remplace lettre ou +ieurs lettres )
upperFirst ( Mettre la premiere lettre en uppercase. Pour le 1er mot ou pour chaque mot )
isEmail ( toujours pratique )
etc ( crée des '...' après x lettres avec option snap en fin de mot )
isLowerCase ( test si les caractères lettres de la chaine est majoritairemennt en lowerCase. Si oui retourne true )
isUpperCase ( l'inverse )
Je sais pas si tu veux mettre des convertions dans cette Class, mais tu pourrais mettre une conversion String à couleur
en fait, c'est juste un parseInt mais comme je vois que tu refais le split lol
neo >> Oui, j'ai refait le split, mais c'est parce que j'avais fait une autre implémentation du stringToArray, et je me suis rendu compte juste avant de la publier que on pouvait faire avec un split ... et j'ai eu la flemme de l'enlever !
Sinon pour la couleur, je l'aurais plus mis dans une classe ColorUtils ... 8|
*envois de sourire*
non juste comme ça
Salut,
Jolis petits morceaux de code :). J'avais fait pas mal d'utilitaires string aussi dans le temps alors j'ai quelques commentaires.
Je crois que depuis le Flash player 6, le plus rapide pour "replace" de faire:
s.split(old).join(new);
Ca supporte les characteres multiples aussi.
Pour les fonctions trim, ltrim, rtrim, tu peux faire quelques ameliorations en virant les les boucles while sur l'array SPACE_CHARS, en faisant:
, alors tu peux gagner encore un peu de vitesse en ne declarant meme pas d'object SPACE_CHARS et en ayant tes condition comme ca:
// cree les chars a trimmer dans un object et pas un array// ca devrait aller plus vite de maniere notable. Un autre truc que tu peux faire, c'est de travailler sur les indexes et ne faire le splice qu'a la fin, au lieu de le faire dans la boucle. Encore un autre truc c'est que si tu ne t'interesses qu'aux characteres speciaux genre , \t, ,SPACE_CHARS = {};
SPACE_CHARS[" "] = true;
SPACE_CHARS["
"] = true;
SPACE_CHARS["
"] = true;
SPACE_CHARS["\t"] = true;
// et apres sans boucle while, tes conditions deviennent
if (SPACE_CHARS[$str.charAt(0)]) {...}
if (SPACE_CHARS[$str.charAt(len)]) {...}
if ($str.charCodeAt(0) <= 32) {...}if ($str.charCodeAt(len) <= 32) {...}
Et pour le reverse, je te conseille de tester ton code avec l'array parce que si ca se trouve c'est plus rapide :).
A+
timoth'
Ouah, le code pour le replace est excellent !
J'y avais pas pensé une seule seconde
s.split(old).join(new);Pour le reverse, je ne sais pas, parce que celui-ci implique le parcours de 3 tableaux, contre un seul parcours de chaine dans l'implémentation actuelle. Mais comme les tableaux sont optimisés dans le player 7 (soi-disant), c'est peut-être plus rapide ...
Sinon, l'avantage de mon while sur le tableau, c'est deja que la classe est facilement configurable pour ce qui est des caractères d'espacement (pour ceux qui veulent uniquement supprimer les retrous à la ligne par exemple), et en plus si on tombe sur un des caractères d'espacement, la boucle s'arrête, et donc ne fait pas les autres tests ... 8| Et pour le splice à la fin, c'est trés futé aussi !
Merci !
Ouaip, je vois bien ce que tu veux dire. En fait, c'est l'avantage d'etre configurable par rapport au "< 32" ou la tu ne peut plus choisir. Utiliser un object a la place d'un tableau te laisse cette configurabilite mais c'est plus efficace :). Ton code serait en fait comme ca (je ne change rien d'autre a part qu'on se met d'accord que SPACE_ARRAY est un object et pas un array, apres a toi de faire le boulot sur les indexes ;)):
public static function lTrim($str:String):String {while (true) {
if ($str == "") {
return $str;
}
if (SPACE_CHARS[$str.charAt(0)]) {
$str = $str.slice(1);
} else {
return $str;
}
}
}
J'espere que ca donne une meilleur idee de ce que je voulais dire. Et puis si tu veux etre configurable, tu devrais aussi passer les characteres a trimmer en parametre de la fonction, ce sera plus propre que de changer la classe pour configurer ton array.
Timoth'
Ou mieux:
public static function lTrim($str:String):String {while ($str.length) {
if (SPACE_CHARS[$str.charAt(0)]) {
$str = $str.slice(1);
} else {
return $str;
}
}
return ""
}
Merci Thimot', c'est pas bête ca !!!
Avec la liste des charactères par défaut en variable statique privée (si on veut pas les passer à chaque fois ...) 
Sinon, j'avais pensé passer une chaîne de caractères contenant les espaces justement, et faire un
if ($space.indexOf($str.charAt(0)) > 0) { ... }Ca faciliterait le passage des charactères en paramètres, mais en terme de performances, je sais pas ce qua ca donnerait ... :? Qu'est-ce que tu en penses ?Ou alors, décomposer la chaine recue en paramètre pour la mettre dans un objet ... 8|
Je suis pas persuadé pour le
while($str.length) { ... }Ca fait un appel à la propriété length a chaque boucle, ce qui peut ralentir le code ... 8|C'est vrai. L'avantage, c'est le test en soit est gere automatiquement. Hmm, de toute facon, tu peux toujours utiliser ton test d'origine, le but c'est de virer un des deux tests, celui de "while (true)" PLUS celui de "if ($str == "")". Donc tu peux aussi faire:
while($str != "") { ... }Seul des tests pourront te dire lequel est le plus rapide. Je dois avouer que je n'ai pas fait de tests non plus mais j'avais juste au reflechis au bytecode, je pensais qu'il y aurait moins d'instruction bytecode pour $str.length. En fait c'est pratiquement pareil et en plus c'est des instructions pas lourde pour le deuxieme cas. Des tests, faut faire des tests :D:// while($str.length) { ... } se traduit par:Ce serait -1 plutot non ? Ca me semble plutot lourd, je dois dire. Moi je suis plutot d'avis de formatter un object une fois et de l'utiliser dans la boucle plutot que de faire une recherche a chaque fois dans la boucle. A la rigueur, tu peux aussi demander a tes utilisateurs de passer un object pre-formatte a la fonction, genre:label1:
push r:$str, 'length'
getMember
not
branchIfTrue label2
...
branch label1
label2:
// while($str != "") { ... } se traduit par:
label1:
push r:$str, ''
equals
not
not
branchIfTrue label2
...
branch label1
label2:
o = {};o[" "] = true; // on ne supprime que les espaces
myString = " Hello ";
myString = com.lalex.utils.StringUtils.lTrim(myString, o); //go!
Comme ca, il pourront meme reutiliser cet objet plusieurs fois pour different trim.
Tant qu'on y est, au lieu de faire une classe avec que des methodes static, tu peux faire une classe que tu instancies et dans laquel tu peux specifier les characteres blancs qui t'interessent. Tu peux meme ajouter quelques methodes comme setAsBlankSpace(aCharacter) ou removeBlankSpace(aCharacter). Du coup ton utilisateur, instancie la classe avec les parametres qu'il veut (ou les defaults que tu as deja) et apres il peut faire tous ses trim rapidement sans avoir a respecifier quoi que ce soit.
C'est juste une idee
Timoth' =
Voila une nouvelle implémentation de lTrim, en fonction de tes remarques.
// StringUtils.lTrim// On supprime tous les caractères d'espacement
// au début de la chaine de caractères.
// Le deuxième paramètre contient les caractères qui seront considérés
// comme des espaces. Si aucun n'est transmis à la fonction, les espaces
// par défaut sont utilisés.
public static function lTrim($str:String,$space:String):String {
// Objet ayant pour propriétés les caractères d'espacements
var spObj:Object = new Object();
// Si aucun caractères n'est spécifié, utilise
// les caractères par défaut.
if (!$space.length) {
$space = DEFAUTL_SPACECHARS;
}
// Remplit l'objet
var spLen:Number = $space.length;
while(--spLen >= 0) {
spObj[$space.charAt(spLen)] = true;
}
// Longueur de la chaine restante
var strLen:Number = $str.length;
// Position actuelle
var strPos:Number = 0;
// Tant que la chaine n'est pas vide
while (strLen > 0) {
// Si on ne trouve pas un espace, on arrete la boucle
if (!spObj[$str.charAt(strPos)]) {
break;
}
// On avance d'une position
strPos++;
// On raccourcit "virtuellement" la chaine
strLen--;
}
// On retourne la chaine sans les caractères trouvés.
return $str.slice(strPos);
}
Par contre, je préfère malgré tout garder une classe composée de méthodes statiques ... car ce type de classe n'a pas vraiment de raison d'être instanciée, à moins qu'on veuille faire pleins de trims avec des paramètres différents, mais c'est quand même assez rare. Les caractères par défaut sont quand même ceux qui sont le plus souvent utilisés ...
Je pense par contre faire une méthode statique qui va modifier la propriété statique, si un utilisateur a besoin d'avoir tout le temps les même caractères, mais pas les mêmes que ceux par défaut. Ca lui évite de passer les caractères en paramètre à chaque fois ... :roll:
Salut
Beau boulot !!
Juste un question vu que je n'ai ni flash MX 2004 ni de connaissances en Java (qui ressemble a l'AS2).
Y a t'il un caractere le fin de chaine pour les string ? Comme en C ou le caractere de fin est '\0' ? Car ceci eviterait de tester la taille de ta chaine dans les boucle.
A+
Euh ... il n'y en a pas à ma connaissance ... 8| Et puis comme tu le vois, je n'accède qu'une seule fois à la longueur de la chaine. Ensuite, je la recalcule cette longueur par décrémentation, étant donné que je supprime les caractères un par un ...
sinon, j'ai le cryptage md5 en AS si ca t'interesses
neo >> Oui, ca m'interesse !!!
Hello
pour le MD5 .. je sais pas si c'est le même :
/*
utilisation :* A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
* Digest Algorithm, as defined in RFC 1321.
* Copyright (C) Paul Johnston 1999 - 2000.
* Updated by Greg Holt 2000 - 2001.
* See <a href="http://pajhome.org.uk/site/legal.html" rel="nofollow">http://pajhome.org.uk/site/legal.html</a> for details.
*/
/*
* Convert a 32-bit number to a hex string with ls-byte first
*/
var hex_chr = "0123456789abcdef";
//
// somehow the expression (bitAND(b, c) | bitAND((~b), d)) didn't return coorect results on Mac
// for:
// b&c = a8a20450, ((~b)&d) = 0101c88b, (bitAND(b, c) | bitAND((~b), d)) = a8a20450 <-- !!!
// looks like the OR is not executed at all.
//
// let's try to trick the P-code compiler into working with us... Prayer beads are GO!
//
function bitOR(a, b) {
var lsb = (a & 0x1) | (b & 0x1);
var msb31 = (a >>> 1) | (b >>> 1);
// trace("MD5 OR #1: a,b = "+rhex(a)+", "+rhex(b)+", (a|b) = "+rhex((a | b))+", return = "+rhex((msb31 << 1) | lsb));
return (msb31 << 1) | lsb;
// return (a | b);
}
//
// will bitXOR be the only one working...?
// Nope. XOR fails too if values with bit31 set are XORed.
//
// Note however that OR (and AND and XOR?!) works alright for the statement
// (msb31 << 1) | lsb
// even if the result of the left-shift operation has bit 31 set.
// So there might be an extra condition here (Guessmode turned on):
// Mac Flash fails (OR, AND and XOR) if either one of the input operands has bit31 set
// *and* both operands have one or more bits both set to 1. In other words: when both
// input bit-patterns 'overlap'.
// Stuff to munch on for the MM guys, I guess...
//
function bitXOR(a, b) {
var lsb = (a & 0x1) ^ (b & 0x1);
var msb31 = (a >>> 1) ^ (b >>> 1);
// trace("MD5 XOR #1: a,b = "+rhex(a)+", "+rhex(b)+", (a^b) = "+rhex((a ^ b))+", return = "+rhex((msb31 << 1) ^ lsb));
return (msb31 << 1) | lsb;
// return (a ^ b);
}
//
// bitwise AND for 32-bit integers. This uses 31 + 1-bit operations internally
// to work around bug in some AS interpreters. (Mac Flash!)
//
function bitAND(a, b) {
var lsb = (a & 0x1) & (b & 0x1);
var msb31 = (a >>> 1) & (b >>> 1);
return (msb31 << 1) | lsb;
// return (a & b);
}
//
// Add integers, wrapping at 2^32. This uses 16-bit operations internally
// to work around bugs in some AS interpreters. (Mac Flash!)
//
function addme(x, y) {
var lsw = (x & 0xFFFF)+(y & 0xFFFF);
var msw = (x >> 16)+(y >> 16)+(lsw >> 16);
// trace("MD5 ADDME #1: x,y = "+rhex(x)+", "+rhex(y)+", msw, lsw = "+rhex(msw)+", "+rhex(lsw));
return (msw << 16) | (lsw & 0xFFFF);
// return (a + b);
}
function rhex(num) {
str = "";
for (var j = 0; j <= 3; j++) {
str += hex_chr.charAt((num >> (j * 8 + 4)) & 0x0F) +
hex_chr.charAt((num >> (j * 8)) & 0x0F);
}
return str;
}
/*
* Convert a string to a sequence of 16-word blocks, stored as an array.
* Append padding bits and the length, as described in the MD5 standard.
*/
function str2blks_MD5(str) {
nblk = ((str.length + <img src="http://common.lalex.com/themes/devblog/smilies/icon_cool.gif" alt="8)" class="smiley" /> >> 6) + 1; // 1 + (len + 8)/64
blks = new Array(nblk * 16);
for( var i = 0; i < nblk * 16; i++) blks[i] = 0;
/*
Input:
'willi' without the quotes.
trace() Output on Intel (and MAC now?):
see TXT files: *.Output.txt
*/
for(var i = 0; i < str.length; i++) {
blks[i >> 2] |= str.charCodeAt(i) << (((str.length * 8 + i) % 4) * 8);
// trace("str2blks_MD5: chr["+i+"] = "+str.charCodeAt(i)+",shift:"+(((str.length * 8 + i) % 4)*8)+",shifted:"+rhex(blks[i >> 2]));
}
blks[i >> 2] |= 0x80 << (((str.length * 8 + i) % 4) * 8);
// trace("str2blks_MD5: END - idx:"+i+" = "+0x80+",shift:"+(((str.length * 8 + i) % 4)*8)+",shifted:"+rhex(blks[i >> 2]));
// blks[nblk * 16 - 2] = str.length * 8;
var l = str.length * 8;
blks[nblk * 16 - 2] = (l & 0xFF);
blks[nblk * 16 - 2] |= ((l >>> <img src="http://common.lalex.com/themes/devblog/smilies/icon_cool.gif" alt="8)" class="smiley" /> & 0xFF) << 8;
blks[nblk * 16 - 2] |= ((l >>> 16) & 0xFF) << 16;
blks[nblk * 16 - 2] |= ((l >>> 24) & 0xFF) << 24;
// trace("str2blks_MD5: LEN - idx*4:"+(nblk * 16 - 2)+" = "+rhex(blks[nblk * 16 - 2]));
return blks;
}
/*
* Bitwise rotate a 32-bit number to the left
*/
function rol(num, cnt) {
return (num << cnt) | (num >>> (32 - cnt));
}
/*
* These functions implement the basic operation for each round of the
* algorithm.
*/
function cmn(q, a, b, x, s, t) {
// trace("MD5 CMN #1: q = "+rhex(q)+", (a + q + x + t) = "+rhex(a + q + x + t)+", rol() = "+rhex(rol((a + q + x + t), s))+", return = "+rhex(rol((a + q + x + t), s) + b)+", returnALT = "+rhex(addme(rol((addme(addme(a, q), addme(x, t))), s), b)));
return addme(rol((addme(addme(a, q), addme(x, t))), s), b);
}
function ff(a, b, c, d, x, s, t) {
// trace("MD5 FF #1: a,b,c,d,x,s,t = "+rhex(a)+","+rhex(b)+","+rhex(c)+","+rhex(d)+","+rhex(x)+","+rhex(s)+","+rhex(t));
// trace("MD5 FF #2: b&c = "+rhex(bitAND(b, c))+", ((~b)&d) = "+rhex(bitAND((~b), d))+", cmn("+rhex(bitOR(bitAND(b, c), bitAND((~b), d)))+",...)");
return cmn(bitOR(bitAND(b, c), bitAND((~b), d)), a, b, x, s, t);
}
function gg(a, b, c, d, x, s, t) {
// trace("MD5 GG #1: a,b,c,d,x,s,t = "+rhex(a)+","+rhex(b)+","+rhex(c)+","+rhex(d)+","+rhex(x)+","+rhex(s)+","+rhex(t));
// trace("MD5 GG #2: b&d = "+rhex(bitAND(b, d))+", (c&(~d)) = "+rhex(bitAND(c, (~d)))+", cmn("+rhex(bitOR(bitAND(b, d), bitAND(c, (~d))))+",...)");
return cmn(bitOR(bitAND(b, d), bitAND(c, (~d))), a, b, x, s, t);
}
function hh(a, b, c, d, x, s, t) {
// trace("MD5 HH #1: a,b,c,d,x,s,t = "+rhex(a)+","+rhex(b)+","+rhex(c)+","+rhex(d)+","+rhex(x)+","+rhex(s)+","+rhex(t));
// trace("MD5 HH #2: (b^c) = "+rhex(b ^ c)+", (b^c^d) = "+rhex(b ^ c ^d)+", cmn("+rhex(bitXOR(bitXOR(b, c), d))+",...)");
return cmn(bitXOR(bitXOR(b, c), d), a, b, x, s, t);
}
function ii(a, b, c, d, x, s, t) {
// trace("MD5 II #1: a,b,c,d,x,s,t = "+rhex(a)+","+rhex(b)+","+rhex(c)+","+rhex(d)+","+rhex(x)+","+rhex(s)+","+rhex(t));
// trace("MD5 II #2: (b|(~d)) = "+rhex(bitOR(b, (~d)))+", (c^(b|(~d)) = "+rhex(c ^ bitOR(b, (~d)))+", cmn("+rhex(bitXOR(c, bitOR(b, (~d))))+",...)");
return cmn(bitXOR(c, bitOR(b, (~d))), a, b, x, s, t);
}
/*
* Take a string and return the hex representation of its MD5.
*/
function calcMD5(str) {
x = str2blks_MD5(str);
a = 1732584193;
b = -271733879;
c = -1732584194;
d = 271733878;
// trace("MD5INIT: a,b,c,d = "+rhex(a)+","+rhex(b)+","+rhex(c)+","+rhex(d));
var step;
for(var i = 0; i < x.length; i += 16) {
var olda = a ;
var oldb = b ;
var oldc = c ;
var oldd = d ;
step = 0;
// trace("MD5LOOP: i = "+i+", step = "+(++step)+", a,b,c,d = "+rhex(a)+","+rhex(b)+","+rhex(c)+","+rhex(d));
a = ff(a, b, c, d, x[i+ 0], 7 , -680876936);
// trace("MD5LOOP: i = "+i+", step = "+(++step)+", a,b,c,d = "+rhex(a)+","+rhex(b)+","+rhex(c)+","+rhex(d)+",x[i+ 0] = "+rhex(x[i+ 0]));
d = ff(d, a, b, c, x[i+ 1], 12, -389564586);
// trace("MD5LOOP: i = "+i+", step = "+(++step)+", a,b,c,d = "+rhex(a)+","+rhex(b)+","+rhex(c)+","+rhex(d)+",x[i+ 1] = "+rhex(x[i+ 1]));
c = ff(c, d, a, b, x[i+ 2], 17, 606105819);
// trace("MD5LOOP: i = "+i+", step = "+(++step)+", a,b,c,d = "+rhex(a)+","+rhex(b)+","+rhex(c)+","+rhex(d)+",x[i+ 2] = "+rhex(x[i+ 2]));
b = ff(b, c, d, a, x[i+ 3], 22, -1044525330);
// trace("MD5LOOP: i = "+i+", step = "+(++step)+", a,b,c,d = "+rhex(a)+","+rhex(b)+","+rhex(c)+","+rhex(d)+",x[i+ 3] = "+rhex(x[i+ 3]));
a = ff(a, b, c, d, x[i+ 4], 7 , -176418897);
d = ff(d, a, b, c, x[i+ 5], 12, 1200080426);
c = ff(c, d, a, b, x[i+ 6], 17, -1473231341);
b = ff(b, c, d, a, x[i+ 7], 22, -45705983);
// trace("MD5LOOP: i = "+i+", step = "+(++step)+", a,b,c,d = "+rhex(a)+","+rhex(b)+","+rhex(c)+","+rhex(d)+",x[i+ 4],x[i+ 5],x[i+ 6],x[i+ 7] = "+rhex(x[i+ 4])+","+rhex(x[i+ 5])+","+rhex(x[i+ 6])+","+rhex(x[i+ 7]));
a = ff(a, b, c, d, x[i+ 8], 7 , 1770035416);
d = ff(d, a, b, c, x[i+ 9], 12, -1958414417);
c = ff(c, d, a, b, x[i+10], 17, -42063);
b = ff(b, c, d, a, x[i+11], 22, -1990404162);
// trace("MD5LOOP: i = "+i+", step = "+(++step)+", a,b,c,d = "+rhex(a)+","+rhex(b)+","+rhex(c)+","+rhex(d)+",x[i+ 8],x[i+ 9],x[i+10],x[i+11] = "+rhex(x[i+ 8])+","+rhex(x[i+ 9])+","+rhex(x[i+10])+","+rhex(x[i+11]));
a = ff(a, b, c, d, x[i+12], 7 , 1804603682);
d = ff(d, a, b, c, x[i+13], 12, -40341101);
c = ff(c, d, a, b, x[i+14], 17, -1502002290);
b = ff(b, c, d, a, x[i+15], 22, 1236535329);
// trace("MD5LOOP: i = "+i+", step = "+(++step)+", a,b,c,d = "+rhex(a)+","+rhex(b)+","+rhex(c)+","+rhex(d)+",x[i+12],x[i+13],x[i+14],x[i+15] = "+rhex(x[i+12])+","+rhex(x[i+13])+","+rhex(x[i+14])+","+rhex(x[i+15]));
a = gg(a, b, c, d, x[i+ 1], 5 , -165796510);
d = gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
c = gg(c, d, a, b, x[i+11], 14, 643717713);
b = gg(b, c, d, a, x[i+ 0], 20, -373897302);
// trace("MD5LOOP: i = "+i+", step = "+(++step)+", a,b,c,d = "+rhex(a)+","+rhex(b)+","+rhex(c)+","+rhex(d)+",x[i+ 1],x[i+ 6],x[i+11],x[i+ 0] = "+rhex(x[i+ 1])+","+rhex(x[i+ 6])+","+rhex(x[i+11])+","+rhex(x[i+ 0]));
a = gg(a, b, c, d, x[i+ 5], 5 , -701558691);
d = gg(d, a, b, c, x[i+10], 9 , 38016083);
c = gg(c, d, a, b, x[i+15], 14, -660478335);
b = gg(b, c, d, a, x[i+ 4], 20, -405537848);
// trace("MD5LOOP: i = "+i+", step = "+(++step)+", a,b,c,d = "+rhex(a)+","+rhex(b)+","+rhex(c)+","+rhex(d)+",x[i+ 5],x[i+10],x[i+15],x[i+ 4] = "+rhex(x[i+ 5])+","+rhex(x[i+10])+","+rhex(x[i+15])+","+rhex(x[i+ 4]));
a = gg(a, b, c, d, x[i+ 9], 5 , 568446438);
d = gg(d, a, b, c, x[i+14], 9 , -1019803690);
c = gg(c, d, a, b, x[i+ 3], 14, -187363961);
b = gg(b, c, d, a, x[i+ 8], 20, 1163531501);
// trace("MD5LOOP: i = "+i+", step = "+(++step)+", a,b,c,d = "+rhex(a)+","+rhex(b)+","+rhex(c)+","+rhex(d)+",x[i+ 9],x[i+14],x[i+ 3],x[i+ 8] = "+rhex(x[i+ 9])+","+rhex(x[i+14])+","+rhex(x[i+ 3])+","+rhex(x[i+ 8]));
a = gg(a, b, c, d, x[i+13], 5 , -1444681467);
d = gg(d, a, b, c, x[i+ 2], 9 , -51403784);
c = gg(c, d, a, b, x[i+ 7], 14, 1735328473);
b = gg(b, c, d, a, x[i+12], 20, -1926607734);
// trace("MD5LOOP: i = "+i+", step = "+(++step)+", a,b,c,d = "+rhex(a)+","+rhex(b)+","+rhex(c)+","+rhex(d)+",x[i+13],x[i+ 2],x[i+ 7],x[i+12] = "+rhex(x[i+13])+","+rhex(x[i+ 2])+","+rhex(x[i+ 7])+","+rhex(x[i+12]));
a = hh(a, b, c, d, x[i+ 5], 4 , -378558);
d = hh(d, a, b, c, x[i+ 8], 11, -2022574463);
c = hh(c, d, a, b, x[i+11], 16, 1839030562);
b = hh(b, c, d, a, x[i+14], 23, -35309556);
// trace("MD5LOOP: i = "+i+", step = "+(++step)+", a,b,c,d = "+rhex(a)+","+rhex(b)+","+rhex(c)+","+rhex(d)+",x[i+ 5],x[i+ 8],x[i+11],x[i+14] = "+rhex(x[i+ 5])+","+rhex(x[i+ 8])+","+rhex(x[i+11])+","+rhex(x[i+14]));
a = hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
d = hh(d, a, b, c, x[i+ 4], 11, 1272893353);
c = hh(c, d, a, b, x[i+ 7], 16, -155497632);
b = hh(b, c, d, a, x[i+10], 23, -1094730640);
// trace("MD5LOOP: i = "+i+", step = "+(++step)+", a,b,c,d = "+rhex(a)+","+rhex(b)+","+rhex(c)+","+rhex(d)+",x[i+ 1],x[i+ 4],x[i+ 7],x[i+10] = "+rhex(x[i+ 1])+","+rhex(x[i+ 4])+","+rhex(x[i+ 7])+","+rhex(x[i+10]));
a = hh(a, b, c, d, x[i+13], 4 , 681279174);
d = hh(d, a, b, c, x[i+ 0], 11, -358537222);
c = hh(c, d, a, b, x[i+ 3], 16, -722521979);
b = hh(b, c, d, a, x[i+ 6], 23, 76029189);
// trace("MD5LOOP: i = "+i+", step = "+(++step)+", a,b,c,d = "+rhex(a)+","+rhex(b)+","+rhex(c)+","+rhex(d)+",x[i+13],x[i+ 0],x[i+ 3],x[i+ 6] = "+rhex(x[i+13])+","+rhex(x[i+ 0])+","+rhex(x[i+ 3])+","+rhex(x[i+ 6]));
a = hh(a, b, c, d, x[i+ 9], 4 , -640364487);
d = hh(d, a, b, c, x[i+12], 11, -421815835);
c = hh(c, d, a, b, x[i+15], 16, 530742520);
b = hh(b, c, d, a, x[i+ 2], 23, -995338651);
// trace("MD5LOOP: i = "+i+", step = "+(++step)+", a,b,c,d = "+rhex(a)+","+rhex(b)+","+rhex(c)+","+rhex(d)+",x[i+ 9],x[i+12],x[i+15],x[i+ 2] = "+rhex(x[i+ 9])+","+rhex(x[i+12])+","+rhex(x[i+15])+","+rhex(x[i+ 2]));
a = ii(a, b, c, d, x[i+ 0], 6 , -198630844);
d = ii(d, a, b, c, x[i+ 7], 10, 1126891415);
c = ii(c, d, a, b, x[i+14], 15, -1416354905);
b = ii(b, c, d, a, x[i+ 5], 21, -57434055);
// trace("MD5LOOP: i = "+i+", step = "+(++step)+", a,b,c,d = "+rhex(a)+","+rhex(b)+","+rhex(c)+","+rhex(d)+",x[i+ 0],x[i+ 7],x[i+14],x[i+ 5] = "+rhex(x[i+ 0])+","+rhex(x[i+ 7])+","+rhex(x[i+14])+","+rhex(x[i+ 5]));
a = ii(a, b, c, d, x[i+12], 6 , 1700485571);
d = ii(d, a, b, c, x[i+ 3], 10, -1894986606);
c = ii(c, d, a, b, x[i+10], 15, -1051523);
b = ii(b, c, d, a, x[i+ 1], 21, -2054922799);
// trace("MD5LOOP: i = "+i+", step = "+(++step)+", a,b,c,d = "+rhex(a)+","+rhex(b)+","+rhex(c)+","+rhex(d)+",x[i+12],x[i+ 3],x[i+10],x[i+ 1] = "+rhex(x[i+12])+","+rhex(x[i+ 3])+","+rhex(x[i+10])+","+rhex(x[i+ 1]));
a = ii(a, b, c, d, x[i+ 8], 6 , 1873313359);
d = ii(d, a, b, c, x[i+15], 10, -30611744);
c = ii(c, d, a, b, x[i+ 6], 15, -1560198380);
b = ii(b, c, d, a, x[i+13], 21, 1309151649);
// trace("MD5LOOP: i = "+i+", step = "+(++step)+", a,b,c,d = "+rhex(a)+","+rhex(b)+","+rhex(c)+","+rhex(d)+",x[i+ 8],x[i+15],x[i+ 6],x[i+13] = "+rhex(x[i+ 8])+","+rhex(x[i+15])+","+rhex(x[i+ 6])+","+rhex(x[i+13]));
a = ii(a, b, c, d, x[i+ 4], 6 , -145523070);
d = ii(d, a, b, c, x[i+11], 10, -1120210379);
c = ii(c, d, a, b, x[i+ 2], 15, 718787259);
b = ii(b, c, d, a, x[i+ 9], 21, -343485551);
// trace("MD5LOOP: i = "+i+", step = "+(++step)+", a,b,c,d = "+rhex(a)+","+rhex(b)+","+rhex(c)+","+rhex(d)+",x[i+ 4],x[i+11],x[i+ 2],x[i+ 9] = "+rhex(x[i+ 4])+","+rhex(x[i+11])+","+rhex(x[i+ 2])+","+rhex(x[i+ 9]));
a = addme(a, olda);
b = addme(b, oldb);
c = addme(c, oldc);
d = addme(d, oldd);
// trace("MD5LOOP: i = "+i+", step = "+(++step)+", a,b,c,d = "+rhex(a)+","+rhex(b)+","+rhex(c)+","+rhex(d));
}
// trace("MD5FINISH: a,b,c,d = "+rhex(a)+","+rhex(b)+","+rhex(c)+","+rhex(d));
return rhex(a) + rhex(b) + rhex(c) + rhex(d);
}