| 1 |
<?php |
| 2 |
/** |
| 3 |
* SCSSPHP |
| 4 |
* |
| 5 |
* @copyright 2012-2018 Leaf Corcoran |
| 6 |
* |
| 7 |
* @license http://opensource.org/licenses/MIT MIT |
| 8 |
* |
| 9 |
* @link http://leafo.github.io/scssphp |
| 10 |
*/ |
| 11 |
|
| 12 |
namespace Leafo\ScssPhp\SourceMap; |
| 13 |
|
| 14 |
/** |
| 15 |
* Base64 VLQ Encoder |
| 16 |
* |
| 17 |
* {@internal Derivative of oyejorge/less.php's lib/SourceMap/Base64VLQ.php, relicensed with permission. }} |
| 18 |
* |
| 19 |
* @author Josh Schmidt <oyejorge@gmail.com> |
| 20 |
* @author Nicolas FRANÇOIS <nicolas.francois@frog-labs.com> |
| 21 |
*/ |
| 22 |
class Base64VLQEncoder |
| 23 |
{ |
| 24 |
/** |
| 25 |
* Shift |
| 26 |
* |
| 27 |
* @var integer |
| 28 |
*/ |
| 29 |
private $shift = 5; |
| 30 |
|
| 31 |
/** |
| 32 |
* Mask |
| 33 |
* |
| 34 |
* @var integer |
| 35 |
*/ |
| 36 |
private $mask = 0x1F; // == (1 << shift) == 0b00011111 |
| 37 |
|
| 38 |
/** |
| 39 |
* Continuation bit |
| 40 |
* |
| 41 |
* @var integer |
| 42 |
*/ |
| 43 |
private $continuationBit = 0x20; // == (mask - 1 ) == 0b00100000 |
| 44 |
|
| 45 |
/** |
| 46 |
* Char to integer map |
| 47 |
* |
| 48 |
* @var array |
| 49 |
*/ |
| 50 |
private $charToIntMap = array( |
| 51 |
'A' => 0, 'B' => 1, 'C' => 2, 'D' => 3, 'E' => 4, 'F' => 5, 'G' => 6, 'H' => 7, |
| 52 |
'I' => 8, 'J' => 9, 'K' => 10, 'L' => 11, 'M' => 12, 'N' => 13, 'O' => 14, 'P' => 15, |
| 53 |
'Q' => 16, 'R' => 17, 'S' => 18, 'T' => 19, 'U' => 20, 'V' => 21, 'W' => 22, 'X' => 23, |
| 54 |
'Y' => 24, 'Z' => 25, 'a' => 26, 'b' => 27, 'c' => 28, 'd' => 29, 'e' => 30, 'f' => 31, |
| 55 |
'g' => 32, 'h' => 33, 'i' => 34, 'j' => 35, 'k' => 36, 'l' => 37, 'm' => 38, 'n' => 39, |
| 56 |
'o' => 40, 'p' => 41, 'q' => 42, 'r' => 43, 's' => 44, 't' => 45, 'u' => 46, 'v' => 47, |
| 57 |
'w' => 48, 'x' => 49, 'y' => 50, 'z' => 51, 0 => 52, 1 => 53, 2 => 54, 3 => 55, |
| 58 |
4 => 56, 5 => 57, 6 => 58, 7 => 59, 8 => 60, 9 => 61, '+' => 62, '/' => 63, |
| 59 |
); |
| 60 |
|
| 61 |
/** |
| 62 |
* Integer to char map |
| 63 |
* |
| 64 |
* @var array |
| 65 |
*/ |
| 66 |
private $intToCharMap = array( |
| 67 |
0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D', 4 => 'E', 5 => 'F', 6 => 'G', 7 => 'H', |
| 68 |
8 => 'I', 9 => 'J', 10 => 'K', 11 => 'L', 12 => 'M', 13 => 'N', 14 => 'O', 15 => 'P', |
| 69 |
16 => 'Q', 17 => 'R', 18 => 'S', 19 => 'T', 20 => 'U', 21 => 'V', 22 => 'W', 23 => 'X', |
| 70 |
24 => 'Y', 25 => 'Z', 26 => 'a', 27 => 'b', 28 => 'c', 29 => 'd', 30 => 'e', 31 => 'f', |
| 71 |
32 => 'g', 33 => 'h', 34 => 'i', 35 => 'j', 36 => 'k', 37 => 'l', 38 => 'm', 39 => 'n', |
| 72 |
40 => 'o', 41 => 'p', 42 => 'q', 43 => 'r', 44 => 's', 45 => 't', 46 => 'u', 47 => 'v', |
| 73 |
48 => 'w', 49 => 'x', 50 => 'y', 51 => 'z', 52 => '0', 53 => '1', 54 => '2', 55 => '3', |
| 74 |
56 => '4', 57 => '5', 58 => '6', 59 => '7', 60 => '8', 61 => '9', 62 => '+', 63 => '/', |
| 75 |
); |
| 76 |
|
| 77 |
/** |
| 78 |
* Constructor |
| 79 |
*/ |
| 80 |
public function __construct() |
| 81 |
{ |
| 82 |
// I leave it here for future reference |
| 83 |
// foreach (str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/') as $i => $char) |
| 84 |
// { |
| 85 |
// $this->charToIntMap[$char] = $i; |
| 86 |
// $this->intToCharMap[$i] = $char; |
| 87 |
// } |
| 88 |
} |
| 89 |
|
| 90 |
/** |
| 91 |
* Convert from a two-complement value to a value where the sign bit is |
| 92 |
* is placed in the least significant bit. For example, as decimals: |
| 93 |
* 1 becomes 2 (10 binary), -1 becomes 3 (11 binary) |
| 94 |
* 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) |
| 95 |
* We generate the value for 32 bit machines, hence -2147483648 becomes 1, not 4294967297, |
| 96 |
* even on a 64 bit machine. |
| 97 |
* |
| 98 |
* @param string $aValue |
| 99 |
*/ |
| 100 |
public function toVLQSigned($aValue) |
| 101 |
{ |
| 102 |
return 0xffffffff & ($aValue < 0 ? ((-$aValue) << 1) + 1 : ($aValue << 1) + 0); |
| 103 |
} |
| 104 |
|
| 105 |
/** |
| 106 |
* Convert to a two-complement value from a value where the sign bit is |
| 107 |
* is placed in the least significant bit. For example, as decimals: |
| 108 |
* 2 (10 binary) becomes 1, 3 (11 binary) becomes -1 |
| 109 |
* 4 (100 binary) becomes 2, 5 (101 binary) becomes -2 |
| 110 |
* We assume that the value was generated with a 32 bit machine in mind. |
| 111 |
* Hence |
| 112 |
* 1 becomes -2147483648 |
| 113 |
* even on a 64 bit machine. |
| 114 |
* |
| 115 |
* @param integer $aValue |
| 116 |
*/ |
| 117 |
public function fromVLQSigned($aValue) |
| 118 |
{ |
| 119 |
return $aValue & 1 ? $this->zeroFill(~$aValue + 2, 1) | (-1 - 0x7fffffff) : $this->zeroFill($aValue, 1); |
| 120 |
} |
| 121 |
|
| 122 |
/** |
| 123 |
* Return the base 64 VLQ encoded value. |
| 124 |
* |
| 125 |
* @param string $aValue The value to encode |
| 126 |
* |
| 127 |
* @return string The encoded value |
| 128 |
*/ |
| 129 |
public function encode($aValue) |
| 130 |
{ |
| 131 |
$encoded = ''; |
| 132 |
$vlq = $this->toVLQSigned($aValue); |
| 133 |
|
| 134 |
do { |
| 135 |
$digit = $vlq & $this->mask; |
| 136 |
$vlq = $this->zeroFill($vlq, $this->shift); |
| 137 |
|
| 138 |
if ($vlq > 0) { |
| 139 |
$digit |= $this->continuationBit; |
| 140 |
} |
| 141 |
|
| 142 |
$encoded .= $this->base64Encode($digit); |
| 143 |
} while ($vlq > 0); |
| 144 |
|
| 145 |
return $encoded; |
| 146 |
} |
| 147 |
|
| 148 |
/** |
| 149 |
* Return the value decoded from base 64 VLQ. |
| 150 |
* |
| 151 |
* @param string $encoded The encoded value to decode |
| 152 |
* |
| 153 |
* @return integer The decoded value |
| 154 |
*/ |
| 155 |
public function decode($encoded) |
| 156 |
{ |
| 157 |
$vlq = 0; |
| 158 |
$i = 0; |
| 159 |
|
| 160 |
do { |
| 161 |
$digit = $this->base64Decode($encoded[$i]); |
| 162 |
$vlq |= ($digit & $this->mask) << ($i * $this->shift); |
| 163 |
$i++; |
| 164 |
} while ($digit & $this->continuationBit); |
| 165 |
|
| 166 |
return $this->fromVLQSigned($vlq); |
| 167 |
} |
| 168 |
|
| 169 |
/** |
| 170 |
* Right shift with zero fill. |
| 171 |
* |
| 172 |
* @param integer $a number to shift |
| 173 |
* @param integer $b number of bits to shift |
| 174 |
* |
| 175 |
* @return integer |
| 176 |
*/ |
| 177 |
public function zeroFill($a, $b) |
| 178 |
{ |
| 179 |
$bmin = $b - 1; |
| 180 |
return ($a >= 0) ? ($a >> $b) : ($a >> $b) & (PHP_INT_MAX >> ($bmin)); |
| 181 |
} |
| 182 |
|
| 183 |
/** |
| 184 |
* Encode single 6-bit digit as base64. |
| 185 |
* |
| 186 |
* @param integer $number |
| 187 |
* |
| 188 |
* @return string |
| 189 |
* |
| 190 |
* @throws \Exception If the number is invalid |
| 191 |
*/ |
| 192 |
public function base64Encode($number) |
| 193 |
{ |
| 194 |
if ($number < 0 || $number > 63) { |
| 195 |
throw new \Exception(sprintf('Invalid number "%s" given. Must be between 0 and 63.', $number)); |
| 196 |
} |
| 197 |
|
| 198 |
return $this->intToCharMap[$number]; |
| 199 |
} |
| 200 |
|
| 201 |
/** |
| 202 |
* Decode single 6-bit digit from base64 |
| 203 |
* |
| 204 |
* @param string $char |
| 205 |
* |
| 206 |
* @return integer |
| 207 |
* |
| 208 |
* @throws \Exception If the number is invalid |
| 209 |
*/ |
| 210 |
public function base64Decode($char) |
| 211 |
{ |
| 212 |
if (! array_key_exists($char, $this->charToIntMap)) { |
| 213 |
throw new \Exception(sprintf('Invalid base 64 digit "%s" given.', $char)); |
| 214 |
} |
| 215 |
|
| 216 |
return $this->charToIntMap[$char]; |
| 217 |
} |
| 218 |
} |
| 219 |
|