summaryrefslogtreecommitdiff
path: root/vendor/wikimedia/cdb/src/Reader/PHP.php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/wikimedia/cdb/src/Reader/PHP.php')
-rw-r--r--vendor/wikimedia/cdb/src/Reader/PHP.php286
1 files changed, 185 insertions, 101 deletions
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 @@
<?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
+ * appears in PHP 5.3.
*
* 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
@@ -34,33 +32,37 @@ use Cdb\Util;
* CDB reader class
*/
class PHP extends Reader {
- /** The filename */
+
+ /** @var string The file name of the CDB file. **/
protected $fileName;
- /* number of hash slots searched under this key */
- protected $loop;
+ /** @var string First 2048b of CDB file, containing the hash table. **/
+ protected $hashTable;
- /* initialized if loop is nonzero */
- protected $khash;
+ /** @var int Offset in file where value of found key starts. **/
+ protected $dataPos;
- /* initialized if loop is nonzero */
- protected $kpos;
+ /** @var int Byte length of found key's value. **/
+ protected $dataLen;
- /* initialized if loop is nonzero */
- protected $hpos;
+ /** @var int File position indicator when iterating over keys. **/
+ protected $keyIterPos = 2048;
- /* initialized if loop is nonzero */
- protected $hslots;
+ /** @var string Read buffer for CDB file. **/
+ protected $buf;
- /* initialized if findNext() returns true */
- protected $dpos;
+ /** @var int File offset where read buffer starts. **/
+ protected $bufStart;
- /* initialized if cdb_findnext() returns 1 */
- protected $dlen;
+ /** @var int File handle position indicator **/
+ protected $filePos = 2048;
/**
+ * Constructor.
+ *
* @param string $fileName
- * @throws Exception
+ * @throws Exception If CDB file cannot be opened or if it contains fewer
+ * than 2048 bytes of data.
*/
public function __construct( $fileName ) {
$this->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;
}
}
-