function gmp_shiftr($x, $n) { if ($x < 0) { return gmp_strval(gmp_com(gmp_div(gmp_com($x), gmp_pow(2, $n)))); } else { return gmp_strval(gmp_div($x, gmp_pow(2, $n))); } }
/** * Calculate Wang hash for 64bit unsigned integer using GMP library * PHP only supports signed integers even with 64bit version * * See <code/nel/include/nel/misc/wang_hash.h> on https://bitbucket.org/ryzom/ryzomcore * * @param string $key * * @return string hash */ function wang_hash64($key) { // force $key to be base 10 $key = gmp_init($key, 10); //$key = (~$key) + ($key << 21); $key = gmp_add(gmp_com($key), gmp_mul($key, 1 << 21)); //$key = $key ^ ($key >> 24); $key = gmp_xor($key, gmp_div($key, 1 << 24)); //$key = $key * 265; $key = gmp_mul($key, 265); //$key = $key ^ ($key >> 14); $key = gmp_xor($key, gmp_div($key, 1 << 14)); //$key = $key * 21; $key = gmp_mul($key, 21); //$key = $key ^ ($key >> 28); $key = gmp_xor($key, gmp_div($key, 1 << 28)); //$key = $key + ($key << 31); $key = gmp_add($key, gmp_mul($key, gmp_pow(2, 31))); // limit to 64bit $key = gmp_and($key, "0xFFFFFFFFFFFFFFFF"); return gmp_strval($key, 10); }
/** * Scan the blobs table for rows not registered in blob_tracking (and thus not * registered in the text table). * * Orphan blobs are indicative of DB corruption. They are inaccessible and * should probably be deleted. */ function findOrphanBlobs() { if (!extension_loaded('gmp')) { echo "Can't find orphan blobs, need bitfield support provided by GMP.\n"; return; } $dbw = wfGetDB(DB_MASTER); foreach ($this->clusters as $cluster) { echo "Searching for orphan blobs in {$cluster}...\n"; $lb = wfGetLBFactory()->getExternalLB($cluster); try { $extDB = $lb->getConnection(DB_SLAVE); } catch (DBConnectionError $e) { if (strpos($e->error, 'Unknown database') !== false) { echo "No database on {$cluster}\n"; } else { echo "Error on {$cluster}: " . $e->getMessage() . "\n"; } continue; } $table = $extDB->getLBInfo('blobs table'); if (is_null($table)) { $table = 'blobs'; } if (!$extDB->tableExists($table)) { echo "No blobs table on cluster {$cluster}\n"; continue; } $startId = 0; $batchesDone = 0; $actualBlobs = gmp_init(0); $endId = $extDB->selectField($table, 'MAX(blob_id)', false, __METHOD__); // Build a bitmap of actual blob rows while (true) { $res = $extDB->select($table, array('blob_id'), array('blob_id > ' . $extDB->addQuotes($startId)), __METHOD__, array('LIMIT' => $this->batchSize, 'ORDER BY' => 'blob_id')); if (!$res->numRows()) { break; } foreach ($res as $row) { gmp_setbit($actualBlobs, $row->blob_id); } $startId = $row->blob_id; ++$batchesDone; if ($batchesDone >= $this->reportingInterval) { $batchesDone = 0; echo "{$startId} / {$endId}\n"; } } // Find actual blobs that weren't tracked by the previous passes // This is a set-theoretic difference A \ B, or in bitwise terms, A & ~B $orphans = gmp_and($actualBlobs, gmp_com($this->trackedBlobs[$cluster])); // Traverse the orphan list $insertBatch = array(); $id = 0; $numOrphans = 0; while (true) { $id = gmp_scan1($orphans, $id); if ($id == -1) { break; } $insertBatch[] = array('bo_cluster' => $cluster, 'bo_blob_id' => $id); if (count($insertBatch) > $this->batchSize) { $dbw->insert('blob_orphans', $insertBatch, __METHOD__); $insertBatch = array(); } ++$id; ++$numOrphans; } if ($insertBatch) { $dbw->insert('blob_orphans', $insertBatch, __METHOD__); } echo "Found {$numOrphans} orphan(s) in {$cluster}\n"; } }
/** * @param $value * @param $mask * * @return string */ public static function remove($value, $mask) { return gmp_strval(gmp_and($value, gmp_com($mask))); }
echo gmp_strval($and1) . "\n"; echo gmp_strval($and2) . "\n"; // gmp_clrbit $clrbit = gmp_init("0xff"); gmp_clrbit($clrbit, 0); echo gmp_strval($clrbit) . "\n"; // gmp_cmp $cmp1 = gmp_cmp("1234", "1000"); // greater than $cmp2 = gmp_cmp("1000", "1234"); // less than $cmp3 = gmp_cmp("1234", "1234"); // equal to echo "{$cmp1} {$cmp2} {$cmp3}" . "\n"; // gmp_com $com = gmp_com("1234"); echo gmp_strval($com) . "\n"; // gmp_div_q $div1 = gmp_div_q("100", "5"); echo gmp_strval($div1) . "\n"; $div2 = gmp_div_q("1", "3"); echo gmp_strval($div2) . "\n"; $div3 = gmp_div_q("1", "3", GMP_ROUND_PLUSINF); echo gmp_strval($div3) . "\n"; $div4 = gmp_div_q("-1", "4", GMP_ROUND_PLUSINF); echo gmp_strval($div4) . "\n"; $div5 = gmp_div_q("-1", "4", GMP_ROUND_MINUSINF); echo gmp_strval($div5) . "\n"; // gmp_div_qr $a = gmp_init("0x41682179fbf5"); $res = gmp_div_qr($a, "0xDEFE75");
/** * Bitwise "not" (~) * * @access public * @return self */ public function bitNot() { $result = gmp_com($this->getRawValue()); return static::factory($result); }
/** * @param int $BitOffset * @param int $ValueMask * @param int $Value * * @return void */ private function Set($BitOffset, $ValueMask, $Value) { $this->Data = gmp_or(gmp_and($this->Data, gmp_com(self::ShiftLeft($ValueMask, $BitOffset))), self::ShiftLeft(gmp_and($Value, $ValueMask), $BitOffset)); }
/** * @param BufferInterface $buffer * @return int */ private function parseBuffer(BufferInterface $buffer) { $size = $buffer->getSize(); if ($size === 0) { return '0'; } $chars = array_map(function ($binary) { return ord($binary); }, str_split($buffer->getBinary(), 1)); $result = 0; for ($i = 0; $i < $size; $i++) { $mul = $this->math->mul($i, 8); $byte = $this->math->leftShift($chars[$i], $mul); $result = $this->math->bitwiseOr($result, $byte); } if ($chars[count($chars) - 1] & 0x80) { $mask = gmp_strval(gmp_com($this->math->leftShift(0x80, 8 * ($size - 1))), 10); return $this->math->sub(0, $this->math->bitwiseAnd($result, $mask)); } return $result; }
/** * Arithmetic right shift * @param resource|int|string $g * @param int $shift number of bits to shift right * @return resource $g shifted right $shift bits */ static function shift_right($g, $shift) { if (0 == $shift) { return $g; } if (0 <= gmp_sign($g)) { $m = gmp_div($g, gmp_pow(self::gmp_2(), $shift)); } else { $g = gmp_and($g, self::gmp_0xfs()); $m = gmp_div($g, gmp_pow(self::gmp_2(), $shift)); $m = gmp_and($m, self::gmp_0xfs()); for ($i = 63; $i >= 63 - $shift; $i--) { gmp_setbit($m, $i); } $m = gmp_neg(gmp_add(gmp_and(gmp_com($m), self::gmp_0xfs()), self::gmp_1())); } return $m; }
protected function readTextRecordInner(&$content, &$pos, $recordType) { switch ($recordType) { case Constants::RECORD_TYPE_ZERO_TEXT: case Constants::RECORD_TYPE_ZERO_TEXT_WITH_END_ELEMENT: return '0'; break; case Constants::RECORD_TYPE_ONE_TEXT: case Constants::RECORD_TYPE_ONE_TEXT_WITH_END_ELEMENT: return '1'; break; case Constants::RECORD_TYPE_FALSE_TEXT: case Constants::RECORD_TYPE_FALSE_TEXT_WITH_END_ELEMENT: return 'false'; break; case Constants::RECORD_TYPE_TRUE_TEXT: case Constants::RECORD_TYPE_TRUE_TEXT_WITH_END_ELEMENT: return 'true'; break; case Constants::RECORD_TYPE_INT8_TEXT: case Constants::RECORD_TYPE_INT8_TEXT_WITH_END_ELEMENT: $record = unpack('c*', $content[$pos]); $pos += 1; return (string) $record[1]; break; case Constants::RECORD_TYPE_INT16_TEXT: case Constants::RECORD_TYPE_INT16_TEXT_WITH_END_ELEMENT: $record = unpack('s*', substr($content, $pos, 2)); $pos += 2; return (string) $record[1]; break; case Constants::RECORD_TYPE_INT32_TEXT: case Constants::RECORD_TYPE_INT32_TEXT_WITH_END_ELEMENT: $record = unpack('l*', substr($content, $pos, 4)); $pos += 4; return (string) $record[1]; break; case Constants::RECORD_TYPE_INT64_TEXT: case Constants::RECORD_TYPE_INT64_TEXT_WITH_END_ELEMENT: if (!function_exists('gmp_init')) { throw new DecodingException('Int64 requires GMP extension'); } list(, $int64Hex) = unpack('H*', strrev(substr($content, $pos, 8))); $pos += 8; return (string) gmp_strval(gmp_init($int64Hex, 16), 10); break; case Constants::RECORD_TYPE_FLOAT_TEXT: case Constants::RECORD_TYPE_FLOAT_TEXT_WITH_END_ELEMENT: $record = unpack('f*', substr($content, $pos, 4)); $pos += 4; return (string) $record[1]; break; case Constants::RECORD_TYPE_DOUBLE_TEXT: case Constants::RECORD_TYPE_DOUBLE_TEXT_WITH_END_ELEMENT: $record = unpack('d*', substr($content, $pos, 8)); $pos += 8; return (string) $record[1]; break; case Constants::RECORD_TYPE_DECIMAL_TEXT: case Constants::RECORD_TYPE_DECIMAL_TEXT_WITH_END_ELEMENT: if (!function_exists('gmp_init')) { throw new DecodingException('Decimal requires GMP extension'); } $pos += 2; // First 2 bytes reserved $scale = ord($content[$pos]); $pos += 1; $sign = ord($content[$pos]); $pos += 1; list(, $hi32Hex) = unpack('H*', strrev(substr($content, $pos, 4))); $pos += 4; $hi32 = gmp_init($hi32Hex, 16); list(, $lo64Hex) = unpack('H*', strrev(substr($content, $pos, 8))); $pos += 8; $lo64 = gmp_init($lo64Hex, 16); $value = gmp_add(gmp_mul($hi32, gmp_pow(2, 64)), $lo64); $record = gmp_strval($value, 10); if ($scale > 0) { if ($scale > strlen($record)) { $record = str_repeat('0', $scale - strlen($record)) . $record; } $record = substr($record, 0, strlen($record) - $scale) . '.' . substr($record, $scale * -1); $record = trim($record, '0'); if ($record[0] == '.') { $record = '0' . $record; } } if ($sign == 0x80) { $record = '-' . $record; } return $record; break; case Constants::RECORD_TYPE_DATETIME_TEXT: case Constants::RECORD_TYPE_DATETIME_TEXT_WITH_END_ELEMENT: if (!function_exists('gmp_init')) { throw new DecodingException('Datetime requires GMP extension'); } $binary = ''; for ($i = 0; $i < 8; ++$i) { list(, $byteint) = unpack('C*', $content[$pos + $i]); $binary = sprintf('%08b', $byteint) . $binary; } $pos += 8; $value = gmp_init(substr($binary, 2, 62), 2); // Only calc with seconds, since PHP datetime doesn't support fractions $secsRemaining = gmp_div($value, '10000000'); // Compensate for using unix timestamp $epochSecsRemaining = gmp_sub($secsRemaining, '62135596800'); // Load PHP datetime with seconds remaining $datetime = new DateTime('@' . gmp_strval($epochSecsRemaining, 10), new DateTimeZone('UTC')); $date = $datetime->format('Y-m-d'); $time = $datetime->format('H:i:s'); // Get fractions $fraction = substr(gmp_strval($value, 10), -7); // Join different time elements if ($fraction !== '0000000') { $record = "{$date}T{$time}.{$fraction}"; } elseif ($time !== '00:00:00') { $record = "{$date}T{$time}"; } else { $record = "{$date}"; } // Add timezone info $tz = gmp_intval(gmp_init(substr($binary, 0, 2), 2)); if ($tz == 2) { // TODO: local time handling } elseif ($tz == 1) { $record .= 'Z'; } return $record; break; case Constants::RECORD_TYPE_CHARS8_TEXT: case Constants::RECORD_TYPE_CHARS8_TEXT_WITH_END_ELEMENT: list(, $recordLength) = unpack('C*', $content[$pos]); $pos += 1; $record = substr($content, $pos, $recordLength); $pos += $recordLength; return $record; break; case Constants::RECORD_TYPE_CHARS16_TEXT: case Constants::RECORD_TYPE_CHARS16_TEXT_WITH_END_ELEMENT: list(, $recordLength) = unpack('S*', substr($content, $pos, 2)); $pos += 2; $record = substr($content, $pos, $recordLength); $pos += $recordLength; return $record; break; case Constants::RECORD_TYPE_CHARS32_TEXT: case Constants::RECORD_TYPE_CHARS32_TEXT_WITH_END_ELEMENT: list(, $recordLength) = unpack('l*', substr($content, $pos, 4)); $pos += 4; $record = substr($content, $pos, $recordLength); $pos += $recordLength; return $record; break; case Constants::RECORD_TYPE_BYTES8_TEXT: case Constants::RECORD_TYPE_BYTES8_TEXT_WITH_END_ELEMENT: list(, $recordLength) = unpack('C*', $content[$pos]); $pos += 1; $record = substr($content, $pos, $recordLength); $pos += $recordLength; return base64_encode($record); break; case Constants::RECORD_TYPE_BYTES16_TEXT: case Constants::RECORD_TYPE_BYTES16_TEXT_WITH_END_ELEMENT: list(, $recordLength) = unpack('S*', substr($content, $pos, 2)); $pos += 2; $record = substr($content, $pos, $recordLength); $pos += $recordLength; return base64_encode($record); break; case Constants::RECORD_TYPE_BYTES32_TEXT: case Constants::RECORD_TYPE_BYTES32_TEXT_WITH_END_ELEMENT: list(, $recordLength) = unpack('l*', substr($content, $pos, 4)); $pos += 4; $record = substr($content, $pos, $recordLength); $pos += $recordLength; return base64_encode($record); break; case Constants::RECORD_TYPE_START_LIST_TEXT: $record = ''; while (ord($content[$pos]) != Constants::RECORD_TYPE_END_LIST_TEXT) { if ($record !== '') { $record .= ' '; } $record .= $this->readTextRecord($content, $pos); } $pos += 1; // skip 1 for end list return $record; break; case Constants::RECORD_TYPE_EMPTY_TEXT: case Constants::RECORD_TYPE_EMPTY_TEXT_WITH_END_ELEMENT: return ''; break; case Constants::RECORD_TYPE_DICTIONARY_TEXT: case Constants::RECORD_TYPE_DICTIONARY_TEXT_WITH_END_ELEMENT: return $this->readDictionaryString($content, $pos); break; case Constants::RECORD_TYPE_UNIQUEID_TEXT: case Constants::RECORD_TYPE_UNIQUEID_TEXT_WITH_END_ELEMENT: case Constants::RECORD_TYPE_UUID_TEXT: case Constants::RECORD_TYPE_UUID_TEXT_WITH_END_ELEMENT: list(, $data1) = unpack('H*', strrev(substr($content, $pos, 4))); $pos += 4; list(, $data2) = unpack('H*', strrev(substr($content, $pos, 2))); $pos += 2; list(, $data3) = unpack('H*', strrev(substr($content, $pos, 2))); $pos += 2; list(, $data4) = unpack('H*', substr($content, $pos, 2)); $pos += 2; list(, $data5) = unpack('H*', substr($content, $pos, 6)); $pos += 6; $record = "{$data1}-{$data2}-{$data3}-{$data4}-{$data5}"; if ($recordType == Constants::RECORD_TYPE_UNIQUEID_TEXT || $recordType == Constants::RECORD_TYPE_UNIQUEID_TEXT_WITH_END_ELEMENT) { $record = 'urn:uuid:' . $record; } return $record; break; case Constants::RECORD_TYPE_TIMESPAN_TEXT: case Constants::RECORD_TYPE_TIMESPAN_TEXT_WITH_END_ELEMENT: if (!function_exists('gmp_init')) { throw new DecodingException('Timespan requires GMP extension'); } $value = ''; for ($i = 0; $i < 8; $i++) { list(, $byte) = unpack('C*', $content[$pos + $i]); $value = sprintf('%08b', $byte) . $value; } $pos += 8; $value = gmp_init($value, 2); $minus = false; if (gmp_testbit($value, 63)) { $minus = true; $mask = gmp_init(str_repeat('1', 64), '2'); $value = gmp_and(gmp_com($value), $mask); $value = gmp_add($value, 1); } $remaining = gmp_abs($value); list($days, $remaining) = gmp_div_qr($remaining, gmp_init('864000000000')); list($hours, $remaining) = gmp_div_qr($remaining, gmp_init('36000000000')); list($mins, $remaining) = gmp_div_qr($remaining, gmp_init('600000000')); list($secs, $remaining) = gmp_div_qr($remaining, gmp_init('10000000')); $fracs = $remaining; $days = gmp_intval($days); $hours = gmp_intval($hours); $mins = gmp_intval($mins); $secs = gmp_intval($secs); $fracs = gmp_intval($fracs); $record = $minus ? '-P' : 'P'; if ($days > 0) { $record .= $days . 'D'; } if ($hours > 0 || $mins > 0 || $secs > 0 || $fracs > 0) { $record .= 'T'; if ($hours > 0) { $record .= $hours . 'H'; } if ($mins > 0) { $record .= $mins . 'M'; } if ($secs > 0 || $fracs > 0) { $record .= $secs; if ($fracs > 0) { $record .= '.' . $fracs; } $record .= 'S'; } } return $record; break; case Constants::RECORD_TYPE_UINT64_TEXT: case Constants::RECORD_TYPE_UINT64_TEXT_WITH_END_ELEMENT: if (!function_exists('gmp_init')) { throw new DecodingException('Uint64 requires GMP extension'); } list(, $uint64Hex) = unpack('H*', strrev(substr($content, $pos, 8))); $pos += 8; return (string) gmp_strval(gmp_init($uint64Hex, 16), 10); break; case Constants::RECORD_TYPE_BOOL_TEXT: case Constants::RECORD_TYPE_BOOL_TEXT_WITH_END_ELEMENT: $record = ord($content[$pos]); $pos += 1; switch ($record) { case 0: return 'false'; break; case 1: return 'true'; break; } throw new DecodingException(sprintf('Unknown boolean value 0x%02X at position %d.', $record, $pos)); break; case Constants::RECORD_TYPE_UNICODECHARS8_TEXT: case Constants::RECORD_TYPE_UNICODECHARS8_TEXT_WITH_END_ELEMENT: list(, $recordLength) = unpack('C*', $content[$pos]); $pos += 1; $record = substr($content, $pos, $recordLength); $pos += $recordLength; return mb_convert_encoding($record, 'UTF-8', 'UTF-16'); break; case Constants::RECORD_TYPE_UNICODECHARS16_TEXT: case Constants::RECORD_TYPE_UNICODECHARS16_TEXT_WITH_END_ELEMENT: list(, $recordLength) = unpack('S*', substr($content, $pos, 2)); $pos += 2; $record = substr($content, $pos, $recordLength); $pos += $recordLength; return mb_convert_encoding($record, 'UTF-8', 'UTF-16'); break; case Constants::RECORD_TYPE_UNICODECHARS32_TEXT: case Constants::RECORD_TYPE_UNICODECHARS32_TEXT_WITH_END_ELEMENT: list(, $recordLength) = unpack('l*', substr($content, $pos, 4)); $pos += 4; $record = substr($content, $pos, $recordLength); $pos += $recordLength; return mb_convert_encoding($record, 'UTF-8', 'UTF-16'); break; case Constants::RECORD_TYPE_QNAMEDICTIONARY_TEXT: case Constants::RECORD_TYPE_QNAMEDICTIONARY_TEXT_WITH_END_ELEMENT: $prefix = chr(97 + ord($content[$pos])); $pos += 1; $name = $this->readDictionaryString($content, $pos); return $prefix . ':' . $name; break; default: throw new DecodingException(sprintf('Unknown record type 0x%02X at position %d.', $recordType, $pos)); break; } }