Split data output code from read() and readUpTo() algorithm
authorTakeshi Yoshino <tyoshino@google.com>
Tue, 28 Jan 2014 13:01:17 +0900
changeset 133 ecde9f975249
parent 132 fc2bc870e7a8
child 134 fda5141dff5d
Split data output code from read() and readUpTo() algorithm
Overview.htm
--- a/Overview.htm	Thu Jan 23 19:50:26 2014 +0900
+++ b/Overview.htm	Tue Jan 28 13:01:17 2014 +0900
@@ -794,77 +794,41 @@
 				<dt>Promise&amp;lt;StreamReadResult&gt; readUpTo()</dt>
 				<dd>
 					<p>
-						This method reads data from the ReadableStream.
-						The returned <a>Promise</a> is fulfilled only when the total cost of produced data becomes equal to or greater than the specified <a>cost</a> or the EOF is reached.
-						The cost of data returned by this method is the same or less than the specified <var>size</var> argument.
-						The cost of data returned by this method can be less than <var>size</var> when data cannot be fully converted into the type specified by <code>readBinaryAs</code> or an EOF was reached.
+						This method reads binary data from the ReadableStream.
+						The returned <a>Promise</a> is fulfilled when any of the following conditions is met:
+						<ul>
+							<li>The total size of produced bytes becomes equal to <var></var></li>
+							<li>The EOF is reached</li>
+						</ul>
+						The <a>cost</a> of data returned by this method can be less than <var>size</var> when data cannot be fully converted into the type specified by <a href="#widl-ReadableStream-readBinaryAs">readBinaryAs</a> or the EOF is reached.
 					</p>
 
 					<p>
-						This method must run the steps below:
+						This method must run these steps:
 						<ol>
-							<li>If <a href="#widl-ReadableStream-readBinaryAs">readBinaryAs</a> is "<code>text</code>", return a <a>Promise</a> rejected with a "<code><a>SyntaxError</a></code>"</li>
+							<li>If <a href="#widl-ReadableStream-readBinaryAs">readBinaryAs</a> is "<code>as-is</code>", return a <a>Promise</a> rejected with a "<code><a>SyntaxError</a></code>"</li>
 
-							<li>If <a>readPending</a> is set, return a <a>Promise</a> rejected with an "<code><a>InvalidStateError</a></code>"</li>
-							<li>Set <a>readPending</a></li>
+							<li>If <a>pendingRead</a> is not <code>null</code>, return a <a>Promise</a> rejected with an "<code><a>InvalidStateError</a></code>"</li>
 
-							<li>Let <var>latchedType</var> be the current value of <a href="#widl-ReadableStream-readBinaryAs">readBinaryAs</a></li>
-							<li>Let <var>latchedEncoding</var> be the current value of <a href="#widl-ReadableStream-readEncoding">readEncoding</a></li>
+							<li>Set <a>pendingRead</a> to a newly-created <a>PendingReadDescriptor</a></li>
+
+							<li>Set <a>pendingRead</a>.<var>binaryAs</var> to the current value of <a href="#widl-ReadableStream-readBinaryAs">readBinaryAs</a></li>
+							<li>Set <a>pendingRead</a>.<var>encoding</var> to the current value of <a href="#widl-ReadableStream-readEncoding">readEncoding</a></li>
+							<li>Set <a>pendingRead</a>.<var>size</var> to <var>size</var></li>
+							<li>Set <a>pendingRead</a>.<var>promise</var> to a newly-created <a>Promise</a></li>
 
 							<li>Set <a>readUpToPullAmount</a> to <var>size</var></li>
 							<li>Set <a>amountBeingReturned</a> to 0</li>
 
 							<li><a>Retrieve data</a></li>
 
