GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
// Copyright Joyent, Inc. and other Node contributors. |
||
2 |
// |
||
3 |
// Permission is hereby granted, free of charge, to any person obtaining a |
||
4 |
// copy of this software and associated documentation files (the |
||
5 |
// "Software"), to deal in the Software without restriction, including |
||
6 |
// without limitation the rights to use, copy, modify, merge, publish, |
||
7 |
// distribute, sublicense, and/or sell copies of the Software, and to permit |
||
8 |
// persons to whom the Software is furnished to do so, subject to the |
||
9 |
// following conditions: |
||
10 |
// |
||
11 |
// The above copyright notice and this permission notice shall be included |
||
12 |
// in all copies or substantial portions of the Software. |
||
13 |
// |
||
14 |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
||
15 |
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||
16 |
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN |
||
17 |
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
||
18 |
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
||
19 |
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
||
20 |
// USE OR OTHER DEALINGS IN THE SOFTWARE. |
||
21 |
|||
22 |
#include "node_buffer.h" |
||
23 |
#include "node.h" |
||
24 |
#include "node_errors.h" |
||
25 |
#include "node_internals.h" |
||
26 |
|||
27 |
#include "env-inl.h" |
||
28 |
#include "string_bytes.h" |
||
29 |
#include "string_search.h" |
||
30 |
#include "util-inl.h" |
||
31 |
#include "v8-profiler.h" |
||
32 |
#include "v8.h" |
||
33 |
|||
34 |
#include <cstring> |
||
35 |
#include <climits> |
||
36 |
|||
37 |
#define THROW_AND_RETURN_UNLESS_BUFFER(env, obj) \ |
||
38 |
THROW_AND_RETURN_IF_NOT_BUFFER(env, obj, "argument") \ |
||
39 |
|||
40 |
#define THROW_AND_RETURN_IF_OOB(r) \ |
||
41 |
do { \ |
||
42 |
if ((r).IsNothing()) return; \ |
||
43 |
if (!(r).FromJust()) \ |
||
44 |
return node::THROW_ERR_OUT_OF_RANGE(env, "Index out of range"); \ |
||
45 |
} while (0) \ |
||
46 |
|||
47 |
namespace node { |
||
48 |
namespace Buffer { |
||
49 |
|||
50 |
using v8::ArrayBuffer; |
||
51 |
using v8::ArrayBufferCreationMode; |
||
52 |
using v8::ArrayBufferView; |
||
53 |
using v8::Context; |
||
54 |
using v8::EscapableHandleScope; |
||
55 |
using v8::FunctionCallbackInfo; |
||
56 |
using v8::Global; |
||
57 |
using v8::Int32; |
||
58 |
using v8::Integer; |
||
59 |
using v8::Isolate; |
||
60 |
using v8::Just; |
||
61 |
using v8::Local; |
||
62 |
using v8::Maybe; |
||
63 |
using v8::MaybeLocal; |
||
64 |
using v8::Nothing; |
||
65 |
using v8::Object; |
||
66 |
using v8::String; |
||
67 |
using v8::Uint32; |
||
68 |
using v8::Uint32Array; |
||
69 |
using v8::Uint8Array; |
||
70 |
using v8::Value; |
||
71 |
using v8::WeakCallbackInfo; |
||
72 |
|||
73 |
namespace { |
||
74 |
|||
75 |
34 |
class CallbackInfo { |
|
76 |
public: |
||
77 |
static inline void Free(char* data, void* hint); |
||
78 |
static inline CallbackInfo* New(Isolate* isolate, |
||
79 |
Local<ArrayBuffer> object, |
||
80 |
FreeCallback callback, |
||
81 |
char* data, |
||
82 |
void* hint = nullptr); |
||
83 |
|||
84 |
CallbackInfo(const CallbackInfo&) = delete; |
||
85 |
CallbackInfo& operator=(const CallbackInfo&) = delete; |
||
86 |
|||
87 |
private: |
||
88 |
static void WeakCallback(const WeakCallbackInfo<CallbackInfo>&); |
||
89 |
inline void WeakCallback(Isolate* isolate); |
||
90 |
inline CallbackInfo(Isolate* isolate, |
||
91 |
Local<ArrayBuffer> object, |
||
92 |
FreeCallback callback, |
||
93 |
char* data, |
||
94 |
void* hint); |
||
95 |
Global<ArrayBuffer> persistent_; |
||
96 |
FreeCallback const callback_; |
||
97 |
char* const data_; |
||
98 |
void* const hint_; |
||
99 |
}; |
||
100 |
|||
101 |
|||
102 |
void CallbackInfo::Free(char* data, void*) { |
||
103 |
::free(data); |
||
104 |
} |
||
105 |
|||
106 |
|||
107 |
17 |
CallbackInfo* CallbackInfo::New(Isolate* isolate, |
|
108 |
Local<ArrayBuffer> object, |
||
109 |
FreeCallback callback, |
||
110 |
char* data, |
||
111 |
void* hint) { |
||
112 |
17 |
return new CallbackInfo(isolate, object, callback, data, hint); |
|
113 |
} |
||
114 |
|||
115 |
|||
116 |
17 |
CallbackInfo::CallbackInfo(Isolate* isolate, |
|
117 |
Local<ArrayBuffer> object, |
||
118 |
FreeCallback callback, |
||
119 |
char* data, |
||
120 |
void* hint) |
||
121 |
: persistent_(isolate, object), |
||
122 |
callback_(callback), |
||
123 |
data_(data), |
||
124 |
34 |
hint_(hint) { |
|
125 |
17 |
ArrayBuffer::Contents obj_c = object->GetContents(); |
|
126 |
✗✓ | 17 |
CHECK_EQ(data_, static_cast<char*>(obj_c.Data())); |
127 |
✓✓ | 17 |
if (object->ByteLength() != 0) |
128 |
✗✓ | 15 |
CHECK_NOT_NULL(data_); |
129 |
|||
130 |
17 |
persistent_.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter); |
|
131 |
17 |
isolate->AdjustAmountOfExternalAllocatedMemory(sizeof(*this)); |
|
132 |
17 |
} |
|
133 |
|||
134 |
|||
135 |
17 |
void CallbackInfo::WeakCallback( |
|
136 |
const WeakCallbackInfo<CallbackInfo>& data) { |
||
137 |
17 |
CallbackInfo* self = data.GetParameter(); |
|
138 |
17 |
self->WeakCallback(data.GetIsolate()); |
|
139 |
✓✗ | 17 |
delete self; |
140 |
17 |
} |
|
141 |
|||
142 |
|||
143 |
17 |
void CallbackInfo::WeakCallback(Isolate* isolate) { |
|
144 |
17 |
callback_(data_, hint_); |
|
145 |
17 |
int64_t change_in_bytes = -static_cast<int64_t>(sizeof(*this)); |
|
146 |
isolate->AdjustAmountOfExternalAllocatedMemory(change_in_bytes); |
||
147 |
17 |
} |
|
148 |
|||
149 |
|||
150 |
// Parse index for external array data. An empty Maybe indicates |
||
151 |
// a pending exception. `false` indicates that the index is out-of-bounds. |
||
152 |
4472386 |
inline MUST_USE_RESULT Maybe<bool> ParseArrayIndex(Environment* env, |
|
153 |
Local<Value> arg, |
||
154 |
size_t def, |
||
155 |
size_t* ret) { |
||
156 |
✗✓ | 8944772 |
if (arg->IsUndefined()) { |
157 |
*ret = def; |
||
158 |
return Just(true); |
||
159 |
} |
||
160 |
|||
161 |
int64_t tmp_i; |
||
162 |
✗✓ | 13417158 |
if (!arg->IntegerValue(env->context()).To(&tmp_i)) |
163 |
return Nothing<bool>(); |
||
164 |
|||
165 |
✗✓ | 4472386 |
if (tmp_i < 0) |
166 |
return Just(false); |
||
167 |
|||
168 |
// Check that the result fits in a size_t. |
||
169 |
4472386 |
const uint64_t kSizeMax = static_cast<uint64_t>(static_cast<size_t>(-1)); |
|
170 |
// coverity[pointless_expression] |
||
171 |
if (static_cast<uint64_t>(tmp_i) > kSizeMax) |
||
172 |
return Just(false); |
||
173 |
|||
174 |
4472386 |
*ret = static_cast<size_t>(tmp_i); |
|
175 |
4472386 |
return Just(true); |
|
176 |
} |
||
177 |
|||
178 |
} // anonymous namespace |
||
179 |
|||
180 |
// Buffer methods |
||
181 |
|||
182 |
12340795 |
bool HasInstance(Local<Value> val) { |
|
183 |
12340795 |
return val->IsArrayBufferView(); |
|
184 |
} |
||
185 |
|||
186 |
|||
187 |
1120745 |
bool HasInstance(Local<Object> obj) { |
|
188 |
1120745 |
return obj->IsArrayBufferView(); |
|
189 |
} |
||
190 |
|||
191 |
|||
192 |
485013 |
char* Data(Local<Value> val) { |
|
193 |
✗✓ | 485013 |
CHECK(val->IsArrayBufferView()); |
194 |
485013 |
Local<ArrayBufferView> ui = val.As<ArrayBufferView>(); |
|
195 |
970026 |
ArrayBuffer::Contents ab_c = ui->Buffer()->GetContents(); |
|
196 |
970025 |
return static_cast<char*>(ab_c.Data()) + ui->ByteOffset(); |
|
197 |
} |
||
198 |
|||
199 |
|||
200 |
262240 |
char* Data(Local<Object> obj) { |
|
201 |
✗✓ | 262240 |
CHECK(obj->IsArrayBufferView()); |
202 |
262240 |
Local<ArrayBufferView> ui = obj.As<ArrayBufferView>(); |
|
203 |
524480 |
ArrayBuffer::Contents ab_c = ui->Buffer()->GetContents(); |
|
204 |
524480 |
return static_cast<char*>(ab_c.Data()) + ui->ByteOffset(); |
|
205 |
} |
||
206 |
|||
207 |
|||
208 |
441335 |
size_t Length(Local<Value> val) { |
|
209 |
✗✓ | 441335 |
CHECK(val->IsArrayBufferView()); |
210 |
441335 |
Local<ArrayBufferView> ui = val.As<ArrayBufferView>(); |
|
211 |
441335 |
return ui->ByteLength(); |
|
212 |
} |
||
213 |
|||
214 |
|||
215 |
243528 |
size_t Length(Local<Object> obj) { |
|
216 |
✗✓ | 243528 |
CHECK(obj->IsArrayBufferView()); |
217 |
243528 |
Local<ArrayBufferView> ui = obj.As<ArrayBufferView>(); |
|
218 |
243528 |
return ui->ByteLength(); |
|
219 |
} |
||
220 |
|||
221 |
|||
222 |
50162 |
inline MaybeLocal<Uint8Array> New(Environment* env, |
|
223 |
Local<ArrayBuffer> ab, |
||
224 |
size_t byte_offset, |
||
225 |
size_t length) { |
||
226 |
✗✓ | 100324 |
CHECK(!env->buffer_prototype_object().IsEmpty()); |
227 |
50162 |
Local<Uint8Array> ui = Uint8Array::New(ab, byte_offset, length); |
|
228 |
Maybe<bool> mb = |
||
229 |
150486 |
ui->SetPrototype(env->context(), env->buffer_prototype_object()); |
|
230 |
✗✓ | 50162 |
if (mb.IsNothing()) |
231 |
return MaybeLocal<Uint8Array>(); |
||
232 |
50162 |
return ui; |
|
233 |
} |
||
234 |
|||
235 |
|||
236 |
175 |
MaybeLocal<Object> New(Isolate* isolate, |
|
237 |
Local<String> string, |
||
238 |
enum encoding enc) { |
||
239 |
175 |
EscapableHandleScope scope(isolate); |
|
240 |
|||
241 |
size_t length; |
||
242 |
✗✓ | 350 |
if (!StringBytes::Size(isolate, string, enc).To(&length)) |
243 |
return Local<Object>(); |
||
244 |
175 |
size_t actual = 0; |
|
245 |
175 |
char* data = nullptr; |
|
246 |
|||
247 |
✓✗ | 175 |
if (length > 0) { |
248 |
175 |
data = UncheckedMalloc(length); |
|
249 |
|||
250 |
✗✓ | 175 |
if (data == nullptr) { |
251 |
THROW_ERR_MEMORY_ALLOCATION_FAILED(isolate); |
||
252 |
return Local<Object>(); |
||
253 |
} |
||
254 |
|||
255 |
175 |
actual = StringBytes::Write(isolate, data, length, string, enc); |
|
256 |
✗✓ | 175 |
CHECK(actual <= length); |
257 |
|||
258 |
✓✓ | 175 |
if (actual == 0) { |
259 |
1 |
free(data); |
|
260 |
1 |
data = nullptr; |
|
261 |
✗✓ | 174 |
} else if (actual < length) { |
262 |
data = node::Realloc(data, actual); |
||
263 |
} |
||
264 |
} |
||
265 |
|||
266 |
350 |
return scope.EscapeMaybe(New(isolate, data, actual)); |
|
267 |
} |
||
268 |
|||
269 |
|||
270 |
1 |
MaybeLocal<Object> New(Isolate* isolate, size_t length) { |
|
271 |
1 |
EscapableHandleScope handle_scope(isolate); |
|
272 |
Local<Object> obj; |
||
273 |
1 |
Environment* env = Environment::GetCurrent(isolate); |
|
274 |
✗✓ | 1 |
if (env == nullptr) { |
275 |
THROW_ERR_BUFFER_CONTEXT_NOT_AVAILABLE(isolate); |
||
276 |
return MaybeLocal<Object>(); |
||
277 |
} |
||
278 |
✓✗ | 2 |
if (Buffer::New(env, length).ToLocal(&obj)) |
279 |
1 |
return handle_scope.Escape(obj); |
|
280 |
return Local<Object>(); |
||
281 |
} |
||
282 |
|||
283 |
|||
284 |
18706 |
MaybeLocal<Object> New(Environment* env, size_t length) { |
|
285 |
18706 |
EscapableHandleScope scope(env->isolate()); |
|
286 |
|||
287 |
// V8 currently only allows a maximum Typed Array index of max Smi. |
||
288 |
✗✓ | 18706 |
if (length > kMaxLength) { |
289 |
env->isolate()->ThrowException(ERR_BUFFER_TOO_LARGE(env->isolate())); |
||
290 |
return Local<Object>(); |
||
291 |
} |
||
292 |
|||
293 |
37412 |
AllocatedBuffer ret(env); |
|
294 |
✓✓ | 18706 |
if (length > 0) { |
295 |
11209 |
ret = env->AllocateManaged(length, false); |
|
296 |
✗✓ | 11209 |
if (ret.data() == nullptr) { |
297 |
THROW_ERR_MEMORY_ALLOCATION_FAILED(env); |
||
298 |
return Local<Object>(); |
||
299 |
} |
||
300 |
} |
||
301 |
|||
302 |
37412 |
return scope.EscapeMaybe(ret.ToBuffer()); |
|
303 |
} |
||
304 |
|||
305 |
|||
306 |
7897 |
MaybeLocal<Object> Copy(Isolate* isolate, const char* data, size_t length) { |
|
307 |
7897 |
EscapableHandleScope handle_scope(isolate); |
|
308 |
7897 |
Environment* env = Environment::GetCurrent(isolate); |
|
309 |
✓✓ | 7897 |
if (env == nullptr) { |
310 |
1 |
THROW_ERR_BUFFER_CONTEXT_NOT_AVAILABLE(isolate); |
|
311 |
1 |
return MaybeLocal<Object>(); |
|
312 |
} |
||
313 |
Local<Object> obj; |
||
314 |
✓✗ | 15792 |
if (Buffer::Copy(env, data, length).ToLocal(&obj)) |
315 |
7896 |
return handle_scope.Escape(obj); |
|
316 |
return Local<Object>(); |
||
317 |
} |
||
318 |
|||
319 |
|||
320 |
9632 |
MaybeLocal<Object> Copy(Environment* env, const char* data, size_t length) { |
|
321 |
9632 |
EscapableHandleScope scope(env->isolate()); |
|
322 |
|||
323 |
// V8 currently only allows a maximum Typed Array index of max Smi. |
||
324 |
✗✓ | 9632 |
if (length > kMaxLength) { |
325 |
env->isolate()->ThrowException(ERR_BUFFER_TOO_LARGE(env->isolate())); |
||
326 |
return Local<Object>(); |
||
327 |
} |
||
328 |
|||
329 |
19264 |
AllocatedBuffer ret(env); |
|
330 |
✓✓ | 9632 |
if (length > 0) { |
331 |
✗✓ | 9494 |
CHECK_NOT_NULL(data); |
332 |
9494 |
ret = env->AllocateManaged(length, false); |
|
333 |
✗✓ | 9494 |
if (ret.data() == nullptr) { |
334 |
THROW_ERR_MEMORY_ALLOCATION_FAILED(env); |
||
335 |
return Local<Object>(); |
||
336 |
} |
||
337 |
9494 |
memcpy(ret.data(), data, length); |
|
338 |
} |
||
339 |
|||
340 |
19264 |
return scope.EscapeMaybe(ret.ToBuffer()); |
|
341 |
} |
||
342 |
|||
343 |
|||
344 |
17 |
MaybeLocal<Object> New(Isolate* isolate, |
|
345 |
char* data, |
||
346 |
size_t length, |
||
347 |
FreeCallback callback, |
||
348 |
void* hint) { |
||
349 |
17 |
EscapableHandleScope handle_scope(isolate); |
|
350 |
17 |
Environment* env = Environment::GetCurrent(isolate); |
|
351 |
✗✓ | 17 |
if (env == nullptr) { |
352 |
callback(data, hint); |
||
353 |
THROW_ERR_BUFFER_CONTEXT_NOT_AVAILABLE(isolate); |
||
354 |
return MaybeLocal<Object>(); |
||
355 |
} |
||
356 |
Local<Object> obj; |
||
357 |
✓✗ | 34 |
if (Buffer::New(env, data, length, callback, hint).ToLocal(&obj)) |
358 |
17 |
return handle_scope.Escape(obj); |
|
359 |
return Local<Object>(); |
||
360 |
} |
||
361 |
|||
362 |
|||
363 |
17 |
MaybeLocal<Object> New(Environment* env, |
|
364 |
char* data, |
||
365 |
size_t length, |
||
366 |
FreeCallback callback, |
||
367 |
void* hint) { |
||
368 |
17 |
EscapableHandleScope scope(env->isolate()); |
|
369 |
|||
370 |
✗✓ | 17 |
if (length > kMaxLength) { |
371 |
env->isolate()->ThrowException(ERR_BUFFER_TOO_LARGE(env->isolate())); |
||
372 |
callback(data, hint); |
||
373 |
return Local<Object>(); |
||
374 |
} |
||
375 |
|||
376 |
17 |
Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), data, length); |
|
377 |
17 |
MaybeLocal<Uint8Array> ui = Buffer::New(env, ab, 0, length); |
|
378 |
|||
379 |
17 |
CallbackInfo::New(env->isolate(), ab, callback, data, hint); |
|
380 |
|||
381 |
✗✓ | 17 |
if (ui.IsEmpty()) |
382 |
return MaybeLocal<Object>(); |
||
383 |
|||
384 |
17 |
return scope.Escape(ui.ToLocalChecked()); |
|
385 |
} |
||
386 |
|||
387 |
// Warning: This function needs `data` to be allocated with malloc() and not |
||
388 |
// necessarily isolate's ArrayBuffer::Allocator. |
||
389 |
175 |
MaybeLocal<Object> New(Isolate* isolate, char* data, size_t length) { |
|
390 |
175 |
EscapableHandleScope handle_scope(isolate); |
|
391 |
175 |
Environment* env = Environment::GetCurrent(isolate); |
|
392 |
✗✓ | 175 |
if (env == nullptr) { |
393 |
free(data); |
||
394 |
THROW_ERR_BUFFER_CONTEXT_NOT_AVAILABLE(isolate); |
||
395 |
return MaybeLocal<Object>(); |
||
396 |
} |
||
397 |
Local<Object> obj; |
||
398 |
✓✗ | 350 |
if (Buffer::New(env, data, length, true).ToLocal(&obj)) |
399 |
175 |
return handle_scope.Escape(obj); |
|
400 |
return Local<Object>(); |
||
401 |
} |
||
402 |
|||
403 |
// Warning: If this call comes through the public node_buffer.h API, |
||
404 |
// the contract for this function is that `data` is allocated with malloc() |
||
405 |
// and not necessarily isolate's ArrayBuffer::Allocator. |
||
406 |
50145 |
MaybeLocal<Object> New(Environment* env, |
|
407 |
char* data, |
||
408 |
size_t length, |
||
409 |
bool uses_malloc) { |
||
410 |
✓✓ | 50145 |
if (length > 0) { |
411 |
✗✓ | 42285 |
CHECK_NOT_NULL(data); |
412 |
✗✓ | 42285 |
CHECK(length <= kMaxLength); |
413 |
} |
||
414 |
|||
415 |
✓✓ | 50145 |
if (uses_malloc) { |
416 |
✗✓ | 536 |
if (!env->isolate_data()->uses_node_allocator()) { |
417 |
// We don't know for sure that the allocator is malloc()-based, so we need |
||
418 |
// to fall back to the FreeCallback variant. |
||
419 |
auto free_callback = [](char* data, void* hint) { free(data); }; |
||
420 |
return New(env, data, length, free_callback, nullptr); |
||
421 |
} else { |
||
422 |
// This is malloc()-based, so we can acquire it into our own |
||
423 |
// ArrayBufferAllocator. |
||
424 |
✗✓ | 536 |
CHECK_NOT_NULL(env->isolate_data()->node_allocator()); |
425 |
536 |
env->isolate_data()->node_allocator()->RegisterPointer(data, length); |
|
426 |
} |
||
427 |
} |
||
428 |
|||
429 |
Local<ArrayBuffer> ab = |
||
430 |
ArrayBuffer::New(env->isolate(), |
||
431 |
data, |
||
432 |
length, |
||
433 |
50145 |
ArrayBufferCreationMode::kInternalized); |
|
434 |
100290 |
return Buffer::New(env, ab, 0, length).FromMaybe(Local<Object>()); |
|
435 |
} |
||
436 |
|||
437 |
namespace { |
||
438 |
|||
439 |
175 |
void CreateFromString(const FunctionCallbackInfo<Value>& args) { |
|
440 |
✗✓ | 525 |
CHECK(args[0]->IsString()); |
441 |
✗✓ | 350 |
CHECK(args[1]->IsInt32()); |
442 |
|||
443 |
525 |
enum encoding enc = static_cast<enum encoding>(args[1].As<Int32>()->Value()); |
|
444 |
Local<Object> buf; |
||
445 |
✓✗ | 525 |
if (New(args.GetIsolate(), args[0].As<String>(), enc).ToLocal(&buf)) |
446 |
350 |
args.GetReturnValue().Set(buf); |
|
447 |
175 |
} |
|
448 |
|||
449 |
|||
450 |
template <encoding encoding> |
||
451 |
539553 |
void StringSlice(const FunctionCallbackInfo<Value>& args) { |
|
452 |
539553 |
Environment* env = Environment::GetCurrent(args); |
|
453 |
539553 |
Isolate* isolate = env->isolate(); |
|
454 |
|||
455 |
✗✓✗✓ ✗✓✗✓ ✗✓✗✓ |
542244 |
THROW_AND_RETURN_UNLESS_BUFFER(env, args.This()); |
456 |
539553 |
ArrayBufferViewContents<char> buffer(args.This()); |
|
457 |
|||
458 |
✓✓✗✓ ✓✓✗✓ ✗✓✗✓ |
539553 |
if (buffer.length() == 0) |
459 |
5366 |
return args.GetReturnValue().SetEmptyString(); |
|
460 |
|||
461 |
536870 |
size_t start = 0; |
|
462 |
536870 |
size_t end = 0; |
|
463 |
✗✓✗✓ ✗✓✗✓ ✗✓✗✓ ✗✓✗✓ ✗✓✗✓ ✗✓✗✓ |
2147480 |
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[0], 0, &start)); |
464 |
✗✓✗✓ ✗✓✗✓ ✗✓✗✓ ✗✓✗✓ ✗✓✗✓ ✗✓✗✓ |
2684350 |
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[1], buffer.length(), &end)); |
465 |
✗✓✗✓ ✗✓✗✓ ✗✓✗✓ |
536870 |
if (end < start) end = start; |
466 |
✗✓✗✓ ✗✓✗✓ ✗✓✗✓ ✗✓✗✓ ✗✓✗✓ ✗✓✗✓ |
1610610 |
THROW_AND_RETURN_IF_OOB(Just(end <= buffer.length())); |
467 |
536870 |
size_t length = end - start; |
|
468 |
|||
469 |
Local<Value> error; |
||
470 |
MaybeLocal<Value> ret = |
||
471 |
StringBytes::Encode(isolate, |
||
472 |
536870 |
buffer.data() + start, |
|
473 |
length, |
||
474 |
encoding, |
||
475 |
536870 |
&error); |
|
476 |
✓✓✓✓ ✓✓✓✓ ✓✓✓✓ |
536869 |
if (ret.IsEmpty()) { |
477 |
✗✓✗✓ ✗✓✗✓ ✗✓✗✓ |
8 |
CHECK(!error.IsEmpty()); |
478 |
8 |
isolate->ThrowException(error); |
|
479 |
8 |
return; |
|
480 |
} |
||
481 |
1073722 |
args.GetReturnValue().Set(ret.ToLocalChecked()); |
|
482 |
} |
||
483 |
|||
484 |
|||
485 |
// bytesCopied = copy(buffer, target[, targetStart][, sourceStart][, sourceEnd]) |
||
486 |
11 |
void Copy(const FunctionCallbackInfo<Value> &args) { |
|
487 |
11 |
Environment* env = Environment::GetCurrent(args); |
|
488 |
|||
489 |
✗✓ | 11 |
THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); |
490 |
✗✓ | 11 |
THROW_AND_RETURN_UNLESS_BUFFER(env, args[1]); |
491 |
11 |
ArrayBufferViewContents<char> source(args[0]); |
|
492 |
22 |
Local<Object> target_obj = args[1].As<Object>(); |
|
493 |
✗✓✓✗ ✗✓ |
66 |
SPREAD_BUFFER_ARG(target_obj, target); |
494 |
|||
495 |
11 |
size_t target_start = 0; |
|
496 |
11 |
size_t source_start = 0; |
|
497 |
11 |
size_t source_end = 0; |
|
498 |
|||
499 |
✗✓✗✓ |
44 |
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[2], 0, &target_start)); |
500 |
✗✓✗✓ |
44 |
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[3], 0, &source_start)); |
501 |
✗✓✗✓ |
55 |
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[4], source.length(), |
502 |
&source_end)); |
||
503 |
|||
504 |
// Copy 0 bytes; we're done |
||
505 |
✓✗✗✓ |
11 |
if (target_start >= target_length || source_start >= source_end) |
506 |
return args.GetReturnValue().Set(0); |
||
507 |
|||
508 |
✗✓ | 11 |
if (source_start > source.length()) |
509 |
return THROW_ERR_OUT_OF_RANGE( |
||
510 |
env, "The value of \"sourceStart\" is out of range."); |
||
511 |
|||
512 |
✗✓ | 11 |
if (source_end - source_start > target_length - target_start) |
513 |
source_end = source_start + target_length - target_start; |
||
514 |
|||
515 |
uint32_t to_copy = std::min( |
||
516 |
22 |
std::min(source_end - source_start, target_length - target_start), |
|
517 |
33 |
source.length() - source_start); |
|
518 |
|||
519 |
11 |
memmove(target_data + target_start, source.data() + source_start, to_copy); |
|
520 |
22 |
args.GetReturnValue().Set(to_copy); |
|
521 |
} |
||
522 |
|||
523 |
|||
524 |
805175 |
void Fill(const FunctionCallbackInfo<Value>& args) { |
|
525 |
805175 |
Environment* env = Environment::GetCurrent(args); |
|
526 |
805175 |
Local<Context> ctx = env->context(); |
|
527 |
|||
528 |
✗✓ | 1610214 |
THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); |
529 |
✗✓✓✗ ✗✓ |
6441400 |
SPREAD_BUFFER_ARG(args[0], ts_obj); |
530 |
|||
531 |
uint32_t start; |
||
532 |
✗✓ | 2415525 |
if (!args[2]->Uint32Value(ctx).To(&start)) return; |
533 |
uint32_t end; |
||
534 |
✗✓ | 2415525 |
if (!args[3]->Uint32Value(ctx).To(&end)) return; |
535 |
805175 |
size_t fill_length = end - start; |
|
536 |
Local<String> str_obj; |
||
537 |
size_t str_length; |
||
538 |
enum encoding enc; |
||
539 |
|||
540 |
// OOB Check. Throw the error in JS. |
||
541 |
✓✓✓✓ |
805175 |
if (start > end || fill_length + start > ts_obj_length) |
542 |
6 |
return args.GetReturnValue().Set(-2); |
|
543 |
|||
544 |
// First check if Buffer has been passed. |
||
545 |
✓✓ | 805172 |
if (Buffer::HasInstance(args[1])) { |
546 |
✗✓✓✓ ✗✓ |
64 |
SPREAD_BUFFER_ARG(args[1], fill_obj); |
547 |
8 |
str_length = fill_obj_length; |
|
548 |
memcpy( |
||
549 |
8 |
ts_obj_data + start, fill_obj_data, std::min(str_length, fill_length)); |
|
550 |
8 |
goto start_fill; |
|
551 |
} |
||
552 |
|||
553 |
// Then coerce everything that's not a string. |
||
554 |
✓✓ | 2415492 |
if (!args[1]->IsString()) { |
555 |
uint32_t val; |
||
556 |
✗✓ | 2415045 |
if (!args[1]->Uint32Value(ctx).To(&val)) return; |
557 |
805015 |
int value = val & 255; |
|
558 |
805015 |
memset(ts_obj_data + start, value, fill_length); |
|
559 |
805015 |
return; |
|
560 |
} |
||
561 |
|||
562 |
596 |
str_obj = args[1]->ToString(env->context()).ToLocalChecked(); |
|
563 |
149 |
enc = ParseEncoding(env->isolate(), args[4], UTF8); |
|
564 |
|||
565 |
// Can't use StringBytes::Write() in all cases. For example if attempting |
||
566 |
// to write a two byte character into a one byte Buffer. |
||
567 |
✓✓ | 149 |
if (enc == UTF8) { |
568 |
96 |
str_length = str_obj->Utf8Length(env->isolate()); |
|
569 |
48 |
node::Utf8Value str(env->isolate(), args[1]); |
|
570 |
48 |
memcpy(ts_obj_data + start, *str, std::min(str_length, fill_length)); |
|
571 |
|||
572 |
✓✓ | 101 |
} else if (enc == UCS2) { |
573 |
39 |
str_length = str_obj->Length() * sizeof(uint16_t); |
|
574 |
39 |
node::TwoByteValue str(env->isolate(), args[1]); |
|
575 |
✗✓ | 39 |
if (IsBigEndian()) |
576 |
SwapBytes16(reinterpret_cast<char*>(&str[0]), str_length); |
||
577 |
|||
578 |
39 |
memcpy(ts_obj_data + start, *str, std::min(str_length, fill_length)); |
|
579 |
|||
580 |
} else { |
||
581 |
// Write initial String to Buffer, then use that memory to copy remainder |
||
582 |
// of string. Correct the string length for cases like HEX where less than |
||
583 |
// the total string length is written. |
||
584 |
str_length = StringBytes::Write(env->isolate(), |
||
585 |
ts_obj_data + start, |
||
586 |
fill_length, |
||
587 |
str_obj, |
||
588 |
enc, |
||
589 |
62 |
nullptr); |
|
590 |
} |
||
591 |
|||
592 |
start_fill: |
||
593 |
|||
594 |
✓✓ | 157 |
if (str_length >= fill_length) |
595 |
15 |
return; |
|
596 |
|||
597 |
// If str_length is zero, then either an empty buffer was provided, or Write() |
||
598 |
// indicated that no bytes could be written. If no bytes could be written, |
||
599 |
// then return -1 because the fill value is invalid. This will trigger a throw |
||
600 |
// in JavaScript. Silently failing should be avoided because it can lead to |
||
601 |
// buffers with unexpected contents. |
||
602 |
✓✓ | 142 |
if (str_length == 0) |
603 |
12 |
return args.GetReturnValue().Set(-1); |
|
604 |
|||
605 |
136 |
size_t in_there = str_length; |
|
606 |
136 |
char* ptr = ts_obj_data + start + str_length; |
|
607 |
|||
608 |
✓✓ | 501 |
while (in_there < fill_length - in_there) { |
609 |
229 |
memcpy(ptr, ts_obj_data + start, in_there); |
|
610 |
229 |
ptr += in_there; |
|
611 |
229 |
in_there *= 2; |
|
612 |
} |
||
613 |
|||
614 |
✓✗ | 136 |
if (in_there < fill_length) { |
615 |
136 |
memcpy(ptr, ts_obj_data + start, fill_length - in_there); |
|
616 |
} |
||
617 |
} |
||
618 |
|||
619 |
|||
620 |
template <encoding encoding> |
||
621 |
581192 |
void StringWrite(const FunctionCallbackInfo<Value>& args) { |
|
622 |
581192 |
Environment* env = Environment::GetCurrent(args); |
|
623 |
|||
624 |
✗✓✗✓ ✗✓✗✓ ✗✓✗✓ |
581196 |
THROW_AND_RETURN_UNLESS_BUFFER(env, args.This()); |
625 |
✗✓✓✗ ✗✓✗✓ ✓✗✗✓ ✗✓✓✓ ✗✓✗✓ ✓✗✗✓ ✗✓✓✓ ✗✓✗✓ ✓✗✗✓ |
4649536 |
SPREAD_BUFFER_ARG(args.This(), ts_obj); |
626 |
|||
627 |
✗✓✗✓ ✗✓✗✓ ✗✓✗✓ |
1743576 |
THROW_AND_RETURN_IF_NOT_STRING(env, args[0], "argument"); |
628 |
|||
629 |
2324768 |
Local<String> str = args[0]->ToString(env->context()).ToLocalChecked(); |
|
630 |
|||
631 |
size_t offset; |
||
632 |
size_t max_length; |
||
633 |
|||
634 |
✗✓✗✓ ✗✓✗✓ ✗✓✗✓ ✗✓✗✓ ✗✓✗✓ ✗✓✗✓ |
2324768 |
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[1], 0, &offset)); |
635 |
✗✓✗✓ ✗✓✗✓ ✗✓✗✓ |
581192 |
if (offset > ts_obj_length) { |
636 |
return node::THROW_ERR_BUFFER_OUT_OF_BOUNDS( |
||
637 |
env, "\"offset\" is outside of buffer bounds"); |
||
638 |
} |
||
639 |
|||
640 |
✗✓✗✓ ✗✓✗✓ ✗✓✗✓ ✗✓✗✓ ✗✓✗✓ ✗✓✗✓ |
2905960 |
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[2], ts_obj_length - offset, |
641 |
&max_length)); |
||
642 |
|||
643 |
581192 |
max_length = std::min(ts_obj_length - offset, max_length); |
|
644 |
|||
645 |
✓✓✗✓ ✓✓✗✓ ✓✓✗✓ |
581192 |
if (max_length == 0) |
646 |
8 |
return args.GetReturnValue().Set(0); |
|
647 |
|||
648 |
uint32_t written = StringBytes::Write(env->isolate(), |
||
649 |
ts_obj_data + offset, |
||
650 |
max_length, |
||
651 |
str, |
||
652 |
encoding, |
||
653 |
581188 |
nullptr); |
|
654 |
1162376 |
args.GetReturnValue().Set(written); |
|
655 |
} |
||
656 |
|||
657 |
189059 |
void ByteLengthUtf8(const FunctionCallbackInfo<Value> &args) { |
|
658 |
189059 |
Environment* env = Environment::GetCurrent(args); |
|
659 |
✗✓ | 567177 |
CHECK(args[0]->IsString()); |
660 |
|||
661 |
// Fast case: avoid StringBytes on UTF8 string. Jump to v8. |
||
662 |
1134354 |
args.GetReturnValue().Set(args[0].As<String>()->Utf8Length(env->isolate())); |
|
663 |
189059 |
} |
|
664 |
|||
665 |
// Normalize val to be an integer in the range of [1, -1] since |
||
666 |
// implementations of memcmp() can vary by platform. |
||
667 |
204934 |
static int normalizeCompareVal(int val, size_t a_length, size_t b_length) { |
|
668 |
✓✓ | 204934 |
if (val == 0) { |
669 |
✓✓ | 104903 |
if (a_length > b_length) |
670 |
6 |
return 1; |
|
671 |
✓✓ | 104897 |
else if (a_length < b_length) |
672 |
5 |
return -1; |
|
673 |
} else { |
||
674 |
✓✓ | 100031 |
if (val > 0) |
675 |
7 |
return 1; |
|
676 |
else |
||
677 |
100024 |
return -1; |
|
678 |
} |
||
679 |
104892 |
return val; |
|
680 |
} |
||
681 |
|||
682 |
9 |
void CompareOffset(const FunctionCallbackInfo<Value> &args) { |
|
683 |
9 |
Environment* env = Environment::GetCurrent(args); |
|
684 |
|||
685 |
✗✓ | 9 |
THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); |
686 |
✗✓ | 9 |
THROW_AND_RETURN_UNLESS_BUFFER(env, args[1]); |
687 |
9 |
ArrayBufferViewContents<char> source(args[0]); |
|
688 |
9 |
ArrayBufferViewContents<char> target(args[1]); |
|
689 |
|||
690 |
9 |
size_t target_start = 0; |
|
691 |
9 |
size_t source_start = 0; |
|
692 |
9 |
size_t source_end = 0; |
|
693 |
9 |
size_t target_end = 0; |
|
694 |
|||
695 |
✗✓✗✓ |
36 |
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[2], 0, &target_start)); |
696 |
✗✓✗✓ |
36 |
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[3], 0, &source_start)); |
697 |
✗✓✗✓ |
45 |
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[4], target.length(), |
698 |
&target_end)); |
||
699 |
✗✓✗✓ |
45 |
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[5], source.length(), |
700 |
&source_end)); |
||
701 |
|||
702 |
✗✓ | 9 |
if (source_start > source.length()) |
703 |
return THROW_ERR_OUT_OF_RANGE( |
||
704 |
env, "The value of \"sourceStart\" is out of range."); |
||
705 |
✗✓ | 9 |
if (target_start > target.length()) |
706 |
return THROW_ERR_OUT_OF_RANGE( |
||
707 |
env, "The value of \"targetStart\" is out of range."); |
||
708 |
|||
709 |
✗✓ | 9 |
CHECK_LE(source_start, source_end); |
710 |
✗✓ | 9 |
CHECK_LE(target_start, target_end); |
711 |
|||
712 |
size_t to_cmp = |
||
713 |
18 |
std::min(std::min(source_end - source_start, target_end - target_start), |
|
714 |
27 |
source.length() - source_start); |
|
715 |
|||
716 |
int val = normalizeCompareVal(to_cmp > 0 ? |
||
717 |
18 |
memcmp(source.data() + source_start, |
|
718 |
18 |
target.data() + target_start, |
|
719 |
18 |
to_cmp) : 0, |
|
720 |
source_end - source_start, |
||
721 |
✓✗ | 18 |
target_end - target_start); |
722 |
|||
723 |
18 |
args.GetReturnValue().Set(val); |
|
724 |
} |
||
725 |
|||
726 |
204925 |
void Compare(const FunctionCallbackInfo<Value> &args) { |
|
727 |
204925 |
Environment* env = Environment::GetCurrent(args); |
|
728 |
|||
729 |
✗✓ | 204925 |
THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); |
730 |
✗✓ | 204925 |
THROW_AND_RETURN_UNLESS_BUFFER(env, args[1]); |
731 |
204925 |
ArrayBufferViewContents<char> a(args[0]); |
|
732 |
204925 |
ArrayBufferViewContents<char> b(args[1]); |
|
733 |
|||
734 |
204925 |
size_t cmp_length = std::min(a.length(), b.length()); |
|
735 |
|||
736 |
int val = normalizeCompareVal(cmp_length > 0 ? |
||
737 |
204866 |
memcmp(a.data(), b.data(), cmp_length) : 0, |
|
738 |
✓✓ | 409791 |
a.length(), b.length()); |
739 |
409850 |
args.GetReturnValue().Set(val); |
|
740 |
} |
||
741 |
|||
742 |
|||
743 |
// Computes the offset for starting an indexOf or lastIndexOf search. |
||
744 |
// Returns either a valid offset in [0...<length - 1>], ie inside the Buffer, |
||
745 |
// or -1 to signal that there is no possible match. |
||
746 |
19786 |
int64_t IndexOfOffset(size_t length, |
|
747 |
int64_t offset_i64, |
||
748 |
int64_t needle_length, |
||
749 |
bool is_forward) { |
||
750 |
19786 |
int64_t length_i64 = static_cast<int64_t>(length); |
|
751 |
✓✓ | 19786 |
if (offset_i64 < 0) { |
752 |
✓✓ | 77 |
if (offset_i64 + length_i64 >= 0) { |
753 |
// Negative offsets count backwards from the end of the buffer. |
||
754 |
59 |
return length_i64 + offset_i64; |
|
755 |
✓✓✗✓ |
18 |
} else if (is_forward || needle_length == 0) { |
756 |
// indexOf from before the start of the buffer: search the whole buffer. |
||
757 |
10 |
return 0; |
|
758 |
} else { |
||
759 |
// lastIndexOf from before the start of the buffer: no match. |
||
760 |
8 |
return -1; |
|
761 |
} |
||
762 |
} else { |
||
763 |
✓✓ | 19709 |
if (offset_i64 + needle_length <= length_i64) { |
764 |
// Valid positive offset. |
||
765 |
19621 |
return offset_i64; |
|
766 |
✓✓ | 88 |
} else if (needle_length == 0) { |
767 |
// Out of buffer bounds, but empty needle: point to end of buffer. |
||
768 |
10 |
return length_i64; |
|
769 |
✓✓ | 78 |
} else if (is_forward) { |
770 |
// indexOf from past the end of the buffer: no match. |
||
771 |
15 |
return -1; |
|
772 |
} else { |
||
773 |
// lastIndexOf from past the end of the buffer: search the whole buffer. |
||
774 |
63 |
return length_i64 - 1; |
|
775 |
} |
||
776 |
} |
||
777 |
} |
||
778 |
|||
779 |
891 |
void IndexOfString(const FunctionCallbackInfo<Value>& args) { |
|
780 |
891 |
Environment* env = Environment::GetCurrent(args); |
|
781 |
891 |
Isolate* isolate = env->isolate(); |
|
782 |
|||
783 |
✗✓ | 2673 |
CHECK(args[1]->IsString()); |
784 |
✗✓ | 1782 |
CHECK(args[2]->IsNumber()); |
785 |
✗✓ | 1782 |
CHECK(args[3]->IsInt32()); |
786 |
✗✓ | 1782 |
CHECK(args[4]->IsBoolean()); |
787 |
|||
788 |
2673 |
enum encoding enc = static_cast<enum encoding>(args[3].As<Int32>()->Value()); |
|
789 |
|||
790 |
✗✓ | 924 |
THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); |
791 |
891 |
ArrayBufferViewContents<char> buffer(args[0]); |
|
792 |
|||
793 |
1782 |
Local<String> needle = args[1].As<String>(); |
|
794 |
2673 |
int64_t offset_i64 = args[2].As<Integer>()->Value(); |
|
795 |
1782 |
bool is_forward = args[4]->IsTrue(); |
|
796 |
|||
797 |
891 |
const char* haystack = buffer.data(); |
|
798 |
// Round down to the nearest multiple of 2 in case of UCS2. |
||
799 |
const size_t haystack_length = (enc == UCS2) ? |
||
800 |
✓✓ | 891 |
buffer.length() &~ 1 : buffer.length(); // NOLINT(whitespace/operators) |
801 |
|||
802 |
size_t needle_length; |
||
803 |
✗✓ | 1782 |
if (!StringBytes::Size(isolate, needle, enc).To(&needle_length)) return; |
804 |
|||
805 |
int64_t opt_offset = IndexOfOffset(haystack_length, |
||
806 |
offset_i64, |
||
807 |
needle_length, |
||
808 |
891 |
is_forward); |
|
809 |
|||
810 |
✓✓ | 891 |
if (needle_length == 0) { |
811 |
// Match String#indexOf() and String#lastIndexOf() behavior. |
||
812 |
24 |
args.GetReturnValue().Set(static_cast<double>(opt_offset)); |
|
813 |
8 |
return; |
|
814 |
} |
||
815 |
|||
816 |
✗✓ | 883 |
if (haystack_length == 0) { |
817 |
return args.GetReturnValue().Set(-1); |
||
818 |
} |
||
819 |
|||
820 |
✓✓ | 883 |
if (opt_offset <= -1) { |
821 |
20 |
return args.GetReturnValue().Set(-1); |
|
822 |
} |
||
823 |
873 |
size_t offset = static_cast<size_t>(opt_offset); |
|
824 |
✗✓ | 873 |
CHECK_LT(offset, haystack_length); |
825 |
✓✓✓✓ ✓✓ |
1744 |
if ((is_forward && needle_length + offset > haystack_length) || |
826 |
871 |
needle_length > haystack_length) { |
|
827 |
30 |
return args.GetReturnValue().Set(-1); |
|
828 |
} |
||
829 |
|||
830 |
858 |
size_t result = haystack_length; |
|
831 |
|||
832 |
✓✓ | 858 |
if (enc == UCS2) { |
833 |
85 |
String::Value needle_value(isolate, needle); |
|
834 |
✗✓ | 85 |
if (*needle_value == nullptr) |
835 |
return args.GetReturnValue().Set(-1); |
||
836 |
|||
837 |
✓✗✗✓ ✗✓ |
85 |
if (haystack_length < 2 || needle_value.length() < 1) { |
838 |
return args.GetReturnValue().Set(-1); |
||
839 |
} |
||
840 |
|||
841 |
✗✓ | 85 |
if (IsBigEndian()) { |
842 |
StringBytes::InlineDecoder decoder; |
||
843 |
if (decoder.Decode(env, needle, enc).IsNothing()) return; |
||
844 |
const uint16_t* decoded_string = |
||
845 |
reinterpret_cast<const uint16_t*>(decoder.out()); |
||
846 |
|||
847 |
if (decoded_string == nullptr) |
||
848 |
return args.GetReturnValue().Set(-1); |
||
849 |
|||
850 |
result = SearchString(reinterpret_cast<const uint16_t*>(haystack), |
||
851 |
haystack_length / 2, |
||
852 |
decoded_string, |
||
853 |
decoder.size() / 2, |
||
854 |
offset / 2, |
||
855 |
is_forward); |
||
856 |
} else { |
||
857 |
result = SearchString(reinterpret_cast<const uint16_t*>(haystack), |
||
858 |
haystack_length / 2, |
||
859 |
85 |
reinterpret_cast<const uint16_t*>(*needle_value), |
|
860 |
85 |
needle_value.length(), |
|
861 |
offset / 2, |
||
862 |
255 |
is_forward); |
|
863 |
} |
||
864 |
✓✗ | 85 |
result *= 2; |
865 |
✓✓ | 773 |
} else if (enc == UTF8) { |
866 |
761 |
String::Utf8Value needle_value(isolate, needle); |
|
867 |
✗✓ | 761 |
if (*needle_value == nullptr) |
868 |
return args.GetReturnValue().Set(-1); |
||
869 |
|||
870 |
result = SearchString(reinterpret_cast<const uint8_t*>(haystack), |
||
871 |
haystack_length, |
||
872 |
761 |
reinterpret_cast<const uint8_t*>(*needle_value), |
|
873 |
needle_length, |
||
874 |
offset, |
||
875 |
✓✗ | 1522 |
is_forward); |
876 |
✓✗ | 12 |
} else if (enc == LATIN1) { |
877 |
12 |
uint8_t* needle_data = node::UncheckedMalloc<uint8_t>(needle_length); |
|
878 |
✗✓ | 12 |
if (needle_data == nullptr) { |
879 |
return args.GetReturnValue().Set(-1); |
||
880 |
} |
||
881 |
needle->WriteOneByte( |
||
882 |
24 |
isolate, needle_data, 0, needle_length, String::NO_NULL_TERMINATION); |
|
883 |
|||
884 |
result = SearchString(reinterpret_cast<const uint8_t*>(haystack), |
||
885 |
haystack_length, |
||
886 |
needle_data, |
||
887 |
needle_length, |
||
888 |
offset, |
||
889 |
12 |
is_forward); |
|
890 |
12 |
free(needle_data); |
|
891 |
} |
||
892 |
|||
893 |
args.GetReturnValue().Set( |
||
894 |
✓✓ | 2574 |
result == haystack_length ? -1 : static_cast<int>(result)); |
895 |
} |
||
896 |
|||
897 |
218 |
void IndexOfBuffer(const FunctionCallbackInfo<Value>& args) { |
|
898 |
✗✓ | 436 |
CHECK(args[1]->IsObject()); |
899 |
✗✓ | 436 |
CHECK(args[2]->IsNumber()); |
900 |
✗✓ | 436 |
CHECK(args[3]->IsInt32()); |
901 |
✗✓ | 436 |
CHECK(args[4]->IsBoolean()); |
902 |
|||
903 |
654 |
enum encoding enc = static_cast<enum encoding>(args[3].As<Int32>()->Value()); |
|
904 |
|||
905 |
✗✓ | 248 |
THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); |
906 |
✗✓ | 218 |
THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[1]); |
907 |
218 |
ArrayBufferViewContents<char> haystack_contents(args[0]); |
|
908 |
218 |
ArrayBufferViewContents<char> needle_contents(args[1]); |
|
909 |
654 |
int64_t offset_i64 = args[2].As<Integer>()->Value(); |
|
910 |
436 |
bool is_forward = args[4]->IsTrue(); |
|
911 |
|||
912 |
218 |
const char* haystack = haystack_contents.data(); |
|
913 |
218 |
const size_t haystack_length = haystack_contents.length(); |
|
914 |
218 |
const char* needle = needle_contents.data(); |
|
915 |
218 |
const size_t needle_length = needle_contents.length(); |
|
916 |
|||
917 |
int64_t opt_offset = IndexOfOffset(haystack_length, |
||
918 |
offset_i64, |
||
919 |
needle_length, |
||
920 |
218 |
is_forward); |
|
921 |
|||
922 |
✓✓ | 218 |
if (needle_length == 0) { |
923 |
// Match String#indexOf() and String#lastIndexOf() behavior. |
||
924 |
36 |
args.GetReturnValue().Set(static_cast<double>(opt_offset)); |
|
925 |
12 |
return; |
|
926 |
} |
||
927 |
|||
928 |
✗✓ | 206 |
if (haystack_length == 0) { |
929 |
return args.GetReturnValue().Set(-1); |
||
930 |
} |
||
931 |
|||
932 |
✓✓ | 206 |
if (opt_offset <= -1) { |
933 |
18 |
return args.GetReturnValue().Set(-1); |
|
934 |
} |
||
935 |
197 |
size_t offset = static_cast<size_t>(opt_offset); |
|
936 |
✗✓ | 197 |
CHECK_LT(offset, haystack_length); |
937 |
✓✓✓✓ ✓✓ |
392 |
if ((is_forward && needle_length + offset > haystack_length) || |
938 |
195 |
needle_length > haystack_length) { |
|
939 |
14 |
return args.GetReturnValue().Set(-1); |
|
940 |
} |
||
941 |
|||
942 |
190 |
size_t result = haystack_length; |
|
943 |
|||
944 |
✓✓ | 190 |
if (enc == UCS2) { |
945 |
✓✗✓✓ |
58 |
if (haystack_length < 2 || needle_length < 2) { |
946 |
4 |
return args.GetReturnValue().Set(-1); |
|
947 |
} |
||
948 |
result = SearchString( |
||
949 |
reinterpret_cast<const uint16_t*>(haystack), |
||
950 |
haystack_length / 2, |
||
951 |
reinterpret_cast<const uint16_t*>(needle), |
||
952 |
needle_length / 2, |
||
953 |
offset / 2, |
||
954 |
56 |
is_forward); |
|
955 |
56 |
result *= 2; |
|
956 |
} else { |
||
957 |
result = SearchString( |
||
958 |
reinterpret_cast<const uint8_t*>(haystack), |
||
959 |
haystack_length, |
||
960 |
reinterpret_cast<const uint8_t*>(needle), |
||
961 |
needle_length, |
||
962 |
offset, |
||
963 |
132 |
is_forward); |
|
964 |
} |
||
965 |
|||
966 |
args.GetReturnValue().Set( |
||
967 |
✓✓ | 564 |
result == haystack_length ? -1 : static_cast<int>(result)); |
968 |
} |
||
969 |
|||
970 |
18678 |
void IndexOfNumber(const FunctionCallbackInfo<Value>& args) { |
|
971 |
✗✓ | 37356 |
CHECK(args[1]->IsUint32()); |
972 |
✗✓ | 37356 |
CHECK(args[2]->IsNumber()); |
973 |
✗✓ | 37356 |
CHECK(args[3]->IsBoolean()); |
974 |
|||
975 |
✓✓ | 18683 |
THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); |
976 |
18677 |
ArrayBufferViewContents<char> buffer(args[0]); |
|
977 |
|||
978 |
56031 |
uint32_t needle = args[1].As<Uint32>()->Value(); |
|
979 |
56031 |
int64_t offset_i64 = args[2].As<Integer>()->Value(); |
|
980 |
37354 |
bool is_forward = args[3]->IsTrue(); |
|
981 |
|||
982 |
int64_t opt_offset = |
||
983 |
18677 |
IndexOfOffset(buffer.length(), offset_i64, 1, is_forward); |
|
984 |
✓✓✗✓ ✓✓ |
18677 |
if (opt_offset <= -1 || buffer.length() == 0) { |
985 |
8 |
return args.GetReturnValue().Set(-1); |
|
986 |
} |
||
987 |
18673 |
size_t offset = static_cast<size_t>(opt_offset); |
|
988 |
✗✓ | 18673 |
CHECK_LT(offset, buffer.length()); |
989 |
|||
990 |
const void* ptr; |
||
991 |
✓✓ | 18673 |
if (is_forward) { |
992 |
18665 |
ptr = memchr(buffer.data() + offset, needle, buffer.length() - offset); |
|
993 |
} else { |
||
994 |
8 |
ptr = node::stringsearch::MemrchrFill(buffer.data(), needle, offset + 1); |
|
995 |
} |
||
996 |
18673 |
const char* ptr_char = static_cast<const char*>(ptr); |
|
997 |
33 |
args.GetReturnValue().Set(ptr ? static_cast<int>(ptr_char - buffer.data()) |
|
998 |
✓✓ | 56052 |
: -1); |
999 |
} |
||
1000 |
|||
1001 |
|||
1002 |
6 |
void Swap16(const FunctionCallbackInfo<Value>& args) { |
|
1003 |
6 |
Environment* env = Environment::GetCurrent(args); |
|
1004 |
✗✓ | 12 |
THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); |
1005 |
✗✓✓✗ ✗✓ |
48 |
SPREAD_BUFFER_ARG(args[0], ts_obj); |
1006 |
6 |
SwapBytes16(ts_obj_data, ts_obj_length); |
|
1007 |
12 |
args.GetReturnValue().Set(args[0]); |
|
1008 |
} |
||
1009 |
|||
1010 |
|||
1011 |
2 |
void Swap32(const FunctionCallbackInfo<Value>& args) { |
|
1012 |
2 |
Environment* env = Environment::GetCurrent(args); |
|
1013 |
✗✓ | 4 |
THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); |
1014 |
✗✓✓✗ ✗✓ |
16 |
SPREAD_BUFFER_ARG(args[0], ts_obj); |
1015 |
2 |
SwapBytes32(ts_obj_data, ts_obj_length); |
|
1016 |
4 |
args.GetReturnValue().Set(args[0]); |
|
1017 |
} |
||
1018 |
|||
1019 |
|||
1020 |
2 |
void Swap64(const FunctionCallbackInfo<Value>& args) { |
|
1021 |
2 |
Environment* env = Environment::GetCurrent(args); |
|
1022 |
✗✓ | 4 |
THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); |
1023 |
✗✓✓✗ ✗✓ |
16 |
SPREAD_BUFFER_ARG(args[0], ts_obj); |
1024 |
2 |
SwapBytes64(ts_obj_data, ts_obj_length); |
|
1025 |
4 |
args.GetReturnValue().Set(args[0]); |
|
1026 |
} |
||
1027 |
|||
1028 |
|||
1029 |
// Encode a single string to a UTF-8 Uint8Array (not Buffer). |
||
1030 |
// Used in TextEncoder.prototype.encode. |
||
1031 |
21 |
static void EncodeUtf8String(const FunctionCallbackInfo<Value>& args) { |
|
1032 |
21 |
Environment* env = Environment::GetCurrent(args); |
|
1033 |
21 |
Isolate* isolate = env->isolate(); |
|
1034 |
✗✓ | 21 |
CHECK_GE(args.Length(), 1); |
1035 |
✗✓ | 63 |
CHECK(args[0]->IsString()); |
1036 |
|||
1037 |
42 |
Local<String> str = args[0].As<String>(); |
|
1038 |
21 |
size_t length = str->Utf8Length(isolate); |
|
1039 |
21 |
AllocatedBuffer buf = env->AllocateManaged(length); |
|
1040 |
str->WriteUtf8(isolate, |
||
1041 |
buf.data(), |
||
1042 |
-1, // We are certain that `data` is sufficiently large |
||
1043 |
nullptr, |
||
1044 |
42 |
String::NO_NULL_TERMINATION | String::REPLACE_INVALID_UTF8); |
|
1045 |
21 |
auto array = Uint8Array::New(buf.ToArrayBuffer(), 0, length); |
|
1046 |
42 |
args.GetReturnValue().Set(array); |
|
1047 |
21 |
} |
|
1048 |
|||
1049 |
|||
1050 |
45 |
static void EncodeInto(const FunctionCallbackInfo<Value>& args) { |
|
1051 |
45 |
Environment* env = Environment::GetCurrent(args); |
|
1052 |
45 |
Isolate* isolate = env->isolate(); |
|
1053 |
✗✓ | 45 |
CHECK_GE(args.Length(), 3); |
1054 |
✗✓ | 135 |
CHECK(args[0]->IsString()); |
1055 |
✗✓ | 90 |
CHECK(args[1]->IsUint8Array()); |
1056 |
✗✓ | 90 |
CHECK(args[2]->IsUint32Array()); |
1057 |
|||
1058 |
90 |
Local<String> source = args[0].As<String>(); |
|
1059 |
|||
1060 |
90 |
Local<Uint8Array> dest = args[1].As<Uint8Array>(); |
|
1061 |
45 |
Local<ArrayBuffer> buf = dest->Buffer(); |
|
1062 |
char* write_result = |
||
1063 |
90 |
static_cast<char*>(buf->GetContents().Data()) + dest->ByteOffset(); |
|
1064 |
45 |
size_t dest_length = dest->ByteLength(); |
|
1065 |
|||
1066 |
// results = [ read, written ] |
||
1067 |
90 |
Local<Uint32Array> result_arr = args[2].As<Uint32Array>(); |
|
1068 |
uint32_t* results = reinterpret_cast<uint32_t*>( |
||
1069 |
135 |
static_cast<char*>(result_arr->Buffer()->GetContents().Data()) + |
|
1070 |
45 |
result_arr->ByteOffset()); |
|
1071 |
|||
1072 |
int nchars; |
||
1073 |
int written = source->WriteUtf8( |
||
1074 |
isolate, |
||
1075 |
write_result, |
||
1076 |
dest_length, |
||
1077 |
&nchars, |
||
1078 |
90 |
String::NO_NULL_TERMINATION | String::REPLACE_INVALID_UTF8); |
|
1079 |
45 |
results[0] = nchars; |
|
1080 |
45 |
results[1] = written; |
|
1081 |
45 |
} |
|
1082 |
|||
1083 |
|||
1084 |
5193 |
void SetBufferPrototype(const FunctionCallbackInfo<Value>& args) { |
|
1085 |
5193 |
Environment* env = Environment::GetCurrent(args); |
|
1086 |
|||
1087 |
✗✓ | 10386 |
CHECK(args[0]->IsObject()); |
1088 |
10386 |
Local<Object> proto = args[0].As<Object>(); |
|
1089 |
5193 |
env->set_buffer_prototype_object(proto); |
|
1090 |
5193 |
} |
|
1091 |
|||
1092 |
|||
1093 |
5193 |
void Initialize(Local<Object> target, |
|
1094 |
Local<Value> unused, |
||
1095 |
Local<Context> context, |
||
1096 |
void* priv) { |
||
1097 |
5193 |
Environment* env = Environment::GetCurrent(context); |
|
1098 |
|||
1099 |
5193 |
env->SetMethod(target, "setBufferPrototype", SetBufferPrototype); |
|
1100 |
5193 |
env->SetMethodNoSideEffect(target, "createFromString", CreateFromString); |
|
1101 |
|||
1102 |
5193 |
env->SetMethodNoSideEffect(target, "byteLengthUtf8", ByteLengthUtf8); |
|
1103 |
5193 |
env->SetMethod(target, "copy", Copy); |
|
1104 |
5193 |
env->SetMethodNoSideEffect(target, "compare", Compare); |
|
1105 |
5193 |
env->SetMethodNoSideEffect(target, "compareOffset", CompareOffset); |
|
1106 |
5193 |
env->SetMethod(target, "fill", Fill); |
|
1107 |
5193 |
env->SetMethodNoSideEffect(target, "indexOfBuffer", IndexOfBuffer); |
|
1108 |
5193 |
env->SetMethodNoSideEffect(target, "indexOfNumber", IndexOfNumber); |
|
1109 |
5193 |
env->SetMethodNoSideEffect(target, "indexOfString", IndexOfString); |
|
1110 |
|||
1111 |
5193 |
env->SetMethod(target, "swap16", Swap16); |
|
1112 |
5193 |
env->SetMethod(target, "swap32", Swap32); |
|
1113 |
5193 |
env->SetMethod(target, "swap64", Swap64); |
|
1114 |
|||
1115 |
5193 |
env->SetMethod(target, "encodeInto", EncodeInto); |
|
1116 |
5193 |
env->SetMethodNoSideEffect(target, "encodeUtf8String", EncodeUtf8String); |
|
1117 |
|||
1118 |
target->Set(env->context(), |
||
1119 |
FIXED_ONE_BYTE_STRING(env->isolate(), "kMaxLength"), |
||
1120 |
25965 |
Integer::NewFromUnsigned(env->isolate(), kMaxLength)).Check(); |
|
1121 |
|||
1122 |
target->Set(env->context(), |
||
1123 |
FIXED_ONE_BYTE_STRING(env->isolate(), "kStringMaxLength"), |
||
1124 |
25965 |
Integer::New(env->isolate(), String::kMaxLength)).Check(); |
|
1125 |
|||
1126 |
5193 |
env->SetMethodNoSideEffect(target, "asciiSlice", StringSlice<ASCII>); |
|
1127 |
5193 |
env->SetMethodNoSideEffect(target, "base64Slice", StringSlice<BASE64>); |
|
1128 |
5193 |
env->SetMethodNoSideEffect(target, "latin1Slice", StringSlice<LATIN1>); |
|
1129 |
5193 |
env->SetMethodNoSideEffect(target, "hexSlice", StringSlice<HEX>); |
|
1130 |
5193 |
env->SetMethodNoSideEffect(target, "ucs2Slice", StringSlice<UCS2>); |
|
1131 |
5193 |
env->SetMethodNoSideEffect(target, "utf8Slice", StringSlice<UTF8>); |
|
1132 |
|||
1133 |
5193 |
env->SetMethod(target, "asciiWrite", StringWrite<ASCII>); |
|
1134 |
5193 |
env->SetMethod(target, "base64Write", StringWrite<BASE64>); |
|
1135 |
5193 |
env->SetMethod(target, "latin1Write", StringWrite<LATIN1>); |
|
1136 |
5193 |
env->SetMethod(target, "hexWrite", StringWrite<HEX>); |
|
1137 |
5193 |
env->SetMethod(target, "ucs2Write", StringWrite<UCS2>); |
|
1138 |
5193 |
env->SetMethod(target, "utf8Write", StringWrite<UTF8>); |
|
1139 |
|||
1140 |
// It can be a nullptr when running inside an isolate where we |
||
1141 |
// do not own the ArrayBuffer allocator. |
||
1142 |
✓✗ | 5193 |
if (NodeArrayBufferAllocator* allocator = |
1143 |
5193 |
env->isolate_data()->node_allocator()) { |
|
1144 |
5193 |
uint32_t* zero_fill_field = allocator->zero_fill_field(); |
|
1145 |
Local<ArrayBuffer> array_buffer = ArrayBuffer::New( |
||
1146 |
5193 |
env->isolate(), zero_fill_field, sizeof(*zero_fill_field)); |
|
1147 |
✗✓ | 25965 |
CHECK(target |
1148 |
->Set(env->context(), |
||
1149 |
FIXED_ONE_BYTE_STRING(env->isolate(), "zeroFill"), |
||
1150 |
Uint32Array::New(array_buffer, 0, 1)) |
||
1151 |
.FromJust()); |
||
1152 |
} |
||
1153 |
5193 |
} |
|
1154 |
|||
1155 |
} // anonymous namespace |
||
1156 |
} // namespace Buffer |
||
1157 |
} // namespace node |
||
1158 |
|||
1159 |
5047 |
NODE_MODULE_CONTEXT_AWARE_INTERNAL(buffer, node::Buffer::Initialize) |
Generated by: GCOVR (Version 3.4) |