diff options
Diffstat (limited to 'vendor/wikimedia/cdb/src/Reader/PHP.php')
-rw-r--r-- | vendor/wikimedia/cdb/src/Reader/PHP.php | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/vendor/wikimedia/cdb/src/Reader/PHP.php b/vendor/wikimedia/cdb/src/Reader/PHP.php new file mode 100644 index 00000000..57b7d8ca --- /dev/null +++ b/vendor/wikimedia/cdb/src/Reader/PHP.php @@ -0,0 +1,220 @@ +<?php + +namespace Cdb\Reader; +use Cdb\Exception; +use Cdb\Reader; +use Cdb\Util; + +/** + * This is a port of D.J. Bernstein's CDB to PHP. It's based on the copy that + * appears in PHP 5.3. Changes are: + * * Error returns replaced with exceptions + * * Exception thrown if sizes or offsets are between 2GB and 4GB + * * Some variables renamed + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + */ + +/** + * CDB reader class + */ +class PHP extends Reader { + /** The filename */ + protected $fileName; + + /* number of hash slots searched under this key */ + protected $loop; + + /* initialized if loop is nonzero */ + protected $khash; + + /* initialized if loop is nonzero */ + protected $kpos; + + /* initialized if loop is nonzero */ + protected $hpos; + + /* initialized if loop is nonzero */ + protected $hslots; + + /* initialized if findNext() returns true */ + protected $dpos; + + /* initialized if cdb_findnext() returns 1 */ + protected $dlen; + + /** + * @param string $fileName + * @throws Exception + */ + public function __construct( $fileName ) { + $this->fileName = $fileName; + $this->handle = fopen( $fileName, 'rb' ); + if ( !$this->handle ) { + throw new Exception( 'Unable to open CDB file "' . $this->fileName . '".' ); + } + $this->findStart(); + } + + public function close() { + if ( isset( $this->handle ) ) { + fclose( $this->handle ); + } + unset( $this->handle ); + } + + /** + * @param mixed $key + * @return bool|string + */ + public function get( $key ) { + // strval is required + if ( $this->find( strval( $key ) ) ) { + return $this->read( $this->dlen, $this->dpos ); + } + + return false; + } + + /** + * @param string $key + * @param int $pos + * @return bool + */ + protected function match( $key, $pos ) { + $buf = $this->read( strlen( $key ), $pos ); + + return $buf === $key; + } + + protected function findStart() { + $this->loop = 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 ( $length == 0 ) { + return ''; + } + + $buf = fread( $this->handle, $length ); + if ( $buf === false || strlen( $buf ) !== $length ) { + throw new Exception( + 'Read from CDB file failed, file "' . $this->fileName . '" may be corrupted.' ); + } + + return $buf; + } + + /** + * Unpack an unsigned integer and throw an exception if it needs more than 31 bits + * @param string $s + * @throws Exception + * @return mixed + */ + protected function unpack31( $s ) { + $data = unpack( 'V', $s ); + if ( $data[1] > 0x7fffffff ) { + throw new Exception( + 'Error in CDB file "' . $this->fileName . '", integer too big.' ); + } + + return $data[1]; + } + + /** + * Unpack a 32-bit signed integer + * @param string $s + * @return int + */ + protected function unpackSigned( $s ) { + $data = unpack( 'va/vb', $s ); + + return $data['a'] | ( $data['b'] << 16 ); + } + + /** + * @param string $key + * @return bool + */ + 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; + } + + while ( $this->loop < $this->hslots ) { + $buf = $this->read( 8, $this->kpos ); + $pos = $this->unpack31( substr( $buf, 4 ) ); + if ( !$pos ) { + return false; + } + $this->loop += 1; + $this->kpos += 8; + if ( $this->kpos == $this->hpos + ( $this->hslots << 3 ) ) { + $this->kpos = $this->hpos; + } + $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; + } + } + } + + return false; + } + + /** + * @param mixed $key + * @return bool + */ + protected function find( $key ) { + $this->findStart(); + + return $this->findNext( $key ); + } +} + |