Better flushing
Lost Utf8 support. Waiting for http://codereview.chromium.org/1539013
This commit is contained in:
parent
7af9cb9e80
commit
3197cf291d
188
lib/net.js
188
lib/net.js
@ -398,73 +398,6 @@ Object.defineProperty(Stream.prototype, 'readyState', {
|
||||
});
|
||||
|
||||
|
||||
// Here's the deal. Character encodings are hard. We need to take javascript
|
||||
// strings and turn them into raw binary to send them to socket. Javascript
|
||||
// strings are pure unicode (I think V8 uses 16-bit arrays to hold them).
|
||||
// So an encoding needs to be given to write it out to socket - this is
|
||||
// usually 'utf8'.
|
||||
//
|
||||
// This function, encodeString, takes a buffer and writes the string to it
|
||||
// starting at buffer.used. If it could fit the entire string into the
|
||||
// buffer then it increases the buffer's .used member and returns buffer.
|
||||
// Otherwise it creates a new buffer large enough to fit the entire string,
|
||||
// writes that string into the new buffer, and then returns it.
|
||||
function encodeString (buffer, string, encoding) {
|
||||
encoding = (encoding || 'utf8').toLowerCase();
|
||||
var bytesWritten;
|
||||
|
||||
if (string.length < buffer.length - buffer.used) {
|
||||
// Try to write
|
||||
if (encoding == 'utf8' || encoding == 'utf-8') {
|
||||
bytesWritten = buffer.utf8Write(string, buffer.used);
|
||||
debug('wrote ' + bytesWritten + ' utf8 bytes to buffer');
|
||||
if (buffer[bytesWritten-1] == 0) {
|
||||
// wrote the whole string.
|
||||
buffer.used += bytesWritten-1;
|
||||
return buffer;
|
||||
}
|
||||
} else {
|
||||
if (encoding == 'ascii') {
|
||||
bytesWritten = buffer.asciiWrite(string, buffer.used);
|
||||
buffer.used += bytesWritten; // bytesWritten-1 ?
|
||||
} else {
|
||||
bytesWritten = buffer.binaryWrite(string, buffer.used);
|
||||
buffer.used += bytesWritten;
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
// Couldn't fit the string in the given buffer. Instead of partially
|
||||
// writing it and then slicing the string, we'll do something stupid and
|
||||
// just create a new temporary buffer just for that string.
|
||||
// (The reasoning is: slicing is expensive.)
|
||||
|
||||
var byteLength = Buffer.byteLength(string, encoding);
|
||||
var newBuffer = new Buffer(byteLength);
|
||||
|
||||
debug('alloced new buffer for string : ' + newBuffer.length);
|
||||
|
||||
if (encoding == 'utf8' || encoding == 'utf-8') {
|
||||
bytesWritten = newBuffer.utf8Write(string, 0);
|
||||
} else if (encoding == 'ascii') {
|
||||
bytesWritten = newBuffer.asciiWrite(string, 0);
|
||||
} else {
|
||||
bytesWritten = newBuffer.binaryWrite(string, 0);
|
||||
}
|
||||
|
||||
debug('filled up new buffer');
|
||||
|
||||
|
||||
assert(bytesWritten == byteLength);
|
||||
|
||||
newBuffer.used = byteLength;
|
||||
newBuffer.sent = 0;
|
||||
|
||||
return newBuffer;
|
||||
}
|
||||
|
||||
|
||||
// Returns true if all the data was flushed to socket. Returns false if
|
||||
// something was queued. If data was queued, then the "drain" event will
|
||||
// signal when it has been finally flushed to socket.
|
||||
@ -479,38 +412,79 @@ Stream.prototype.write = function (data, encoding) {
|
||||
return false;
|
||||
} else {
|
||||
// Fast.
|
||||
return this._writeOut(data, encoding);
|
||||
// The most common case. There is no write queue. Just push the data
|
||||
// directly to the socket.
|
||||
return this._writeOut(data, encoding);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Directly writes the data to socket.
|
||||
// unshifts remainder onto _writeQueue.
|
||||
//
|
||||
// Steps:
|
||||
// 1. If it's a string, write it to the `pool`. (If not space remains
|
||||
// on the pool make a new one.)
|
||||
// 2. Write data to socket. Return true if flushed.
|
||||
// 3. Slice out remaining
|
||||
// 4. Unshift remaining onto _writeQueue. Return false.
|
||||
Stream.prototype._writeOut = function (data, encoding) {
|
||||
if (!this.writable) throw new Error('Stream is not writable');
|
||||
|
||||
// The most common case. There is no write queue. Just push the data
|
||||
// directly to the socket.
|
||||
|
||||
var buffer, off, len;
|
||||
var buffer, off, len;
|
||||
var bytesWritten, charsWritten;
|
||||
|
||||
if (typeof data == 'string') {
|
||||
if (!pool) allocNewPool();
|
||||
var startOffset = pool.used;
|
||||
buffer = encodeString(pool, data, encoding);
|
||||
off = (buffer == pool ? startOffset : 0);
|
||||
len = buffer.used - off;
|
||||
} else {
|
||||
var queuedData = false;
|
||||
|
||||
if (typeof data != 'string') {
|
||||
// 'data' is a buffer, ignore 'encoding'
|
||||
buffer = data;
|
||||
off = data.sent || 0;
|
||||
off = 0;
|
||||
len = data.length;
|
||||
|
||||
} else {
|
||||
assert(typeof data == 'string')
|
||||
|
||||
if (!pool || pool.length - pool.used < 128) {
|
||||
pool = null;
|
||||
allocNewPool();
|
||||
}
|
||||
|
||||
if (encoding == 'utf8' || encoding == 'utf-8') {
|
||||
bytesWritten = pool.utf8Write(data, pool.used);
|
||||
charsWritten = bytesWritten; // XXX FIXME
|
||||
} else if (encoding == 'ascii') {
|
||||
bytesWritten = pool.asciiWrite(data, pool.used);
|
||||
charsWritten = bytesWritten;
|
||||
assert(charsWritten <= data.length);
|
||||
} else {
|
||||
bytesWritten = pool.binaryWrite(data, pool.used);
|
||||
charsWritten = bytesWritten;
|
||||
assert(charsWritten <= data.length);
|
||||
}
|
||||
|
||||
assert(bytesWritten > 0);
|
||||
|
||||
buffer = pool;
|
||||
len = bytesWritten;
|
||||
off = pool.used;
|
||||
|
||||
pool.used += bytesWritten;
|
||||
|
||||
debug('wrote ' + bytesWritten + ' bytes to pool');
|
||||
|
||||
if (charsWritten != data.length) {
|
||||
//debug("couldn't fit " + (data.length - charsWritten) + " bytes into the pool\n");
|
||||
// Unshift whatever didn't fit onto the buffer
|
||||
this._writeQueue.unshift(data.slice(charsWritten));
|
||||
this._writeQueueEncoding.unshift(encoding);
|
||||
this._writeWatcher.start();
|
||||
queuedData = true;
|
||||
}
|
||||
}
|
||||
|
||||
debug('write [fd, off, len] =' + JSON.stringify([this.fd, off, len]));
|
||||
|
||||
// Send the buffer.
|
||||
|
||||
var bytesWritten;
|
||||
|
||||
try {
|
||||
bytesWritten = write(this.fd, buffer, off, len);
|
||||
@ -519,49 +493,33 @@ Stream.prototype._writeOut = function (data, encoding) {
|
||||
return false;
|
||||
}
|
||||
|
||||
debug('wrote ' + bytesWritten);
|
||||
debug('wrote ' + bytesWritten + ' to socket. [fd, off, len] = ' + JSON.stringify([this.fd, off, len]) + "\n");
|
||||
|
||||
timeout.active(this);
|
||||
|
||||
if (bytesWritten == len) {
|
||||
// awesome. sent to buffer - save that space
|
||||
buffer.used -= len;
|
||||
return true;
|
||||
// awesome. sent to buffer.
|
||||
buffer.used -= len; // Optimization - save the space
|
||||
if (queuedData) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//sys.error('write incomplete ' + bytesWritten + ' < ' + len);
|
||||
|
||||
// Didn't write the entire thing to buffer.
|
||||
// Need to wait for the socket to become available before trying again.
|
||||
this._writeWatcher.start();
|
||||
|
||||
|
||||
if (buffer == data) {
|
||||
//sys.error('string');
|
||||
bytesWritten = bytesWritten || 0;
|
||||
data = buffer.slice(bytesWritten, len);
|
||||
data.sent = 0;
|
||||
data.used = data.length;
|
||||
// Slice out the data left.
|
||||
var leftOver = data.slice(off + bytesWritten, off + len);
|
||||
leftOver.used = leftOver.length; // used the whole thing...
|
||||
|
||||
} else if (buffer == pool) {
|
||||
//sys.error('pool');
|
||||
data = pool.slice(off + bytesWritten, off + len);
|
||||
data.sent = 0;
|
||||
data.used = data.length;
|
||||
|
||||
} else {
|
||||
data = buffer;
|
||||
data.sent = bytesWritten;
|
||||
}
|
||||
|
||||
assert(typeof data.used == 'number');
|
||||
assert(typeof data.sent == 'number');
|
||||
|
||||
// sys.error('data.used = ' + data.used);
|
||||
// sys.error('data.sent = ' + data.sent);
|
||||
// sys.error('bytes left, data.used - data.send = ' + (data.used - data.sent));
|
||||
// sys.error('data.used = ' + data.used);
|
||||
//if (!this._writeQueue) initWriteStream(this);
|
||||
|
||||
|
||||
// data should be the next thing to write.
|
||||
this._writeQueue.unshift(data);
|
||||
this._writeQueue.unshift(leftOver);
|
||||
this._writeQueueEncoding.unshift(null);
|
||||
|
||||
return false;
|
||||
|
@ -969,7 +969,9 @@ static Handle<Value> Write(const Arguments& args) {
|
||||
ssize_t written = write(fd, (char*)buffer->data() + off, len);
|
||||
|
||||
if (written < 0) {
|
||||
if (errno == EAGAIN || errno == EINTR) return Null();
|
||||
if (errno == EAGAIN || errno == EINTR) {
|
||||
return scope.Close(Integer::New(0));
|
||||
}
|
||||
return ThrowException(ErrnoException(errno, "write"));
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user