70 private const XDL_BDOP_INS = 1;
71 private const XDL_BDOP_CPY = 2;
72 private const XDL_BDOP_INSB = 3;
75 if ( !function_exists(
'gzdeflate' ) ) {
76 throw new MWException(
"Need zlib support to read or write DiffHistoryBlob\n" );
86 if ( $this->mFrozen ) {
87 throw new MWException( __METHOD__ .
": Cannot add more items after sleep/wakeup" );
90 $this->mItems[] = $text;
91 $this->mSize += strlen( $text );
93 return (
string)( count( $this->mItems ) - 1 );
101 return $this->mItems[(int)$key];
108 $this->mDefaultKey = $this->
addItem( $text );
115 return $this->
getItem( $this->mDefaultKey );
121 private function compress() {
122 if ( !function_exists(
'xdiff_string_rabdiff' ) ) {
123 throw new MWException(
"Need xdiff support to write DiffHistoryBlob\n" );
125 if ( isset( $this->mDiffs ) ) {
129 if ( $this->mItems === [] ) {
148 $mItemsCount = count( $this->mItems );
149 for ( $i = 0; $i < $mItemsCount; $i++ ) {
150 $text = $this->mItems[$i];
154 $mainTail = $sequences[
'main'][
'tail'];
155 if ( strlen( $text ) < strlen( $mainTail ) * $smallFactor ) {
162 $tail = $sequences[$seqName][
'tail'];
163 $diff = $this->diff( $tail, $text );
164 $sequences[$seqName][
'diffs'][] = $diff;
165 $sequences[$seqName][
'map'][] = $i;
166 $sequences[$seqName][
'tail'] = $text;
172 $this->mDiffMap = [];
173 foreach ( $sequences as $seq ) {
174 if ( $seq[
'diffs'] === [] ) {
177 if ( $tail ===
'' ) {
178 $this->mDiffs[] = $seq[
'diffs'][0];
180 $head = $this->patch(
'', $seq[
'diffs'][0] );
181 $this->mDiffs[] = $this->diff( $tail, $head );
183 $this->mDiffMap[] = $seq[
'map'][0];
184 $diffsCount = count( $seq[
'diffs'] );
185 for ( $i = 1; $i < $diffsCount; $i++ ) {
186 $this->mDiffs[] = $seq[
'diffs'][$i];
187 $this->mDiffMap[] = $seq[
'map'][$i];
189 $tail = $seq[
'tail'];
198 private function diff( $t1, $t2 ) {
199 return xdiff_string_rabdiff( $t1, $t2 );
207 private function patch( $base, $diff ) {
208 if ( function_exists(
'xdiff_string_bpatch' ) ) {
209 return xdiff_string_bpatch( $base, $diff );
212 # Pure PHP implementation
214 $header = unpack(
'Vofp/Vcsize', substr( $diff, 0, 8 ) );
218 if ( $ofp !== substr( $diff, 0, 4 ) ) {
219 wfDebug( __METHOD__ .
": incorrect base checksum" );
222 if (
$header[
'csize'] != strlen( $base ) ) {
223 wfDebug( __METHOD__ .
": incorrect base length" );
229 while ( $p < strlen( $diff ) ) {
230 $x = unpack(
'Cop', substr( $diff, $p, 1 ) );
234 case self::XDL_BDOP_INS:
235 $x = unpack(
'Csize', substr( $diff, $p, 1 ) );
237 $out .= substr( $diff, $p, $x[
'size'] );
240 case self::XDL_BDOP_INSB:
241 $x = unpack(
'Vcsize', substr( $diff, $p, 4 ) );
243 $out .= substr( $diff, $p, $x[
'csize'] );
246 case self::XDL_BDOP_CPY:
247 $x = unpack(
'Voff/Vcsize', substr( $diff, $p, 8 ) );
249 $out .= substr( $base, $x[
'off'], $x[
'csize'] );
252 wfDebug( __METHOD__ .
": invalid op" );
268 if ( $init ===
null ) {
269 $init = str_repeat(
"\xf0", 205 ) .
"\xee" . str_repeat(
"\xf0", 67 ) .
"\x02";
275 return strrev( hash(
'adler32', $init . $s,
true ) );
278 private function uncompress() {
279 if ( !$this->mDiffs ) {
283 $mDiffsCount = count( $this->mDiffs );
284 for ( $diffKey = 0; $diffKey < $mDiffsCount; $diffKey++ ) {
285 $textKey = $this->mDiffMap[$diffKey];
286 $text = $this->patch( $tail, $this->mDiffs[$diffKey] );
287 $this->mItems[$textKey] = $text;
297 if ( $this->mItems === [] ) {
303 foreach ( $this->mDiffMap as $i ) {
315 if ( isset( $this->mDefaultKey ) ) {
318 $this->mCompressed = gzdeflate( serialize( $info ) );
319 return [
'mCompressed' ];
324 $this->mFrozen =
true;
325 $info = HistoryBlobUtils::unserializeArray( gzinflate( $this->mCompressed ) );
326 $this->mCompressed =
null;
333 if ( isset( $info[
'default'] ) ) {
334 $this->mDefaultKey = $info[
'default'];
336 $this->mDiffs = $info[
'diffs'];
337 if ( isset( $info[
'base'] ) ) {
339 $this->mDiffMap = range( 0, count( $this->mDiffs ) - 1 );
340 array_unshift( $this->mDiffs,
341 pack(
'VVCV', 0, 0, self::XDL_BDOP_INSB, strlen( $info[
'base'] ) ) .
345 $map = explode(
',', $info[
'map'] );
347 $this->mDiffMap = [];
348 foreach ( $map as $i ) {
350 $this->mDiffMap[] = $cur;
363 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 ...