From a1789ddde42033f1b05cc4929491214ee6e79383 Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Thu, 17 Dec 2015 09:15:42 +0100 Subject: Update to MediaWiki 1.26.0 --- vendor/wikimedia/cdb/src/Reader/PHP.php | 286 +++++++++++++++++++++----------- 1 file changed, 185 insertions(+), 101 deletions(-) (limited to 'vendor/wikimedia/cdb/src/Reader/PHP.php') diff --git a/vendor/wikimedia/cdb/src/Reader/PHP.php b/vendor/wikimedia/cdb/src/Reader/PHP.php index 57b7d8ca..0f0d36ef 100644 --- a/vendor/wikimedia/cdb/src/Reader/PHP.php +++ b/vendor/wikimedia/cdb/src/Reader/PHP.php @@ -1,16 +1,14 @@ fileName = $fileName; @@ -68,9 +70,15 @@ class PHP extends Reader { if ( !$this->handle ) { throw new Exception( 'Unable to open CDB file "' . $this->fileName . '".' ); } - $this->findStart(); + $this->hashTable = fread( $this->handle, 2048 ); + if ( strlen( $this->hashTable ) !== 2048 ) { + throw new Exception( 'CDB file contains fewer than 2048 bytes of data.' ); + } } + /** + * Close the handle on the CDB file. + */ public function close() { if ( isset( $this->handle ) ) { fclose( $this->handle ); @@ -79,127 +87,180 @@ class PHP extends Reader { } /** + * Get the value of a key. + * * @param mixed $key - * @return bool|string + * @return bool|string The key's value or false if not found. */ public function get( $key ) { // strval is required if ( $this->find( strval( $key ) ) ) { - return $this->read( $this->dlen, $this->dpos ); + return $this->read( $this->dataPos, $this->dataLen ); } return false; } /** - * @param string $key - * @param int $pos - * @return bool + * Read data from the CDB file. + * + * @throws Exception When attempting to read past the end of the file. + * @param int $start Start reading from this position. + * @param int $len Number of bytes to read. + * @return string Read data. */ - protected function match( $key, $pos ) { - $buf = $this->read( strlen( $key ), $pos ); + protected function read( $start, $len ) { + $end = $start + $len; - return $buf === $key; - } + // The first 2048 bytes are the lookup table, which is read into + // memory on initialization. + if ( $end <= 2048 ) { + return substr( $this->hashTable, $start, $len ); + } - protected function findStart() { - $this->loop = 0; - } + // Read data from the internal buffer first. + $bytes = ''; + if ( $this->buf && $start >= $this->bufStart ) { + $bytes .= substr( $this->buf, $start - $this->bufStart, $len ); + $bytesRead = strlen( $bytes ); + $len -= $bytesRead; + $start += $bytesRead; + } else { + $bytesRead = 0; + } - /** - * @throws Exception - * @param int $length - * @param int $pos - * @return string - */ - protected function read( $length, $pos ) { - if ( fseek( $this->handle, $pos ) == -1 ) { - // This can easily happen if the internal pointers are incorrect - throw new Exception( - 'Seek failed, file "' . $this->fileName . '" may be corrupted.' ); + if ( !$len ) { + return $bytes; + } + + // Many reads are sequential, so the file position indicator may + // already be in the right place, in which case we can avoid the + // call to fseek(). + if ( $start !== $this->filePos ) { + if ( fseek( $this->handle, $start ) === -1 ) { + // This can easily happen if the internal pointers are incorrect + throw new Exception( + 'Seek failed, file "' . $this->fileName . '" may be corrupted.' ); + } } - if ( $length == 0 ) { - return ''; + $buf = fread( $this->handle, max( $len, 1024 ) ); + if ( $buf === false ) { + $buf = ''; } - $buf = fread( $this->handle, $length ); - if ( $buf === false || strlen( $buf ) !== $length ) { + $bytes .= substr( $buf, 0, $len ); + if ( strlen( $bytes ) !== $len + $bytesRead ) { throw new Exception( 'Read from CDB file failed, file "' . $this->fileName . '" may be corrupted.' ); } - return $buf; + $this->filePos = $end; + $this->bufStart = $start; + $this->buf = $buf; + + return $bytes; } /** - * Unpack an unsigned integer and throw an exception if it needs more than 31 bits - * @param string $s - * @throws Exception - * @return mixed + * Unpack an unsigned integer and throw an exception if it needs more than 31 bits. + * + * @param int $pos Position to read from. + * @throws Exception When the integer cannot be represented in 31 bits. + * @return int */ - protected function unpack31( $s ) { - $data = unpack( 'V', $s ); - if ( $data[1] > 0x7fffffff ) { + protected function readInt31( $pos = 0 ) { + $uint31 = $this->readInt32( $pos ); + if ( $uint31 > 0x7fffffff ) { throw new Exception( 'Error in CDB file "' . $this->fileName . '", integer too big.' ); } - return $data[1]; + return $uint31; } /** - * Unpack a 32-bit signed integer - * @param string $s + * Unpack a 32-bit integer. + * + * @param int $pos * @return int */ - protected function unpackSigned( $s ) { - $data = unpack( 'va/vb', $s ); + protected function readInt32( $pos = 0 ) { + static $lookups; + + if ( !$lookups ) { + $lookups = array(); + for ( $i = 1; $i < 256; $i++ ) { + $lookups[ chr( $i ) ] = $i; + } + } - return $data['a'] | ( $data['b'] << 16 ); + $buf = $this->read( $pos, 4 ); + + $rv = 0; + + if ( $buf[0] !== "\x0" ) { + $rv = $lookups[ $buf[0] ]; + } + if ( $buf[1] !== "\x0" ) { + $rv |= ( $lookups[ $buf[1] ] << 8 ); + } + if ( $buf[2] !== "\x0" ) { + $rv |= ( $lookups[ $buf[2] ] << 16 ); + } + if ( $buf[3] !== "\x0" ) { + $rv |= ( $lookups[ $buf[3] ] << 24 ); + } + + return $rv; } /** + * Search the CDB file for a key. + * + * Sets `dataLen` and `dataPos` properties if successful. + * * @param string $key - * @return bool + * @return bool Whether the key was found. */ - protected function findNext( $key ) { - if ( !$this->loop ) { - $u = Util::hash( $key ); - $buf = $this->read( 8, ( $u << 3 ) & 2047 ); - $this->hslots = $this->unpack31( substr( $buf, 4 ) ); - if ( !$this->hslots ) { - return false; - } - $this->hpos = $this->unpack31( substr( $buf, 0, 4 ) ); - $this->khash = $u; - $u = Util::unsignedShiftRight( $u, 8 ); - $u = Util::unsignedMod( $u, $this->hslots ); - $u <<= 3; - $this->kpos = $this->hpos + $u; - } + protected function find( $key ) { + $keyLen = strlen( $key ); - while ( $this->loop < $this->hslots ) { - $buf = $this->read( 8, $this->kpos ); - $pos = $this->unpack31( substr( $buf, 4 ) ); + $u = Util::hash( $key ); + $upos = ( $u << 3 ) & 2047; + $hashSlots = $this->readInt31( $upos + 4 ); + if ( !$hashSlots ) { + return false; + } + $hashPos = $this->readInt31( $upos ); + $keyHash = $u; + $u = Util::unsignedShiftRight( $u, 8 ); + $u = Util::unsignedMod( $u, $hashSlots ); + $u <<= 3; + $keyPos = $hashPos + $u; + + for ( $i = 0; $i < $hashSlots; $i++ ) { + $hash = $this->readInt32( $keyPos ); + $pos = $this->readInt31( $keyPos + 4 ); if ( !$pos ) { return false; } - $this->loop += 1; - $this->kpos += 8; - if ( $this->kpos == $this->hpos + ( $this->hslots << 3 ) ) { - $this->kpos = $this->hpos; + $keyPos += 8; + if ( $keyPos == $hashPos + ( $hashSlots << 3 ) ) { + $keyPos = $hashPos; } - $u = $this->unpackSigned( substr( $buf, 0, 4 ) ); - if ( $u === $this->khash ) { - $buf = $this->read( 8, $pos ); - $keyLen = $this->unpack31( substr( $buf, 0, 4 ) ); - if ( $keyLen == strlen( $key ) && $this->match( $key, $pos + 8 ) ) { - // Found - $this->dlen = $this->unpack31( substr( $buf, 4 ) ); - $this->dpos = $pos + 8 + $keyLen; - - return true; + if ( $hash === $keyHash ) { + if ( $keyLen === $this->readInt31( $pos ) ) { + $dataLen = $this->readInt31( $pos + 4 ); + $dataPos = $pos + 8 + $keyLen; + $foundKey = $this->read( $pos + 8, $keyLen ); + if ( $foundKey === $key ) { + // Found + $this->dataLen = $dataLen; + $this->dataPos = $dataPos; + + return true; + } } } } @@ -208,13 +269,36 @@ class PHP extends Reader { } /** - * @param mixed $key - * @return bool + * Check if a key exists in the CDB file. + * + * @param string $key + * @return bool Whether the key exists. */ - protected function find( $key ) { - $this->findStart(); + public function exists( $key ) { + return $this->find( strval( $key ) ); + } + + /** + * Get the first key from the CDB file and reset the key iterator. + * + * @return string Key. + */ + public function firstkey() { + $this->keyIterPos = 2048; + return $this->nextkey(); + } + + /** + * Get the next key from the CDB file. + * + * @return string Key. + */ + public function nextkey() { + $keyLen = $this->readInt31( $this->keyIterPos ); + $dataLen = $this->readInt31( $this->keyIterPos + 4 ); + $key = $this->read( $this->keyIterPos + 8, $keyLen ); + $this->keyIterPos += 8 + $keyLen + $dataLen; - return $this->findNext( $key ); + return $key; } } - -- cgit v1.2.3-54-g00ecf