72 if ( !function_exists(
'gzdeflate' ) ) {
73 throw new MWException(
"Need zlib support to read or write DiffHistoryBlob\n" );
83 if ( $this->mFrozen ) {
84 throw new MWException( __METHOD__ .
": Cannot add more items after sleep/wakeup" );
87 $this->mItems[] = $text;
88 $this->mSize += strlen( $text );
90 return count( $this->mItems ) - 1;
98 return $this->mItems[$key];
105 $this->mDefaultKey = $this->
addItem( $text );
112 return $this->
getItem( $this->mDefaultKey );
119 if ( !function_exists(
'xdiff_string_rabdiff' ) ) {
120 throw new MWException(
"Need xdiff 1.5+ support to write DiffHistoryBlob\n" );
122 if ( isset( $this->mDiffs ) ) {
126 if ( $this->mItems === [] ) {
145 $mItemsCount =
count( $this->mItems );
146 for ( $i = 0; $i < $mItemsCount; $i++ ) {
147 $text = $this->mItems[$i];
151 $mainTail = $sequences[
'main'][
'tail'];
152 if ( strlen( $text ) < strlen( $mainTail ) * $smallFactor ) {
158 $seq =& $sequences[$seqName];
159 $tail = $seq[
'tail'];
160 $diff = $this->
diff( $tail, $text );
161 $seq[
'diffs'][] = $diff;
163 $seq[
'tail'] = $text;
170 $this->mDiffMap = [];
171 foreach ( $sequences
as $seq ) {
172 if ( $seq[
'diffs'] === [] ) {
175 if ( $tail ===
'' ) {
176 $this->mDiffs[] = $seq[
'diffs'][0];
178 $head = $this->
patch(
'', $seq[
'diffs'][0] );
179 $this->mDiffs[] = $this->
diff( $tail, $head );
181 $this->mDiffMap[] = $seq[
'map'][0];
182 $diffsCount =
count( $seq[
'diffs'] );
183 for ( $i = 1; $i < $diffsCount; $i++ ) {
184 $this->mDiffs[] = $seq[
'diffs'][$i];
185 $this->mDiffMap[] = $seq[
'map'][$i];
187 $tail = $seq[
'tail'];
197 # Need to do a null concatenation with warnings off, due to bugs in the current version of xdiff
198 # "String is not zero-terminated"
199 Wikimedia\suppressWarnings();
200 $diff = xdiff_string_rabdiff( $t1, $t2 ) .
'';
201 Wikimedia\restoreWarnings();
211 if ( function_exists(
'xdiff_string_bpatch' ) ) {
212 Wikimedia\suppressWarnings();
213 $text = xdiff_string_bpatch(
$base, $diff ) .
'';
214 Wikimedia\restoreWarnings();
218 # Pure PHP implementation
220 $header = unpack(
'Vofp/Vcsize', substr( $diff, 0, 8 ) );
222 # Check the checksum if hash extension is available
224 if ( $ofp !==
false && $ofp !== substr( $diff, 0, 4 ) ) {
225 wfDebug( __METHOD__ .
": incorrect base checksum\n" );
229 wfDebug( __METHOD__ .
": incorrect base length\n" );
235 while ( $p < strlen( $diff ) ) {
236 $x = unpack(
'Cop', substr( $diff, $p, 1 ) );
241 $x = unpack(
'Csize', substr( $diff, $p, 1 ) );
243 $out .= substr( $diff, $p, $x[
'size'] );
247 $x = unpack(
'Vcsize', substr( $diff, $p, 4 ) );
249 $out .= substr( $diff, $p, $x[
'csize'] );
253 $x = unpack(
'Voff/Vcsize', substr( $diff, $p, 8 ) );
255 $out .= substr(
$base, $x[
'off'], $x[
'csize'] );
258 wfDebug( __METHOD__ .
": invalid op\n" );
273 if ( !function_exists(
'hash' ) ) {
278 if ( $init ===
null ) {
279 $init = str_repeat(
"\xf0", 205 ) .
"\xee" . str_repeat(
"\xf0", 67 ) .
"\x02";
285 return strrev( hash(
'adler32', $init .
$s,
true ) );
289 if ( !$this->mDiffs ) {
293 $mDiffsCount =
count( $this->mDiffs );
294 for ( $diffKey = 0; $diffKey < $mDiffsCount; $diffKey++ ) {
295 $textKey = $this->mDiffMap[$diffKey];
296 $text = $this->
patch( $tail, $this->mDiffs[$diffKey] );
297 $this->mItems[$textKey] = $text;
307 if ( $this->mItems === [] ) {
313 foreach ( $this->mDiffMap
as $i ) {
325 if ( isset( $this->mDefaultKey ) ) {
328 $this->mCompressed = gzdeflate(
serialize( $info ) );
329 return [
'mCompressed' ];
334 $this->mFrozen =
true;
335 $info =
unserialize( gzinflate( $this->mCompressed ) );
336 unset( $this->mCompressed );
343 if ( isset( $info[
'default'] ) ) {
344 $this->mDefaultKey = $info[
'default'];
346 $this->mDiffs = $info[
'diffs'];
347 if ( isset( $info[
'base'] ) ) {
349 $this->mDiffMap = range( 0,
count( $this->mDiffs ) - 1 );
350 array_unshift( $this->mDiffs,
351 pack(
'VVCV', 0, 0, self::XDL_BDOP_INSB, strlen( $info[
'base'] ) ) .
355 $map = explode(
',', $info[
'map'] );
357 $this->mDiffMap = [];
358 foreach ( $map
as $i ) {
360 $this->mDiffMap[] = $cur;
373 return $this->mSize < $this->mMaxSize