-							<li>Let <var>readPromise</var> be a newly-created <a>Promise</a></li>
-							<li>Return <var>readPromise</var>, and then continue to process the steps in this algorithm</li>
-
-							<li>Wait until the total cost of data in <a>readDataBuffer</a> becomes equal to or greater than <var>size</var> or <a>eofReached</a> is set</li>
-
-							<li>
-								Queue a task which runs the steps below:
-								<ol>
-									<li>Let <var>readableAmount</var> be the total cost of data in <a>readDataBuffer</a></li>
-									<li>Let <var>maxAmountToConsume</var> be min(<var>size</var>, <var>readableAmount</var>)</li>
-
-									<li>Let <var>result</var> be a newly-created <a>StreamReadResult</a></li>
-									<li>
-										<dl class="switch">
-											<dt>If <var>latchedType</var> is "<code>none</code>"</dt>
-											<dd>
-												<ol>
-													<li>Set <var>result</var>.<a href="#widl-StreamReadResult-data">data</a> to <code>undefined</code></li>
-													<li>Pop data from <a>readDataBuffer</a> as much as possible but up to <var>maxAmountToConsume</var></li>
-												</ol>
-											</dd>
-											<dt>Otherwise</dt>
-											<dd>
-												<ol>
-													<li>Set <var>result</var>.<a href="#widl-StreamReadResult-data">data</a> to an object of the type specified by <var>latchedType</var> that can be created by consuming as much data as possible from <a>readDataBuffer</a> but up to <var>maxAmountToConsume</var></li>
-													<li>Pop the consumed data from <a>readDataBuffer</a></li>
-											</dd>
-										</dl>
-									</li>
-									<li>Let <var>amountConsumed</var> be the total cost of data consumed in the last step</li>
-									<li>Set <a href="#widl-StreamReadResult-amountConsumed">amountConsumed</a> of <var>result</var> to <var>amountConsumed</var></li>
-									<li>Set <a href="#widl-StreamReadResult-eof">eof</a> of <var>result</var> to <code>true</code> if <a>readDataBuffer</a> is empty and <a>eofReached</a> is set</li>
-									<li>Set <var>result</var>.<a href="#widl-StreamReadResult-error">error</a> to <a>sourceErrorDetail</a></li>
-
-									<li>Set <a>amountBeingReturned</a> to <var>amountConsumed</var></li>
-
-									<li>Set <a>readUpToPullAmount</a> to 0</li>
-
-									<li>Unset <a>readPending</a></li>
-
-									<li>Fulfill <var>readPromise</var> with <var>result</var></li>
-								</ol>
-							</li>
+							<li>Return <a>pendingRead</a>.<var>promise</var></li>
 						</ol>
 					</p>
 
 					<dl class="parameters">
 						<dt>[Clamp] unsigned long long size</dt>
-						<dd>Max number of bytes to read.</dd>
+						<dd>Upper limit of total cost of data to be read</dd>
 					</dl>
 
 					<section class="note">
@@ -881,123 +845,27 @@
 					<p>
 						This method reads data from the ReadableStream.
 						The speed of reading can be roughly adjusted by using <a href="#widl-StreamReadResult-pullAmount">pullAmount</a>.
-						pullAmount doesn't necessarily limit the size of data being read by this method.
+						<a href="#widl-StreamReadResult-pullAmount">pullAmount</a> doesn't necessarily limit the size of data being read by this method.
 					</p>
 
 					<p>
-						This method must run the steps below:
+						This method must run these steps:
 
 						<ol>
-							<li>If <a>readPending</a> is set, return a Promise reject with an "<code><a>InvalidStateError</a></code>"</li>
-							<li>Set <a>readPending</a></li>
+							<li>If <a>pendingRead</a> is not <code>null</code>, return a Promise reject with an "<code><a>InvalidStateError</a></code>"</li>
 
-							<li>Let <var>latchedType</var> be the current value of <a href="#widl-ReadableStream-readBinaryAs">readBinaryAs</a> attribute</li>
-							<li>Let <var>latchedEncoding</var> be the current value of <a href="#widl-ReadableStream-readEncoding">readEncoding</a> attribute</li>
+							<li>Set <a>pendingRead</a> to a newly-created <a>PendingReadDescriptor</a></li>
+
+							<li>Set <a>pendingRead</a>.<var>binaryAs</var> to the current value of <a href="#widl-ReadableStream-readBinaryAs">readBinaryAs</a></li>
+							<li>Set <a>pendingRead</a>.<var>encoding</var> to the current value of <a href="#widl-ReadableStream-readEncoding">readEncoding</a></li>
+							<li>Set <a>pendingRead</a>.<var>size</var> to <code>undefined</code></li>
+							<li>Set <a>pendingRead</a>.<var>promise</var> to a newly-created <a>Promise</a></li>
 
 							<li>Set <a>amountBeingReturned</a> to 0</li>
 
 							<li><a>Retrieve data</a></li>
 
