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_blob.h" |
||
25 |
#include "node_errors.h" |
||
26 |
#include "node_external_reference.h" |
||
27 |
#include "node_i18n.h" |
||
28 |
#include "node_internals.h" |
||
29 |
|||
30 |
#include "env-inl.h" |
||
31 |
#include "simdutf.h" |
||
32 |
#include "string_bytes.h" |
||
33 |
#include "string_search.h" |
||
34 |
#include "util-inl.h" |
||
35 |
#include "v8.h" |
||
36 |
|||
37 |
#include <cstring> |
||
38 |
#include <climits> |
||
39 |
|||
40 |
#define THROW_AND_RETURN_UNLESS_BUFFER(env, obj) \ |
||
41 |
THROW_AND_RETURN_IF_NOT_BUFFER(env, obj, "argument") \ |
||
42 |
|||
43 |
#define THROW_AND_RETURN_IF_OOB(r) \ |
||
44 |
do { \ |
||
45 |
Maybe<bool> m = (r); \ |
||
46 |
if (m.IsNothing()) return; \ |
||
47 |
if (!m.FromJust()) \ |
||
48 |
return node::THROW_ERR_OUT_OF_RANGE(env, "Index out of range"); \ |
||
49 |
} while (0) \ |
||
50 |
|||
51 |
namespace node { |
||
52 |
namespace Buffer { |
||
53 |
|||
54 |
using v8::ArrayBuffer; |
||
55 |
using v8::ArrayBufferView; |
||
56 |
using v8::BackingStore; |
||
57 |
using v8::Context; |
||
58 |
using v8::EscapableHandleScope; |
||
59 |
using v8::FunctionCallbackInfo; |
||
60 |
using v8::Global; |
||
61 |
using v8::HandleScope; |
||
62 |
using v8::Int32; |
||
63 |
using v8::Integer; |
||
64 |
using v8::Isolate; |
||
65 |
using v8::Just; |
||
66 |
using v8::Local; |
||
67 |
using v8::Maybe; |
||
68 |
using v8::MaybeLocal; |
||
69 |
using v8::Nothing; |
||
70 |
using v8::Number; |
||
71 |
using v8::Object; |
||
72 |
using v8::SharedArrayBuffer; |
||
73 |
using v8::String; |
||
74 |
using v8::Uint32; |
||
75 |
using v8::Uint32Array; |
||
76 |
using v8::Uint8Array; |
||
77 |
using v8::Value; |
||
78 |
|||
79 |
namespace { |
||
80 |
|||
81 |
class CallbackInfo { |
||
82 |
public: |
||
83 |
static inline Local<ArrayBuffer> CreateTrackedArrayBuffer( |
||
84 |
Environment* env, |
||
85 |
char* data, |
||
86 |
size_t length, |
||
87 |
FreeCallback callback, |
||
88 |
void* hint); |
||
89 |
|||
90 |
CallbackInfo(const CallbackInfo&) = delete; |
||
91 |
CallbackInfo& operator=(const CallbackInfo&) = delete; |
||
92 |
|||
93 |
private: |
||
94 |
static void CleanupHook(void* data); |
||
95 |
inline void OnBackingStoreFree(); |
||
96 |
inline void CallAndResetCallback(); |
||
97 |
inline CallbackInfo(Environment* env, |
||
98 |
FreeCallback callback, |
||
99 |
char* data, |
||
100 |
void* hint); |
||
101 |
Global<ArrayBuffer> persistent_; |
||
102 |
Mutex mutex_; // Protects callback_. |
||
103 |
FreeCallback callback_; |
||
104 |
char* const data_; |
||
105 |
void* const hint_; |
||
106 |
Environment* const env_; |
||
107 |
}; |
||
108 |
|||
109 |
|||
110 |
30 |
Local<ArrayBuffer> CallbackInfo::CreateTrackedArrayBuffer( |
|
111 |
Environment* env, |
||
112 |
char* data, |
||
113 |
size_t length, |
||
114 |
FreeCallback callback, |
||
115 |
void* hint) { |
||
116 |
✗✓ | 30 |
CHECK_NOT_NULL(callback); |
117 |
✓✓✗✓ |
30 |
CHECK_IMPLIES(data == nullptr, length == 0); |
118 |
|||
119 |
30 |
CallbackInfo* self = new CallbackInfo(env, callback, data, hint); |
|
120 |
std::unique_ptr<BackingStore> bs = |
||
121 |
28 |
ArrayBuffer::NewBackingStore(data, length, [](void*, size_t, void* arg) { |
|
122 |
28 |
static_cast<CallbackInfo*>(arg)->OnBackingStoreFree(); |
|
123 |
30 |
}, self); |
|
124 |
30 |
Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), std::move(bs)); |
|
125 |
|||
126 |
// V8 simply ignores the BackingStore deleter callback if data == nullptr, |
||
127 |
// but our API contract requires it being called. |
||
128 |
✓✓ | 30 |
if (data == nullptr) { |
129 |
4 |
ab->Detach(Local<Value>()).Check(); |
|
130 |
2 |
self->OnBackingStoreFree(); // This calls `callback` asynchronously. |
|
131 |
} else { |
||
132 |
// Store the ArrayBuffer so that we can detach it later. |
||
133 |
28 |
self->persistent_.Reset(env->isolate(), ab); |
|
134 |
28 |
self->persistent_.SetWeak(); |
|
135 |
} |
||
136 |
|||
137 |
30 |
return ab; |
|
138 |
} |
||
139 |
|||
140 |
|||
141 |
30 |
CallbackInfo::CallbackInfo(Environment* env, |
|
142 |
FreeCallback callback, |
||
143 |
char* data, |
||
144 |
30 |
void* hint) |
|
145 |
: callback_(callback), |
||
146 |
data_(data), |
||
147 |
hint_(hint), |
||
148 |
60 |
env_(env) { |
|
149 |
30 |
env->AddCleanupHook(CleanupHook, this); |
|
150 |
30 |
env->isolate()->AdjustAmountOfExternalAllocatedMemory(sizeof(*this)); |
|
151 |
30 |
} |
|
152 |
|||
153 |
9 |
void CallbackInfo::CleanupHook(void* data) { |
|
154 |
9 |
CallbackInfo* self = static_cast<CallbackInfo*>(data); |
|
155 |
|||
156 |
{ |
||
157 |
18 |
HandleScope handle_scope(self->env_->isolate()); |
|
158 |
18 |
Local<ArrayBuffer> ab = self->persistent_.Get(self->env_->isolate()); |
|
159 |
✓✗✓✗ ✓✗ |
18 |
if (!ab.IsEmpty() && ab->IsDetachable()) { |
160 |
18 |
ab->Detach(Local<Value>()).Check(); |
|
161 |
9 |
self->persistent_.Reset(); |
|
162 |
} |
||
163 |
} |
||
164 |
|||
165 |
// Call the callback in this case, but don't delete `this` yet because the |
||
166 |
// BackingStore deleter callback will do so later. |
||
167 |
9 |
self->CallAndResetCallback(); |
|
168 |
9 |
} |
|
169 |
|||
170 |
39 |
void CallbackInfo::CallAndResetCallback() { |
|
171 |
FreeCallback callback; |
||
172 |
{ |
||
173 |
39 |
Mutex::ScopedLock lock(mutex_); |
|
174 |
39 |
callback = callback_; |
|
175 |
39 |
callback_ = nullptr; |
|
176 |
} |
||
177 |
✓✓ | 39 |
if (callback != nullptr) { |
178 |
// Clean up all Environment-related state and run the callback. |
||
179 |
30 |
env_->RemoveCleanupHook(CleanupHook, this); |
|
180 |
30 |
int64_t change_in_bytes = -static_cast<int64_t>(sizeof(*this)); |
|
181 |
30 |
env_->isolate()->AdjustAmountOfExternalAllocatedMemory(change_in_bytes); |
|
182 |
|||
183 |
30 |
callback(data_, hint_); |
|
184 |
} |
||
185 |
39 |
} |
|
186 |
|||
187 |
30 |
void CallbackInfo::OnBackingStoreFree() { |
|
188 |
// This method should always release the memory for `this`. |
||
189 |
30 |
std::unique_ptr<CallbackInfo> self { this }; |
|
190 |
30 |
Mutex::ScopedLock lock(mutex_); |
|
191 |
// If callback_ == nullptr, that means that the callback has already run from |
||
192 |
// the cleanup hook, and there is nothing left to do here besides to clean |
||
193 |
// up the memory involved. In particular, the underlying `Environment` may |
||
194 |
// be gone at this point, so don’t attempt to call SetImmediateThreadsafe(). |
||
195 |
✗✓ | 30 |
if (callback_ == nullptr) return; |
196 |
|||
197 |
30 |
env_->SetImmediateThreadsafe([self = std::move(self)](Environment* env) { |
|
198 |
✗✓ | 30 |
CHECK_EQ(self->env_, env); // Consistency check. |
199 |
|||
200 |
30 |
self->CallAndResetCallback(); |
|
201 |
30 |
}); |
|
202 |
} |
||
203 |
|||
204 |
|||
205 |
// Parse index for external array data. An empty Maybe indicates |
||
206 |
// a pending exception. `false` indicates that the index is out-of-bounds. |
||
207 |
640919 |
inline MUST_USE_RESULT Maybe<bool> ParseArrayIndex(Environment* env, |
|
208 |
Local<Value> arg, |
||
209 |
size_t def, |
||
210 |
size_t* ret) { |
||
211 |
✗✓ | 1281838 |
if (arg->IsUndefined()) { |
212 |
*ret = def; |
||
213 |
return Just(true); |
||
214 |
} |
||
215 |
|||
216 |
int64_t tmp_i; |
||
217 |
✗✓ | 1281838 |
if (!arg->IntegerValue(env->context()).To(&tmp_i)) |
218 |
return Nothing<bool>(); |
||
219 |
|||
220 |
✓✓ | 640919 |
if (tmp_i < 0) |
221 |
2 |
return Just(false); |
|
222 |
|||
223 |
// Check that the result fits in a size_t. |
||
224 |
// coverity[pointless_expression] |
||
225 |
✗✓ | 640917 |
if (static_cast<uint64_t>(tmp_i) > std::numeric_limits<size_t>::max()) |
226 |
return Just(false); |
||
227 |
|||
228 |
640917 |
*ret = static_cast<size_t>(tmp_i); |
|
229 |
640917 |
return Just(true); |
|
230 |
} |
||
231 |
|||
232 |
} // anonymous namespace |
||
233 |
|||
234 |
// Buffer methods |
||
235 |
|||
236 |
4306926 |
bool HasInstance(Local<Value> val) { |
|
237 |
4306926 |
return val->IsArrayBufferView(); |
|
238 |
} |
||
239 |
|||
240 |
|||
241 |
320382 |
bool HasInstance(Local<Object> obj) { |
|
242 |
320382 |
return obj->IsArrayBufferView(); |
|
243 |
} |
||
244 |
|||
245 |
|||
246 |
451743 |
char* Data(Local<Value> val) { |
|
247 |
✗✓ | 451743 |
CHECK(val->IsArrayBufferView()); |
248 |
451743 |
Local<ArrayBufferView> ui = val.As<ArrayBufferView>(); |
|
249 |
1355229 |
return static_cast<char*>(ui->Buffer()->Data()) + ui->ByteOffset(); |
|
250 |
} |
||
251 |
|||
252 |
|||
253 |
337339 |
char* Data(Local<Object> obj) { |
|
254 |
337339 |
return Data(obj.As<Value>()); |
|
255 |
} |
||
256 |
|||
257 |
|||
258 |
88683 |
size_t Length(Local<Value> val) { |
|
259 |
✗✓ | 88683 |
CHECK(val->IsArrayBufferView()); |
260 |
88683 |
Local<ArrayBufferView> ui = val.As<ArrayBufferView>(); |
|
261 |
88683 |
return ui->ByteLength(); |
|
262 |
} |
||
263 |
|||
264 |
|||
265 |
335241 |
size_t Length(Local<Object> obj) { |
|
266 |
✗✓ | 335241 |
CHECK(obj->IsArrayBufferView()); |
267 |
335241 |
Local<ArrayBufferView> ui = obj.As<ArrayBufferView>(); |
|
268 |
335241 |
return ui->ByteLength(); |
|
269 |
} |
||
270 |
|||
271 |
|||
272 |
1030053 |
MaybeLocal<Uint8Array> New(Environment* env, |
|
273 |
Local<ArrayBuffer> ab, |
||
274 |
size_t byte_offset, |
||
275 |
size_t length) { |
||
276 |
✗✓ | 2060106 |
CHECK(!env->buffer_prototype_object().IsEmpty()); |
277 |
1030053 |
Local<Uint8Array> ui = Uint8Array::New(ab, byte_offset, length); |
|
278 |
Maybe<bool> mb = |
||
279 |
2060106 |
ui->SetPrototype(env->context(), env->buffer_prototype_object()); |
|
280 |
✗✓ | 1030053 |
if (mb.IsNothing()) |
281 |
return MaybeLocal<Uint8Array>(); |
||
282 |
1030053 |
return ui; |
|
283 |
} |
||
284 |
|||
285 |
373 |
MaybeLocal<Uint8Array> New(Isolate* isolate, |
|
286 |
Local<ArrayBuffer> ab, |
||
287 |
size_t byte_offset, |
||
288 |
size_t length) { |
||
289 |
373 |
Environment* env = Environment::GetCurrent(isolate); |
|
290 |
✗✓ | 373 |
if (env == nullptr) { |
291 |
THROW_ERR_BUFFER_CONTEXT_NOT_AVAILABLE(isolate); |
||
292 |
return MaybeLocal<Uint8Array>(); |
||
293 |
} |
||
294 |
373 |
return New(env, ab, byte_offset, length); |
|
295 |
} |
||
296 |
|||
297 |
|||
298 |
374 |
MaybeLocal<Object> New(Isolate* isolate, |
|
299 |
Local<String> string, |
||
300 |
enum encoding enc) { |
||
301 |
374 |
EscapableHandleScope scope(isolate); |
|
302 |
|||
303 |
size_t length; |
||
304 |
✗✓ | 748 |
if (!StringBytes::Size(isolate, string, enc).To(&length)) |
305 |
return Local<Object>(); |
||
306 |
374 |
size_t actual = 0; |
|
307 |
374 |
std::unique_ptr<BackingStore> store; |
|
308 |
|||
309 |
✓✗ | 374 |
if (length > 0) { |
310 |
374 |
store = ArrayBuffer::NewBackingStore(isolate, length); |
|
311 |
|||
312 |
✗✓ | 374 |
if (UNLIKELY(!store)) { |
313 |
THROW_ERR_MEMORY_ALLOCATION_FAILED(isolate); |
||
314 |
return Local<Object>(); |
||
315 |
} |
||
316 |
|||
317 |
374 |
actual = StringBytes::Write( |
|
318 |
isolate, |
||
319 |
374 |
static_cast<char*>(store->Data()), |
|
320 |
length, |
||
321 |
string, |
||
322 |
enc); |
||
323 |
✗✓ | 374 |
CHECK(actual <= length); |
324 |
|||
325 |
✓✓ | 374 |
if (LIKELY(actual > 0)) { |
326 |
✗✓ | 373 |
if (actual < length) |
327 |
store = BackingStore::Reallocate(isolate, std::move(store), actual); |
||
328 |
373 |
Local<ArrayBuffer> buf = ArrayBuffer::New(isolate, std::move(store)); |
|
329 |
Local<Object> obj; |
||
330 |
✗✓ | 746 |
if (UNLIKELY(!New(isolate, buf, 0, actual).ToLocal(&obj))) |
331 |
return MaybeLocal<Object>(); |
||
332 |
373 |
return scope.Escape(obj); |
|
333 |
} |
||
334 |
} |
||
335 |
|||
336 |
2 |
return scope.EscapeMaybe(New(isolate, 0)); |
|
337 |
} |
||
338 |
|||
339 |
|||
340 |
2 |
MaybeLocal<Object> New(Isolate* isolate, size_t length) { |
|
341 |
2 |
EscapableHandleScope handle_scope(isolate); |
|
342 |
Local<Object> obj; |
||
343 |
2 |
Environment* env = Environment::GetCurrent(isolate); |
|
344 |
✗✓ | 2 |
if (env == nullptr) { |
345 |
THROW_ERR_BUFFER_CONTEXT_NOT_AVAILABLE(isolate); |
||
346 |
return MaybeLocal<Object>(); |
||
347 |
} |
||
348 |
✓✗ | 4 |
if (Buffer::New(env, length).ToLocal(&obj)) |
349 |
2 |
return handle_scope.Escape(obj); |
|
350 |
return Local<Object>(); |
||
351 |
} |
||
352 |
|||
353 |
|||
354 |
2078 |
MaybeLocal<Object> New(Environment* env, size_t length) { |
|
355 |
2078 |
Isolate* isolate(env->isolate()); |
|
356 |
2078 |
EscapableHandleScope scope(isolate); |
|
357 |
|||
358 |
// V8 currently only allows a maximum Typed Array index of max Smi. |
||
359 |
✗✓ | 2078 |
if (length > kMaxLength) { |
360 |
isolate->ThrowException(ERR_BUFFER_TOO_LARGE(isolate)); |
||
361 |
return Local<Object>(); |
||
362 |
} |
||
363 |
|||
364 |
Local<ArrayBuffer> ab; |
||
365 |
{ |
||
366 |
4156 |
NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); |
|
367 |
std::unique_ptr<BackingStore> bs = |
||
368 |
2078 |
ArrayBuffer::NewBackingStore(isolate, length); |
|
369 |
|||
370 |
✗✓ | 2078 |
CHECK(bs); |
371 |
|||
372 |
2078 |
ab = ArrayBuffer::New(isolate, std::move(bs)); |
|
373 |
} |
||
374 |
|||
375 |
MaybeLocal<Object> obj = |
||
376 |
2078 |
New(env, ab, 0, ab->ByteLength()) |
|
377 |
2078 |
.FromMaybe(Local<Uint8Array>()); |
|
378 |
|||
379 |
2078 |
return scope.EscapeMaybe(obj); |
|
380 |
} |
||
381 |
|||
382 |
|||
383 |
3522 |
MaybeLocal<Object> Copy(Isolate* isolate, const char* data, size_t length) { |
|
384 |
3522 |
EscapableHandleScope handle_scope(isolate); |
|
385 |
3522 |
Environment* env = Environment::GetCurrent(isolate); |
|
386 |
✓✓ | 3522 |
if (env == nullptr) { |
387 |
1 |
THROW_ERR_BUFFER_CONTEXT_NOT_AVAILABLE(isolate); |
|
388 |
1 |
return MaybeLocal<Object>(); |
|
389 |
} |
||
390 |
Local<Object> obj; |
||
391 |
✓✗ | 7042 |
if (Buffer::Copy(env, data, length).ToLocal(&obj)) |
392 |
3521 |
return handle_scope.Escape(obj); |
|
393 |
return Local<Object>(); |
||
394 |
} |
||
395 |
|||
396 |
|||
397 |
22053 |
MaybeLocal<Object> Copy(Environment* env, const char* data, size_t length) { |
|
398 |
22053 |
Isolate* isolate(env->isolate()); |
|
399 |
22053 |
EscapableHandleScope scope(isolate); |
|
400 |
|||
401 |
// V8 currently only allows a maximum Typed Array index of max Smi. |
||
402 |
✗✓ | 22053 |
if (length > kMaxLength) { |
403 |
isolate->ThrowException(ERR_BUFFER_TOO_LARGE(isolate)); |
||
404 |
return Local<Object>(); |
||
405 |
} |
||
406 |
|||
407 |
Local<ArrayBuffer> ab; |
||
408 |
{ |
||
409 |
44106 |
NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); |
|
410 |
std::unique_ptr<BackingStore> bs = |
||
411 |
22053 |
ArrayBuffer::NewBackingStore(isolate, length); |
|
412 |
|||
413 |
✗✓ | 22053 |
CHECK(bs); |
414 |
|||
415 |
22053 |
memcpy(bs->Data(), data, length); |
|
416 |
|||
417 |
22053 |
ab = ArrayBuffer::New(isolate, std::move(bs)); |
|
418 |
} |
||
419 |
|||
420 |
MaybeLocal<Object> obj = |
||
421 |
22053 |
New(env, ab, 0, ab->ByteLength()) |
|
422 |
22053 |
.FromMaybe(Local<Uint8Array>()); |
|
423 |
|||
424 |
22053 |
return scope.EscapeMaybe(obj); |
|
425 |
} |
||
426 |
|||
427 |
|||
428 |
30 |
MaybeLocal<Object> New(Isolate* isolate, |
|
429 |
char* data, |
||
430 |
size_t length, |
||
431 |
FreeCallback callback, |
||
432 |
void* hint) { |
||
433 |
30 |
EscapableHandleScope handle_scope(isolate); |
|
434 |
30 |
Environment* env = Environment::GetCurrent(isolate); |
|
435 |
✗✓ | 30 |
if (env == nullptr) { |
436 |
callback(data, hint); |
||
437 |
THROW_ERR_BUFFER_CONTEXT_NOT_AVAILABLE(isolate); |
||
438 |
return MaybeLocal<Object>(); |
||
439 |
} |
||
440 |
return handle_scope.EscapeMaybe( |
||
441 |
60 |
Buffer::New(env, data, length, callback, hint)); |
|
442 |
} |
||
443 |
|||
444 |
|||
445 |
30 |
MaybeLocal<Object> New(Environment* env, |
|
446 |
char* data, |
||
447 |
size_t length, |
||
448 |
FreeCallback callback, |
||
449 |
void* hint) { |
||
450 |
30 |
EscapableHandleScope scope(env->isolate()); |
|
451 |
|||
452 |
✗✓ | 30 |
if (length > kMaxLength) { |
453 |
env->isolate()->ThrowException(ERR_BUFFER_TOO_LARGE(env->isolate())); |
||
454 |
callback(data, hint); |
||
455 |
return Local<Object>(); |
||
456 |
} |
||
457 |
|||
458 |
Local<ArrayBuffer> ab = |
||
459 |
30 |
CallbackInfo::CreateTrackedArrayBuffer(env, data, length, callback, hint); |
|
460 |
90 |
if (ab->SetPrivate(env->context(), |
|
461 |
env->untransferable_object_private_symbol(), |
||
462 |
✗✓ | 90 |
True(env->isolate())).IsNothing()) { |
463 |
return Local<Object>(); |
||
464 |
} |
||
465 |
30 |
MaybeLocal<Uint8Array> maybe_ui = Buffer::New(env, ab, 0, length); |
|
466 |
|||
467 |
Local<Uint8Array> ui; |
||
468 |
✗✓ | 30 |
if (!maybe_ui.ToLocal(&ui)) |
469 |
return MaybeLocal<Object>(); |
||
470 |
|||
471 |
30 |
return scope.Escape(ui); |
|
472 |
} |
||
473 |
|||
474 |
// Warning: This function needs `data` to be allocated with malloc() and not |
||
475 |
// necessarily isolate's ArrayBuffer::Allocator. |
||
476 |
MaybeLocal<Object> New(Isolate* isolate, char* data, size_t length) { |
||
477 |
EscapableHandleScope handle_scope(isolate); |
||
478 |
Environment* env = Environment::GetCurrent(isolate); |
||
479 |
if (env == nullptr) { |
||
480 |
free(data); |
||
481 |
THROW_ERR_BUFFER_CONTEXT_NOT_AVAILABLE(isolate); |
||
482 |
return MaybeLocal<Object>(); |
||
483 |
} |
||
484 |
Local<Object> obj; |
||
485 |
if (Buffer::New(env, data, length).ToLocal(&obj)) |
||
486 |
return handle_scope.Escape(obj); |
||
487 |
return Local<Object>(); |
||
488 |
} |
||
489 |
|||
490 |
// The contract for this function is that `data` is allocated with malloc() |
||
491 |
// and not necessarily isolate's ArrayBuffer::Allocator. |
||
492 |
1001241 |
MaybeLocal<Object> New(Environment* env, |
|
493 |
char* data, |
||
494 |
size_t length) { |
||
495 |
✓✓ | 1001241 |
if (length > 0) { |
496 |
✗✓ | 1001240 |
CHECK_NOT_NULL(data); |
497 |
// V8 currently only allows a maximum Typed Array index of max Smi. |
||
498 |
✗✓ | 1001240 |
if (length > kMaxLength) { |
499 |
Isolate* isolate(env->isolate()); |
||
500 |
isolate->ThrowException(ERR_BUFFER_TOO_LARGE(isolate)); |
||
501 |
free(data); |
||
502 |
return Local<Object>(); |
||
503 |
} |
||
504 |
} |
||
505 |
|||
506 |
1001241 |
EscapableHandleScope handle_scope(env->isolate()); |
|
507 |
|||
508 |
1001240 |
auto free_callback = [](void* data, size_t length, void* deleter_data) { |
|
509 |
1001240 |
free(data); |
|
510 |
1001240 |
}; |
|
511 |
std::unique_ptr<BackingStore> bs = |
||
512 |
2002482 |
v8::ArrayBuffer::NewBackingStore(data, length, free_callback, nullptr); |
|
513 |
|||
514 |
1001241 |
Local<ArrayBuffer> ab = v8::ArrayBuffer::New(env->isolate(), std::move(bs)); |
|
515 |
|||
516 |
Local<Object> obj; |
||
517 |
✓✗ | 2002482 |
if (Buffer::New(env, ab, 0, length).ToLocal(&obj)) |
518 |
1001241 |
return handle_scope.Escape(obj); |
|
519 |
return Local<Object>(); |
||
520 |
} |
||
521 |
|||
522 |
namespace { |
||
523 |
|||
524 |
✓✗ | 374 |
void CreateFromString(const FunctionCallbackInfo<Value>& args) { |
525 |
✗✓ | 748 |
CHECK(args[0]->IsString()); |
526 |
✗✓ | 374 |
CHECK(args[1]->IsInt32()); |
527 |
|||
528 |
✓✗ | 748 |
enum encoding enc = static_cast<enum encoding>(args[1].As<Int32>()->Value()); |
529 |
Local<Object> buf; |
||
530 |
✓✗ | 1122 |
if (New(args.GetIsolate(), args[0].As<String>(), enc).ToLocal(&buf)) |
531 |
748 |
args.GetReturnValue().Set(buf); |
|
532 |
374 |
} |
|
533 |
|||
534 |
|||
535 |
template <encoding encoding> |
||
536 |
264936 |
void StringSlice(const FunctionCallbackInfo<Value>& args) { |
|
537 |
264936 |
Environment* env = Environment::GetCurrent(args); |
|
538 |
264936 |
Isolate* isolate = env->isolate(); |
|
539 |
|||
540 |
✗✓ | 265434 |
THROW_AND_RETURN_UNLESS_BUFFER(env, args.This()); |
541 |
264936 |
ArrayBufferViewContents<char> buffer(args.This()); |
|
542 |
|||
543 |
✓✓ | 264936 |
if (buffer.length() == 0) |
544 |
968 |
return args.GetReturnValue().SetEmptyString(); |
|
545 |
|||
546 |
264452 |
size_t start = 0; |
|
547 |
✓✗ | 264452 |
size_t end = 0; |
548 |
✗✓✗✓ |
793356 |
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[0], 0, &start)); |
549 |
✗✓✗✓ |
1057808 |
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[1], buffer.length(), &end)); |
550 |
✗✓ | 264452 |
if (end < start) end = start; |
551 |
✗✓✗✓ |
528904 |
THROW_AND_RETURN_IF_OOB(Just(end <= buffer.length())); |
552 |
264452 |
size_t length = end - start; |
|
553 |
|||
554 |
Local<Value> error; |
||
555 |
MaybeLocal<Value> maybe_ret = |
||
556 |
264452 |
StringBytes::Encode(isolate, |
|
557 |
264452 |
buffer.data() + start, |
|
558 |
length, |
||
559 |
encoding, |
||
560 |
&error); |
||
561 |
Local<Value> ret; |
||
562 |
✓✓ | 264452 |
if (!maybe_ret.ToLocal(&ret)) { |
563 |
✗✓ | 14 |
CHECK(!error.IsEmpty()); |
564 |
14 |
isolate->ThrowException(error); |
|
565 |
14 |
return; |
|
566 |
} |
||
567 |
528876 |
args.GetReturnValue().Set(ret); |
|
568 |
} |
||
569 |
|||
570 |
// Convert the input into an encoded string |
||
571 |
1198 |
void DecodeUTF8(const FunctionCallbackInfo<Value>& args) { |
|
572 |
1198 |
Environment* env = Environment::GetCurrent(args); // list, flags |
|
573 |
|||
574 |
✗✓ | 1198 |
CHECK_GE(args.Length(), 1); |
575 |
|||
576 |
✓✓✓✓ |
3023 |
if (!(args[0]->IsArrayBuffer() || args[0]->IsSharedArrayBuffer() || |
577 |
✓✓✓✓ |
2110 |
args[0]->IsArrayBufferView())) { |
578 |
5 |
return node::THROW_ERR_INVALID_ARG_TYPE( |
|
579 |
env->isolate(), |
||
580 |
"The \"list\" argument must be an instance of SharedArrayBuffer, " |
||
581 |
108 |
"ArrayBuffer or ArrayBufferView."); |
|
582 |
} |
||
583 |
|||
584 |
1193 |
ArrayBufferViewContents<char> buffer(args[0]); |
|
585 |
|||
586 |
✓✗ | 1193 |
bool ignore_bom = args[1]->IsTrue(); |
587 |
1193 |
bool has_fatal = args[2]->IsTrue(); |
|
588 |
|||
589 |
1193 |
const char* data = buffer.data(); |
|
590 |
1193 |
size_t length = buffer.length(); |
|
591 |
|||
592 |
✓✓ | 1193 |
if (has_fatal) { |
593 |
327 |
auto result = simdutf::validate_utf8_with_errors(data, length); |
|
594 |
|||
595 |
✓✓ | 327 |
if (result.error) { |
596 |
72 |
return node::THROW_ERR_ENCODING_INVALID_ENCODED_DATA( |
|
597 |
72 |
env->isolate(), "The encoded data was not valid for encoding utf-8"); |
|
598 |
} |
||
599 |
} |
||
600 |
|||
601 |
✓✓✓✓ |
1121 |
if (!ignore_bom && length >= 3) { |
602 |
✓✓ | 1079 |
if (memcmp(data, "\xEF\xBB\xBF", 3) == 0) { |
603 |
10 |
data += 3; |
|
604 |
10 |
length -= 3; |
|
605 |
} |
||
606 |
} |
||
607 |
|||
608 |
✓✓ | 1183 |
if (length == 0) return args.GetReturnValue().SetEmptyString(); |
609 |
|||
610 |
Local<Value> error; |
||
611 |
MaybeLocal<Value> maybe_ret = |
||
612 |
1090 |
StringBytes::Encode(env->isolate(), data, length, UTF8, &error); |
|
613 |
Local<Value> ret; |
||
614 |
|||
615 |
✗✓ | 1090 |
if (!maybe_ret.ToLocal(&ret)) { |
616 |
CHECK(!error.IsEmpty()); |
||
617 |
env->isolate()->ThrowException(error); |
||
618 |
return; |
||
619 |
} |
||
620 |
|||
621 |
2180 |
args.GetReturnValue().Set(ret); |
|
622 |
} |
||
623 |
|||
624 |
// bytesCopied = copy(buffer, target[, targetStart][, sourceStart][, sourceEnd]) |
||
625 |
82 |
void Copy(const FunctionCallbackInfo<Value> &args) { |
|
626 |
82 |
Environment* env = Environment::GetCurrent(args); |
|
627 |
|||
628 |
✗✓ | 82 |
THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); |
629 |
✗✓ | 82 |
THROW_AND_RETURN_UNLESS_BUFFER(env, args[1]); |
630 |
82 |
ArrayBufferViewContents<char> source(args[0]); |
|
631 |
164 |
Local<Object> target_obj = args[1].As<Object>(); |
|
632 |
✗✓✓✗ ✗✓ |
410 |
SPREAD_BUFFER_ARG(target_obj, target); |
633 |
|||
634 |
82 |
size_t target_start = 0; |
|
635 |
82 |
size_t source_start = 0; |
|
636 |
✓✗ | 82 |
size_t source_end = 0; |
637 |
|||
638 |
✗✓✗✓ |
246 |
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[2], 0, &target_start)); |
639 |
✗✓✗✓ |
246 |
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[3], 0, &source_start)); |
640 |
✗✓✗✓ |
328 |
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[4], source.length(), |
641 |
&source_end)); |
||
642 |
|||
643 |
// Copy 0 bytes; we're done |
||
644 |
✓✗✗✓ |
82 |
if (target_start >= target_length || source_start >= source_end) |
645 |
return args.GetReturnValue().Set(0); |
||
646 |
|||
647 |
✗✓ | 82 |
if (source_start > source.length()) |
648 |
return THROW_ERR_OUT_OF_RANGE( |
||
649 |
env, "The value of \"sourceStart\" is out of range."); |
||
650 |
|||
651 |
✗✓ | 82 |
if (source_end - source_start > target_length - target_start) |
652 |
source_end = source_start + target_length - target_start; |
||
653 |
|||
654 |
82 |
uint32_t to_copy = std::min( |
|
655 |
164 |
std::min(source_end - source_start, target_length - target_start), |
|
656 |
82 |
source.length() - source_start); |
|
657 |
|||
658 |
82 |
memmove(target_data + target_start, source.data() + source_start, to_copy); |
|
659 |
✓✗ | 164 |
args.GetReturnValue().Set(to_copy); |
660 |
} |
||
661 |
|||
662 |
|||
663 |
179 |
void Fill(const FunctionCallbackInfo<Value>& args) { |
|
664 |
179 |
Environment* env = Environment::GetCurrent(args); |
|
665 |
179 |
Local<Context> ctx = env->context(); |
|
666 |
|||
667 |
✗✓ | 202 |
THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); |
668 |
✗✓✓✗ ✗✓ |
1074 |
SPREAD_BUFFER_ARG(args[0], ts_obj); |
669 |
|||
670 |
✓✗ | 179 |
size_t start = 0; |
671 |
✗✓✓✓ |
537 |
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[2], 0, &start)); |
672 |
size_t end; |
||
673 |
✗✓✓✓ |
534 |
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[3], 0, &end)); |
674 |
|||
675 |
✓✗ | 177 |
size_t fill_length = end - start; |
676 |
Local<String> str_obj; |
||
677 |
size_t str_length; |
||
678 |
enum encoding enc; |
||
679 |
|||
680 |
// OOB Check. Throw the error in JS. |
||
681 |
✓✗✗✓ |
177 |
if (start > end || fill_length + start > ts_obj_length) |
682 |
return args.GetReturnValue().Set(-2); |
||
683 |
|||
684 |
// First check if Buffer has been passed. |
||
685 |
✓✓ | 177 |
if (Buffer::HasInstance(args[1])) { |
686 |
✗✓✓✓ ✗✓ |
48 |
SPREAD_BUFFER_ARG(args[1], fill_obj); |
687 |
8 |
str_length = fill_obj_length; |
|
688 |
16 |
memcpy( |
|
689 |
8 |
ts_obj_data + start, fill_obj_data, std::min(str_length, fill_length)); |
|
690 |
8 |
goto start_fill; |
|
691 |
} |
||
692 |
|||
693 |
// Then coerce everything that's not a string. |
||
694 |
✓✓ | 338 |
if (!args[1]->IsString()) { |
695 |
uint32_t val; |
||
696 |
✗✓ | 8 |
if (!args[1]->Uint32Value(ctx).To(&val)) return; |
697 |
4 |
int value = val & 255; |
|
698 |
4 |
memset(ts_obj_data + start, value, fill_length); |
|
699 |
4 |
return; |
|
700 |
} |
||
701 |
|||
702 |
✓✗ | 330 |
str_obj = args[1]->ToString(env->context()).ToLocalChecked(); |
703 |
165 |
enc = ParseEncoding(env->isolate(), args[4], UTF8); |
|
704 |
|||
705 |
// Can't use StringBytes::Write() in all cases. For example if attempting |
||
706 |
// to write a two byte character into a one byte Buffer. |
||
707 |
✓✓ | 165 |
if (enc == UTF8) { |
708 |
✓✗ | 49 |
str_length = str_obj->Utf8Length(env->isolate()); |
709 |
49 |
node::Utf8Value str(env->isolate(), args[1]); |
|
710 |
49 |
memcpy(ts_obj_data + start, *str, std::min(str_length, fill_length)); |
|
711 |
|||
712 |
✓✓ | 116 |
} else if (enc == UCS2) { |
713 |
✓✗ | 23 |
str_length = str_obj->Length() * sizeof(uint16_t); |
714 |
23 |
node::TwoByteValue str(env->isolate(), args[1]); |
|
715 |
✗✓ | 23 |
if (IsBigEndian()) |
716 |
SwapBytes16(reinterpret_cast<char*>(&str[0]), str_length); |
||
717 |
|||
718 |
23 |
memcpy(ts_obj_data + start, *str, std::min(str_length, fill_length)); |
|
719 |
|||
720 |
} else { |
||
721 |
// Write initial String to Buffer, then use that memory to copy remainder |
||
722 |
// of string. Correct the string length for cases like HEX where less than |
||
723 |
// the total string length is written. |
||
724 |
93 |
str_length = StringBytes::Write( |
|
725 |
93 |
env->isolate(), ts_obj_data + start, fill_length, str_obj, enc); |
|
726 |
} |
||
727 |
|||
728 |
173 |
start_fill: |
|
729 |
|||
730 |
✓✓ | 173 |
if (str_length >= fill_length) |
731 |
11 |
return; |
|
732 |
|||
733 |
// If str_length is zero, then either an empty buffer was provided, or Write() |
||
734 |
// indicated that no bytes could be written. If no bytes could be written, |
||
735 |
// then return -1 because the fill value is invalid. This will trigger a throw |
||
736 |
// in JavaScript. Silently failing should be avoided because it can lead to |
||
737 |
// buffers with unexpected contents. |
||
738 |
✓✓ | 162 |
if (str_length == 0) |
739 |
12 |
return args.GetReturnValue().Set(-1); |
|
740 |
|||
741 |
156 |
size_t in_there = str_length; |
|
742 |
156 |
char* ptr = ts_obj_data + start + str_length; |
|
743 |
|||
744 |
✓✓ | 445 |
while (in_there < fill_length - in_there) { |
745 |
289 |
memcpy(ptr, ts_obj_data + start, in_there); |
|
746 |
289 |
ptr += in_there; |
|
747 |
289 |
in_there *= 2; |
|
748 |
} |
||
749 |
|||
750 |
✓✗ | 156 |
if (in_there < fill_length) { |
751 |
156 |
memcpy(ptr, ts_obj_data + start, fill_length - in_there); |
|
752 |
} |
||
753 |
} |
||
754 |
|||
755 |
|||
756 |
template <encoding encoding> |
||
757 |
375828 |
void StringWrite(const FunctionCallbackInfo<Value>& args) { |
|
758 |
375828 |
Environment* env = Environment::GetCurrent(args); |
|
759 |
|||
760 |
✗✓ | 375840 |
THROW_AND_RETURN_UNLESS_BUFFER(env, args.This()); |
761 |
✗✓✓✓ ✗✓ |
2254968 |
SPREAD_BUFFER_ARG(args.This(), ts_obj); |
762 |
|||
763 |
✗✓ | 751656 |
THROW_AND_RETURN_IF_NOT_STRING(env, args[0], "argument"); |
764 |
|||
765 |
375828 |
Local<String> str = args[0]->ToString(env->context()).ToLocalChecked(); |
|
766 |
|||
767 |
375828 |
size_t offset = 0; |
|
768 |
✓✗ | 375828 |
size_t max_length = 0; |
769 |
|||
770 |
✗✓✗✓ |
1127484 |
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[1], 0, &offset)); |
771 |
✗✓ | 375828 |
if (offset > ts_obj_length) { |
772 |
return node::THROW_ERR_BUFFER_OUT_OF_BOUNDS( |
||
773 |
env, "\"offset\" is outside of buffer bounds"); |
||
774 |
} |
||
775 |
|||
776 |
✓✗✗✓ ✗✓ |
1503312 |
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[2], ts_obj_length - offset, |
777 |
&max_length)); |
||
778 |
|||
779 |
375828 |
max_length = std::min(ts_obj_length - offset, max_length); |
|
780 |
|||
781 |
✓✓ | 375828 |
if (max_length == 0) |
782 |
24 |
return args.GetReturnValue().Set(0); |
|
783 |
|||
784 |
375816 |
uint32_t written = StringBytes::Write( |
|
785 |
375816 |
env->isolate(), ts_obj_data + offset, max_length, str, encoding); |
|
786 |
✓✗ | 751632 |
args.GetReturnValue().Set(written); |
787 |
} |
||
788 |
|||
789 |
194126 |
void ByteLengthUtf8(const FunctionCallbackInfo<Value> &args) { |
|
790 |
194126 |
Environment* env = Environment::GetCurrent(args); |
|
791 |
✗✓ | 388252 |
CHECK(args[0]->IsString()); |
792 |
|||
793 |
// Fast case: avoid StringBytes on UTF8 string. Jump to v8. |
||
794 |
✓✗ | 582378 |
args.GetReturnValue().Set(args[0].As<String>()->Utf8Length(env->isolate())); |
795 |
194126 |
} |
|
796 |
|||
797 |
// Normalize val to be an integer in the range of [1, -1] since |
||
798 |
// implementations of memcmp() can vary by platform. |
||
799 |
3653 |
static int normalizeCompareVal(int val, size_t a_length, size_t b_length) { |
|
800 |
✓✓ | 3653 |
if (val == 0) { |
801 |
✓✓ | 3626 |
if (a_length > b_length) |
802 |
6 |
return 1; |
|
803 |
✓✓ | 3620 |
else if (a_length < b_length) |
804 |
5 |
return -1; |
|
805 |
} else { |
||
806 |
✓✓ | 27 |
if (val > 0) |
807 |
7 |
return 1; |
|
808 |
else |
||
809 |
20 |
return -1; |
|
810 |
} |
||
811 |
3615 |
return val; |
|
812 |
} |
||
813 |
|||
814 |
9 |
void CompareOffset(const FunctionCallbackInfo<Value> &args) { |
|
815 |
9 |
Environment* env = Environment::GetCurrent(args); |
|
816 |
|||
817 |
✗✓ | 9 |
THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); |
818 |
✗✓ | 9 |
THROW_AND_RETURN_UNLESS_BUFFER(env, args[1]); |
819 |
9 |
ArrayBufferViewContents<char> source(args[0]); |
|
820 |
9 |
ArrayBufferViewContents<char> target(args[1]); |
|
821 |
|||
822 |
9 |
size_t target_start = 0; |
|
823 |
9 |
size_t source_start = 0; |
|
824 |
9 |
size_t source_end = 0; |
|
825 |
✓✗ | 9 |
size_t target_end = 0; |
826 |
|||
827 |
✗✓✗✓ |
27 |
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[2], 0, &target_start)); |
828 |
✗✓✗✓ |
27 |
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[3], 0, &source_start)); |
829 |
✗✓✗✓ |
36 |
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[4], target.length(), |
830 |
&target_end)); |
||
831 |
✗✓✗✓ |
36 |
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[5], source.length(), |
832 |
&source_end)); |
||
833 |
|||
834 |
✗✓ | 9 |
if (source_start > source.length()) |
835 |
return THROW_ERR_OUT_OF_RANGE( |
||
836 |
env, "The value of \"sourceStart\" is out of range."); |
||
837 |
✗✓ | 9 |
if (target_start > target.length()) |
838 |
return THROW_ERR_OUT_OF_RANGE( |
||
839 |
env, "The value of \"targetStart\" is out of range."); |
||
840 |
|||
841 |
✗✓ | 9 |
CHECK_LE(source_start, source_end); |
842 |
✗✓ | 9 |
CHECK_LE(target_start, target_end); |
843 |
|||
844 |
size_t to_cmp = |
||
845 |
18 |
std::min(std::min(source_end - source_start, target_end - target_start), |
|
846 |
9 |
source.length() - source_start); |
|
847 |
|||
848 |
✓✗ | 18 |
int val = normalizeCompareVal(to_cmp > 0 ? |
849 |
9 |
memcmp(source.data() + source_start, |
|
850 |
9 |
target.data() + target_start, |
|
851 |
to_cmp) : 0, |
||
852 |
source_end - source_start, |
||
853 |
target_end - target_start); |
||
854 |
|||
855 |
18 |
args.GetReturnValue().Set(val); |
|
856 |
} |
||
857 |
|||
858 |
3644 |
void Compare(const FunctionCallbackInfo<Value> &args) { |
|
859 |
3644 |
Environment* env = Environment::GetCurrent(args); |
|
860 |
|||
861 |
✗✓ | 3644 |
THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); |
862 |
✗✓ | 3644 |
THROW_AND_RETURN_UNLESS_BUFFER(env, args[1]); |
863 |
3644 |
ArrayBufferViewContents<char> a(args[0]); |
|
864 |
3644 |
ArrayBufferViewContents<char> b(args[1]); |
|
865 |
|||
866 |
3644 |
size_t cmp_length = std::min(a.length(), b.length()); |
|
867 |
|||
868 |
✓✓ | 7222 |
int val = normalizeCompareVal(cmp_length > 0 ? |
869 |
3578 |
memcmp(a.data(), b.data(), cmp_length) : 0, |
|
870 |
a.length(), b.length()); |
||
871 |
7288 |
args.GetReturnValue().Set(val); |
|
872 |
} |
||
873 |
|||
874 |
|||
875 |
// Computes the offset for starting an indexOf or lastIndexOf search. |
||
876 |
// Returns either a valid offset in [0...<length - 1>], ie inside the Buffer, |
||
877 |
// or -1 to signal that there is no possible match. |
||
878 |
1294 |
int64_t IndexOfOffset(size_t length, |
|
879 |
int64_t offset_i64, |
||
880 |
int64_t needle_length, |
||
881 |
bool is_forward) { |
||
882 |
1294 |
int64_t length_i64 = static_cast<int64_t>(length); |
|
883 |
✓✓ | 1294 |
if (offset_i64 < 0) { |
884 |
✓✓ | 81 |
if (offset_i64 + length_i64 >= 0) { |
885 |
// Negative offsets count backwards from the end of the buffer. |
||
886 |
63 |
return length_i64 + offset_i64; |
|
887 |
✓✓✗✓ |
18 |
} else if (is_forward || needle_length == 0) { |
888 |
// indexOf from before the start of the buffer: search the whole buffer. |
||
889 |
10 |
return 0; |
|
890 |
} else { |
||
891 |
// lastIndexOf from before the start of the buffer: no match. |
||
892 |
8 |
return -1; |
|
893 |
} |
||
894 |
} else { |
||
895 |
✓✓ | 1213 |
if (offset_i64 + needle_length <= length_i64) { |
896 |
// Valid positive offset. |
||
897 |
1125 |
return offset_i64; |
|
898 |
✓✓ | 88 |
} else if (needle_length == 0) { |
899 |
// Out of buffer bounds, but empty needle: point to end of buffer. |
||
900 |
10 |
return length_i64; |
|
901 |
✓✓ | 78 |
} else if (is_forward) { |
902 |
// indexOf from past the end of the buffer: no match. |
||
903 |
15 |
return -1; |
|
904 |
} else { |
||
905 |
// lastIndexOf from past the end of the buffer: search the whole buffer. |
||
906 |
63 |
return length_i64 - 1; |
|
907 |
} |
||
908 |
} |
||
909 |
} |
||
910 |
|||
911 |
1024 |
void IndexOfString(const FunctionCallbackInfo<Value>& args) { |
|
912 |
1024 |
Environment* env = Environment::GetCurrent(args); |
|
913 |
1024 |
Isolate* isolate = env->isolate(); |
|
914 |
|||
915 |
✗✓ | 2048 |
CHECK(args[1]->IsString()); |
916 |
✗✓ | 1024 |
CHECK(args[2]->IsNumber()); |
917 |
✗✓ | 1024 |
CHECK(args[3]->IsInt32()); |
918 |
✗✓ | 1024 |
CHECK(args[4]->IsBoolean()); |
919 |
|||
920 |
✓✗ | 2048 |
enum encoding enc = static_cast<enum encoding>(args[3].As<Int32>()->Value()); |
921 |
|||
922 |
✗✓ | 1057 |
THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); |
923 |
1024 |
ArrayBufferViewContents<char> buffer(args[0]); |
|
924 |
|||
925 |
✓✗ | 2048 |
Local<String> needle = args[1].As<String>(); |
926 |
✓✗ | 2048 |
int64_t offset_i64 = args[2].As<Integer>()->Value(); |
927 |
1024 |
bool is_forward = args[4]->IsTrue(); |
|
928 |
|||
929 |
1024 |
const char* haystack = buffer.data(); |
|
930 |
// Round down to the nearest multiple of 2 in case of UCS2. |
||
931 |
✓✓ | 1118 |
const size_t haystack_length = (enc == UCS2) ? |
932 |
1118 |
buffer.length() &~ 1 : buffer.length(); // NOLINT(whitespace/operators) |
|
933 |
|||
934 |
size_t needle_length; |
||
935 |
✗✓ | 2048 |
if (!StringBytes::Size(isolate, needle, enc).To(&needle_length)) return; |
936 |
|||
937 |
1024 |
int64_t opt_offset = IndexOfOffset(haystack_length, |
|
938 |
offset_i64, |
||
939 |
needle_length, |
||
940 |
is_forward); |
||
941 |
|||
942 |
✓✓ | 1024 |
if (needle_length == 0) { |
943 |
// Match String#indexOf() and String#lastIndexOf() behavior. |
||
944 |
8 |
args.GetReturnValue().Set(static_cast<double>(opt_offset)); |
|
945 |
8 |
return; |
|
946 |
} |
||
947 |
|||
948 |
✗✓ | 1016 |
if (haystack_length == 0) { |
949 |
return args.GetReturnValue().Set(-1); |
||
950 |
} |
||
951 |
|||
952 |
✓✓ | 1016 |
if (opt_offset <= -1) { |
953 |
20 |
return args.GetReturnValue().Set(-1); |
|
954 |
} |
||
955 |
1006 |
size_t offset = static_cast<size_t>(opt_offset); |
|
956 |
✗✓ | 1006 |
CHECK_LT(offset, haystack_length); |
957 |
✓✓✓✓ |
1006 |
if ((is_forward && needle_length + offset > haystack_length) || |
958 |
✓✓ | 1004 |
needle_length > haystack_length) { |
959 |
30 |
return args.GetReturnValue().Set(-1); |
|
960 |
} |
||
961 |
|||
962 |
991 |
size_t result = haystack_length; |
|
963 |
|||
964 |
✓✓ | 991 |
if (enc == UCS2) { |
965 |
89 |
String::Value needle_value(isolate, needle); |
|
966 |
✗✓ | 89 |
if (*needle_value == nullptr) |
967 |
return args.GetReturnValue().Set(-1); |
||
968 |
|||
969 |
✓✗✗✓ ✗✓ |
89 |
if (haystack_length < 2 || needle_value.length() < 1) { |
970 |
return args.GetReturnValue().Set(-1); |
||
971 |
} |
||
972 |
|||
973 |
✗✓ | 89 |
if (IsBigEndian()) { |
974 |
StringBytes::InlineDecoder decoder; |
||
975 |
if (decoder.Decode(env, needle, enc).IsNothing()) return; |
||
976 |
const uint16_t* decoded_string = |
||
977 |
reinterpret_cast<const uint16_t*>(decoder.out()); |
||
978 |
|||
979 |
if (decoded_string == nullptr) |
||
980 |
return args.GetReturnValue().Set(-1); |
||
981 |
|||
982 |
result = SearchString(reinterpret_cast<const uint16_t*>(haystack), |
||
983 |
haystack_length / 2, |
||
984 |
decoded_string, |
||
985 |
decoder.size() / 2, |
||
986 |
offset / 2, |
||
987 |
is_forward); |
||
988 |
} else { |
||
989 |
89 |
result = SearchString(reinterpret_cast<const uint16_t*>(haystack), |
|
990 |
haystack_length / 2, |
||
991 |
89 |
reinterpret_cast<const uint16_t*>(*needle_value), |
|
992 |
89 |
needle_value.length(), |
|
993 |
offset / 2, |
||
994 |
is_forward); |
||
995 |
} |
||
996 |
89 |
result *= 2; |
|
997 |
✓✓ | 902 |
} else if (enc == UTF8) { |
998 |
890 |
String::Utf8Value needle_value(isolate, needle); |
|
999 |
✗✓ | 890 |
if (*needle_value == nullptr) |
1000 |
return args.GetReturnValue().Set(-1); |
||
1001 |
|||
1002 |
890 |
result = SearchString(reinterpret_cast<const uint8_t*>(haystack), |
|
1003 |
haystack_length, |
||
1004 |
890 |
reinterpret_cast<const uint8_t*>(*needle_value), |
|
1005 |
needle_length, |
||
1006 |
offset, |
||
1007 |
is_forward); |
||
1008 |
✓✗ | 12 |
} else if (enc == LATIN1) { |
1009 |
12 |
uint8_t* needle_data = node::UncheckedMalloc<uint8_t>(needle_length); |
|
1010 |
✗✓ | 12 |
if (needle_data == nullptr) { |
1011 |
return args.GetReturnValue().Set(-1); |
||
1012 |
} |
||
1013 |
12 |
needle->WriteOneByte( |
|
1014 |
isolate, needle_data, 0, needle_length, String::NO_NULL_TERMINATION); |
||
1015 |
|||
1016 |
12 |
result = SearchString(reinterpret_cast<const uint8_t*>(haystack), |
|
1017 |
haystack_length, |
||
1018 |
needle_data, |
||
1019 |
needle_length, |
||
1020 |
offset, |
||
1021 |
is_forward); |
||
1022 |
12 |
free(needle_data); |
|
1023 |
} |
||
1024 |
|||
1025 |
1982 |
args.GetReturnValue().Set( |
|
1026 |
✓✓ | 991 |
result == haystack_length ? -1 : static_cast<int>(result)); |
1027 |
} |
||
1028 |
|||
1029 |
✓✗ | 220 |
void IndexOfBuffer(const FunctionCallbackInfo<Value>& args) { |
1030 |
✗✓ | 220 |
CHECK(args[1]->IsObject()); |
1031 |
✗✓ | 220 |
CHECK(args[2]->IsNumber()); |
1032 |
✗✓ | 220 |
CHECK(args[3]->IsInt32()); |
1033 |
✗✓ | 220 |
CHECK(args[4]->IsBoolean()); |
1034 |
|||
1035 |
✓✗ | 440 |
enum encoding enc = static_cast<enum encoding>(args[3].As<Int32>()->Value()); |
1036 |
|||
1037 |
✗✓ | 250 |
THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); |
1038 |
✗✓ | 220 |
THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[1]); |
1039 |
220 |
ArrayBufferViewContents<char> haystack_contents(args[0]); |
|
1040 |
220 |
ArrayBufferViewContents<char> needle_contents(args[1]); |
|
1041 |
✓✗ | 440 |
int64_t offset_i64 = args[2].As<Integer>()->Value(); |
1042 |
220 |
bool is_forward = args[4]->IsTrue(); |
|
1043 |
|||
1044 |
220 |
const char* haystack = haystack_contents.data(); |
|
1045 |
220 |
const size_t haystack_length = haystack_contents.length(); |
|
1046 |
220 |
const char* needle = needle_contents.data(); |
|
1047 |
220 |
const size_t needle_length = needle_contents.length(); |
|
1048 |
|||
1049 |
220 |
int64_t opt_offset = IndexOfOffset(haystack_length, |
|
1050 |
offset_i64, |
||
1051 |
needle_length, |
||
1052 |
is_forward); |
||
1053 |
|||
1054 |
✓✓ | 220 |
if (needle_length == 0) { |
1055 |
// Match String#indexOf() and String#lastIndexOf() behavior. |
||
1056 |
12 |
args.GetReturnValue().Set(static_cast<double>(opt_offset)); |
|
1057 |
12 |
return; |
|
1058 |
} |
||
1059 |
|||
1060 |
✗✓ | 208 |
if (haystack_length == 0) { |
1061 |
return args.GetReturnValue().Set(-1); |
||
1062 |
} |
||
1063 |
|||
1064 |
✓✓ | 208 |
if (opt_offset <= -1) { |
1065 |
18 |
return args.GetReturnValue().Set(-1); |
|
1066 |
} |
||
1067 |
199 |
size_t offset = static_cast<size_t>(opt_offset); |
|
1068 |
✗✓ | 199 |
CHECK_LT(offset, haystack_length); |
1069 |
✓✓✓✓ |
199 |
if ((is_forward && needle_length + offset > haystack_length) || |
1070 |
✓✓ | 197 |
needle_length > haystack_length) { |
1071 |
14 |
return args.GetReturnValue().Set(-1); |
|
1072 |
} |
||
1073 |
|||
1074 |
192 |
size_t result = haystack_length; |
|
1075 |
|||
1076 |
✓✓ | 192 |
if (enc == UCS2) { |
1077 |
✓✗✓✓ |
59 |
if (haystack_length < 2 || needle_length < 2) { |
1078 |
4 |
return args.GetReturnValue().Set(-1); |
|
1079 |
} |
||
1080 |
57 |
result = SearchString( |
|
1081 |
reinterpret_cast<const uint16_t*>(haystack), |
||
1082 |
haystack_length / 2, |
||
1083 |
reinterpret_cast<const uint16_t*>(needle), |
||
1084 |
needle_length / 2, |
||
1085 |
offset / 2, |
||
1086 |
is_forward); |
||
1087 |
57 |
result *= 2; |
|
1088 |
} else { |
||
1089 |
133 |
result = SearchString( |
|
1090 |
reinterpret_cast<const uint8_t*>(haystack), |
||
1091 |
haystack_length, |
||
1092 |
reinterpret_cast<const uint8_t*>(needle), |
||
1093 |
needle_length, |
||
1094 |
offset, |
||
1095 |
is_forward); |
||
1096 |
} |
||
1097 |
|||
1098 |
380 |
args.GetReturnValue().Set( |
|
1099 |
✓✓ | 190 |
result == haystack_length ? -1 : static_cast<int>(result)); |
1100 |
} |
||
1101 |
|||
1102 |
✓✗ | 50 |
void IndexOfNumber(const FunctionCallbackInfo<Value>& args) { |
1103 |
✗✓ | 50 |
CHECK(args[1]->IsUint32()); |
1104 |
✗✓ | 50 |
CHECK(args[2]->IsNumber()); |
1105 |
✗✓ | 50 |
CHECK(args[3]->IsBoolean()); |
1106 |
|||
1107 |
✗✓ | 54 |
THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); |
1108 |
50 |
ArrayBufferViewContents<char> buffer(args[0]); |
|
1109 |
|||
1110 |
✓✗ | 100 |
uint32_t needle = args[1].As<Uint32>()->Value(); |
1111 |
✓✗ | 100 |
int64_t offset_i64 = args[2].As<Integer>()->Value(); |
1112 |
50 |
bool is_forward = args[3]->IsTrue(); |
|
1113 |
|||
1114 |
int64_t opt_offset = |
||
1115 |
50 |
IndexOfOffset(buffer.length(), offset_i64, 1, is_forward); |
|
1116 |
✓✓✗✓ ✓✓ |
50 |
if (opt_offset <= -1 || buffer.length() == 0) { |
1117 |
8 |
return args.GetReturnValue().Set(-1); |
|
1118 |
} |
||
1119 |
46 |
size_t offset = static_cast<size_t>(opt_offset); |
|
1120 |
✗✓ | 46 |
CHECK_LT(offset, buffer.length()); |
1121 |
|||
1122 |
const void* ptr; |
||
1123 |
✓✓ | 46 |
if (is_forward) { |
1124 |
38 |
ptr = memchr(buffer.data() + offset, needle, buffer.length() - offset); |
|
1125 |
} else { |
||
1126 |
8 |
ptr = node::stringsearch::MemrchrFill(buffer.data(), needle, offset + 1); |
|
1127 |
} |
||
1128 |
46 |
const char* ptr_char = static_cast<const char*>(ptr); |
|
1129 |
✓✓ | 92 |
args.GetReturnValue().Set(ptr ? static_cast<int>(ptr_char - buffer.data()) |
1130 |
: -1); |
||
1131 |
} |
||
1132 |
|||
1133 |
|||
1134 |
2 |
void Swap16(const FunctionCallbackInfo<Value>& args) { |
|
1135 |
2 |
Environment* env = Environment::GetCurrent(args); |
|
1136 |
✗✓ | 2 |
THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); |
1137 |
✗✓✓✗ ✗✓ |
12 |
SPREAD_BUFFER_ARG(args[0], ts_obj); |
1138 |
2 |
SwapBytes16(ts_obj_data, ts_obj_length); |
|
1139 |
✓✗ | 4 |
args.GetReturnValue().Set(args[0]); |
1140 |
} |
||
1141 |
|||
1142 |
|||
1143 |
2 |
void Swap32(const FunctionCallbackInfo<Value>& args) { |
|
1144 |
2 |
Environment* env = Environment::GetCurrent(args); |
|
1145 |
✗✓ | 2 |
THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); |
1146 |
✗✓✓✗ ✗✓ |
12 |
SPREAD_BUFFER_ARG(args[0], ts_obj); |
1147 |
2 |
SwapBytes32(ts_obj_data, ts_obj_length); |
|
1148 |
✓✗ | 4 |
args.GetReturnValue().Set(args[0]); |
1149 |
} |
||
1150 |
|||
1151 |
|||
1152 |
2 |
void Swap64(const FunctionCallbackInfo<Value>& args) { |
|
1153 |
2 |
Environment* env = Environment::GetCurrent(args); |
|
1154 |
✗✓ | 2 |
THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); |
1155 |
✗✓✓✗ ✗✓ |
12 |
SPREAD_BUFFER_ARG(args[0], ts_obj); |
1156 |
2 |
SwapBytes64(ts_obj_data, ts_obj_length); |
|
1157 |
✓✗ | 4 |
args.GetReturnValue().Set(args[0]); |
1158 |
} |
||
1159 |
|||
1160 |
|||
1161 |
// Encode a single string to a UTF-8 Uint8Array (not Buffer). |
||
1162 |
// Used in TextEncoder.prototype.encode. |
||
1163 |
479 |
static void EncodeUtf8String(const FunctionCallbackInfo<Value>& args) { |
|
1164 |
479 |
Environment* env = Environment::GetCurrent(args); |
|
1165 |
479 |
Isolate* isolate = env->isolate(); |
|
1166 |
✗✓ | 479 |
CHECK_GE(args.Length(), 1); |
1167 |
✗✓ | 958 |
CHECK(args[0]->IsString()); |
1168 |
|||
1169 |
958 |
Local<String> str = args[0].As<String>(); |
|
1170 |
479 |
size_t length = str->Utf8Length(isolate); |
|
1171 |
|||
1172 |
Local<ArrayBuffer> ab; |
||
1173 |
{ |
||
1174 |
958 |
NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); |
|
1175 |
std::unique_ptr<BackingStore> bs = |
||
1176 |
479 |
ArrayBuffer::NewBackingStore(isolate, length); |
|
1177 |
|||
1178 |
✗✓ | 479 |
CHECK(bs); |
1179 |
|||
1180 |
479 |
str->WriteUtf8(isolate, |
|
1181 |
479 |
static_cast<char*>(bs->Data()), |
|
1182 |
-1, // We are certain that `data` is sufficiently large |
||
1183 |
nullptr, |
||
1184 |
String::NO_NULL_TERMINATION | String::REPLACE_INVALID_UTF8); |
||
1185 |
|||
1186 |
479 |
ab = ArrayBuffer::New(isolate, std::move(bs)); |
|
1187 |
} |
||
1188 |
|||
1189 |
479 |
auto array = Uint8Array::New(ab, 0, length); |
|
1190 |
479 |
args.GetReturnValue().Set(array); |
|
1191 |
479 |
} |
|
1192 |
|||
1193 |
|||
1194 |
87 |
static void EncodeInto(const FunctionCallbackInfo<Value>& args) { |
|
1195 |
87 |
Environment* env = Environment::GetCurrent(args); |
|
1196 |
87 |
Isolate* isolate = env->isolate(); |
|
1197 |
✗✓ | 87 |
CHECK_GE(args.Length(), 3); |
1198 |
✗✓ | 174 |
CHECK(args[0]->IsString()); |
1199 |
✗✓ | 87 |
CHECK(args[1]->IsUint8Array()); |
1200 |
✗✓ | 87 |
CHECK(args[2]->IsUint32Array()); |
1201 |
|||
1202 |
✓✗ | 174 |
Local<String> source = args[0].As<String>(); |
1203 |
|||
1204 |
174 |
Local<Uint8Array> dest = args[1].As<Uint8Array>(); |
|
1205 |
87 |
Local<ArrayBuffer> buf = dest->Buffer(); |
|
1206 |
174 |
char* write_result = static_cast<char*>(buf->Data()) + dest->ByteOffset(); |
|
1207 |
87 |
size_t dest_length = dest->ByteLength(); |
|
1208 |
|||
1209 |
// results = [ read, written ] |
||
1210 |
174 |
Local<Uint32Array> result_arr = args[2].As<Uint32Array>(); |
|
1211 |
uint32_t* results = reinterpret_cast<uint32_t*>( |
||
1212 |
174 |
static_cast<char*>(result_arr->Buffer()->Data()) + |
|
1213 |
87 |
result_arr->ByteOffset()); |
|
1214 |
|||
1215 |
int nchars; |
||
1216 |
87 |
int written = source->WriteUtf8( |
|
1217 |
isolate, |
||
1218 |
write_result, |
||
1219 |
dest_length, |
||
1220 |
&nchars, |
||
1221 |
String::NO_NULL_TERMINATION | String::REPLACE_INVALID_UTF8); |
||
1222 |
87 |
results[0] = nchars; |
|
1223 |
87 |
results[1] = written; |
|
1224 |
87 |
} |
|
1225 |
|||
1226 |
37 |
static void IsUtf8(const FunctionCallbackInfo<Value>& args) { |
|
1227 |
37 |
Environment* env = Environment::GetCurrent(args); |
|
1228 |
✗✓ | 37 |
CHECK_EQ(args.Length(), 1); |
1229 |
✓✓✗✓ ✗✓✗✗ ✗✓ |
38 |
CHECK(args[0]->IsTypedArray() || args[0]->IsArrayBuffer() || |
1230 |
args[0]->IsSharedArrayBuffer()); |
||
1231 |
37 |
ArrayBufferViewContents<char> abv(args[0]); |
|
1232 |
|||
1233 |
✓✓ | 37 |
if (abv.WasDetached()) { |
1234 |
1 |
return node::THROW_ERR_INVALID_STATE( |
|
1235 |
1 |
env, "Cannot validate on a detached buffer"); |
|
1236 |
} |
||
1237 |
|||
1238 |
✓✓ | 72 |
args.GetReturnValue().Set(simdutf::validate_utf8(abv.data(), abv.length())); |
1239 |
} |
||
1240 |
|||
1241 |
827 |
void SetBufferPrototype(const FunctionCallbackInfo<Value>& args) { |
|
1242 |
827 |
Environment* env = Environment::GetCurrent(args); |
|
1243 |
|||
1244 |
✗✓ | 827 |
CHECK(args[0]->IsObject()); |
1245 |
827 |
Local<Object> proto = args[0].As<Object>(); |
|
1246 |
827 |
env->set_buffer_prototype_object(proto); |
|
1247 |
827 |
} |
|
1248 |
|||
1249 |
7322 |
void GetZeroFillToggle(const FunctionCallbackInfo<Value>& args) { |
|
1250 |
7322 |
Environment* env = Environment::GetCurrent(args); |
|
1251 |
7322 |
NodeArrayBufferAllocator* allocator = env->isolate_data()->node_allocator(); |
|
1252 |
Local<ArrayBuffer> ab; |
||
1253 |
// It can be a nullptr when running inside an isolate where we |
||
1254 |
// do not own the ArrayBuffer allocator. |
||
1255 |
✓✓ | 7322 |
if (allocator == nullptr) { |
1256 |
// Create a dummy Uint32Array - the JS land can only toggle the C++ land |
||
1257 |
// setting when the allocator uses our toggle. With this the toggle in JS |
||
1258 |
// land results in no-ops. |
||
1259 |
69 |
ab = ArrayBuffer::New(env->isolate(), sizeof(uint32_t)); |
|
1260 |
} else { |
||
1261 |
7253 |
uint32_t* zero_fill_field = allocator->zero_fill_field(); |
|
1262 |
std::unique_ptr<BackingStore> backing = |
||
1263 |
ArrayBuffer::NewBackingStore(zero_fill_field, |
||
1264 |
sizeof(*zero_fill_field), |
||
1265 |
6638 |
[](void*, size_t, void*) {}, |
|
1266 |
7253 |
nullptr); |
|
1267 |
7253 |
ab = ArrayBuffer::New(env->isolate(), std::move(backing)); |
|
1268 |
} |
||
1269 |
|||
1270 |
14644 |
ab->SetPrivate( |
|
1271 |
env->context(), |
||
1272 |
env->untransferable_object_private_symbol(), |
||
1273 |
21966 |
True(env->isolate())).Check(); |
|
1274 |
|||
1275 |
7322 |
args.GetReturnValue().Set(Uint32Array::New(ab, 0, 1)); |
|
1276 |
7322 |
} |
|
1277 |
|||
1278 |
964 |
void DetachArrayBuffer(const FunctionCallbackInfo<Value>& args) { |
|
1279 |
964 |
Environment* env = Environment::GetCurrent(args); |
|
1280 |
✓✗ | 964 |
if (args[0]->IsArrayBuffer()) { |
1281 |
1928 |
Local<ArrayBuffer> buf = args[0].As<ArrayBuffer>(); |
|
1282 |
✓✓ | 964 |
if (buf->IsDetachable()) { |
1283 |
961 |
std::shared_ptr<BackingStore> store = buf->GetBackingStore(); |
|
1284 |
2883 |
buf->Detach(Local<Value>()).Check(); |
|
1285 |
1922 |
args.GetReturnValue().Set(ArrayBuffer::New(env->isolate(), store)); |
|
1286 |
} |
||
1287 |
} |
||
1288 |
964 |
} |
|
1289 |
|||
1290 |
namespace { |
||
1291 |
|||
1292 |
388 |
std::pair<void*, size_t> DecomposeBufferToParts(Local<Value> buffer) { |
|
1293 |
void* pointer; |
||
1294 |
size_t byte_length; |
||
1295 |
✓✗ | 388 |
if (buffer->IsArrayBuffer()) { |
1296 |
388 |
Local<ArrayBuffer> ab = buffer.As<ArrayBuffer>(); |
|
1297 |
388 |
pointer = ab->Data(); |
|
1298 |
388 |
byte_length = ab->ByteLength(); |
|
1299 |
} else if (buffer->IsSharedArrayBuffer()) { |
||
1300 |
Local<SharedArrayBuffer> ab = buffer.As<SharedArrayBuffer>(); |
||
1301 |
pointer = ab->Data(); |
||
1302 |
byte_length = ab->ByteLength(); |
||
1303 |
} else { |
||
1304 |
UNREACHABLE(); // Caller must validate. |
||
1305 |
} |
||
1306 |
388 |
return {pointer, byte_length}; |
|
1307 |
} |
||
1308 |
|||
1309 |
} // namespace |
||
1310 |
|||
1311 |
✓✗ | 194 |
void CopyArrayBuffer(const FunctionCallbackInfo<Value>& args) { |
1312 |
// args[0] == Destination ArrayBuffer |
||
1313 |
// args[1] == Destination ArrayBuffer Offset |
||
1314 |
// args[2] == Source ArrayBuffer |
||
1315 |
// args[3] == Source ArrayBuffer Offset |
||
1316 |
// args[4] == bytesToCopy |
||
1317 |
|||
1318 |
✗✓✗✗ ✗✓ |
194 |
CHECK(args[0]->IsArrayBuffer() || args[0]->IsSharedArrayBuffer()); |
1319 |
✗✓ | 194 |
CHECK(args[1]->IsUint32()); |
1320 |
✗✓✗✗ ✗✓ |
194 |
CHECK(args[2]->IsArrayBuffer() || args[2]->IsSharedArrayBuffer()); |
1321 |
✗✓ | 194 |
CHECK(args[3]->IsUint32()); |
1322 |
✗✓ | 194 |
CHECK(args[4]->IsUint32()); |
1323 |
|||
1324 |
void* destination; |
||
1325 |
size_t destination_byte_length; |
||
1326 |
194 |
std::tie(destination, destination_byte_length) = |
|
1327 |
✓✗ | 194 |
DecomposeBufferToParts(args[0]); |
1328 |
|||
1329 |
void* source; |
||
1330 |
size_t source_byte_length; |
||
1331 |
✓✗ | 194 |
std::tie(source, source_byte_length) = DecomposeBufferToParts(args[2]); |
1332 |
|||
1333 |
✓✗ | 388 |
uint32_t destination_offset = args[1].As<Uint32>()->Value(); |
1334 |
✓✗ | 388 |
uint32_t source_offset = args[3].As<Uint32>()->Value(); |
1335 |
388 |
size_t bytes_to_copy = args[4].As<Uint32>()->Value(); |
|
1336 |
|||
1337 |
✗✓ | 194 |
CHECK_GE(destination_byte_length - destination_offset, bytes_to_copy); |
1338 |
✗✓ | 194 |
CHECK_GE(source_byte_length - source_offset, bytes_to_copy); |
1339 |
|||
1340 |
194 |
uint8_t* dest = static_cast<uint8_t*>(destination) + destination_offset; |
|
1341 |
194 |
uint8_t* src = static_cast<uint8_t*>(source) + source_offset; |
|
1342 |
194 |
memcpy(dest, src, bytes_to_copy); |
|
1343 |
194 |
} |
|
1344 |
|||
1345 |
827 |
void Initialize(Local<Object> target, |
|
1346 |
Local<Value> unused, |
||
1347 |
Local<Context> context, |
||
1348 |
void* priv) { |
||
1349 |
827 |
Environment* env = Environment::GetCurrent(context); |
|
1350 |
827 |
Isolate* isolate = env->isolate(); |
|
1351 |
|||
1352 |
827 |
SetMethod(context, target, "setBufferPrototype", SetBufferPrototype); |
|
1353 |
827 |
SetMethodNoSideEffect(context, target, "createFromString", CreateFromString); |
|
1354 |
827 |
SetMethodNoSideEffect(context, target, "decodeUTF8", DecodeUTF8); |
|
1355 |
|||
1356 |
827 |
SetMethodNoSideEffect(context, target, "byteLengthUtf8", ByteLengthUtf8); |
|
1357 |
827 |
SetMethod(context, target, "copy", Copy); |
|
1358 |
827 |
SetMethodNoSideEffect(context, target, "compare", Compare); |
|
1359 |
827 |
SetMethodNoSideEffect(context, target, "compareOffset", CompareOffset); |
|
1360 |
827 |
SetMethod(context, target, "fill", Fill); |
|
1361 |
827 |
SetMethodNoSideEffect(context, target, "indexOfBuffer", IndexOfBuffer); |
|
1362 |
827 |
SetMethodNoSideEffect(context, target, "indexOfNumber", IndexOfNumber); |
|
1363 |
827 |
SetMethodNoSideEffect(context, target, "indexOfString", IndexOfString); |
|
1364 |
|||
1365 |
827 |
SetMethod(context, target, "detachArrayBuffer", DetachArrayBuffer); |
|
1366 |
827 |
SetMethod(context, target, "copyArrayBuffer", CopyArrayBuffer); |
|
1367 |
|||
1368 |
827 |
SetMethod(context, target, "swap16", Swap16); |
|
1369 |
827 |
SetMethod(context, target, "swap32", Swap32); |
|
1370 |
827 |
SetMethod(context, target, "swap64", Swap64); |
|
1371 |
|||
1372 |
827 |
SetMethod(context, target, "encodeInto", EncodeInto); |
|
1373 |
827 |
SetMethodNoSideEffect(context, target, "encodeUtf8String", EncodeUtf8String); |
|
1374 |
|||
1375 |
827 |
SetMethodNoSideEffect(context, target, "isUtf8", IsUtf8); |
|
1376 |
|||
1377 |
target |
||
1378 |
827 |
->Set(context, |
|
1379 |
FIXED_ONE_BYTE_STRING(isolate, "kMaxLength"), |
||
1380 |
3308 |
Number::New(isolate, kMaxLength)) |
|
1381 |
.Check(); |
||
1382 |
|||
1383 |
target |
||
1384 |
827 |
->Set(context, |
|
1385 |
FIXED_ONE_BYTE_STRING(isolate, "kStringMaxLength"), |
||
1386 |
2481 |
Integer::New(isolate, String::kMaxLength)) |
|
1387 |
.Check(); |
||
1388 |
|||
1389 |
827 |
SetMethodNoSideEffect(context, target, "asciiSlice", StringSlice<ASCII>); |
|
1390 |
827 |
SetMethodNoSideEffect(context, target, "base64Slice", StringSlice<BASE64>); |
|
1391 |
827 |
SetMethodNoSideEffect( |
|
1392 |
context, target, "base64urlSlice", StringSlice<BASE64URL>); |
||
1393 |
827 |
SetMethodNoSideEffect(context, target, "latin1Slice", StringSlice<LATIN1>); |
|
1394 |
827 |
SetMethodNoSideEffect(context, target, "hexSlice", StringSlice<HEX>); |
|
1395 |
827 |
SetMethodNoSideEffect(context, target, "ucs2Slice", StringSlice<UCS2>); |
|
1396 |
827 |
SetMethodNoSideEffect(context, target, "utf8Slice", StringSlice<UTF8>); |
|
1397 |
|||
1398 |
827 |
SetMethod(context, target, "asciiWrite", StringWrite<ASCII>); |
|
1399 |
827 |
SetMethod(context, target, "base64Write", StringWrite<BASE64>); |
|
1400 |
827 |
SetMethod(context, target, "base64urlWrite", StringWrite<BASE64URL>); |
|
1401 |
827 |
SetMethod(context, target, "latin1Write", StringWrite<LATIN1>); |
|
1402 |
827 |
SetMethod(context, target, "hexWrite", StringWrite<HEX>); |
|
1403 |
827 |
SetMethod(context, target, "ucs2Write", StringWrite<UCS2>); |
|
1404 |
827 |
SetMethod(context, target, "utf8Write", StringWrite<UTF8>); |
|
1405 |
|||
1406 |
827 |
SetMethod(context, target, "getZeroFillToggle", GetZeroFillToggle); |
|
1407 |
827 |
} |
|
1408 |
|||
1409 |
} // anonymous namespace |
||
1410 |
|||
1411 |
5717 |
void RegisterExternalReferences(ExternalReferenceRegistry* registry) { |
|
1412 |
5717 |
registry->Register(SetBufferPrototype); |
|
1413 |
5717 |
registry->Register(CreateFromString); |
|
1414 |
5717 |
registry->Register(DecodeUTF8); |
|
1415 |
|||
1416 |
5717 |
registry->Register(ByteLengthUtf8); |
|
1417 |
5717 |
registry->Register(Copy); |
|
1418 |
5717 |
registry->Register(Compare); |
|
1419 |
5717 |
registry->Register(CompareOffset); |
|
1420 |
5717 |
registry->Register(Fill); |
|
1421 |
5717 |
registry->Register(IndexOfBuffer); |
|
1422 |
5717 |
registry->Register(IndexOfNumber); |
|
1423 |
5717 |
registry->Register(IndexOfString); |
|
1424 |
|||
1425 |
5717 |
registry->Register(Swap16); |
|
1426 |
5717 |
registry->Register(Swap32); |
|
1427 |
5717 |
registry->Register(Swap64); |
|
1428 |
|||
1429 |
5717 |
registry->Register(EncodeInto); |
|
1430 |
5717 |
registry->Register(EncodeUtf8String); |
|
1431 |
|||
1432 |
5717 |
registry->Register(IsUtf8); |
|
1433 |
|||
1434 |
5717 |
registry->Register(StringSlice<ASCII>); |
|
1435 |
5717 |
registry->Register(StringSlice<BASE64>); |
|
1436 |
5717 |
registry->Register(StringSlice<BASE64URL>); |
|
1437 |
5717 |
registry->Register(StringSlice<LATIN1>); |
|
1438 |
5717 |
registry->Register(StringSlice<HEX>); |
|
1439 |
5717 |
registry->Register(StringSlice<UCS2>); |
|
1440 |
5717 |
registry->Register(StringSlice<UTF8>); |
|
1441 |
|||
1442 |
5717 |
registry->Register(StringWrite<ASCII>); |
|
1443 |
5717 |
registry->Register(StringWrite<BASE64>); |
|
1444 |
5717 |
registry->Register(StringWrite<BASE64URL>); |
|
1445 |
5717 |
registry->Register(StringWrite<LATIN1>); |
|
1446 |
5717 |
registry->Register(StringWrite<HEX>); |
|
1447 |
5717 |
registry->Register(StringWrite<UCS2>); |
|
1448 |
5717 |
registry->Register(StringWrite<UTF8>); |
|
1449 |
5717 |
registry->Register(GetZeroFillToggle); |
|
1450 |
|||
1451 |
5717 |
registry->Register(DetachArrayBuffer); |
|
1452 |
5717 |
registry->Register(CopyArrayBuffer); |
|
1453 |
5717 |
} |
|
1454 |
|||
1455 |
} // namespace Buffer |
||
1456 |
} // namespace node |
||
1457 |
|||
1458 |
5788 |
NODE_BINDING_CONTEXT_AWARE_INTERNAL(buffer, node::Buffer::Initialize) |
|
1459 |
5717 |
NODE_BINDING_EXTERNAL_REFERENCE(buffer, |
|
1460 |
node::Buffer::RegisterExternalReferences) |
Generated by: GCOVR (Version 4.2) |