diff --git a/doc/api/buffer.markdown b/doc/api/buffer.markdown index 5d9cd9b9b7a1bb..175840a4a2309c 100644 --- a/doc/api/buffer.markdown +++ b/doc/api/buffer.markdown @@ -764,6 +764,24 @@ 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. + +### buf.lastIndexOf(value[, fromIndex]) + +* `value` Buffer or String or Number +* `fromIndex` Number, Optional, Default: 0 + +Finds the index within the buffer of the *last* 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..e9e677ed9e5dd9 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -332,6 +332,27 @@ 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); +}; + +Buffer.prototype.lastIndexOf = 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.lastIndexOf(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..5b1b016bb7102c 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,97 @@ void Compare(const FunctionCallbackInfo &args) { args.GetReturnValue().Set(val); } +int32_t IndexOf_DoSearch(char* obj_data, int32_t obj_length, + char* search_data, int32_t search_length, + int32_t 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 -1; + } + if (search_length == 1) { + // Nothing more to compare, we found it. + return chrpos; + } + if (search_length > obj_length - chrpos) { + // Needle is longer than the rest of the haystack, + // no way it is contained in there. + return -1; + } + int cmp = memcmp(&chr[1], &search_data[1], search_length - 1); + if (cmp == 0) { + // All bytes are equal, we found it. + return chrpos; + } + // Advance start position for next iteration. + start = chrpos + 1; + } + + return -1; +} + +void IndexOf_Do(const FunctionCallbackInfo &args, bool isLast) { + 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); + int32_t end = obj_length; + + if (isLast) { + if (search_length == 0) { + return args.GetReturnValue().Set(end); + } + int32_t best = -1; + while (search_length <= end - start) { + int32_t t = IndexOf_DoSearch(obj_data, obj_length, + search_data, search_length, + start); + if (t < 0) { + if (best < 0) { + break; + } else { + end = start; + start = best + 1; + if (start >= end) { + break; + } + } + } else { + best = t; + start = t + 1 + ((int32_t)(end - t) / 2); + } + } + args.GetReturnValue().Set(best); + } else { + if (search_length == 0) { + return args.GetReturnValue().Set(start); + } + int32_t ret = IndexOf_DoSearch(obj_data, obj_length, + search_data, search_length, + start); + args.GetReturnValue().Set(ret); + } +} + +void IndexOf(const FunctionCallbackInfo &args) { + IndexOf_Do(args, false); +} + +void LastIndexOf(const FunctionCallbackInfo &args) { + IndexOf_Do(args, true); +} // pass Buffer object to load prototype methods void SetupBufferJS(const FunctionCallbackInfo& args) { @@ -629,6 +721,8 @@ 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, "lastIndexOf", LastIndexOf); 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..c08139e7901e2c 100644 --- a/test/simple/test-buffer.js +++ b/test/simple/test-buffer.js @@ -1184,3 +1184,23 @@ 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) + +// LastIndexOf Tests +assert.equal(Buffer('abcabc').lastIndexOf(''), 6) +assert.equal(Buffer('abcabc').lastIndexOf('bd'), -1) +assert.equal(Buffer('abcabc').lastIndexOf('bc'), 4) +assert.equal(Buffer('abcabc').lastIndexOf(0x62), 4) +assert.equal(Buffer('abcabc').lastIndexOf(Buffer('bc')), 4) +assert.equal(Buffer('abcabc').lastIndexOf(Buffer([0x62,0x63])), 4) +assert.equal(Buffer('abcabc').lastIndexOf('bc', 1), 4) +assert.equal(Buffer('abcabc').lastIndexOf('bc', 5), -1)