From 225ff51e0930589a5e6e6899f4a47b6f6af98cfa Mon Sep 17 00:00:00 2001 From: Tim Starling Date: Sat, 1 Nov 2008 13:36:08 +0000 Subject: [PATCH] Implemented xdiff_string_bpatch() in pure PHP, allowing DiffHistoryBlob objects to be decompressed without libxdiff. Negligible performance difference. This provides a stability guarantee -- if libxdiff disappears or changes, we can continue to read old blobs. It also means we don't have to package libxdiff for all app servers. --- includes/HistoryBlob.php | 69 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 7 deletions(-) diff --git a/includes/HistoryBlob.php b/includes/HistoryBlob.php index 7489399770..323d10745b 100644 --- a/includes/HistoryBlob.php +++ b/includes/HistoryBlob.php @@ -319,11 +319,13 @@ class DiffHistoryBlob implements HistoryBlob { * The maximum number of text items before the object becomes sad */ var $mMaxCount = 100; + + /** Constants from xdiff.h */ + const XDL_BDOP_INS = 1; + const XDL_BDOP_CPY = 2; + const XDL_BDOP_INSB = 3; function __construct() { - if ( !function_exists( 'xdiff_string_bdiff' ) ){ - throw new MWException( "Need xdiff 1.5+ support to read or write DiffHistoryBlob\n" ); - } if ( !function_exists( 'gzdeflate' ) ) { throw new MWException( "Need zlib support to read or write DiffHistoryBlob\n" ); } @@ -353,6 +355,9 @@ class DiffHistoryBlob implements HistoryBlob { } function compress() { + if ( !function_exists( 'xdiff_string_bdiff' ) ){ + throw new MWException( "Need xdiff 1.5+ support to write DiffHistoryBlob\n" ); + } if ( isset( $this->mDiffs ) ) { // Already compressed return; @@ -431,10 +436,60 @@ class DiffHistoryBlob implements HistoryBlob { } function patch( $base, $diff ) { - wfSuppressWarnings(); - $text = xdiff_string_bpatch( $base, $diff ) . ''; - wfRestoreWarnings(); - return $text; + if ( function_exists( 'xdiff_string_bpatch' ) ) { + wfSuppressWarnings(); + $text = xdiff_string_bpatch( $base, $diff ) . ''; + wfRestoreWarnings(); + return $text; + } + + # Pure PHP implementation + + $header = unpack( 'Vofp/Vcsize', substr( $diff, 0, 8 ) ); + + # Check the checksum if mhash is available + if ( extension_loaded( 'mhash' ) ) { + $ofp = mhash( MHASH_ADLER32, $base ); + if ( $ofp !== substr( $diff, 0, 4 ) ) { + wfDebug( __METHOD__. ": incorrect base checksum\n" ); + return false; + } + } + if ( $header['csize'] != strlen( $base ) ) { + wfDebug( __METHOD__. ": incorrect base length {$header['csize']} -> {strlen($base)}\n" ); + return false; + } + + $p = 8; + $out = ''; + while ( $p < strlen( $diff ) ) { + $x = unpack( 'Cop', substr( $diff, $p, 1 ) ); + $op = $x['op']; + ++$p; + switch ( $op ) { + case self::XDL_BDOP_INS: + $x = unpack( 'Csize', substr( $diff, $p, 1 ) ); + $p++; + $out .= substr( $diff, $p, $x['size'] ); + $p += $x['size']; + break; + case self::XDL_BDOP_INSB: + $x = unpack( 'Vcsize', substr( $diff, $p, 4 ) ); + $p += 4; + $out .= substr( $diff, $p, $x['csize'] ); + $p += $x['csize']; + break; + case self::XDL_BDOP_CPY: + $x = unpack( 'Voff/Vcsize', substr( $diff, $p, 8 ) ); + $p += 8; + $out .= substr( $base, $x['off'], $x['csize'] ); + break; + default: + wfDebug( __METHOD__.": invalid op\n" ); + return false; + } + } + return $out; } function uncompress() { -- 2.20.1