-							<li>Let <var>readPromise</var> be a newly-created <a>Promise</a></li>
-							<li>Return <var>readPromise</var>, and then continue to process the steps in this algorithm</li>
-
-							<li>
-								<dl class="switch">
-									<dt>If <var>latchedType</var> is "<code>text</code>"</dt>
-									<dd>
-										Wait until any of the following conditions is met, and then run the steps for the condition:
-										<dl class="switch">
-											<dt>All or part of bytes in <a>readDataBuffer</a> can be converted into a non-empty <a>DOMString</a> when decoded using <var>latchedEncoding</var></dt>
-											<dd>
-												<ol>
-													<li>Pop the bytes from <a>readDataBuffer</a></li>
-													<li>Let <var>amountConsumed</var> be the number of the popped bytes</li>
-													<li>Let <var>readData</var> be the result of decoding</li>
-												</ol>
-											</dd>
-											<dt>It's clear decoding bytes in <a>readDataBuffer</a> using <var>latchedEncoding</var> fails regardless bytes to be retrieved in the future are</dt>
-											<dd>
-												<ol>
-													<li>Reject <var>readPromise</var> with an "<code><a>InvalidStateError</a></code>"</li>
-													<li>Terminate these steps</li>
-												</ol>
-											</dd>
-											<dt>Anything that doesn't represent bytes in <a>readDataBuffer</a> is encountered</dt>
-											<dd>
-												<ol>
-													<li>Reject <var>readPromise</var> with an "<code><a>InvalidStateError</a></code>"</li>
-													<li>Terminate these steps</li>
-												</ol>
-											</dd>
-										</dl>
-									</dd>
-									<dt>If <var>latchedType</var> is "<code>arraybuffer</code>" or "<code>blob</code>"</dt>
-									<dd>
-										Wait until any of the following conditions is met, and then run the steps for the condition:
-										<dl class="switch">
-											<dt>There're non-zero number of bytes in <a>readDataBuffer</a></dt>
-											<dd>
-												<ol>
-													<li>
-														Pop the bytes from <a>readDataBuffer</a>, and then let <var>readBytes</var> be the popped bytes
-														<section class="note">
-															Implementations may choose to pop only part of bytes readable if it helps reducing memory copy.
-														</section>
-													</li>
-													<li>Let <var>readData</var> be an object of the type specified by <var>latchedType</var> which represents <var>readBytes</var></li>
-													<li>Let <var>amountConsumed</var> be the number of the popped bytes</li>
-												</ol>
-											</dd>
-											<dt>Anything that doesn't represent bytes in <a>readDataBuffer</a> is encountered</dt>
-											<dd>
-												<ol>
-													<li>Reject <var>readPromise</var> with an "<code><a>InvalidStateError</a></code>"</li>
-													<li>Terminate these steps</li>
-												</ol>
-											</dd>
-										</dl>
-									</dd>
-									<dt>Otherwise</dt>
-									<dd>
-										<ol>
-											<li>Wait until <a>readDataBuffer</a> becomes non-empty or <a>eofReached</a> is set</li>
-											<li>
-												<dl class="switch">
-													<dt>If <var>latchedType</var> is "<code>none</code>"</dt>
-													<dd>
-														<ol>
-															<li>Pop data from <a>readDataBuffer</a></li>
-															<li>Let <var>readData</var> be <code>undefined</code></li>
-														</ol>
-													</dd>
-													<dt>Otherwise</dt>
-													<dd>
-														<ol>
-															<li>Pop data from <a>readDataBuffer</a></li>
-															<li>Let <var>readData</var> be an object representing the data</li>
-															<li>Let <var>amountConsumed</var> be the <a>cost</a> of the data</li>
-														</ol>
-													</dd>
-												</dl>
-											</li>
-										</ol>
-									</dd>
-								</dl>
-							</li>
-
-							<li>Queue a task which runs the rest of this algorithm</li>
-
-							<li>Let <var>result</var> be a newly-created <a>StreamReadResult</a>.</li>
-							<li>Set <var>result</var>.<a href="#widl-StreamReadResult-data">data</a> to <var>readData</var></li>
-							<li>Set <var>result</var>.<a href="#widl-StreamReadResult-eof">eof</a> to <code>true</code> if <a>eofReached</a> is set and <a>readDataBuffer</a> is empty</li>
-							<li>Set <var>result</var>.<a href="#widl-StreamReadResult-amountConsumed">amountConsumed</a> to <var>amountConsumed</var></li>
-							<li>Set <var>result</var>.<a href="#widl-StreamReadResult-error">error</a> to <a>sourceErrorDetail</a></li>
-
-							<li>Set <a>amountBeingReturned</a> to <var>amountConsumed</var></li>
-
-							<li>Unset <a>readPending</a></li>
-
-							<li>Fulfill <var>readPromise</var> with <var>result</var></li>
+							<li>Return <a>pendingRead</a>.<var>promise</var></li>
 						</ol>
 					</p>
 
@@ -1183,10 +1051,15 @@
 						This method must run the steps below:
 						<ol>
 							<li>Let <var>abortPromise</var> be a newly-created <a>Promise</a></li>
