Skip to content

Commit 8c34c04

Browse files
committed
feat: add lastIndexOf method.
- Add lastIndexOf method to gets the last position at which the given value can be found in the buffer. - Add test cases for lastIndexOf method. - Add indexOfWithDir method to reuse code of indexOf and lastIndexOf.
1 parent d55a4ad commit 8c34c04

File tree

2 files changed

+164
-21
lines changed

2 files changed

+164
-21
lines changed

src/dynamicBuffer.ts

Lines changed: 74 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -244,10 +244,10 @@ export class DynamicBuffer {
244244
* not present.
245245
*
246246
* ```js
247-
* buf.append('Hello world');
248-
* console.log(buf.indexOf('world'));
249-
* // 6
250-
* console.log(buf.indexOf('not in buffer'));
247+
* buf.append('ABCABCABC');
248+
* console.log(buf.indexOf('ABC'));
249+
* // 0
250+
* console.log(buf.indexOf('abc'));
251251
* // -1
252252
* ```
253253
*
@@ -262,24 +262,35 @@ export class DynamicBuffer {
262262
value: string | Buffer | Uint8Array | number | DynamicBuffer,
263263
byteOffset: number = 0,
264264
encoding: BufferEncoding = 'utf8',
265-
) {
266-
let search: string | Buffer | Uint8Array | number;
267-
if (value instanceof DynamicBuffer) {
268-
search = value.buffer?.subarray(0, value.length) || '';
269-
} else {
270-
search = value;
271-
}
272-
273-
if (!this.buffer || this.length === 0) {
274-
return (typeof search === 'object' || typeof search === 'string') && search.length === 0 ? 0 : -1;
275-
}
276-
277-
let start = byteOffset >= 0 ? byteOffset : this.length + byteOffset;
278-
if (start >= this.length) {
279-
start = this.length;
280-
}
265+
): number {
266+
return this.indexOfWithDir(true, value, byteOffset, encoding);
267+
}
281268

282-
return this.buffer.subarray(0, this.length).indexOf(search, start, encoding);
269+
/**
270+
* Gets the last index at which the given value can be found in the buffer, or `-1` if it is
271+
* not present.
272+
*
273+
* ```js
274+
* buf.append('ABCABCABC');
275+
* console.log(buf.indexOf('ABC'));
276+
* // 6
277+
* console.log(buf.indexOf('abc'));
278+
* // -1
279+
* ```
280+
*
281+
* @param value The value what to search for.
282+
* @param byteOffset Where to begin searching in the buffer, and it'll be calculated from the
283+
* end of buffer if it's negative. Default `this.length`.
284+
* @param encoding The character encoding if the value is a string, default 'utf8'.
285+
* @returns The index of last occurrence of value in the buffer, and `-1` if the buffer does
286+
* not contain this value.
287+
*/
288+
lastIndexOf(
289+
value: string | Buffer | Uint8Array | number | DynamicBuffer,
290+
byteOffset: number = this.length,
291+
encoding: BufferEncoding = 'utf8',
292+
): number {
293+
return this.indexOfWithDir(false, value, byteOffset, encoding);
283294
}
284295

285296
/**
@@ -644,6 +655,48 @@ export class DynamicBuffer {
644655
this.size = newSize;
645656
}
646657

658+
/**
659+
* Gets the first or last index at which the given value can be found in the buffer, or `-1`
660+
* if it is not present.
661+
*
662+
* @param isFirst A boolean value to indicate the expected index is the first or last occurrence.
663+
* @param value The value what to search for.
664+
* @param byteOffset here to begin searching in the buffer.
665+
* @param encoding The character encoding if the value is a string
666+
* @returns The index of first or last occurrence of value in the buffer, and `-1` if the buffer
667+
* does not contain this value.
668+
*/
669+
private indexOfWithDir(
670+
isFirst: boolean,
671+
value: string | Buffer | Uint8Array | number | DynamicBuffer,
672+
byteOffset: number,
673+
encoding: BufferEncoding,
674+
): number {
675+
let search: string | Buffer | Uint8Array | number;
676+
if (value instanceof DynamicBuffer) {
677+
search = value.buffer?.subarray(0, value.length) || '';
678+
} else {
679+
search = value;
680+
}
681+
682+
if (!this.buffer || this.length === 0) {
683+
return (typeof search === 'object' || typeof search === 'string') && search.length === 0
684+
? 0
685+
: -1;
686+
}
687+
688+
let start = byteOffset >= 0 ? byteOffset : this.length + byteOffset;
689+
if (start >= this.length) {
690+
start = this.length;
691+
}
692+
693+
if (isFirst) {
694+
return this.buffer.subarray(0, this.length).indexOf(search, start, encoding);
695+
}
696+
697+
return this.buffer.subarray(0, this.length).lastIndexOf(search, start, encoding);
698+
}
699+
647700
/**
648701
* Calculate start and end offsets by optional parameters.
649702
*

test/search.spec.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,3 +182,93 @@ describe('Includes tests', () => {
182182
assert.equal(buf.includes('', 32), true);
183183
});
184184
});
185+
186+
describe('LastIndexOf tests', () => {
187+
it('Test lastIndexOf with empty buffer and empty value.', () => {
188+
const buf = new DynamicBuffer();
189+
190+
assert.equal(buf.lastIndexOf('ABC'), -1);
191+
assert.equal(buf.lastIndexOf(''), 0);
192+
assert.equal(buf.lastIndexOf(0), -1);
193+
assert.equal(buf.lastIndexOf(Buffer.alloc(0)), 0);
194+
assert.equal(buf.lastIndexOf(new Uint8Array()), 0);
195+
assert.equal(buf.lastIndexOf(new DynamicBuffer()), 0);
196+
});
197+
198+
it('Test lastIndexOf with empty value.', () => {
199+
const buf = new DynamicBuffer();
200+
buf.append('ABCABCABC');
201+
202+
assert.equal(buf.lastIndexOf(''), 9);
203+
assert.equal(buf.lastIndexOf(0), -1);
204+
assert.equal(buf.lastIndexOf(Buffer.alloc(0)), 9);
205+
assert.equal(buf.lastIndexOf(new Uint8Array()), 9);
206+
assert.equal(buf.lastIndexOf(new DynamicBuffer()), 9);
207+
});
208+
209+
it('Test lastIndexOf with string.', () => {
210+
const buf = new DynamicBuffer();
211+
buf.append('ABCABCABC');
212+
213+
assert.equal(buf.lastIndexOf('ABC'), 6);
214+
assert.equal(buf.lastIndexOf('BCA'), 4);
215+
assert.equal(buf.lastIndexOf('abc'), -1);
216+
});
217+
218+
it('Test lastIndexOf with number.', () => {
219+
const buf = new DynamicBuffer();
220+
buf.append('ABCABCABC');
221+
222+
assert.equal(buf.lastIndexOf(65), 6); // A
223+
assert.equal(buf.lastIndexOf(67), 8); // C
224+
assert.equal(buf.lastIndexOf(97), -1); // a
225+
});
226+
227+
it('Test lastIndexOf with Buffer.', () => {
228+
const buf = new DynamicBuffer();
229+
buf.append('ABCABCABC');
230+
231+
assert.equal(buf.lastIndexOf(Buffer.from('ABC')), 6);
232+
assert.equal(buf.lastIndexOf(Buffer.from('BCA')), 4);
233+
assert.equal(buf.lastIndexOf(Buffer.from('abc')), -1);
234+
});
235+
236+
it('Test lastIndexOf with Uint8Array.', () => {
237+
const buf = new DynamicBuffer();
238+
buf.append('ABCABCABC');
239+
240+
assert.equal(buf.lastIndexOf(new Uint8Array([0x41, 0x42, 0x43])), 6);
241+
assert.equal(buf.lastIndexOf(new Uint8Array([0x42, 0x43, 0x41])), 4);
242+
assert.equal(buf.lastIndexOf(new Uint8Array([0x61, 0x62, 0x63])), -1);
243+
});
244+
245+
it('Test lastIndexOf with DynamicBuffer.', () => {
246+
const buf = new DynamicBuffer();
247+
buf.append('ABCABCABC');
248+
249+
const buf1 = new DynamicBuffer();
250+
buf1.append('ABC');
251+
assert.equal(buf.lastIndexOf(buf1), 6);
252+
253+
const buf2 = new DynamicBuffer();
254+
buf2.append('BCA');
255+
assert.equal(buf.lastIndexOf(buf2), 4);
256+
257+
const buf3 = new DynamicBuffer();
258+
buf3.append('abc');
259+
assert.equal(buf.lastIndexOf(buf3), -1);
260+
});
261+
262+
it('Test lastIndexOf with byteOffset parameter.', () => {
263+
const buf = new DynamicBuffer();
264+
buf.append('ABCABCABC');
265+
266+
assert.equal(buf.lastIndexOf('ABC'), 6);
267+
assert.equal(buf.lastIndexOf('ABC', 0), 0);
268+
assert.equal(buf.lastIndexOf('ABC', 1), 0);
269+
assert.equal(buf.lastIndexOf('ABC', -1), 6);
270+
assert.equal(buf.lastIndexOf('ABC', -9), 0); // -11 equals 0
271+
assert.equal(buf.lastIndexOf('ABC', 32), 6);
272+
assert.equal(buf.lastIndexOf('', 32), 9);
273+
});
274+
});

0 commit comments

Comments
 (0)