83 if ( !function_exists(
'gzdeflate' ) ) {
84 throw new MWException(
"Need zlib support to read or write this "
85 .
"kind of history object (ConcatenatedGzipHistoryBlob)\n" );
96 if ( !isset( $this->mItems[$hash] ) ) {
97 $this->mItems[$hash] = $text;
98 $this->mSize += strlen( $text );
109 if ( array_key_exists( $hash, $this->mItems ) ) {
110 return $this->mItems[$hash];
122 $this->mDefaultHash = $this->
addItem( $text );
130 return $this->
getItem( $this->mDefaultHash );
139 $this->mSize -= strlen( $this->mItems[$hash] );
140 unset( $this->mItems[$hash] );
147 if ( !$this->mCompressed ) {
148 $this->mItems = gzdeflate(
serialize( $this->mItems ) );
149 $this->mCompressed =
true;
157 if ( $this->mCompressed ) {
158 $this->mItems =
unserialize( gzinflate( $this->mItems ) );
159 $this->mCompressed =
false;
168 return [
'mVersion',
'mCompressed',
'mItems',
'mDefaultHash' ];
182 return $this->mSize < $this->mMaxSize
213 $this->mHash = $hash;
245 if ( isset( self::$blobCache[$this->mOldId] ) ) {
249 $row =
$dbr->selectRow(
251 [
'old_flags',
'old_text' ],
252 [
'old_id' => $this->mOldId ]
259 $flags = explode(
',', $row->old_flags );
260 if ( in_array(
'external',
$flags ) ) {
261 $url = $row->old_text;
262 $parts = explode(
'://', $url, 2 );
263 if ( !isset( $parts[1] ) || $parts[1] ==
'' ) {
270 if ( !in_array(
'object',
$flags ) ) {
274 if ( in_array(
'gzip',
$flags ) ) {
282 if ( !is_object( $obj ) ) {
290 self::$blobCache = [ $this->mOldId => $obj ];
293 return $obj->getItem( $this->mHash );
322 $this->mCurId = $curid;
340 $row =
$dbr->selectRow(
'cur', [
'cur_text' ], [
'cur_id' => $this->mCurId ] );
344 return $row->cur_text;
397 if ( !function_exists(
'gzdeflate' ) ) {
398 throw new MWException(
"Need zlib support to read or write DiffHistoryBlob\n" );
408 if ( $this->mFrozen ) {
409 throw new MWException( __METHOD__ .
": Cannot add more items after sleep/wakeup" );
412 $this->mItems[] = $text;
413 $this->mSize += strlen( $text );
414 $this->mDiffs =
null;
415 return count( $this->mItems ) - 1;
423 return $this->mItems[$key];
430 $this->mDefaultKey = $this->
addItem( $text );
437 return $this->
getItem( $this->mDefaultKey );
444 if ( !function_exists(
'xdiff_string_rabdiff' ) ) {
445 throw new MWException(
"Need xdiff 1.5+ support to write DiffHistoryBlob\n" );
447 if ( isset( $this->mDiffs ) ) {
451 if ( !
count( $this->mItems ) ) {
471 $mItemsCount =
count( $this->mItems );
472 for ( $i = 0; $i < $mItemsCount; $i++ ) {
473 $text = $this->mItems[$i];
477 $mainTail = $sequences[
'main'][
'tail'];
478 if ( strlen( $text ) < strlen( $mainTail ) * $smallFactor ) {
484 $seq =& $sequences[$seqName];
485 $tail = $seq[
'tail'];
486 $diff = $this->
diff( $tail, $text );
487 $seq[
'diffs'][] = $diff;
489 $seq[
'tail'] = $text;
496 $this->mDiffMap = [];
497 foreach ( $sequences
as $seq ) {
498 if ( !
count( $seq[
'diffs'] ) ) {
501 if ( $tail ===
'' ) {
502 $this->mDiffs[] = $seq[
'diffs'][0];
504 $head = $this->
patch(
'', $seq[
'diffs'][0] );
505 $this->mDiffs[] = $this->
diff( $tail, $head );
507 $this->mDiffMap[] = $seq[
'map'][0];
508 $diffsCount =
count( $seq[
'diffs'] );
509 for ( $i = 1; $i < $diffsCount; $i++ ) {
510 $this->mDiffs[] = $seq[
'diffs'][$i];
511 $this->mDiffMap[] = $seq[
'map'][$i];
513 $tail = $seq[
'tail'];
523 # Need to do a null concatenation with warnings off, due to bugs in the current version of xdiff
524 # "String is not zero-terminated"
525 MediaWiki\suppressWarnings();
526 $diff = xdiff_string_rabdiff( $t1, $t2 ) .
'';
527 MediaWiki\restoreWarnings();
537 if ( function_exists(
'xdiff_string_bpatch' ) ) {
538 MediaWiki\suppressWarnings();
539 $text = xdiff_string_bpatch(
$base, $diff ) .
'';
540 MediaWiki\restoreWarnings();
544 # Pure PHP implementation
546 $header = unpack(
'Vofp/Vcsize', substr( $diff, 0, 8 ) );
548 # Check the checksum if hash extension is available
550 if ( $ofp !==
false && $ofp !== substr( $diff, 0, 4 ) ) {
551 wfDebug( __METHOD__ .
": incorrect base checksum\n" );
555 wfDebug( __METHOD__ .
": incorrect base length\n" );
561 while ( $p < strlen( $diff ) ) {
562 $x = unpack(
'Cop', substr( $diff, $p, 1 ) );
567 $x = unpack(
'Csize', substr( $diff, $p, 1 ) );
569 $out .= substr( $diff, $p, $x[
'size'] );
573 $x = unpack(
'Vcsize', substr( $diff, $p, 4 ) );
575 $out .= substr( $diff, $p, $x[
'csize'] );
579 $x = unpack(
'Voff/Vcsize', substr( $diff, $p, 8 ) );
581 $out .= substr(
$base, $x[
'off'], $x[
'csize'] );
584 wfDebug( __METHOD__ .
": invalid op\n" );
599 if ( !function_exists(
'hash' ) ) {
604 if ( $init ===
null ) {
605 $init = str_repeat(
"\xf0", 205 ) .
"\xee" . str_repeat(
"\xf0", 67 ) .
"\x02";
611 return strrev( hash(
'adler32', $init .
$s,
true ) );
615 if ( !$this->mDiffs ) {
619 $mDiffsCount =
count( $this->mDiffs );
620 for ( $diffKey = 0; $diffKey < $mDiffsCount; $diffKey++ ) {
621 $textKey = $this->mDiffMap[$diffKey];
622 $text = $this->
patch( $tail, $this->mDiffs[$diffKey] );
623 $this->mItems[$textKey] = $text;
633 if ( !
count( $this->mItems ) ) {
640 foreach ( $this->mDiffMap
as $i ) {
652 if ( isset( $this->mDefaultKey ) ) {
655 $this->mCompressed = gzdeflate(
serialize( $info ) );
656 return [
'mCompressed' ];
661 $this->mFrozen =
true;
662 $info =
unserialize( gzinflate( $this->mCompressed ) );
663 unset( $this->mCompressed );
670 if ( isset( $info[
'default'] ) ) {
671 $this->mDefaultKey = $info[
'default'];
673 $this->mDiffs = $info[
'diffs'];
674 if ( isset( $info[
'base'] ) ) {
676 $this->mDiffMap = range( 0,
count( $this->mDiffs ) - 1 );
677 array_unshift( $this->mDiffs,
678 pack(
'VVCV', 0, 0, self::XDL_BDOP_INSB, strlen( $info[
'base'] ) ) .
682 $map = explode(
',', $info[
'map'] );
684 $this->mDiffMap = [];
685 foreach ( $map
as $i ) {
687 $this->mDiffMap[] = $cur;
700 return $this->mSize < $this->mMaxSize