<commit_before><?php

/**
* Octave-daemon -- a network daemon for Octave, written in PHP
*
* Copyright (C) 2011 <NAME> <<EMAIL>>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.  If not, see {@link http://www.gnu.org/licenses/}.
*
* @package octave-daemon-server
*/

/**
* The basic process controller for Octave.
*
* It only performs the most basic tasks (it starts the process, sends commands and reads raw output).
*
* @author <NAME> <<EMAIL>>
* @version 1.0
* @copyright Copyright (c) 2011, <NAME>
* @license http://www.gnu.org/licenses/agpl-3.0.html GNU Affero GPL
* @package octave-daemon-server
*
* @example controller_example.php Usage example
*/

class Octave_controller
{
	/**
	* Explicit path to the Cctave binary.
	*
	* In most setups you shouldn't need to populate this.
	*
	* @var string
	*/
	public $octave_path="";

	/**
	* The name of the Octave binary.
	*
	* You typically don't need to change this.
	*
	* @var string
	*/ 
	public $octave_binary="octave";

	/**
	* The path Octave will be started in.
	*
	* Leave it to NULL if you want to use the working directory of the current PHP process.
	* This can also be set via the constructor.
	*
	* @var string
	*/
	public $cwd=NULL;

	/**
	* If set to true, the destructor leaves the process alone.
	*
	* @var boolean
	*/
	public $hangingProcess=false;

	/**
	* The Octave process's standard input stream
	*
	* @var resource
	*/
	private $stdin;

	/**
	* The Octave process's standard output stream
	*
	* @var resource
	*/
	private $stdout;

	/**
	* The Octave process's standard error stream
	*
	* @var resource
	*/
	private $stderr;

	/**
	* The Octave process
	*
	* @var resource
	*/
	private $process;

	/**
	* Change this to true if you don't want warnings from Octave to bubble up.
	*
	* @var boolean
	*/
	public $quiet=false;

	/**
	* The Octave errors triggered by the last command.
	*
	* Read these if you set {@link $quiet} to false.
	*
	* @var string
	*/
	public $lastError="";

	/**
	* Octave's cursor; it indicates the end of Octave output.
	*
	* @var string
	*/
	protected $octave_cursor="<od-msg-end>";

	/**
	* The class constructor.
	*
	* @return void
	*/
	public function __construct($cwd=NULL)
	{
		if ($cwd!==NULL)
			$this->cwd=$cwd;
	}

	/**
	* The class destructor.
	*
	* Cleanly quits Octave, closes the pipes and the process.
	* @return void
	*/
	public function __destruct()
	{
		if ($this->hangingProcess) {
			$this->_closePipes();
			return;
		}

		$this->_send("quit\n");
		$this->_closePipes();
		proc_close($this->process);
	}

	private function _closePipes()
	{
		fclose($this->stdin);
		fclose($this->stdout);
		fclose($this->stderr);
	}

	/**
	* Initializes the controller.
	*
	* Starts the Octave process and drops the welcome message.
	* Throws a RuntimeException if it fails to start the Octave process.
	*
	* @return void
	*/
	public function init()
	{
		$this->process=proc_open(
			$this->octave_path.$this->octave_binary." -i",
			array(
				0 => array("pipe", "r"),
				1 => array("pipe", "w"),
				2 => array("pipe", "w")
			),
			$pipes,
			$this->cwd
		);

		if (!is_resource($this->process))
			throw new RuntimeException("Failed starting the Octave process.");

		list($this->stdin,$this->stdout,$this->stderr)=$pipes;

		// Waiting for some output on either stdin or stderr
		$read=array($this->stdout,$this->stderr);
		$write=NULL;
		$except=NULL;

		if (!stream_select($read, $write, $except, 5))
			throw new RuntimeException("The process timed out on all pipes -- this should never happen, please report this problem!");

		if ($read[0]==$this->stderr)
			throw new RuntimeException("Failed starting the Octave process: ".trim(stream_get_contents($this->stderr)));

		// All is well
		$this->_send('PS1("\\n'.$this->octave_cursor.'\\n"); format none;'."\n");
		$this->_read(); // dump the welcome message
	}

	/**
	* Sends a string to the Octave process.
	*
	* @param string $payload The string to send over.
	* @return boolean Whether the payload was successfully delivered
	*/
	private function _send($payload)
	{
		return strlen($payload)==fwrite($this->stdin,$payload);
	}

	/**
	* Reads from one of Octave process's sockets.
	*
	* @return string Whatever was found in Octave's buffer
	*/
	private function _read()
	{
		$N1=$N2=NULL;

		$result=array(
			'stdout'=>"",
			'stderr'=>"",
		);

		while (true) {
			$stdout=array($this->stdout);
			$stderr=array($this->stderr);

			while (stream_select($stderr,$N1,$N2,0))
				$result['stderr'].=fgets($this->stderr);

			while (stream_select($stdout,$N1,$N2,0)) {
				$line=fgets($this->stdout);
				if ($line==$this->octave_cursor."\n")
					return $result;
				$result['stdout'].=$line;
			}
			usleep(100);
		}
	}

