Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F91082
D7768.diff
All Users
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
6 KB
Referenced Files
None
Subscribers
None
D7768.diff
View Options
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -15,6 +15,7 @@
'AASTToken' => 'parser/aast/api/AASTToken.php',
'AASTTree' => 'parser/aast/api/AASTTree.php',
'AbstractDirectedGraph' => 'utils/AbstractDirectedGraph.php',
+ 'PhutilRope' => 'utils/PhutilRope.php',
'AbstractDirectedGraphTestCase' => 'utils/__tests__/AbstractDirectedGraphTestCase.php',
'AphrontDatabaseConnection' => 'aphront/storage/connection/AphrontDatabaseConnection.php',
'AphrontDatabaseTransactionState' => 'aphront/storage/connection/AphrontDatabaseTransactionState.php',
diff --git a/src/future/exec/ExecFuture.php b/src/future/exec/ExecFuture.php
--- a/src/future/exec/ExecFuture.php
+++ b/src/future/exec/ExecFuture.php
@@ -69,6 +69,7 @@
public function __construct($command) {
$argv = func_get_args();
$this->command = call_user_func_array('csprintf', $argv);
+ $this->stdin = new PhutilRope();
}
@@ -275,7 +276,12 @@
* @task interact
*/
public function write($data, $keep_pipe = false) {
- $this->stdin .= $data;
+ if (strlen($data)) {
+ if (!$this->stdin) {
+ throw new Exception(pht('Writing to a closed pipe!'));
+ }
+ $this->stdin->append($data);
+ }
$this->closePipe = !$keep_pipe;
return $this;
@@ -473,7 +479,7 @@
public function getWriteSockets() {
list($stdin, $stdout, $stderr) = $this->pipes;
$sockets = array();
- if (isset($stdin) && strlen($this->stdin) && !feof($stdin)) {
+ if (isset($stdin) && $this->stdin->getByteLength() && !feof($stdin)) {
$sockets[] = $stdin;
}
return $sockets;
@@ -609,19 +615,24 @@
list($stdin, $stdout, $stderr) = $this->pipes;
- if (isset($this->stdin) && strlen($this->stdin)) {
- $bytes = fwrite($stdin, $this->stdin);
+ while (isset($this->stdin) && $this->stdin->getByteLength()) {
+ $write_segment = $this->stdin->getAnyPrefix();
+
+ $bytes = fwrite($stdin, $write_segment);
if ($bytes === false) {
throw new Exception('Unable to write to stdin!');
} else if ($bytes) {
- $this->stdin = substr($this->stdin, $bytes);
+ $this->stdin->removeBytesFromHead($bytes);
+ } else {
+ // Writes are blocked for now.
+ break;
}
}
$this->tryToCloseStdin();
- // Read status before reading pipes so that we can never miss data that
- // arrives between our last read and the process exiting.
+ // Read status before reading pipes so that we can never miss data that
+ // arrives between our last read and the process exiting.
$status = $this->procGetStatus();
$this->stdout .= $this->readAndDiscard(
@@ -737,7 +748,7 @@
return;
}
- if (strlen($this->stdin)) {
+ if ($this->stdin->getByteLength()) {
// We still have bytes to write.
return;
}
diff --git a/src/utils/PhutilRope.php b/src/utils/PhutilRope.php
new file mode 100644
--- /dev/null
+++ b/src/utils/PhutilRope.php
@@ -0,0 +1,113 @@
+<?php
+
+/**
+ * String-like object which reduces the cost of managing large strings. This
+ * is particularly useful for buffering large amounts of data that is being
+ * passed to `fwrite()`.
+ *
+ * @group util
+ */
+final class PhutilRope extends Phobject {
+
+ private $length = 0;
+ private $buffers = array();
+ private $segmentSize = 16384;
+
+ /**
+ * Append a string to the rope.
+ *
+ * @param string String to append.
+ * @return this
+ */
+ public function append($string) {
+ if (!strlen($string)) {
+ return $this;
+ }
+
+ $len = strlen($string);
+ $this->length += $len;
+
+ if ($len <= $this->segmentSize) {
+ $this->buffers[] = $string;
+ } else {
+ for ($cursor = 0; $cursor < $len; $cursor += $this->segmentSize) {
+ $this->buffers[] = substr($string, $cursor, $this->segmentSize);
+ }
+ }
+
+ return $this;
+ }
+
+
+ /**
+ * Get the length of the rope.
+ *
+ * @return int Length of the rope in bytes.
+ */
+ public function getByteLength() {
+ return $this->length;
+ }
+
+
+ /**
+ * Get an arbitrary, nonempty prefix of the rope.
+ *
+ * @return string Some rope prefix.
+ */
+ public function getAnyPrefix() {
+ $result = reset($this->buffers);
+ if ($result === false) {
+ return null;
+ }
+
+ return $result;
+ }
+
+
+ /**
+ * Return the entire rope as a normal string.
+ *
+ * @return string Normal string.
+ */
+ public function getAsString() {
+ return implode('', $this->buffers);
+ }
+
+
+ /**
+ * Remove a specified number of bytes from the head of the rope.
+ *
+ * @param int Bytes to remove.
+ * @return this
+ */
+ public function removeBytesFromHead($length) {
+ if ($length <= 0) {
+ throw new InvalidArgumentException(
+ pht('Length must be larger than 0!'));
+ }
+
+ $remaining_length = $length;
+ foreach ($this->buffers as $key => $buf) {
+ $len = strlen($buf);
+ if ($len <= $length) {
+ unset($this->buffers[$key]);
+ $remaining_length -= $len;
+ if (!$remaining_length) {
+ break;
+ }
+ } else {
+ $this->buffers[$key] = substr($buf, $length);
+ break;
+ }
+ }
+
+ if ($length <= $this->length) {
+ $this->length -= $length;
+ } else {
+ $this->length = 0;
+ }
+
+ return $this;
+ }
+
+}
diff --git a/src/utils/__tests__/PhutilRopeTestCase.php b/src/utils/__tests__/PhutilRopeTestCase.php
new file mode 100644
--- /dev/null
+++ b/src/utils/__tests__/PhutilRopeTestCase.php
@@ -0,0 +1,24 @@
+<?php
+
+final class PhutilRopeTestCase extends PhutilTestCase {
+
+ public function testRopeOperations() {
+ $rope = new PhutilRope();
+ $rope->append('aaa');
+ $rope->append('bbb');
+
+ $this->assertEqual(6, $rope->getByteLength());
+ $this->assertEqual('aaabbb', $rope->getAsString());
+
+ $rope->removeBytesFromHead(2);
+
+ $this->assertEqual(4, $rope->getByteLength());
+ $this->assertEqual('abbb', $rope->getAsString());
+
+ $rope->removeBytesFromHead(4);
+
+ $this->assertEqual(0, $rope->getByteLength());
+ $this->assertEqual('', $rope->getAsString());
+ }
+
+}
File Metadata
Details
Attached
Mime Type
text/x-diff
Storage Engine
amazon-s3
Storage Format
Raw Data
Storage Handle
phabricator/rq/l6/kihhepf4ooyu4sot
Default Alt Text
D7768.diff (6 KB)
Attached To
Mode
D7768: Dramatically improve subprocess I/O for large buffers
Attached
Detach File
Event Timeline
Log In to Comment