-							<li>Return <var>abortPromise</var>, and then continue the process the steps in this algorithm</li>
-							<li><a>Read-abort</a> <a>dataSource</a> with <var>reason</var></li>
-							<li>Wait until <a>dataSource</a> acknowledges the read-abort</li>
-							<li>Fulfill <var>abortPromise</var> with <code>undefined</code></li>
+							<li>
+								Run these steps asynchronously:
+								<ol>
+									<li><a>Read-abort</a> <a>dataSource</a> with <var>reason</var></li>
+									<li>Wait until <a>dataSource</a> acknowledges the read-abort</li>
+									<li>Fulfill <var>abortPromise</var> with <code>undefined</code></li>
+								</ol>
+							</li>
+							<li>Return <var>abortPromise</var></li>
 						</ol>
 					</p>
 				</dd>
@@ -1307,6 +1180,21 @@
 			</p>
 
 			<p>
+				A struct type <dfn>PendingReadDescriptor</dfn> has the following members:
+				<ul>
+					<li>A <a>Promise</a> <var>promise</var></li>
+					<li>An integer <var>amount</var></li>
+					<li>A <a>DOMString</a> <var>binaryAs</var></li>
+					<li>A <a>DOMString</a> <var>encoding</var></li>
+				</ul>
+			</p>
+
+			<p>
+				A WritableStream has an associated <a>PendingReadDescriptor</a> <dfn>pendingRead</dfn>.
+				This variable is initialized to <code>null</code>.
+			</p>
+
+			<p>
 				An associated flag <dfn>eofReached</dfn> which indicates that the EOF was received from the <a>dataSource</a>.
 			</p>
 
@@ -1325,12 +1213,151 @@
 			</p>
 
 			<p>
