From 4dedc0929472976e8730fd317b05a4eabf522f86 Mon Sep 17 00:00:00 2001 From: Sam Rijs Date: Sun, 14 Dec 2014 13:27:53 +1100 Subject: [PATCH] buffer: add indexOf method Adds a `.indexOf` method to `Buffer`, which borrows semantics from both `Array.prototype.indexOf` and `String.prototype.indexOf`. `Buffer.prototype.indexOf` can be invoked with a Buffer, a string or a number as the needle. If the needle a Buffer or string, it will find the first occurrence of this sequence of bytes. If the needle is a number, it will find the first occurrence of this byte. Reviewed-by: Sam Rijs Fixes: https://github.com/iojs/io.js/issues/95 PR-URL: https://github.com/iojs/io.js/pull/160 --- doc/api/buffer.markdown | 7 ++++++ lib/buffer.js | 10 ++++++++ src/node_buffer.cc | 50 ++++++++++++++++++++++++++++++++++++++ test/simple/test-buffer.js | 10 ++++++++ 4 files changed, 77 insertions(+) diff --git a/doc/api/buffer.markdown b/doc/api/buffer.markdown index 5d9cd9b9b7a1bb..19790b44fd9e9d 100644 --- a/doc/api/buffer.markdown +++ b/doc/api/buffer.markdown @@ -764,6 +764,13 @@ buffer. var b = new Buffer(50); b.fill("h"); +### buf.indexOf(value[, fromIndex]) + +* `value` Buffer or String or Number +* `fromIndex` Number, Optional, Default: 0 + +Finds the index within the buffer of the first occurrence of the specified value, starting the search at fromIndex. Returns -1 if the value is not found. + ## buffer.INSPECT_MAX_BYTES * Number, Default: 50 diff --git a/lib/buffer.js b/lib/buffer.js index f14783d62650cf..6d0e7c8203c49d 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -332,6 +332,16 @@ Buffer.prototype.fill = function fill(val, start, end) { return this; }; +Buffer.prototype.indexOf = function indexOf(needle, pos) { + if (typeof needle === 'number') { + needle = new Buffer([needle]); + } else if (typeof needle === 'string') { + needle = new Buffer(needle); + } else if (!(needle instanceof Buffer)) { + throw new TypeError('Argument must be a Buffer, number or string'); + } + return internal.indexOf(this, needle, pos); +}; // XXX remove in v0.13 Buffer.prototype.get = util.deprecate(function get(offset) { diff --git a/src/node_buffer.cc b/src/node_buffer.cc index 4c1caeb6eccabe..e3fdc10f4dd7b9 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -34,6 +34,7 @@ #include #define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) #define CHECK_NOT_OOB(r) \ do { \ @@ -585,6 +586,54 @@ void Compare(const FunctionCallbackInfo &args) { args.GetReturnValue().Set(val); } +void IndexOf(const FunctionCallbackInfo &args) { + Local obj = args[0]->ToObject(); + char* obj_data = + static_cast(obj->GetIndexedPropertiesExternalArrayData()); + int32_t obj_length = obj->GetIndexedPropertiesExternalArrayDataLength(); + + Local search = args[1]->ToObject(); + char* search_data = + static_cast(search->GetIndexedPropertiesExternalArrayData()); + int32_t search_length = search->GetIndexedPropertiesExternalArrayDataLength(); + + int32_t pos = args[2]->Int32Value(); + int32_t start = MIN(MAX(pos, 0), obj_length); + + if (search_length == 0) { + return args.GetReturnValue().Set(start); + } + + while (search_length <= obj_length - start) { + // Search for the first byte of the needle. + char *chr = reinterpret_cast( + memchr(&obj_data[start], search_data[0], obj_length - start)); + int32_t chrpos = (intptr_t)chr - (intptr_t)obj_data; + if (chr == NULL) { + // First byte not found, short circuit. + return args.GetReturnValue().Set(-1); + } + if (search_length == 1) { + // Nothing more to compare, we found it. + return args.GetReturnValue().Set(chrpos); + } + if (search_length > obj_length - chrpos) { + // Needle is longer than the rest of the haystack, + // no way it is contained in there. + return args.GetReturnValue().Set(-1); + } + int cmp = memcmp(&chr[1], &search_data[1], search_length - 1); + if (cmp == 0) { + // All bytes are equal, we found it. + return args.GetReturnValue().Set(chrpos); + } + // Advance start position for next iteration. + start = chrpos + 1; + } + + return args.GetReturnValue().Set(-1); +} + // pass Buffer object to load prototype methods void SetupBufferJS(const FunctionCallbackInfo& args) { @@ -629,6 +678,7 @@ void SetupBufferJS(const FunctionCallbackInfo& args) { env->SetMethod(internal, "byteLength", ByteLength); env->SetMethod(internal, "compare", Compare); env->SetMethod(internal, "fill", Fill); + env->SetMethod(internal, "indexOf", IndexOf); env->SetMethod(internal, "readDoubleBE", ReadDoubleBE); env->SetMethod(internal, "readDoubleLE", ReadDoubleLE); diff --git a/test/simple/test-buffer.js b/test/simple/test-buffer.js index bf742f93480934..e56e34773364f2 100644 --- a/test/simple/test-buffer.js +++ b/test/simple/test-buffer.js @@ -1184,3 +1184,13 @@ assert.throws(function() { var b = new Buffer(1); b.equals('abc'); }); + +// IndexOf Tests +assert.equal(Buffer('abc').indexOf(''), 0) +assert.equal(Buffer('abc').indexOf('bd'), -1) +assert.equal(Buffer('abc').indexOf('bc'), 1) +assert.equal(Buffer('abc').indexOf(0x62), 1) +assert.equal(Buffer('abc').indexOf(Buffer('bc')), 1) +assert.equal(Buffer('abc').indexOf(Buffer([0x62,0x63])), 1) +assert.equal(Buffer('abc').indexOf('bc', 1), 1) +assert.equal(Buffer('abc').indexOf('bc', 2), -1)