From: Trevor Norris Date: Thu, 22 Jan 2015 23:44:13 +0000 (-0800) Subject: buffer: add indexOf() method X-Git-Tag: v1.5.0~38 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=78581c8d902f2ed32712b342091d89016030e4ba;p=platform%2Fupstream%2Fnodejs.git buffer: add indexOf() method Add Buffer#indexOf(). Support strings, numbers and other Buffers. Also included docs and tests. Special thanks to Sam Rijs for first proposing this change. PR-URL: https://github.com/iojs/io.js/pull/561 Reviewed-by: Ben Noordhuis Reviewed-By: Brian White Reviewed-By: Chris Dickinson --- diff --git a/doc/api/buffer.markdown b/doc/api/buffer.markdown index 8bbcae7..740498e 100644 --- a/doc/api/buffer.markdown +++ b/doc/api/buffer.markdown @@ -392,6 +392,19 @@ byte from the original Buffer. // abc // !bc + +### buf.indexOf(value[, byteOffset]) + +* `value` String, Buffer or Number +* `byteOffset` Number, Optional, Default: 0 +* Return: Number + +Operates similar to +[Array#indexOf()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf). +Accepts a String, Buffer or Number. Strings are interpreted as UTF8. Buffers +will use the entire buffer. So in order to compare a partial Buffer use +`Buffer#slice()`. Numbers can range from 0 to 255. + ### buf.readUInt8(offset[, noAssert]) * `offset` Number diff --git a/lib/buffer.js b/lib/buffer.js index bb30475..784546d 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -303,6 +303,24 @@ Buffer.prototype.compare = function compare(b) { }; +Buffer.prototype.indexOf = function indexOf(val, byteOffset) { + if (byteOffset > 0x7fffffff) + byteOffset = 0x7fffffff; + else if (byteOffset < -0x80000000) + byteOffset = -0x80000000; + byteOffset >>= 0; + + if (typeof val === 'string') + return binding.indexOfString(this, val, byteOffset); + if (val instanceof Buffer) + return binding.indexOfBuffer(this, val, byteOffset); + if (typeof val === 'number') + return binding.indexOfNumber(this, val, byteOffset); + + throw new TypeError('val must be string, number or Buffer'); +}; + + Buffer.prototype.fill = function fill(val, start, end) { start = start >> 0; end = (end === undefined) ? this.length : end >> 0; diff --git a/src/node_buffer.cc b/src/node_buffer.cc index e4e6d73..2d00ca9 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -602,6 +602,119 @@ void Compare(const FunctionCallbackInfo &args) { } +int32_t IndexOf(const char* haystack, + size_t h_length, + const char* needle, + size_t n_length) { + CHECK_GE(h_length, n_length); + // TODO(trevnorris): Implement Boyer-Moore string search algorithm. + for (size_t i = 0; i < h_length - n_length + 1; i++) { + if (haystack[i] == needle[0]) { + if (memcmp(haystack + i, needle, n_length) == 0) + return i; + } + } + return -1; +} + + +void IndexOfString(const FunctionCallbackInfo& args) { + ASSERT(args[0]->IsObject()); + ASSERT(args[1]->IsString()); + ASSERT(args[2]->IsNumber()); + + ARGS_THIS(args[0].As()); + node::Utf8Value str(args.GetIsolate(), args[1]); + int32_t offset_i32 = args[2]->Int32Value(); + uint32_t offset; + + if (offset_i32 < 0) { + if (offset_i32 + static_cast(obj_length) < 0) + offset = 0; + else + offset = static_cast(obj_length + offset_i32); + } else { + offset = static_cast(offset_i32); + } + + if (str.length() == 0 || + obj_length == 0 || + (offset != 0 && str.length() + offset <= str.length()) || + str.length() + offset > obj_length) + return args.GetReturnValue().Set(-1); + + int32_t r = + IndexOf(obj_data + offset, obj_length - offset, *str, str.length()); + args.GetReturnValue().Set(r == -1 ? -1 : static_cast(r + offset)); +} + + +void IndexOfBuffer(const FunctionCallbackInfo& args) { + ASSERT(args[0]->IsObject()); + ASSERT(args[1]->IsObject()); + ASSERT(args[2]->IsNumber()); + + ARGS_THIS(args[0].As()); + Local buf = args[1].As(); + int32_t offset_i32 = args[2]->Int32Value(); + size_t buf_length = buf->GetIndexedPropertiesExternalArrayDataLength(); + char* buf_data = + static_cast(buf->GetIndexedPropertiesExternalArrayData()); + uint32_t offset; + + if (buf_length > 0) + CHECK_NE(buf_data, nullptr); + + if (offset_i32 < 0) { + if (offset_i32 + static_cast(obj_length) < 0) + offset = 0; + else + offset = static_cast(obj_length + offset_i32); + } else { + offset = static_cast(offset_i32); + } + + if (buf_length == 0 || + obj_length == 0 || + (offset != 0 && buf_length + offset <= buf_length) || + buf_length + offset > obj_length) + return args.GetReturnValue().Set(-1); + + int32_t r = + IndexOf(obj_data + offset, obj_length - offset, buf_data, buf_length); + args.GetReturnValue().Set(r == -1 ? -1 : static_cast(r + offset)); +} + + +void IndexOfNumber(const FunctionCallbackInfo& args) { + ASSERT(args[0]->IsObject()); + ASSERT(args[1]->IsNumber()); + ASSERT(args[2]->IsNumber()); + + ARGS_THIS(args[0].As()); + uint32_t needle = args[1]->Uint32Value(); + int32_t offset_i32 = args[2]->Int32Value(); + uint32_t offset; + + if (offset_i32 < 0) { + if (offset_i32 + static_cast(obj_length) < 0) + offset = 0; + else + offset = static_cast(obj_length + offset_i32); + } else { + offset = static_cast(offset_i32); + } + + if (obj_length == 0 || offset + 1 > obj_length) + return args.GetReturnValue().Set(-1); + + void* ptr = memchr(obj_data + offset, needle, obj_length - offset); + char* ptr_char = static_cast(ptr); + args.GetReturnValue().Set( + ptr ? static_cast(ptr_char - obj_data) : -1); +} + + // pass Buffer object to load prototype methods void SetupBufferJS(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); @@ -650,6 +763,9 @@ void Initialize(Handle target, env->SetMethod(target, "byteLength", ByteLength); env->SetMethod(target, "compare", Compare); env->SetMethod(target, "fill", Fill); + env->SetMethod(target, "indexOfBuffer", IndexOfBuffer); + env->SetMethod(target, "indexOfNumber", IndexOfNumber); + env->SetMethod(target, "indexOfString", IndexOfString); env->SetMethod(target, "readDoubleBE", ReadDoubleBE); env->SetMethod(target, "readDoubleLE", ReadDoubleLE); diff --git a/test/parallel/test-buffer-indexof.js b/test/parallel/test-buffer-indexof.js new file mode 100644 index 0000000..32748dc --- /dev/null +++ b/test/parallel/test-buffer-indexof.js @@ -0,0 +1,75 @@ +var common = require('../common'); +var assert = require('assert'); + +var Buffer = require('buffer').Buffer; + +var b = new Buffer('abcdef'); +var buf_a = new Buffer('a'); +var buf_bc = new Buffer('bc'); +var buf_f = new Buffer('f'); +var buf_z = new Buffer('z'); +var buf_empty = new Buffer(''); + +assert.equal(b.indexOf('a'), 0); +assert.equal(b.indexOf('a', 1), -1); +assert.equal(b.indexOf('a', -1), -1); +assert.equal(b.indexOf('a', -4), -1); +assert.equal(b.indexOf('a', -b.length), 0); +assert.equal(b.indexOf('a', NaN), 0); +assert.equal(b.indexOf('a', -Infinity), 0); +assert.equal(b.indexOf('a', Infinity), -1); +assert.equal(b.indexOf('bc'), 1); +assert.equal(b.indexOf('bc', 2), -1); +assert.equal(b.indexOf('bc', -1), -1); +assert.equal(b.indexOf('bc', -3), -1); +assert.equal(b.indexOf('bc', -5), 1); +assert.equal(b.indexOf('bc', NaN), 1); +assert.equal(b.indexOf('bc', -Infinity), 1); +assert.equal(b.indexOf('bc', Infinity), -1); +assert.equal(b.indexOf('f'), b.length - 1); +assert.equal(b.indexOf('z'), -1); +assert.equal(b.indexOf(''), -1); +assert.equal(b.indexOf('', 1), -1); +assert.equal(b.indexOf('', b.length + 1), -1); +assert.equal(b.indexOf('', Infinity), -1); +assert.equal(b.indexOf(buf_a), 0); +assert.equal(b.indexOf(buf_a, 1), -1); +assert.equal(b.indexOf(buf_a, -1), -1); +assert.equal(b.indexOf(buf_a, -4), -1); +assert.equal(b.indexOf(buf_a, -b.length), 0); +assert.equal(b.indexOf(buf_a, NaN), 0); +assert.equal(b.indexOf(buf_a, -Infinity), 0); +assert.equal(b.indexOf(buf_a, Infinity), -1); +assert.equal(b.indexOf(buf_bc), 1); +assert.equal(b.indexOf(buf_bc, 2), -1); +assert.equal(b.indexOf(buf_bc, -1), -1); +assert.equal(b.indexOf(buf_bc, -3), -1); +assert.equal(b.indexOf(buf_bc, -5), 1); +assert.equal(b.indexOf(buf_bc, NaN), 1); +assert.equal(b.indexOf(buf_bc, -Infinity), 1); +assert.equal(b.indexOf(buf_bc, Infinity), -1); +assert.equal(b.indexOf(buf_f), b.length - 1); +assert.equal(b.indexOf(buf_z), -1); +assert.equal(b.indexOf(buf_empty), -1); +assert.equal(b.indexOf(buf_empty, 1), -1); +assert.equal(b.indexOf(buf_empty, b.length + 1), -1); +assert.equal(b.indexOf(buf_empty, Infinity), -1); +assert.equal(b.indexOf(0x61), 0); +assert.equal(b.indexOf(0x61, 1), -1); +assert.equal(b.indexOf(0x61, -1), -1); +assert.equal(b.indexOf(0x61, -4), -1); +assert.equal(b.indexOf(0x61, -b.length), 0); +assert.equal(b.indexOf(0x61, NaN), 0); +assert.equal(b.indexOf(0x61, -Infinity), 0); +assert.equal(b.indexOf(0x61, Infinity), -1); +assert.equal(b.indexOf(0x0), -1); + +assert.throws(function() { + b.indexOf(function() { }); +}); +assert.throws(function() { + b.indexOf({}); +}); +assert.throws(function() { + b.indexOf([]); +});