2
\$\begingroup\$

I made simple JavaScript xor function. It accepts string, numeric array and mixed array (char, string, num) as params. It returns string or an array.

Returning an array is a must!. All numbers are printable on screen and there is no potential loss of information. String works excellent if you pass it in a variable. But if you want to print it on screen, there might be some unprintable characters in a string. Like \n \t \cr \lf, or whole array of unprintable unicode char's.

Also array as password is also needed because that's an easy way to have low numbers like [0,1,2,3...] which are not so easy to represent as a string.

My questions are the usual. What do you think of it? Does any improvement come in mind when you look at the code? Did I miss something? Any potential bugs?

/*
    source: string, [1,2,3,...], ['a', 'b', 'c', ...], [1, 'a', 2, 'b', ...]
    password: string, [1,2,3,...], ['a', 'b', 'c', ...], [1, 'a', 2, 'b', ...]
    return type str || []

*/

function xor(source, password, return_type) {
  var ret_ar = [],
    i, len,
    pwlen,
    pwindex = 0,
    s, p;
  if(typeof source === 'string') {
    source = source.split('');
  }
  if(Object.prototype.toString.call(source) === '[object Array]') {
    for(i = 0, len = source.length; i < len; ++i) {
      s = source[i];
      if(typeof s === 'string') {
        source[i] = s.charCodeAt(0);
      } else if(typeof s !== 'number') {
        console.log(s);
        throw("Only str or [number, 'c', 'h',...] in xor \n Bad first param in function xor:" + s);
      }
    }
  } else {
    throw('Very Bad first param in function xor:' + source);
  }
  if(typeof password === 'string') {
    password = password.split('');
  }
  if(Object.prototype.toString.call(password) === '[object Array]') {
    for(i = 0, len = password.length; i < len; ++i) {
      p = password[i];
      if(typeof p === 'string') {
        password[i] = p.charCodeAt(0);
      } else if(typeof p !== 'number') {
        console.log(p);
        throw("Only str or [number, 'c', 'h',...] in xor \n Bad second param in function xor:" + p);
      }
    }
  } else {
    throw('Very Bad second param in function xor:' + p);
  }
  pwindex = 0;
  pwlen = password.length - 1;
  len = source.length;
  if(pwlen >= 0) {
    for(i = 0; i < len; ++i) {
      ret_ar[i] = source[i] ^ password[pwindex]; // unefined ^7 === 7
      if(pwindex >= pwlen) pwindex = 0;
      else ++pwindex;
    }
  } else // ~source on '', undefined passwprd
  {
    for(i = 0; i < len; ++i) {
      ret_ar[i] = ~source[i]; // ~unefined === -1
    }
  }
  source = password = i = pwlen = pwindex = null;
  //debugger;
  if(typeof return_type === 'string') {
    // code -> char . join('')
    return ret_ar.map(function (code) {
      return String.fromCharCode(code)
    }).join('');
  } else {
    return ret_ar;
  }
} // ------------------------- end xor () ----------------------------

Here are tests for function if anyone need it.

out = document.getElementById('my_div');

var str = JSON.stringify(pitanja_ar);
var str = 'small test';
var pass = 'password';
var p_ar = [0,1,2,3,4,5];

out.innerHTML = '';
out.innerHTML += '1.<br> xor (str, pass, str):<br><br>';
var sss =  xor(str, pass, 'str');
out.innerHTML += sss;
out.innerHTML += '<br><br>Equal: ' + (str === xor(sss, pass, 'str'));

out.innerHTML += '<hr><br>2.<br> xor (str, pass, []):<br>';
var ssa = xor(str, pass, []);
out.innerHTML += ssa;
out.innerHTML += '<br><br>Equal: ' + (str === xor(ssa, pass, ''));

out.innerHTML += '<hr><br>3.<br> xor (str, p_ar, str):<br>';
var sas =  xor(str, p_ar, 'str');
out.innerHTML += sas;
out.innerHTML += '<br><br>Equal: ' + (str === xor(sas, p_ar, ''));

