Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

buffer: add indexOf/lastIndexOf method #161

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions doc/api/buffer.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 21 additions & 0 deletions lib/buffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
94 changes: 94 additions & 0 deletions src/node_buffer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <limits.h>

#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))

#define CHECK_NOT_OOB(r) \
do { \
Expand Down Expand Up @@ -585,6 +586,97 @@ void Compare(const FunctionCallbackInfo<Value> &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<char *>(
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<Value> &args, bool isLast) {
Local<Object> obj = args[0]->ToObject();
char* obj_data =
static_cast<char*>(obj->GetIndexedPropertiesExternalArrayData());
int32_t obj_length = obj->GetIndexedPropertiesExternalArrayDataLength();

Local<Object> search = args[1]->ToObject();
char* search_data =
static_cast<char*>(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<Value> &args) {
IndexOf_Do(args, false);
}

void LastIndexOf(const FunctionCallbackInfo<Value> &args) {
IndexOf_Do(args, true);
}

// pass Buffer object to load prototype methods
void SetupBufferJS(const FunctionCallbackInfo<Value>& args) {
Expand Down Expand Up @@ -629,6 +721,8 @@ void SetupBufferJS(const FunctionCallbackInfo<Value>& 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);
Expand Down
20 changes: 20 additions & 0 deletions test/simple/test-buffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)