Skip to content

Commit 78581c8

Browse files
committed
buffer: add indexOf() method
Add Buffer#indexOf(). Support strings, numbers and other Buffers. Also included docs and tests. Special thanks to Sam Rijs <[email protected]> for first proposing this change. PR-URL: #561 Reviewed-by: Ben Noordhuis <[email protected]> Reviewed-By: Brian White <[email protected]> Reviewed-By: Chris Dickinson <[email protected]>
1 parent abb00cc commit 78581c8

File tree

4 files changed

+222
-0
lines changed

4 files changed

+222
-0
lines changed

doc/api/buffer.markdown

+13
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,19 @@ byte from the original Buffer.
392392
// abc
393393
// !bc
394394

395+
396+
### buf.indexOf(value[, byteOffset])
397+
398+
* `value` String, Buffer or Number
399+
* `byteOffset` Number, Optional, Default: 0
400+
* Return: Number
401+
402+
Operates similar to
403+
[Array#indexOf()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf).
404+
Accepts a String, Buffer or Number. Strings are interpreted as UTF8. Buffers
405+
will use the entire buffer. So in order to compare a partial Buffer use
406+
`Buffer#slice()`. Numbers can range from 0 to 255.
407+
395408
### buf.readUInt8(offset[, noAssert])
396409

397410
* `offset` Number

lib/buffer.js

+18
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,24 @@ Buffer.prototype.compare = function compare(b) {
303303
};
304304

305305

306+
Buffer.prototype.indexOf = function indexOf(val, byteOffset) {
307+
if (byteOffset > 0x7fffffff)
308+
byteOffset = 0x7fffffff;
309+
else if (byteOffset < -0x80000000)
310+
byteOffset = -0x80000000;
311+
byteOffset >>= 0;
312+
313+
if (typeof val === 'string')
314+
return binding.indexOfString(this, val, byteOffset);
315+
if (val instanceof Buffer)
316+
return binding.indexOfBuffer(this, val, byteOffset);
317+
if (typeof val === 'number')
318+
return binding.indexOfNumber(this, val, byteOffset);
319+
320+
throw new TypeError('val must be string, number or Buffer');
321+
};
322+
323+
306324
Buffer.prototype.fill = function fill(val, start, end) {
307325
start = start >> 0;
308326
end = (end === undefined) ? this.length : end >> 0;

src/node_buffer.cc

+116
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,119 @@ void Compare(const FunctionCallbackInfo<Value> &args) {
602602
}
603603

604604

605+
int32_t IndexOf(const char* haystack,
606+
size_t h_length,
607+
const char* needle,
608+
size_t n_length) {
609+
CHECK_GE(h_length, n_length);
610+
// TODO(trevnorris): Implement Boyer-Moore string search algorithm.
611+
for (size_t i = 0; i < h_length - n_length + 1; i++) {
612+
if (haystack[i] == needle[0]) {
613+
if (memcmp(haystack + i, needle, n_length) == 0)
614+
return i;
615+
}
616+
}
617+
return -1;
618+
}
619+
620+
621+
void IndexOfString(const FunctionCallbackInfo<Value>& args) {
622+
ASSERT(args[0]->IsObject());
623+
ASSERT(args[1]->IsString());
624+
ASSERT(args[2]->IsNumber());
625+
626+
ARGS_THIS(args[0].As<Object>());
627+
node::Utf8Value str(args.GetIsolate(), args[1]);
628+
int32_t offset_i32 = args[2]->Int32Value();
629+
uint32_t offset;
630+
631+
if (offset_i32 < 0) {
632+
if (offset_i32 + static_cast<int32_t>(obj_length) < 0)
633+
offset = 0;
634+
else
635+
offset = static_cast<uint32_t>(obj_length + offset_i32);
636+
} else {
637+
offset = static_cast<uint32_t>(offset_i32);
638+
}
639+
640+
if (str.length() == 0 ||
641+
obj_length == 0 ||
642+
(offset != 0 && str.length() + offset <= str.length()) ||
643+
str.length() + offset > obj_length)
644+
return args.GetReturnValue().Set(-1);
645+
646+
int32_t r =
647+
IndexOf(obj_data + offset, obj_length - offset, *str, str.length());
648+
args.GetReturnValue().Set(r == -1 ? -1 : static_cast<int32_t>(r + offset));
649+
}
650+
651+
652+
void IndexOfBuffer(const FunctionCallbackInfo<Value>& args) {
653+
ASSERT(args[0]->IsObject());
654+
ASSERT(args[1]->IsObject());
655+
ASSERT(args[2]->IsNumber());
656+
657+
ARGS_THIS(args[0].As<Object>());
658+
Local<Object> buf = args[1].As<Object>();
659+
int32_t offset_i32 = args[2]->Int32Value();
660+
size_t buf_length = buf->GetIndexedPropertiesExternalArrayDataLength();
661+
char* buf_data =
662+
static_cast<char*>(buf->GetIndexedPropertiesExternalArrayData());
663+
uint32_t offset;
664+
665+
if (buf_length > 0)
666+
CHECK_NE(buf_data, nullptr);
667+
668+
if (offset_i32 < 0) {
669+
if (offset_i32 + static_cast<int32_t>(obj_length) < 0)
670+
offset = 0;
671+
else
672+
offset = static_cast<uint32_t>(obj_length + offset_i32);
673+
} else {
674+
offset = static_cast<uint32_t>(offset_i32);
675+
}
676+
677+
if (buf_length == 0 ||
678+
obj_length == 0 ||
679+
(offset != 0 && buf_length + offset <= buf_length) ||
680+
buf_length + offset > obj_length)
681+
return args.GetReturnValue().Set(-1);
682+
683+
int32_t r =
684+
IndexOf(obj_data + offset, obj_length - offset, buf_data, buf_length);
685+
args.GetReturnValue().Set(r == -1 ? -1 : static_cast<int32_t>(r + offset));
686+
}
687+
688+
689+
void IndexOfNumber(const FunctionCallbackInfo<Value>& args) {
690+
ASSERT(args[0]->IsObject());
691+
ASSERT(args[1]->IsNumber());
692+
ASSERT(args[2]->IsNumber());
693+
694+
ARGS_THIS(args[0].As<Object>());
695+
uint32_t needle = args[1]->Uint32Value();
696+
int32_t offset_i32 = args[2]->Int32Value();
697+
uint32_t offset;
698+
699+
if (offset_i32 < 0) {
700+
if (offset_i32 + static_cast<int32_t>(obj_length) < 0)
701+
offset = 0;
702+
else
703+
offset = static_cast<uint32_t>(obj_length + offset_i32);
704+
} else {
705+
offset = static_cast<uint32_t>(offset_i32);
706+
}
707+
708+
if (obj_length == 0 || offset + 1 > obj_length)
709+
return args.GetReturnValue().Set(-1);
710+
711+
void* ptr = memchr(obj_data + offset, needle, obj_length - offset);
712+
char* ptr_char = static_cast<char*>(ptr);
713+
args.GetReturnValue().Set(
714+
ptr ? static_cast<int32_t>(ptr_char - obj_data) : -1);
715+
}
716+
717+
605718
// pass Buffer object to load prototype methods
606719
void SetupBufferJS(const FunctionCallbackInfo<Value>& args) {
607720
Environment* env = Environment::GetCurrent(args);
@@ -650,6 +763,9 @@ void Initialize(Handle<Object> target,
650763
env->SetMethod(target, "byteLength", ByteLength);
651764
env->SetMethod(target, "compare", Compare);
652765
env->SetMethod(target, "fill", Fill);
766+
env->SetMethod(target, "indexOfBuffer", IndexOfBuffer);
767+
env->SetMethod(target, "indexOfNumber", IndexOfNumber);
768+
env->SetMethod(target, "indexOfString", IndexOfString);
653769

654770
env->SetMethod(target, "readDoubleBE", ReadDoubleBE);
655771
env->SetMethod(target, "readDoubleLE", ReadDoubleLE);

test/parallel/test-buffer-indexof.js

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
var common = require('../common');
2+
var assert = require('assert');
3+
4+
var Buffer = require('buffer').Buffer;
5+
6+
var b = new Buffer('abcdef');
7+
var buf_a = new Buffer('a');
8+
var buf_bc = new Buffer('bc');
9+
var buf_f = new Buffer('f');
10+
var buf_z = new Buffer('z');
11+
var buf_empty = new Buffer('');
12+
13+
assert.equal(b.indexOf('a'), 0);
14+
assert.equal(b.indexOf('a', 1), -1);
15+
assert.equal(b.indexOf('a', -1), -1);
16+
assert.equal(b.indexOf('a', -4), -1);
17+
assert.equal(b.indexOf('a', -b.length), 0);
18+
assert.equal(b.indexOf('a', NaN), 0);
19+
assert.equal(b.indexOf('a', -Infinity), 0);
20+
assert.equal(b.indexOf('a', Infinity), -1);
21+
assert.equal(b.indexOf('bc'), 1);
22+
assert.equal(b.indexOf('bc', 2), -1);
23+
assert.equal(b.indexOf('bc', -1), -1);
24+
assert.equal(b.indexOf('bc', -3), -1);
25+
assert.equal(b.indexOf('bc', -5), 1);
26+
assert.equal(b.indexOf('bc', NaN), 1);
27+
assert.equal(b.indexOf('bc', -Infinity), 1);
28+
assert.equal(b.indexOf('bc', Infinity), -1);
29+
assert.equal(b.indexOf('f'), b.length - 1);
30+
assert.equal(b.indexOf('z'), -1);
31+
assert.equal(b.indexOf(''), -1);
32+
assert.equal(b.indexOf('', 1), -1);
33+
assert.equal(b.indexOf('', b.length + 1), -1);
34+
assert.equal(b.indexOf('', Infinity), -1);
35+
assert.equal(b.indexOf(buf_a), 0);
36+
assert.equal(b.indexOf(buf_a, 1), -1);
37+
assert.equal(b.indexOf(buf_a, -1), -1);
38+
assert.equal(b.indexOf(buf_a, -4), -1);
39+
assert.equal(b.indexOf(buf_a, -b.length), 0);
40+
assert.equal(b.indexOf(buf_a, NaN), 0);
41+
assert.equal(b.indexOf(buf_a, -Infinity), 0);
42+
assert.equal(b.indexOf(buf_a, Infinity), -1);
43+
assert.equal(b.indexOf(buf_bc), 1);
44+
assert.equal(b.indexOf(buf_bc, 2), -1);
45+
assert.equal(b.indexOf(buf_bc, -1), -1);
46+
assert.equal(b.indexOf(buf_bc, -3), -1);
47+
assert.equal(b.indexOf(buf_bc, -5), 1);
48+
assert.equal(b.indexOf(buf_bc, NaN), 1);
49+
assert.equal(b.indexOf(buf_bc, -Infinity), 1);
50+
assert.equal(b.indexOf(buf_bc, Infinity), -1);
51+
assert.equal(b.indexOf(buf_f), b.length - 1);
52+
assert.equal(b.indexOf(buf_z), -1);
53+
assert.equal(b.indexOf(buf_empty), -1);
54+
assert.equal(b.indexOf(buf_empty, 1), -1);
55+
assert.equal(b.indexOf(buf_empty, b.length + 1), -1);
56+
assert.equal(b.indexOf(buf_empty, Infinity), -1);
57+
assert.equal(b.indexOf(0x61), 0);
58+
assert.equal(b.indexOf(0x61, 1), -1);
59+
assert.equal(b.indexOf(0x61, -1), -1);
60+
assert.equal(b.indexOf(0x61, -4), -1);
61+
assert.equal(b.indexOf(0x61, -b.length), 0);
62+
assert.equal(b.indexOf(0x61, NaN), 0);
63+
assert.equal(b.indexOf(0x61, -Infinity), 0);
64+
assert.equal(b.indexOf(0x61, Infinity), -1);
65+
assert.equal(b.indexOf(0x0), -1);
66+
67+
assert.throws(function() {
68+
b.indexOf(function() { });
69+
});
70+
assert.throws(function() {
71+
b.indexOf({});
72+
});
73+
assert.throws(function() {
74+
b.indexOf([]);
75+
});

0 commit comments

Comments
 (0)