	/**
	* Reads generic output from the Octave process.
	*
	* This method tames {@link _read()} to some degree, but you typically shouldn't
	* need to call it directly anyway -- use {@link runRead()}.
	*
	* @return string Whatever was found in Octave's output buffer.
	*/
	private function _retrieve()
	{
		$payload=$this->_read();
<commit_msg>More efficient way of reading the pipes<commit_after><?php

/**
* Octave-daemon -- a network daemon for Octave, written in PHP
*
* Copyright (C) 2011 <NAME> <<EMAIL>>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.  If not, see {@link http://www.gnu.org/licenses/}.
*
* @package octave-daemon-server
*/

/**
* The basic process controller for Octave.
*
* It only performs the most basic tasks (it starts the process, sends commands and reads raw output).
*
* @author <NAME> <<EMAIL>>
* @version 1.0
* @copyright Copyright (c) 2011, <NAME>
* @license http://www.gnu.org/licenses/agpl-3.0.html GNU Affero GPL
* @package octave-daemon-server
*
* @example controller_example.php Usage example
*/

class Octave_controller
{
	/**
	* Explicit path to the Cctave binary.
	*
	* In most setups you shouldn't need to populate this.
	*
	* @var string
	*/
	public $octave_path="";

	/**
	* The name of the Octave binary.
	*
	* You typically don't need to change this.
	*
	* @var string
	*/ 
	public $octave_binary="octave";

	/**
	* The path Octave will be started in.
	*
	* Leave it to NULL if you want to use the working directory of the current PHP process.
	* This can also be set via the constructor.
	*
	* @var string
	*/
	public $cwd=NULL;

	/**
	* If set to true, the destructor leaves the process alone.
	*
	* @var boolean
	*/
	public $hangingProcess=false;

	/**
	* The Octave process's standard input stream
	*
	* @var resource
	*/
	private $stdin;

	/**
	* The chunk size for pipe reading.
	*
	* By default it's 1 MiB.
	* @var int
	*/
	public $stream_limit=1048576;

	/**
	* The Octave process's standard output stream
	*
	* @var resource
	*/
	private $stdout;

	/**
	* The Octave process's standard error stream
	*
	* @var resource
	*/
	private $stderr;

	/**
	* The Octave process
	*
	* @var resource
	*/
	private $process;

	/**
	* Change this to true if you don't want warnings from Octave to bubble up.
	*
	* @var boolean
	*/
	public $quiet=false;

	/**
	* The Octave errors triggered by the last command.
	*
	* Read these if you set {@link $quiet} to false.
	*
	* @var string
	*/
	public $lastError="";

	/**
	* Octave's cursor; it indicates the end of Octave output.
	*
	* @var string
	*/
	protected $octave_cursor="<od-msg-end>";

	/**
	* The class constructor.
	*
	* @return void
	*/
	public function __construct($cwd=NULL)
	{
		if ($cwd!==NULL)
			$this->cwd=$cwd;
	}

	/**
	* The class destructor.
	*
	* Cleanly quits Octave, closes the pipes and the process.
	* @return void
	*/
	public function __destruct()
	{
		if ($this->hangingProcess) {
			$this->_closePipes();
			return;
		}

		$this->_send("quit\n");
		$this->_closePipes();
		proc_close($this->process);
	}

	private function _closePipes()
	{
		fclose($this->stdin);
		fclose($this->stdout);
		fclose($this->stderr);
	}

	/**
	* Initializes the controller.
	*
	* Starts the Octave process and drops the welcome message.
	* Throws a RuntimeException if it fails to start the Octave process.
	*
	* @return void
	*/
	public function init()
	{
		$this->process=proc_open(
			$this->octave_path.$this->octave_binary." -i",
			array(
				0 => array("pipe", "r"),
				1 => array("pipe", "w"),
				2 => array("pipe", "w")
			),
			$pipes,
			$this->cwd
		);

		if (!is_resource($this->process))
			throw new RuntimeException("Failed starting the Octave process.");

		list($this->stdin,$this->stdout,$this->stderr)=$pipes;

		// Waiting for some output on either stdin or stderr
		$read=array($this->stdout,$this->stderr);
		$write=NULL;
		$except=NULL;

		if (!stream_select($read, $write, $except, 5))
			throw new RuntimeException("The process timed out on all pipes -- this should never happen, please report this problem!");

		if ($read[0]==$this->stderr)
			throw new RuntimeException("Failed starting the Octave process: ".trim(stream_get_contents($this->stderr)));

		// All is well
		$this->_send('PS1("\\n'.$this->octave_cursor.'\\n"); format none;'."\n");
		$this->_read(); // dump the welcome message
	}

	/**
	* Sends a string to the Octave process.
	*
	* @param string $payload The string to send over.
	* @return boolean Whether the payload was successfully delivered
	*/
	private function _send($payload)
	{
		return strlen($payload)==fwrite($this->stdin,$payload);
	}

	/**
	* Reads from one of Octave process's sockets.
	*
	* @return string Whatever was found in Octave's buffer
	*/
	private function _read()
	{
		$N1=$N2=NULL;

		$result=array(
			'stdout'=>"",
			'stderr'=>"",
		);

		$len=-strlen($this->octave_cursor)-1;

		while (true) {
			$read=array($this->stdout,$this->stderr);
			while (stream_select($read,$N1,$N2,0)) {
				foreach($read as $stream) {
					if ($stream==$this->stdout)
						$result['stdout'].=fread($stream,$this->stream_limit);
					if ($stream==$this->stderr)
						$result['stderr'].=fread($stream,$this->stream_limit);
				}
			}

			if (substr($result['stdout'],$len)==$this->octave_cursor."\n") {
				$result['stdout']=substr($result['stdout'],0,$len);
				return $result;
			}

			usleep(100);
		}
	}

	/**
	* Reads generic output from the Octave process.
	*
	* This method tames {@link _read()} to some degree, but you typically shouldn't
	* need to call it directly anyway -- use {@link runRead()}.
	*
	* @return string Whatever was found in Octave's output buffer.
	*/
	private function _retrieve()
	{
		$payload=$this->_read();

{'fraction_non_alphanumeric': 0.1453897627531068, 'fraction_numerical': 0.0058225428000347615, 'mean_word_length': 3.5271439811172307, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 94, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7140577', 'n_tokens_mistral': 4142, 'n_tokens_neox': 3812, 'n_words': 1220}