tags:

views:

242

answers:

3

After some searching I found this youtube style url generator with encryption to hide the original id... however I am hoping to improve the efficiency as it will be used a lot. So far I have improved it by 20%... can anyone help me improve it more.

This is the original:

function alphaID($in, $to_num = false, $pad_up = false, $passKey = null)
{
    $index = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    if ($passKey !== null) {
        // Although this function's purpose is to just make the
        // ID short - and not so much secure,
        // with this patch by Simon Franz (http://blog.snaky.org/)
        // you can optionally supply a password to make it harder
        // to calculate the corresponding numeric ID

        for ($n = 0; $n<strlen($index); $n++) {
            $i[] = substr( $index,$n ,1);
        }

        $passhash = hash('sha256',$passKey);
        $passhash = (strlen($passhash) < strlen($index))
            ? hash('sha512',$passKey)
            : $passhash;

        for ($n=0; $n < strlen($index); $n++) {
            $p[] = substr($passhash, $n ,1);
        }

        array_multisort($p, SORT_DESC, $i);
        $index = implode($i);
    }

    $base = strlen($index);

    if ($to_num) {
        // Digital number <<-- alphabet letter code
        $in = strrev($in);
        $out = 0;
        $len = strlen($in) - 1;
        for ($t = 0; $t <= $len; $t++) {
            $bcpow = bcpow($base, $len - $t);
            $out = $out + strpos($index, substr($in, $t, 1)) * $bcpow;
        }

        if (is_numeric($pad_up)) {
            $pad_up--;
            if ($pad_up > 0) {
                $out -= pow($base, $pad_up);
            }
        }
    } else {
        // Digital number -->> alphabet letter code
        if (is_numeric($pad_up)) {
            $pad_up--;
            if ($pad_up > 0) {
                $in += pow($base, $pad_up);
            }
        }

        $out = "";
        for ($t = floor(log10($in) / log10($base)); $t >= 0; $t--) {
            $a = floor($in / bcpow($base, $t));
            $out = $out . substr($index, $a, 1);
            $in = $in - ($a * bcpow($base, $t));
        }
        $out = strrev($out); // reverse
    }

    return $out;
}

Here is my modified code so far:

function alphaID($in, $to_num = false, $pad_up = false, $passKey = null)
{
    $index = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $i = array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
    if ($passKey !== null) {
        // Although this function's purpose is to just make the
        // ID short - and not so much secure,
        // with this patch by Simon Franz (http://blog.snaky.org/)
        // you can optionally supply a password to make it harder
        // to calculate the corresponding numeric ID
       $len = strlen($index); 


        $passhash = hash('sha256',$passKey);
        $passhash = (strlen($passhash) < $len)
            ? hash('sha512',$passKey)
            : $passhash;

        for ($n=0; $n < $len; $n++) {
            $p[] = substr($passhash, $n ,1);
        }

        array_multisort($p, SORT_DESC, $i);
        $index = implode($i);
    }

    $base = strlen($index);

    if ($to_num) {
        // Digital number <<-- alphabet letter code
        $in = strrev($in);
        $out = 0;
        $len = strlen($in) - 1;
        for ($t = 0; $t <= $len; $t++) {
            $bcpow = bcpow($base, $len - $t);
            $out = $out + strpos($index, substr($in, $t, 1)) * $bcpow;
        }

        if (is_numeric($pad_up)) {
            $pad_up--;
            if ($pad_up > 0) {
                $out -= pow($base, $pad_up);
            }
        }
    } else {
        // Digital number -->> alphabet letter code
        if (is_numeric($pad_up)) {
            $pad_up--;
            if ($pad_up > 0) {
                $in += pow($base, $pad_up);
            }
        }

        $out = "";
        for ($t = floor(log10($in) / log10($base)); $t >= 0; $t--) {
            $a = floor($in / bcpow($base, $t));
            $out = $out . substr($index, $a, 1);
            $in = $in - ($a * bcpow($base, $t));
        }
        $out = strrev($out); // reverse
    }

    return $out;
}

As you can see no major difference, only I removed the strlen from the for loops and stored it in a variable and precalculated the array for the index (a bit clumsy... but the array generation is what was making up the bulk of the computation).