+				To <dfn>output data</dfn>, run these steps:
+
+				<ol>
+					<li>Let <var>result</var> be a newly-created <a>StreamReadResult</a></li>
+
+					<li>
+						<dl class="switch">
+							<dt>If <a>pendingRead<a>.<var>binaryAs</var> is "<code>as-is</code>"</dt>
+							<dd>
+								<dl class="switch">
+									<dt>If <a>pendingRead</a>.<var>size</var> is <code>undefined</code></dt>
+									<dd>
+										<ol>
+											<li>Pop one element from <a>readDataBuffer</a>, and set <var>result</var>.<a href="#widl-StreamReadResult-data">data</a> to the element</li>
+											<li>Let <var>amountConsumed</var> be <a>cost</a> of the element</li>
+										</ol>
+									</dd>
+									<dt>Otherwise</dt>
+									<dd>Reject <var>pendingRead.promise</var> with a "<code><a>SyntaxError</a></code>"</dd>
+								</dl>
+							</dd>
+
+							<dt>Otherwise</dt>
+							<dd>
+								<ol>
+									<li>Let <var>temporaryBuffer</var> be an empty queue</li>
+									<li>Let <var>remainingSize</var> be <code>undefined</code></li>
+									<li>If <a>pendingRead</a>.<var>size</var> is <code>undefined</code>, set <var>remainingSize</var> to <var>pendingRead.size</var></li>
+									<li>
+										<dl class="switch">
+											<dd>
+												Repeat these steps while <a>readDataBuffer</a> is not empty:
+												<ol>
+													<li>Let <var>head</var> be the first element in <a>readDataBuffer</a></li>
+													<li>
+														<dl class="switch">
+															<dt>If <var>remainingSize</var> is 0</dt>
+															<dd>
+																Exit from this loop
+															</dd>
+															<dt>If <var>head</var> is not an object representing binary data</dt>
+															<dd>
+																<ol>
+																	<li>Reject <var>pendingRead.promise</var> with an "<code><a>InvalidStateError</a></code>"</li>
+																	<li>Exit from this loop</li>
+																</ol>
+															</dd>
+															<dt>If <var>remainingSize</var> is <code>undefined</code></dt>
+															<dd>
+																<ol>
+																	<li>Pop <var>head</var> from <a>readDataBuffer</a></li>
+																	<li>Push <var>head</var> to <var>temporaryBuffer</var></li>
+																</ol>
+
+																<section class="note">
+																	Implementations may choose to pop only part of bytes readable if it helps reducing memory copy.
+																</section>
+															</dd>
+															<dt>If <var>remainingSize</var> is not <code>undefined</code> and <a>cost</a> of <var>head</var> is not greater than <var>remainingSize</var></dt>
+															<dd>
+																<ol>
+																	<li>Pop <var>head</var> from <a>readDataBuffer</a></li>
+																	<li>Push <var>head</var> to <var>temporaryBuffer</var></li>
+																	<li>Set <var>remainingSize</var> to <var>remainingSize</var> - (<a>cost</a> of <var>head</var>)</li>
+																</ol>
+															</dd>
+															<dt>Otherwise</dt>
+															<dd>
+																<ol>
+																	<li>Split <var>head</var> into two elements <var>first</var> and <var>last</var> where size of <var>first</var> is <var>remainingSize</var></li>
+																	<li>Replace <var>head</var> in <a>readDataBuffer</a> with <var>last</var></li>
+																	<li>Push <var>first</var> to <var>temporaryBuffer</var></li>
+																	<li>Set <var>remainingSize</var> to 0</li>
+																</ol>
+															</dd>
+														</dl>
+													</li>
+												</ol>
+											</dd>
+											<dt>Otherwise</dt>
+											<dd>
+												
+											</dd>
+										</dl>
+									</li>
+
+									<li>
+										<dl class="switch">
+											<dt>If <a>pendingRead</a>.<var>binaryAs</var> is any of "<code>arraybuffer</code>" or "<code>blob</code>"</dt>
+											<dd>
+												<ol>
+													<li>Set <var>result</var>.<a href="#widl-StreamReadResult-data">data</a> to an object of the type specified by <var>pendingRead.binaryAs</var> that represents binary data stored in <var>temporaryBuffer</var></li>
+													<li>Let <var>amountConsumed</var> be the number of bytes in <var>temporaryBuffer</var></li>
+												</ol>
+											</dd>
+											<dt>If <a>pendingRead</a>.<var>binaryAs</var> is "<code>text</code>"</dt>
+											<dd>
+												<ol>
+													<li>Set <var>result</var>.<a href="#widl-StreamReadResult-data">data</a> to a DOMString that is the result of decoding as many bytes in <var>temporaryBuffer</var> as possible using <var>pendingRead.encoding</var></li>
+													<li>Let <var>unusedBytes</var> be the bytes in <var>temporaryBuffer</var> which were not used in the step 1</li>
+													<li>If <a>eofReached</a> is set and <var>unusedBytes</var> is not empty, reject <var>pendingRead.promise</var> with "<code><a>EncodingError</a></code>"</li>
+													<li>Prepend <var>unusedBytes</var> to <a>readDataBuffer</a></li>
+													<li>Let <var>amountConsumed</var> be the number of bytes in <var>temporaryBuffer</var> used in the step 1</li>
+												</ol>
+											</dd>
+											<dt>If <a>pendingRead</a>.<var>binaryAs</var> is "<code>none</code>"</dt>
+											<dd>
+												<ol>
+													<li>Set <var>result</var>.<a href="#widl-StreamReadResult-data">data</a> to <code>undefined</code></li>
+													<li>Let <var>amountConsumed</var> be the number of bytes in <var>temporaryBuffer</var></li>
+												</ol>
+											</dd>
+										</dl>
+									</li>
+									<li>Clear <var>temporaryBuffer</var></li>
+								</ol>
+							</dd>
+						</dl>
+					</li>
+
+					<li>Set <var>result</var>.<a href="#widl-StreamReadResult-size">size</a> to <var>amountConsumed</var></li>
+					<li>Set <var>result</var>.<a href="#widl-StreamReadResult-eof">eof</a> to <code>true</code> if <a>readDataBuffer</a> is empty and <a>eofReached</a> is set</li>
+					<li>Set <var>result</var>.<a href="#widl-StreamReadResult-error">error</a> to <a>sourceErrorDetail</a></li>
+
+					<li>Set <a>amountBeingReturned</a> to <var>amountConsumed</var></li>
+
+					<li>Set <a>readUpToPullAmount</a> to 0</li>
+
+					<li>Let <var>readPromise</var> to <var>pendingRead.promise</var></li>
+					<li>Set <a>pendingRead</a> to null</li>
+					<li>Fulfill <var>readPromise</var> with <var>result</var></li>
+				</ol>
+			</p>
+
+			<p>
 				When data is received from <a>dataSource</a>, queue a task which runs the steps below:
 				<ol>
 					<li>Let <var>receivedData</var> be the received bytes</li>
 					<li>Let <var>receivedAmount</var> be the size of <var>receivedData</var></li>
 					<li>Set <a>amountRequested</a> to <a>amountRequested</a> - <var>receivedAmount</var></li>
 					<li>Push <var>receivedData</var> to <a>readDataBuffer</a></li>
+
+					<li>
+						If <a>pendingRead</a> is not null, <a>output data</a>
+					</li>
 				</ol>
 			</p>