67 private const XDL_BDOP_INS = 1;
68 private const XDL_BDOP_CPY = 2;
69 private const XDL_BDOP_INSB = 3;
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 (
string)( count( $this->mItems ) - 1 );
98 return $this->mItems[(int)$key];
105 $this->mDefaultKey = $this->
addItem( $text );
112 return $this->
getItem( $this->mDefaultKey );
118 private function compress() {
119 if ( !function_exists(
'xdiff_string_rabdiff' ) ) {
120 throw new MWException(
"Need xdiff 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 ) {
159 $tail = $sequences[$seqName][
'tail'];
160 $diff = $this->diff( $tail, $text );
161 $sequences[$seqName][
'diffs'][] = $diff;
162 $sequences[$seqName][
'map'][] = $i;
163 $sequences[$seqName][
'tail'] = $text;
169 $this->mDiffMap = [];
170 foreach ( $sequences as $seq ) {
171 if ( $seq[
'diffs'] === [] ) {
174 if ( $tail ===
'' ) {
175 $this->mDiffs[] = $seq[
'diffs'][0];
177 $head = $this->patch(
'', $seq[
'diffs'][0] );
178 $this->mDiffs[] = $this->diff( $tail, $head );
180 $this->mDiffMap[] = $seq[
'map'][0];
181 $diffsCount = count( $seq[
'diffs'] );
182 for ( $i = 1; $i < $diffsCount; $i++ ) {
183 $this->mDiffs[] = $seq[
'diffs'][$i];
184 $this->mDiffMap[] = $seq[
'map'][$i];
186 $tail = $seq[
'tail'];
195 private function diff( $t1, $t2 ) {
196 return xdiff_string_rabdiff( $t1, $t2 );
204 private function patch(
$base, $diff ) {
205 if ( function_exists(
'xdiff_string_bpatch' ) ) {
206 return xdiff_string_bpatch(
$base, $diff );
209 # Pure PHP implementation
211 $header = unpack(
'Vofp/Vcsize', substr( $diff, 0, 8 ) );
215 if ( $ofp !== substr( $diff, 0, 4 ) ) {
216 wfDebug( __METHOD__ .
": incorrect base checksum" );
220 wfDebug( __METHOD__ .
": incorrect base length" );
226 while ( $p < strlen( $diff ) ) {
227 $x = unpack(
'Cop', substr( $diff, $p, 1 ) );
231 case self::XDL_BDOP_INS:
232 $x = unpack(
'Csize', substr( $diff, $p, 1 ) );
234 $out .= substr( $diff, $p, $x[
'size'] );
237 case self::XDL_BDOP_INSB:
238 $x = unpack(
'Vcsize', substr( $diff, $p, 4 ) );
240 $out .= substr( $diff, $p, $x[
'csize'] );
243 case self::XDL_BDOP_CPY:
244 $x = unpack(
'Voff/Vcsize', substr( $diff, $p, 8 ) );
246 $out .= substr(
$base, $x[
'off'], $x[
'csize'] );
249 wfDebug( __METHOD__ .
": invalid op" );
265 if ( $init ===
null ) {
266 $init = str_repeat(
"\xf0", 205 ) .
"\xee" . str_repeat(
"\xf0", 67 ) .
"\x02";
272 return strrev( hash(
'adler32', $init .
$s,
true ) );
275 private function uncompress() {
276 if ( !$this->mDiffs ) {
280 $mDiffsCount = count( $this->mDiffs );
281 for ( $diffKey = 0; $diffKey < $mDiffsCount; $diffKey++ ) {
282 $textKey = $this->mDiffMap[$diffKey];
283 $text = $this->patch( $tail, $this->mDiffs[$diffKey] );
284 $this->mItems[$textKey] = $text;
294 if ( $this->mItems === [] ) {
300 foreach ( $this->mDiffMap as $i ) {
312 if ( isset( $this->mDefaultKey ) ) {
315 $this->mCompressed = gzdeflate(
serialize( $info ) );
316 return [
'mCompressed' ];
321 $this->mFrozen =
true;
322 $info =
unserialize( gzinflate( $this->mCompressed ) );
323 $this->mCompressed =
null;
330 if ( isset( $info[
'default'] ) ) {
331 $this->mDefaultKey = $info[
'default'];
333 $this->mDiffs = $info[
'diffs'];
334 if ( isset( $info[
'base'] ) ) {
336 $this->mDiffMap = range( 0, count( $this->mDiffs ) - 1 );
337 array_unshift( $this->mDiffs,
338 pack(
'VVCV', 0, 0, self::XDL_BDOP_INSB, strlen( $info[
'base'] ) ) .
342 $map = explode(
',', $info[
'map'] );
344 $this->mDiffMap = [];
345 foreach ( $map as $i ) {
347 $this->mDiffMap[] = $cur;
360 return $this->mSize < $this->mMaxSize
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Diff-based history compression Requires xdiff and zlib.
bool $mFrozen
True if the object is locked against further writes.
xdiffAdler32( $s)
Compute a binary "Adler-32" checksum as defined by LibXDiff, i.e.
int $mMaxSize
The maximum uncompressed size before the object becomes sad Should be less than max_allowed_packet.
int $mMaxCount
The maximum number of text items before the object becomes sad.
isHappy()
Helper function for compression jobs Returns true until the object is "full" and ready to be committe...
Base class for general text storage via the "object" flag in old_flags, or two-part external storage ...