Credit where its due: here is the original authors info: * @author Kevin van Zonneveld * @author Simon Franz * @copyright 2008 Kevin van Zonneveld (kevin dot vanzonneveld dot net) * @license www dot opensource dot org/licenses/bsd-license dot php New BSD Licence * @version SVN: Release: $Id: alphaID.inc.php 344 2009-06-10 17:43:59Z kevin $ * @link kevin dot vanzonneveld dot net

I can't post the url's as I have to low reputation :S

A: 

It looks like you've done a good job getting rid of the redundant calls.

It looks like there are a couple calls to bcpow($base, $t) that could be reduced to one...

$out = "";
for ($t = floor(log10($in) / log10($base)); $t >= 0; $t--) {
    $newbase = bcpow($base, $t);
    $a = floor($in / $newbase);
    $out = $out . substr($index, $a, 1);
    $in = $in - ($a * $newbase);
}
$out = strrev($out); // reverse

You probably won't notice a difference.

Chris Gutierrez
+3  A: 

I've made some slight optimizations to remove some extra CPU cycles here and there. Mostly things like unnecessary assignments, extra comparisons, etc. Also, strings can be treated as arrays, so I worked that in, too:

function alphaID($in, $to_num = false, $pad_up = false, $passKey = null)
{
    static $passcache;
 if(empty($passcache))
  $passcache = array();

    $index = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $i = array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
    if (!empty($passKey)) {
        // Although this function's purpose is to just make the
        // ID short - and not so much secure,
        // with this patch by Simon Franz (http://blog.snaky.org/)
        // you can optionally supply a password to make it harder
        // to calculate the corresponding numeric ID

  if(isset($passcache[$passKey]))
   $index = $passcache[$passKey];
  else {
   if(strlen($passhash = hash('sha256',$passKey)) < strlen($index))
    $passhash = hash('sha512',$passKey);

   $p = str_split($passhash);

   array_multisort($p, SORT_DESC, $i);
   $index = implode($i);
   $passcache = $index;
  }
    }

    $base = strlen($index);

    if ($to_num) {
        // Digital number <<-- alphabet letter code
        $in = strrev($in);
        $out = 0;
        $len = strlen($in) - 1;
        for ($t = 0; $t <= $len; $t++) {
            $bcpow = bcpow($base, $len - $t);
            $out += strpos($index, $in[$t]) * $bcpow;
        }

        if (is_numeric($pad_up)) {
            $pad_up--;
            if ($pad_up > 0) {
                $out -= pow($base, $pad_up);
            }
        }
    } else {
        // Digital number -->> alphabet letter code
        if (is_numeric($pad_up)) {
            $pad_up--;
            if ($pad_up > 0) {
                $in += pow($base, $pad_up);
            }
        }

        $out = "";
        for ($t = floor(log10($in) / log10($base)); $t >= 0; $t--) {
      $bcp = bcpow($base, $t);
            $a = floor($in / $bcp);
            $out .= $index[$a];
            $in -= $a *  $bcp;
        }
        $out = strrev($out); // reverse
    }

    return $out;
}

Edit: I've updated to include a cache. Try it now!

mattbasta
wow.. you did a fantastic job... 1000 executions used to take 600ms... now its just 250ms. One small error in your code is that the array_multisort causes an error if they are of a different length... so I added this: $p = array_shift(array_chunk ($p, $len));That said... 100ms of the 250 are the array_multisort... any ideas of an alternative more efficient solution?
ma long
The update that I just made will have that 100ms delay the first time you run it, but each subsequent run will be much much faster. Let me know if it works!
mattbasta
Thanks a lot mattbasta, it works a treat.
ma long
A: 

I think you should just generate an index and use it site wide.. let me explain why...

The hashing and everything above '$base = strlen($index);' is just to generate the index... you will have to use the same $passkey to decode a url. As you have no way of knowing which passkey was used you would have to make it site wide. Which would mean each call to generate the url will use the same passkey and thus generate the same index.

And so... I would strip the index generating code and generate a unique index and store it in the '$index' variable.

Mark