浅析node的buffer模块(二写入)
by snoopyxdy
at 2013-05-08 10:48:20
original http://snoopyxdy.blog.163.com/blog/static/6011744020134731114493
Class Method: Buffer.concat(list, [totalLength])new Buffer(str, [encoding])buf.copy(targetBuffer, [targetStart], [sourceStart], [sourceEnd])buf.write(string, [offset], [length], [encoding])buf.fill(value, [offset], [end])还有各种wrtieInt16等方法
if (type === 'string') {// We are a stringthis.length = this.write(subject, 0, encoding);// if subject is buffer then use built-in copy method}
我们看一下node_buffer.cc中是如何实现copy操作的:// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
Buffer.prototype.copy = function(target, target_start, start, end) {
// set undefined/NaN or out of bounds values equal to their default//各种参数的合理化
if (!(target_start >= 0)) target_start = 0;
if (!(start >= 0)) start = 0;
if (!(end < this.length)) end = this.length;
// Copy 0 bytes; we're done
if (end === start || //如果源的开始 等于 源的结束
target.length === 0 || //如果目标的buffer大小为0,
this.length === 0 || //如果源的大小为0,
start > this.length) //如果源开始的位置大于源的buffer大小
return 0; //即拷贝0,则返回0
if (end < start)//如果源的起始位置大于结束位置
throw new RangeError('sourceEnd < sourceStart');
if (target_start >= target.length) //如果目标的其实拷贝位置大于目标的大小
throw new RangeError('targetStart out of bounds');
if (target.length - target_start < end - start) //如果目标的剩余大小小于要拷贝入的大小
end = target.length - target_start + start; //调整end的位置
return this.parent.copy(target.parent || target, //最后调用buffer实例的parent的copy方法来进行拷贝,这里的parent属性就对应着slowbuffer对象,所以这个copy方法也是对应它的
target_start + (target.offset || 0),
start + this.offset,
end + this.offset);
};
// var bytesCopied = buffer.copy(target, targetStart, sourceStart, sourceEnd);Handle<Value> Buffer::Copy(const Arguments &args) {HandleScope scope;Buffer *source = ObjectWrap::Unwrap<Buffer>(args.This()); //将buffer实例解除对象的包装if (!Buffer::HasInstance(args[0])) { //检查第一个参数是否是Buffer类的实例return ThrowTypeError("First arg should be a Buffer");}Local<Value> target = args[0];char* target_data = Buffer::Data(target); //根据inline的两个 Buffer::Data 获得源的内容size_t target_length = Buffer::Length(target); //根据inline的两个Buffer::Length函数得到目标buffer的大小size_t target_start = args[1]->IsUndefined() ? 0 : args[1]->Uint32Value(); //获得目标buffer写入位置size_t source_start = args[2]->IsUndefined() ? 0 : args[2]->Uint32Value(); //获得读取其实位置size_t source_end = args[3]->IsUndefined() ? source->length_ //获得源的读取结束位置: args[3]->Uint32Value();if (source_end < source_start) { //和buffer.js一样进行合法性验证,不多解释,下同return ThrowRangeError("sourceEnd < sourceStart");}// Copy 0 bytes; we're doneif (source_end == source_start) {return scope.Close(Integer::New(0));}if (target_start >= target_length) {return ThrowRangeError("targetStart out of bounds");}if (source_start >= source->length_) {return ThrowRangeError("sourceStart out of bounds");}if (source_end > source->length_) {return ThrowRangeError("sourceEnd out of bounds");}//执行两次MIN方法,第一次返回源的开始到结束的空间 和 目标的开始到结束的空间的 想比较小的数值//第二次比较上述的值和源的长度到起始值的值的较小值,保证将拷贝的数值安全值size_t to_copy = MIN(MIN(source_end - source_start,target_length - target_start),source->length_ - source_start);// need to use slightly slower memmove is the ranges might overlap//需要使用稍微慢的memove,因为返回可能重叠memmove((void *)(target_data + target_start), //传入指向目标buffer的开始位置之后的指针(const void*)(source->data_ + source_start), //传入指向源的开始位置之后的指针to_copy); //传入需要拷贝的长度return scope.Close(Integer::New(to_copy)); //最后返回整形的拷贝了多少长度}
void *memcpy(void *dst, const void *src, size_t count);void *memmove(void *dst, const void *src, size_t count);
utf8Write的代码如下,其实其他write代码都大同小异的// write(string, offset = 0, length = buffer.length-offset, encoding = 'utf8')
Buffer.prototype.write = function(string, offset, length, encoding) {
// Support both (string, offset, length, encoding)
// and the legacy (string, encoding, offset, length)//对参数的各种支持
if (isFinite(offset)) { //如果偏移位置不是数字
if (!isFinite(length)) { //写入buffer的长度如果是数字
encoding = length; //则将参数encoding编码改为长度
length = undefined; //将参数length改为未定义
}
} else { // legacy
var swap = encoding;
encoding = offset;
offset = length;
length = swap;
}
offset = +offset || 0; //初始化offset
var remaining = this.length - offset;
if (!length) {
length = remaining;
} else {
length = +length;
if (length > remaining) {
length = remaining;
}
}
encoding = String(encoding || 'utf8').toLowerCase();var ret;
switch (encoding) {
case 'hex':
ret = this.parent.hexWrite(string, this.offset + offset, length);
break;
case 'utf8':
case 'utf-8':
ret = this.parent.utf8Write(string, this.offset + offset, length); //如果是utf-8则调用utf-8的接口
break;
case 'ascii':
ret = this.parent.asciiWrite(string, this.offset + offset, length);
break;
case 'binary':
ret = this.parent.binaryWrite(string, this.offset + offset, length);
break;
case 'base64':
// Warning: maxLength not taken into account in base64Write
ret = this.parent.base64Write(string, this.offset + offset, length);
break;
case 'ucs2':
case 'ucs-2':
case 'utf16le':
case 'utf-16le':
ret = this.parent.ucs2Write(string, this.offset + offset, length);
break;
default:
throw new TypeError('Unknown encoding: ' + encoding);
}
Buffer._charsWritten = SlowBuffer._charsWritten; //同步记录多少字符被写入了
return ret;};
// var charsWritten = buffer.utf8Write(string, offset, [maxLength]);Handle<Value> Buffer::Utf8Write(const Arguments &args) {HandleScope scope;Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This());if (!args[0]->IsString()) { //有效性检查return ThrowException(Exception::TypeError(String::New("Argument must be a string")));}Local<String> s = args[0]->ToString();size_t offset = args[1]->Uint32Value();int length = s->Length();if (length == 0) {constructor_template->GetFunction()->Set(chars_written_sym,Integer::New(0)); //将写入的字符数设置为0return scope.Close(Integer::New(0)); //返回0}if (length > 0 && offset >= buffer->length_) { //如果偏移大于等于整个buffer的长度则抛出错误return ThrowTypeError("Offset is out of bounds");}size_t max_length = args[2]->IsUndefined() ? buffer->length_ - offset: args[2]->Uint32Value(); //设置最大写入字符大小max_length = MIN(buffer->length_ - offset, max_length); //取 写入buffer的剩余值 和 用户传入的maxlength的小的一个值char* p = buffer->data_ + offset;//将指针偏移到写入的位置int char_written;//调用V8的writeUTF-8写入buffer的接口,返回写入的字符大小//V8EXPORT int WriteUtf8 (char *buffer, int length=-1, int *nchars_ref=NULL, int options=NO_OPTIONS) constint written = s->WriteUtf8(p,max_length,&char_written,(String::HINT_MANY_WRITES_EXPECTED |String::NO_NULL_TERMINATION));constructor_template->GetFunction()->Set(chars_written_sym,Integer::New(char_written));return scope.Close(Integer::New(written)); //返回写入的字符数}
最后我们看下Buffer::fill方法:// fill(value, start=0, end=buffer.length)
Buffer.prototype.fill = function fill(value, start, end) {
//初始化参数value || (value = 0);
start || (start = 0);
end || (end = this.length);
//校验字符串有效性
if (typeof value === 'string') {
value = value.charCodeAt(0); //获取字符串第一个Unicode 编码
}
//下面各种合法性检查
if (typeof value !== 'number' || isNaN(value)) {
throw new TypeError('value is not a number');
}
if (end < start) throw new RangeError('end < start');
// Fill 0 bytes; we're done
if (end === start) return 0;
if (this.length == 0) return 0;
if (start < 0 || start >= this.length) {
throw new RangeError('start out of bounds');
}
if (end < 0 || end > this.length) {
throw new RangeError('end out of bounds');
}
//调用slowerbuffer的fill方法,传入参数//这里的offset还记得吗?就是当前buffer如果共享则会记录offset共享内存的便宜量
return this.parent.fill(value,
start + this.offset,
end + this.offset);
};
// buffer.fill(value, start, end);
Handle<Value> Buffer::Fill(const Arguments &args) {
HandleScope scope;
//参数检查if (!args[0]->IsInt32()) {
return ThrowException(Exception::Error(String::New(
"value is not a number")));
}
int value = (char)args[0]->Int32Value();
Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This());//合法性检查
SLICE_ARGS(args[1], args[2])
//memset:作用是在一段内存块中填充某个给定的值,它对较大的结构体或数组进行清零操作的一种最快方法。
//void *memset(void *s, int c, size_t n);
memset( (void*)(parent->data_ + start),
value,
end - start);
return Undefined();
}