From fb54343d6f4019e27f980813671dfc6bdceaf402 Mon Sep 17 00:00:00 2001 From: Kunal Mehta Date: Mon, 10 Nov 2014 10:56:04 -0800 Subject: [PATCH] cdb: One class per file Change-Id: I18c6f58d8b3747c4da9d74884a3c9a3571f403f3 --- includes/AutoLoader.php | 16 +- includes/libs/cdb/CdbException.php | 26 ++ includes/libs/cdb/CdbFunctions.php | 97 +++++++ includes/libs/cdb/CdbReader.php | 82 ++++++ includes/libs/cdb/CdbReaderDBA.php | 44 +++ includes/libs/cdb/CdbReaderPHP.php | 214 ++++++++++++++ includes/libs/cdb/{Cdb.php => CdbWriter.php} | 69 +---- .../libs/cdb/{CdbDBA.php => CdbWriterDBA.php} | 23 -- .../libs/cdb/{CdbPHP.php => CdbWriterPHP.php} | 260 ------------------ 9 files changed, 472 insertions(+), 359 deletions(-) create mode 100644 includes/libs/cdb/CdbException.php create mode 100644 includes/libs/cdb/CdbFunctions.php create mode 100644 includes/libs/cdb/CdbReader.php create mode 100644 includes/libs/cdb/CdbReaderDBA.php create mode 100644 includes/libs/cdb/CdbReaderPHP.php rename includes/libs/cdb/{Cdb.php => CdbWriter.php} (64%) rename includes/libs/cdb/{CdbDBA.php => CdbWriterDBA.php} (76%) rename includes/libs/cdb/{CdbPHP.php => CdbWriterPHP.php} (53%) diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index 9c618ad227..9f9deb7557 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -702,14 +702,14 @@ $wgAutoloadLocalClasses = array( 'XmlTypeCheck' => 'includes/libs/XmlTypeCheck.php', # includes/libs/cdb - 'CdbException' => 'includes/libs/cdb/Cdb.php', - 'CdbFunctions' => 'includes/libs/cdb/CdbPHP.php', - 'CdbReader' => 'includes/libs/cdb/Cdb.php', - 'CdbReaderDBA' => 'includes/libs/cdb/CdbDBA.php', - 'CdbReaderPHP' => 'includes/libs/cdb/CdbPHP.php', - 'CdbWriter' => 'includes/libs/cdb/Cdb.php', - 'CdbWriterDBA' => 'includes/libs/cdb/CdbDBA.php', - 'CdbWriterPHP' => 'includes/libs/cdb/CdbPHP.php', + 'CdbException' => 'includes/libs/cdb/CdbException.php', + 'CdbFunctions' => 'includes/libs/cdb/CdbFunctions.php', + 'CdbReader' => 'includes/libs/cdb/CdbReader.php', + 'CdbReaderDBA' => 'includes/libs/cdb/CdbReaderDBA.php', + 'CdbReaderPHP' => 'includes/libs/cdb/CdbReaderPHP.php', + 'CdbWriter' => 'includes/libs/cdb/CdbWriter.php', + 'CdbWriterDBA' => 'includes/libs/cdb/CdbWriterDBA.php', + 'CdbWriterPHP' => 'includes/libs/cdb/CdbWriterPHP.php', # includes/libs/lessphp 'lessc' => 'includes/libs/lessc.inc.php', diff --git a/includes/libs/cdb/CdbException.php b/includes/libs/cdb/CdbException.php new file mode 100644 index 0000000000..6cda529d28 --- /dev/null +++ b/includes/libs/cdb/CdbException.php @@ -0,0 +1,26 @@ +> $b ) | ( 0x40000000 >> ( $b - 1 ) ); + } else { + return $a >> $b; + } + } + + /** + * The CDB hash function. + * + * @param string $s + * + * @return int + */ + public static function hash( $s ) { + $h = 5381; + $len = strlen( $s ); + for ( $i = 0; $i < $len; $i++ ) { + $h5 = ( $h << 5 ) & 0xffffffff; + // Do a 32-bit sum + // Inlined here for speed + $sum = ( $h & 0x3fffffff ) + ( $h5 & 0x3fffffff ); + $h = + ( + ( $sum & 0x40000000 ? 1 : 0 ) + + ( $h & 0x80000000 ? 2 : 0 ) + + ( $h & 0x40000000 ? 1 : 0 ) + + ( $h5 & 0x80000000 ? 2 : 0 ) + + ( $h5 & 0x40000000 ? 1 : 0 ) + ) << 30 + | ( $sum & 0x3fffffff ); + $h ^= ord( $s[$i] ); + $h &= 0xffffffff; + } + + return $h; + } +} diff --git a/includes/libs/cdb/CdbReader.php b/includes/libs/cdb/CdbReader.php new file mode 100644 index 0000000000..0ca9b9dc29 --- /dev/null +++ b/includes/libs/cdb/CdbReader.php @@ -0,0 +1,82 @@ +handle = dba_open( $fileName, 'r-', 'cdb' ); + if ( !$this->handle ) { + throw new CdbException( 'Unable to open CDB file "' . $fileName . '"' ); + } + } + + public function close() { + if ( isset( $this->handle ) ) { + dba_close( $this->handle ); + } + unset( $this->handle ); + } + + public function get( $key ) { + return dba_fetch( $key, $this->handle ); + } +} diff --git a/includes/libs/cdb/CdbReaderPHP.php b/includes/libs/cdb/CdbReaderPHP.php new file mode 100644 index 0000000000..e448414f5e --- /dev/null +++ b/includes/libs/cdb/CdbReaderPHP.php @@ -0,0 +1,214 @@ +fileName = $fileName; + $this->handle = fopen( $fileName, 'rb' ); + if ( !$this->handle ) { + throw new CdbException( '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 ); + } else { + 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 CdbException + * @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 CdbException( + '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 CdbException( + '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 CdbException + * @return mixed + */ + protected function unpack31( $s ) { + $data = unpack( 'V', $s ); + if ( $data[1] > 0x7fffffff ) { + throw new CdbException( + '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 = CdbFunctions::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 = CdbFunctions::unsignedShiftRight( $u, 8 ); + $u = CdbFunctions::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 ); + } +} + diff --git a/includes/libs/cdb/Cdb.php b/includes/libs/cdb/CdbWriter.php similarity index 64% rename from includes/libs/cdb/Cdb.php rename to includes/libs/cdb/CdbWriter.php index 3ceb620f67..b0a90c31d6 100644 --- a/includes/libs/cdb/Cdb.php +++ b/includes/libs/cdb/CdbWriter.php @@ -20,70 +20,10 @@ * @file */ -/** - * Read from a CDB file. - * Native and pure PHP implementations are provided. - * http://cr.yp.to/cdb.html - */ -abstract class CdbReader { - /** - * The file handle - */ - protected $handle; - - /** - * Open a file and return a subclass instance - * - * @param string $fileName - * - * @return CdbReader - */ - public static function open( $fileName ) { - return self::haveExtension() ? - new CdbReaderDBA( $fileName ) : - new CdbReaderPHP( $fileName ); - } - - /** - * Returns true if the native extension is available - * - * @return bool - */ - public static function haveExtension() { - if ( !function_exists( 'dba_handlers' ) ) { - return false; - } - $handlers = dba_handlers(); - if ( !in_array( 'cdb', $handlers ) || !in_array( 'cdb_make', $handlers ) ) { - return false; - } - - return true; - } - - /** - * Create the object and open the file - * - * @param string $fileName - */ - abstract public function __construct( $fileName ); - - /** - * Close the file. Optional, you can just let the variable go out of scope. - */ - abstract public function close(); - - /** - * Get a value with a given key. Only string values are supported. - * - * @param string $key - */ - abstract public function get( $key ); -} - /** * Write to a CDB file. * Native and pure PHP implementations are provided. + * http://cr.yp.to/cdb.html */ abstract class CdbWriter { /** @@ -154,10 +94,3 @@ abstract class CdbWriter { return substr( php_uname(), 0, 7 ) == 'Windows'; } } - -/** - * Exception for Cdb errors. - * This explicitly doesn't subclass MWException to encourage reuse. - */ -class CdbException extends Exception { -} diff --git a/includes/libs/cdb/CdbDBA.php b/includes/libs/cdb/CdbWriterDBA.php similarity index 76% rename from includes/libs/cdb/CdbDBA.php rename to includes/libs/cdb/CdbWriterDBA.php index efcaf21f86..1de371d425 100644 --- a/includes/libs/cdb/CdbDBA.php +++ b/includes/libs/cdb/CdbWriterDBA.php @@ -20,29 +20,6 @@ * @file */ -/** - * Reader class which uses the DBA extension - */ -class CdbReaderDBA extends CdbReader { - public function __construct( $fileName ) { - $this->handle = dba_open( $fileName, 'r-', 'cdb' ); - if ( !$this->handle ) { - throw new CdbException( 'Unable to open CDB file "' . $fileName . '"' ); - } - } - - public function close() { - if ( isset( $this->handle ) ) { - dba_close( $this->handle ); - } - unset( $this->handle ); - } - - public function get( $key ) { - return dba_fetch( $key, $this->handle ); - } -} - /** * Writer class which uses the DBA extension */ diff --git a/includes/libs/cdb/CdbPHP.php b/includes/libs/cdb/CdbWriterPHP.php similarity index 53% rename from includes/libs/cdb/CdbPHP.php rename to includes/libs/cdb/CdbWriterPHP.php index 19d747a739..bfc0d8780f 100644 --- a/includes/libs/cdb/CdbPHP.php +++ b/includes/libs/cdb/CdbWriterPHP.php @@ -24,266 +24,6 @@ * @file */ -/** - * Common functions for readers and writers - */ -class CdbFunctions { - /** - * Take a modulo of a signed integer as if it were an unsigned integer. - * $b must be less than 0x40000000 and greater than 0 - * - * @param int $a - * @param int $b - * - * @return int - */ - public static function unsignedMod( $a, $b ) { - if ( $a & 0x80000000 ) { - $m = ( $a & 0x7fffffff ) % $b + 2 * ( 0x40000000 % $b ); - - return $m % $b; - } else { - return $a % $b; - } - } - - /** - * Shift a signed integer right as if it were unsigned - * @param int $a - * @param int $b - * @return int - */ - public static function unsignedShiftRight( $a, $b ) { - if ( $b == 0 ) { - return $a; - } - if ( $a & 0x80000000 ) { - return ( ( $a & 0x7fffffff ) >> $b ) | ( 0x40000000 >> ( $b - 1 ) ); - } else { - return $a >> $b; - } - } - - /** - * The CDB hash function. - * - * @param string $s - * - * @return int - */ - public static function hash( $s ) { - $h = 5381; - $len = strlen( $s ); - for ( $i = 0; $i < $len; $i++ ) { - $h5 = ( $h << 5 ) & 0xffffffff; - // Do a 32-bit sum - // Inlined here for speed - $sum = ( $h & 0x3fffffff ) + ( $h5 & 0x3fffffff ); - $h = - ( - ( $sum & 0x40000000 ? 1 : 0 ) - + ( $h & 0x80000000 ? 2 : 0 ) - + ( $h & 0x40000000 ? 1 : 0 ) - + ( $h5 & 0x80000000 ? 2 : 0 ) - + ( $h5 & 0x40000000 ? 1 : 0 ) - ) << 30 - | ( $sum & 0x3fffffff ); - $h ^= ord( $s[$i] ); - $h &= 0xffffffff; - } - - return $h; - } -} - -/** - * CDB reader class - */ -class CdbReaderPHP extends CdbReader { - /** 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 CdbException - */ - public function __construct( $fileName ) { - $this->fileName = $fileName; - $this->handle = fopen( $fileName, 'rb' ); - if ( !$this->handle ) { - throw new CdbException( '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 ); - } else { - 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 CdbException - * @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 CdbException( - '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 CdbException( - '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 CdbException - * @return mixed - */ - protected function unpack31( $s ) { - $data = unpack( 'V', $s ); - if ( $data[1] > 0x7fffffff ) { - throw new CdbException( - '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 = CdbFunctions::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 = CdbFunctions::unsignedShiftRight( $u, 8 ); - $u = CdbFunctions::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 ); - } -} - /** * CDB writer class */ -- 2.20.1