out.innerHTML += '<hr><br>4.<br> xor (str, p_ar, []):<br>';
var saa =  xor(str, p_ar, []);
out.innerHTML += saa;
out.innerHTML += '<br><br>Equal: ' + (str === xor(saa, p_ar, ''));

//
pitanja_ar = [1,2,3,4,5,6,7,8,9,10];
out.innerHTML += '<hr><br>5.<br> xor([], pass, str)<br>';
var ass = xor(pitanja_ar, pass, '');
out.innerHTML += ass;
var asa = xor(ass, pass, [])
out.innerHTML += '<br><br>Equal: ' + arrays_equal(pitanja_ar,asa);


out.innerHTML += '<hr><br>6.<br> xor([], pass, [])<br>';
var asa = xor(pitanja_ar, pass, []);
out.innerHTML += asa;
out.innerHTML += '<br><br>Equal: ' + arrays_equal(pitanja_ar,xor(asa, pass, []));



out.innerHTML += '<hr><br>7.<br> xor([], p_ar, str)<br>';
var aas =  xor(pitanja_ar, p_ar, '');
out.innerHTML += aas;
out.innerHTML += '<br><br>Equal: ' + arrays_equal(pitanja_ar, xor(aas, p_ar, []));


out.innerHTML += '<hr><br>8.<br> xor([], p_ar, [])<br>';
var aaa =  xor(pitanja_ar, p_ar, []);
out.innerHTML += aaa;
out.innerHTML += '<br><br>Equal: ' + arrays_equal(pitanja_ar, xor(aaa, p_ar, []));

//
\$\endgroup\$
  • \$\begingroup\$ Don't overload functions so horribly. An xor for only Strings would have been enough \$\endgroup\$ – copy May 4 '13 at 11:52
  • \$\begingroup\$ 1. What is horrible and why? 2. Returning a string is optional, returning an array is a must. There are many non printable chars! Number array is copy/paste friendly. \$\endgroup\$ – CoR May 4 '13 at 13:09
  • \$\begingroup\$ Writing functions that take many different types as its arguments is horrible. Just see how simple your function could be written if it only accepted Strings. Copy-paste friendlyness is hardly an argument for anything. \$\endgroup\$ – copy May 4 '13 at 15:00
  • \$\begingroup\$ Hmmm... If you xor one number (^) with other number, result may be below printable characters on screen. So if copy/paste is not working, you will simply loose data. Only stringified arrays can survive copy/paste. For ex. how do you print \0 or eof on screen? :) But character '0' in array is ok. \$\endgroup\$ – CoR May 4 '13 at 17:44
  • \$\begingroup\$ Note that Strings can contain non-printable characters using Unicode escape sequences like \u0008. \$\endgroup\$ – Quentin Pradet May 14 '13 at 7:27
3
\$\begingroup\$

From a once over:

  • p is a terrible variable name, I can live with i and s but I draw the line at p, at the very least you should put a comment next var p to indicate what p stands for
  • lowerCamelCase is good for you, return_type -> returnType etc.
  • don't use console.log in production code
  • Standardize on either single quote or double quote strings, preferably single quote strings
  • Your treatment of password[] and source[] is copy pasted, use a common function
  • I cringe at throw('Very Bad second param in function xor:' + p); it would have been more useful to have throw('password must be a string, an array of strings, or an array of numbers');
  • source = password = i = pwlen = pwindex = null; is pointless
  • I would have gone for if(returnType === 'string') instead of if(typeof return_type === 'string'), checking for any string is asking for unintended consequences
  • pwindex = 0; -> that should have been set in the var statement
  • This is odd :

    pwlen = password.length - 1;
    len = source.length;
    
    • If you go for pwlen (passwordLength), should the other one not be sourceLength ?
    • It is not clear from the code why you reduce passwordLength by 1, it's either a bug or deserves a line of comment
  • Please check your comments, they need spell check and a number of them are pointless
  • Instead of writing a special case for loop when password is missing, I would simply do password = source; and that should work
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.