MediaWiki  1.23.0
CdbPHP.php
Go to the documentation of this file.
1 <?php
30 class CdbFunctions {
40  public static function unsignedMod( $a, $b ) {
41  if ( $a & 0x80000000 ) {
42  $m = ( $a & 0x7fffffff ) % $b + 2 * ( 0x40000000 % $b );
43 
44  return $m % $b;
45  } else {
46  return $a % $b;
47  }
48  }
49 
56  public static function unsignedShiftRight( $a, $b ) {
57  if ( $b == 0 ) {
58  return $a;
59  }
60  if ( $a & 0x80000000 ) {
61  return ( ( $a & 0x7fffffff ) >> $b ) | ( 0x40000000 >> ( $b - 1 ) );
62  } else {
63  return $a >> $b;
64  }
65  }
66 
74  public static function hash( $s ) {
75  $h = 5381;
76  $len = strlen( $s );
77  for ( $i = 0; $i < $len; $i++ ) {
78  $h5 = ( $h << 5 ) & 0xffffffff;
79  // Do a 32-bit sum
80  // Inlined here for speed
81  $sum = ( $h & 0x3fffffff ) + ( $h5 & 0x3fffffff );
82  $h =
83  (
84  ( $sum & 0x40000000 ? 1 : 0 )
85  + ( $h & 0x80000000 ? 2 : 0 )
86  + ( $h & 0x40000000 ? 1 : 0 )
87  + ( $h5 & 0x80000000 ? 2 : 0 )
88  + ( $h5 & 0x40000000 ? 1 : 0 )
89  ) << 30
90  | ( $sum & 0x3fffffff );
91  $h ^= ord( $s[$i] );
92  $h &= 0xffffffff;
93  }
94 
95  return $h;
96  }
97 }
98 
102 class CdbReaderPHP extends CdbReader {
105 
106  /* number of hash slots searched under this key */
107  var $loop;
108 
109  /* initialized if loop is nonzero */
110  var $khash;
111 
112  /* initialized if loop is nonzero */
113  var $kpos;
114 
115  /* initialized if loop is nonzero */
116  var $hpos;
117 
118  /* initialized if loop is nonzero */
119  var $hslots;
120 
121  /* initialized if findNext() returns true */
122  var $dpos;
123 
124  /* initialized if cdb_findnext() returns 1 */
125  var $dlen;
126 
131  public function __construct( $fileName ) {
132  $this->fileName = $fileName;
133  $this->handle = fopen( $fileName, 'rb' );
134  if ( !$this->handle ) {
135  throw new CdbException( 'Unable to open CDB file "' . $this->fileName . '".' );
136  }
137  $this->findStart();
138  }
139 
140  public function close() {
141  if ( isset( $this->handle ) ) {
142  fclose( $this->handle );
143  }
144  unset( $this->handle );
145  }
146 
151  public function get( $key ) {
152  // strval is required
153  if ( $this->find( strval( $key ) ) ) {
154  return $this->read( $this->dlen, $this->dpos );
155  } else {
156  return false;
157  }
158  }
159 
165  protected function match( $key, $pos ) {
166  $buf = $this->read( strlen( $key ), $pos );
167 
168  return $buf === $key;
169  }
170 
171  protected function findStart() {
172  $this->loop = 0;
173  }
174 
181  protected function read( $length, $pos ) {
182  if ( fseek( $this->handle, $pos ) == -1 ) {
183  // This can easily happen if the internal pointers are incorrect
184  throw new CdbException(
185  'Seek failed, file "' . $this->fileName . '" may be corrupted.' );
186  }
187 
188  if ( $length == 0 ) {
189  return '';
190  }
191 
192  $buf = fread( $this->handle, $length );
193  if ( $buf === false || strlen( $buf ) !== $length ) {
194  throw new CdbException(
195  'Read from CDB file failed, file "' . $this->fileName . '" may be corrupted.' );
196  }
197 
198  return $buf;
199  }
200 
207  protected function unpack31( $s ) {
208  $data = unpack( 'V', $s );
209  if ( $data[1] > 0x7fffffff ) {
210  throw new CdbException(
211  'Error in CDB file "' . $this->fileName . '", integer too big.' );
212  }
213 
214  return $data[1];
215  }
216 
222  protected function unpackSigned( $s ) {
223  $data = unpack( 'va/vb', $s );
224 
225  return $data['a'] | ( $data['b'] << 16 );
226  }
227 
232  protected function findNext( $key ) {
233  if ( !$this->loop ) {
234  $u = CdbFunctions::hash( $key );
235  $buf = $this->read( 8, ( $u << 3 ) & 2047 );
236  $this->hslots = $this->unpack31( substr( $buf, 4 ) );
237  if ( !$this->hslots ) {
238  return false;
239  }
240  $this->hpos = $this->unpack31( substr( $buf, 0, 4 ) );
241  $this->khash = $u;
242  $u = CdbFunctions::unsignedShiftRight( $u, 8 );
243  $u = CdbFunctions::unsignedMod( $u, $this->hslots );
244  $u <<= 3;
245  $this->kpos = $this->hpos + $u;
246  }
247 
248  while ( $this->loop < $this->hslots ) {
249  $buf = $this->read( 8, $this->kpos );
250  $pos = $this->unpack31( substr( $buf, 4 ) );
251  if ( !$pos ) {
252  return false;
253  }
254  $this->loop += 1;
255  $this->kpos += 8;
256  if ( $this->kpos == $this->hpos + ( $this->hslots << 3 ) ) {
257  $this->kpos = $this->hpos;
258  }
259  $u = $this->unpackSigned( substr( $buf, 0, 4 ) );
260  if ( $u === $this->khash ) {
261  $buf = $this->read( 8, $pos );
262  $keyLen = $this->unpack31( substr( $buf, 0, 4 ) );
263  if ( $keyLen == strlen( $key ) && $this->match( $key, $pos + 8 ) ) {
264  // Found
265  $this->dlen = $this->unpack31( substr( $buf, 4 ) );
266  $this->dpos = $pos + 8 + $keyLen;
267 
268  return true;
269  }
270  }
271  }
272 
273  return false;
274  }
275 
280  protected function find( $key ) {
281  $this->findStart();
282 
283  return $this->findNext( $key );
284  }
285 }
286 
290 class CdbWriterPHP extends CdbWriter {
291  var $hplist;
293 
297  public function __construct( $fileName ) {
298  $this->realFileName = $fileName;
299  $this->tmpFileName = $fileName . '.tmp.' . mt_rand( 0, 0x7fffffff );
300  $this->handle = fopen( $this->tmpFileName, 'wb' );
301  if ( !$this->handle ) {
302  $this->throwException(
303  'Unable to open CDB file "' . $this->tmpFileName . '" for write.' );
304  }
305  $this->hplist = array();
306  $this->numentries = 0;
307  $this->pos = 2048; // leaving space for the pointer array, 256 * 8
308  if ( fseek( $this->handle, $this->pos ) == -1 ) {
309  $this->throwException( 'fseek failed in file "' . $this->tmpFileName . '".' );
310  }
311  }
312 
317  public function set( $key, $value ) {
318  if ( strval( $key ) === '' ) {
319  // DBA cross-check hack
320  return;
321  }
322  $this->addbegin( strlen( $key ), strlen( $value ) );
323  $this->write( $key );
324  $this->write( $value );
325  $this->addend( strlen( $key ), strlen( $value ), CdbFunctions::hash( $key ) );
326  }
327 
331  public function close() {
332  $this->finish();
333  if ( isset( $this->handle ) ) {
334  fclose( $this->handle );
335  }
336  if ( $this->isWindows() && file_exists( $this->realFileName ) ) {
337  unlink( $this->realFileName );
338  }
339  if ( !rename( $this->tmpFileName, $this->realFileName ) ) {
340  $this->throwException( 'Unable to move the new CDB file into place.' );
341  }
342  unset( $this->handle );
343  }
344 
349  protected function write( $buf ) {
350  $len = fwrite( $this->handle, $buf );
351  if ( $len !== strlen( $buf ) ) {
352  $this->throwException( 'Error writing to CDB file "' . $this->tmpFileName . '".' );
353  }
354  }
355 
360  protected function posplus( $len ) {
361  $newpos = $this->pos + $len;
362  if ( $newpos > 0x7fffffff ) {
363  $this->throwException(
364  'A value in the CDB file "' . $this->tmpFileName . '" is too large.' );
365  }
366  $this->pos = $newpos;
367  }
368 
374  protected function addend( $keylen, $datalen, $h ) {
375  $this->hplist[] = array(
376  'h' => $h,
377  'p' => $this->pos
378  );
379 
380  $this->numentries++;
381  $this->posplus( 8 );
382  $this->posplus( $keylen );
383  $this->posplus( $datalen );
384  }
385 
391  protected function addbegin( $keylen, $datalen ) {
392  if ( $keylen > 0x7fffffff ) {
393  $this->throwException( 'Key length too long in file "' . $this->tmpFileName . '".' );
394  }
395  if ( $datalen > 0x7fffffff ) {
396  $this->throwException( 'Data length too long in file "' . $this->tmpFileName . '".' );
397  }
398  $buf = pack( 'VV', $keylen, $datalen );
399  $this->write( $buf );
400  }
401 
405  protected function finish() {
406  // Hack for DBA cross-check
407  $this->hplist = array_reverse( $this->hplist );
408 
409  // Calculate the number of items that will be in each hashtable
410  $counts = array_fill( 0, 256, 0 );
411  foreach ( $this->hplist as $item ) {
412  ++$counts[255 & $item['h']];
413  }
414 
415  // Fill in $starts with the *end* indexes
416  $starts = array();
417  $pos = 0;
418  for ( $i = 0; $i < 256; ++$i ) {
419  $pos += $counts[$i];
420  $starts[$i] = $pos;
421  }
422 
423  // Excessively clever and indulgent code to simultaneously fill $packedTables
424  // with the packed hashtables, and adjust the elements of $starts
425  // to actually point to the starts instead of the ends.
426  $packedTables = array_fill( 0, $this->numentries, false );
427  foreach ( $this->hplist as $item ) {
428  $packedTables[--$starts[255 & $item['h']]] = $item;
429  }
430 
431  $final = '';
432  for ( $i = 0; $i < 256; ++$i ) {
433  $count = $counts[$i];
434 
435  // The size of the hashtable will be double the item count.
436  // The rest of the slots will be empty.
437  $len = $count + $count;
438  $final .= pack( 'VV', $this->pos, $len );
439 
440  $hashtable = array();
441  for ( $u = 0; $u < $len; ++$u ) {
442  $hashtable[$u] = array( 'h' => 0, 'p' => 0 );
443  }
444 
445  // Fill the hashtable, using the next empty slot if the hashed slot
446  // is taken.
447  for ( $u = 0; $u < $count; ++$u ) {
448  $hp = $packedTables[$starts[$i] + $u];
449  $where = CdbFunctions::unsignedMod(
450  CdbFunctions::unsignedShiftRight( $hp['h'], 8 ), $len );
451  while ( $hashtable[$where]['p'] ) {
452  if ( ++$where == $len ) {
453  $where = 0;
454  }
455  }
456  $hashtable[$where] = $hp;
457  }
458 
459  // Write the hashtable
460  for ( $u = 0; $u < $len; ++$u ) {
461  $buf = pack( 'vvV',
462  $hashtable[$u]['h'] & 0xffff,
463  CdbFunctions::unsignedShiftRight( $hashtable[$u]['h'], 16 ),
464  $hashtable[$u]['p'] );
465  $this->write( $buf );
466  $this->posplus( 8 );
467  }
468  }
469 
470  // Write the pointer array at the start of the file
471  rewind( $this->handle );
472  if ( ftell( $this->handle ) != 0 ) {
473  $this->throwException( 'Error rewinding to start of file "' . $this->tmpFileName . '".' );
474  }
475  $this->write( $final );
476  }
477 
484  protected function throwException( $msg ) {
485  if ( $this->handle ) {
486  fclose( $this->handle );
487  unlink( $this->tmpFileName );
488  }
489  throw new CdbException( $msg );
490  }
491 }
CdbReaderPHP\close
close()
Close the file.
Definition: CdbPHP.php:140
CdbFunctions\unsignedShiftRight
static unsignedShiftRight( $a, $b)
Shift a signed integer right as if it were unsigned.
Definition: CdbPHP.php:56
php
skin txt MediaWiki includes four core it has been set as the default in MediaWiki since the replacing Monobook it had been been the default skin since before being replaced by Vector largely rewritten in while keeping its appearance Several legacy skins were removed in the as the burden of supporting them became too heavy to bear Those in etc for skin dependent CSS etc for skin dependent JavaScript These can also be customised on a per user by etc This feature has led to a wide variety of user styles becoming that gallery is a good place to ending in php
Definition: skin.txt:62
CdbFunctions\hash
static hash( $s)
The CDB hash function.
Definition: CdbPHP.php:74
CdbWriterPHP\close
close()
Definition: CdbPHP.php:331
$s
$s
Definition: mergeMessageFileList.php:156
CdbFunctions\unsignedMod
static unsignedMod( $a, $b)
Take a modulo of a signed integer as if it were an unsigned integer.
Definition: CdbPHP.php:40
CdbReaderPHP\$khash
$khash
Definition: CdbPHP.php:110
CdbReaderPHP\findStart
findStart()
Definition: CdbPHP.php:171
CdbWriterPHP\addbegin
addbegin( $keylen, $datalen)
Definition: CdbPHP.php:391
CdbWriterPHP\__construct
__construct( $fileName)
Definition: CdbPHP.php:297
CdbReaderPHP\__construct
__construct( $fileName)
Definition: CdbPHP.php:131
CdbWriterPHP\throwException
throwException( $msg)
Clean up the temp file and throw an exception.
Definition: CdbPHP.php:484
CdbReaderPHP\findNext
findNext( $key)
Definition: CdbPHP.php:232
CdbWriterPHP\$hplist
$hplist
Definition: CdbPHP.php:291
CdbReaderPHP\$dlen
$dlen
Definition: CdbPHP.php:125
CdbWriterPHP\$numentries
$numentries
Definition: CdbPHP.php:292
array
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
CdbWriterPHP\write
write( $buf)
Definition: CdbPHP.php:349
CdbReaderPHP\$dpos
$dpos
Definition: CdbPHP.php:122
CdbFunctions
Common functions for readers and writers.
Definition: CdbPHP.php:30
CdbWriterPHP
CDB writer class.
Definition: CdbPHP.php:290
$value
$value
Definition: styleTest.css.php:45
CdbWriterPHP\posplus
posplus( $len)
Definition: CdbPHP.php:360
CdbReaderPHP\unpackSigned
unpackSigned( $s)
Unpack a 32-bit signed integer.
Definition: CdbPHP.php:222
CdbReaderPHP\unpack31
unpack31( $s)
Unpack an unsigned integer and throw an exception if it needs more than 31 bits.
Definition: CdbPHP.php:207
CdbWriter\isWindows
isWindows()
Are we running on Windows?
Definition: Cdb.php:150
CdbReaderPHP
CDB reader class.
Definition: CdbPHP.php:102
$count
$count
Definition: UtfNormalTest2.php:96
CdbReader
Read from a CDB file.
Definition: Cdb.php:28
CdbException
Exception for Cdb errors.
Definition: Cdb.php:159
CdbReaderPHP\read
read( $length, $pos)
Definition: CdbPHP.php:181
CdbReaderPHP\match
match( $key, $pos)
Definition: CdbPHP.php:165
CdbWriter
Write to a CDB file.
Definition: Cdb.php:88
CdbReaderPHP\$hslots
$hslots
Definition: CdbPHP.php:119
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
CdbWriterPHP\$pos
$pos
Definition: CdbPHP.php:292
CdbReaderPHP\find
find( $key)
Definition: CdbPHP.php:280
CdbReaderPHP\$kpos
$kpos
Definition: CdbPHP.php:113
CdbReaderPHP\$loop
$loop
Definition: CdbPHP.php:107
CdbReaderPHP\$hpos
$hpos
Definition: CdbPHP.php:116
CdbWriterPHP\finish
finish()
Definition: CdbPHP.php:405
CdbWriterPHP\addend
addend( $keylen, $datalen, $h)
Definition: CdbPHP.php:374
CdbReaderPHP\$fileName
$fileName
The filename.
Definition: CdbPHP.php:104