BSPatch for PHP

Da ich keinen Blog habe, in dem ich so etwas veröffentlichen könnte, der Code aber für irgendwen auf der Welt nützlich sein könnte, poste ich ihn mal hier. Es ist ein Port vom BSPatch C-Code nach PHP. Er ist im Vergleich zur C Version ziemlich lahm. Ich habe ihn jedoch ein wenig auf PHP optimiert, sodass er doch noch schnell genug läuft, um ihn produktiv zu nutzen :wink:

Hintergrund:
Jede mal, wenn ein IP-Symcon Update hochgeladen wird, muss die ganze Datei über die langsame Leitung hochgeladen werden, was viel Zeit kostet, die anders genutzt werden könnte. Mit BSDiff/BSPatch müssen nun nur noch die Änderungen an den jeweiligen Dateien hochgeladen werden.

Wen interessiert wie das alles genau funktioniert: Binary diff

Bedingt dadurch, dass PHP kein Int64 unterstützt, können damit nur Dateien bis 1,07 GB gepatched werden.


<?

function BSPatch($oldfile, $newfile, $patch)
{
	$contents = file_get_contents($patch);
	
	//verify header
	if (readstr($contents, 8) != "BSDIFF40")
	   die("invalid BSDIFF patchfile");
	   
	//read patch sizes
	$BzCtrlLen = readInt64(readstr($contents, 8));
	$BzDataLen = readInt64(readstr($contents, 8));
	$NewSize = readInt64(readstr($contents, 8));
	//echo $BzCtrlLen." ".$BzDataLen." ".$NewSize."
";

	//read patch data
	$CtrlStr = bzdecompress(readstr($contents, $BzCtrlLen));
	$DataStr = bzdecompress(readstr($contents, $BzDataLen));
	$XtraStr = bzdecompress($contents);
	
	//read old file
	$OldData = file_get_contents($oldfile);
	$OldSize = strlen($OldData);
	
	//init new data variable
	$NewData = "";
	
	$OldPos = 0;
	$NewPos = 0;
	$C0 = chr(0);
	
	while($NewPos < $NewSize)
	{
	   $Ctrl0 = readInt64(readstr($CtrlStr, 8));
	   $Ctrl1 = readInt64(readstr($CtrlStr, 8));
	   $Ctrl2 = readInt64(readstr($CtrlStr, 8));

		//echo $Ctrl0." ".$Ctrl1." ".$Ctrl2."
";

	   if($NewPos+$Ctrl0 > $NewSize)
	      die("corrupt patch with Ctrl0");

		//add data from diff file
		/*** original source
		$NewData .= readstr($DataStr, $Ctrl0);
		*/
		
		$ChangeData = readstr($DataStr, $Ctrl0);
		$NewData .= substr($OldData, $OldPos, $Ctrl0);
		
		//add old data
		for($i=0; $i<$Ctrl0; $i++)
		{
		   /*** original source
		   if(!(($OldPos+$i >= 0) && ($OldPos+$i < $OldSize)))
		      die("wrong oldata position");

			$NewData{$NewPos+$i} = chr(ord($NewData{$NewPos+$i}) + ord($OldData{$OldPos+$i}));
			*/
			
			if($ChangeData{$i} != $C0)
				$NewData{$NewPos+$i} = chr(ord($NewData{$NewPos+$i}) + ord($ChangeData{$i}));
		}

		$NewPos += $Ctrl0;
		$OldPos += $Ctrl0;

	   if($NewPos != strlen($NewData))
			die("incorrent len 1");

	   if($NewPos+$Ctrl1 > $NewSize)
	      die("corrupt patch with Ctrl1");

		//add extra data from diff file
		$NewData .= readstr($XtraStr, $Ctrl1);

		$NewPos += $Ctrl1;
		$OldPos += $Ctrl2;
		
	   if($NewPos != strlen($NewData))
			die("incorrent len 2");
	}

	if($NewPos <> $NewSize)
	   die("size does not match!");
	   
	file_put_contents($newfile, $NewData);
}

function readInt64($buffer)
{
	$a = unpack("L", $buffer);
	if((ord($buffer{7}) && 0x80) > 0)
		return -$a[1];
	else
		return $a[1];
}

function readstr(&$buffer, $length)
{
	$r = substr($buffer, 0, $length);
	$buffer = substr($buffer, $length);
	return $r;
}

?>