/src/node/src/node_buffer.cc
Line | Count | 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_debug.h" |
26 | | #include "node_errors.h" |
27 | | #include "node_external_reference.h" |
28 | | #include "node_i18n.h" |
29 | | #include "node_internals.h" |
30 | | |
31 | | #include "env-inl.h" |
32 | | #include "simdutf.h" |
33 | | #include "string_bytes.h" |
34 | | |
35 | | #include "util-inl.h" |
36 | | #include "v8-fast-api-calls.h" |
37 | | #include "v8.h" |
38 | | |
39 | | #include <stdint.h> |
40 | | #include <climits> |
41 | | #include <cstring> |
42 | | #include "nbytes.h" |
43 | | |
44 | | #define THROW_AND_RETURN_UNLESS_BUFFER(env, obj) \ |
45 | 35 | THROW_AND_RETURN_IF_NOT_BUFFER(env, obj, "argument") \ |
46 | | |
47 | | #define THROW_AND_RETURN_IF_OOB(r) \ |
48 | 70 | do { \ |
49 | 70 | Maybe<bool> m = (r); \ |
50 | 70 | if (m.IsNothing()) return; \ |
51 | 70 | if (!m.FromJust()) \ |
52 | 70 | return node::THROW_ERR_OUT_OF_RANGE(env, "Index out of range"); \ |
53 | 70 | } while (0) \ |
54 | | |
55 | | namespace node { |
56 | | namespace Buffer { |
57 | | |
58 | | using v8::ArrayBuffer; |
59 | | using v8::ArrayBufferView; |
60 | | using v8::BackingStore; |
61 | | using v8::BackingStoreInitializationMode; |
62 | | using v8::CFunction; |
63 | | using v8::Context; |
64 | | using v8::EscapableHandleScope; |
65 | | using v8::FastApiCallbackOptions; |
66 | | using v8::FastOneByteString; |
67 | | using v8::FunctionCallbackInfo; |
68 | | using v8::Global; |
69 | | using v8::HandleScope; |
70 | | using v8::Int32; |
71 | | using v8::Integer; |
72 | | using v8::Isolate; |
73 | | using v8::Just; |
74 | | using v8::Local; |
75 | | using v8::Maybe; |
76 | | using v8::MaybeLocal; |
77 | | using v8::Nothing; |
78 | | using v8::Number; |
79 | | using v8::Object; |
80 | | using v8::SharedArrayBuffer; |
81 | | using v8::String; |
82 | | using v8::Uint32; |
83 | | using v8::Uint32Array; |
84 | | using v8::Uint8Array; |
85 | | using v8::Value; |
86 | | |
87 | | namespace { |
88 | | |
89 | | class CallbackInfo : public Cleanable { |
90 | | public: |
91 | | static inline Local<ArrayBuffer> CreateTrackedArrayBuffer( |
92 | | Environment* env, |
93 | | char* data, |
94 | | size_t length, |
95 | | FreeCallback callback, |
96 | | void* hint); |
97 | | |
98 | | CallbackInfo(const CallbackInfo&) = delete; |
99 | | CallbackInfo& operator=(const CallbackInfo&) = delete; |
100 | | |
101 | | private: |
102 | | void Clean(); |
103 | | inline void OnBackingStoreFree(); |
104 | | inline void CallAndResetCallback(); |
105 | | inline CallbackInfo(Environment* env, |
106 | | FreeCallback callback, |
107 | | char* data, |
108 | | void* hint); |
109 | | Global<ArrayBuffer> persistent_; |
110 | | Mutex mutex_; // Protects callback_. |
111 | | FreeCallback callback_; |
112 | | char* const data_; |
113 | | void* const hint_; |
114 | | Environment* const env_; |
115 | | }; |
116 | | |
117 | | Local<ArrayBuffer> CallbackInfo::CreateTrackedArrayBuffer( |
118 | | Environment* env, |
119 | | char* data, |
120 | | size_t length, |
121 | | FreeCallback callback, |
122 | 0 | void* hint) { |
123 | 0 | CHECK_NOT_NULL(callback); |
124 | 0 | CHECK_IMPLIES(data == nullptr, length == 0); |
125 | | |
126 | 0 | CallbackInfo* self = new CallbackInfo(env, callback, data, hint); |
127 | 0 | std::unique_ptr<BackingStore> bs = |
128 | 0 | ArrayBuffer::NewBackingStore(data, length, [](void*, size_t, void* arg) { |
129 | 0 | static_cast<CallbackInfo*>(arg)->OnBackingStoreFree(); |
130 | 0 | }, self); |
131 | 0 | Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), std::move(bs)); |
132 | | |
133 | | // V8 simply ignores the BackingStore deleter callback if data == nullptr, |
134 | | // but our API contract requires it being called. |
135 | 0 | if (data == nullptr) { |
136 | 0 | ab->Detach(Local<Value>()).Check(); |
137 | 0 | self->OnBackingStoreFree(); // This calls `callback` asynchronously. |
138 | 0 | } else { |
139 | | // Store the ArrayBuffer so that we can detach it later. |
140 | 0 | self->persistent_.Reset(env->isolate(), ab); |
141 | 0 | self->persistent_.SetWeak(); |
142 | 0 | } |
143 | |
|
144 | 0 | return ab; |
145 | 0 | } |
146 | | |
147 | | |
148 | | CallbackInfo::CallbackInfo(Environment* env, |
149 | | FreeCallback callback, |
150 | | char* data, |
151 | | void* hint) |
152 | 0 | : callback_(callback), |
153 | 0 | data_(data), |
154 | 0 | hint_(hint), |
155 | 0 | env_(env) { |
156 | 0 | env->cleanable_queue()->PushFront(this); |
157 | 0 | env->external_memory_accounter()->Increase(env->isolate(), sizeof(*this)); |
158 | 0 | } |
159 | | |
160 | 0 | void CallbackInfo::Clean() { |
161 | 0 | { |
162 | 0 | HandleScope handle_scope(env_->isolate()); |
163 | 0 | Local<ArrayBuffer> ab = persistent_.Get(env_->isolate()); |
164 | 0 | if (!ab.IsEmpty() && ab->IsDetachable()) { |
165 | 0 | ab->Detach(Local<Value>()).Check(); |
166 | 0 | persistent_.Reset(); |
167 | 0 | } |
168 | 0 | } |
169 | | |
170 | | // Call the callback in this case, but don't delete `this` yet because the |
171 | | // BackingStore deleter callback will do so later. |
172 | 0 | CallAndResetCallback(); |
173 | 0 | } |
174 | | |
175 | 0 | void CallbackInfo::CallAndResetCallback() { |
176 | 0 | FreeCallback callback; |
177 | 0 | { |
178 | 0 | Mutex::ScopedLock lock(mutex_); |
179 | 0 | callback = callback_; |
180 | 0 | callback_ = nullptr; |
181 | 0 | } |
182 | 0 | if (callback != nullptr) { |
183 | | // Clean up all Environment-related state and run the callback. |
184 | 0 | cleanable_queue_.Remove(); |
185 | 0 | env_->external_memory_accounter()->Decrease(env_->isolate(), sizeof(*this)); |
186 | |
|
187 | 0 | callback(data_, hint_); |
188 | 0 | } |
189 | 0 | } |
190 | | |
191 | 0 | void CallbackInfo::OnBackingStoreFree() { |
192 | | // This method should always release the memory for `this`. |
193 | 0 | std::unique_ptr<CallbackInfo> self { this }; |
194 | 0 | Mutex::ScopedLock lock(mutex_); |
195 | | // If callback_ == nullptr, that means that the callback has already run from |
196 | | // the cleanup hook, and there is nothing left to do here besides to clean |
197 | | // up the memory involved. In particular, the underlying `Environment` may |
198 | | // be gone at this point, so don’t attempt to call SetImmediateThreadsafe(). |
199 | 0 | if (callback_ == nullptr) return; |
200 | | |
201 | 0 | env_->SetImmediateThreadsafe([self = std::move(self)](Environment* env) { |
202 | 0 | CHECK_EQ(self->env_, env); // Consistency check. |
203 | | |
204 | 0 | self->CallAndResetCallback(); |
205 | 0 | }); |
206 | 0 | } |
207 | | |
208 | | |
209 | | // Parse index for external array data. An empty Maybe indicates |
210 | | // a pending exception. `false` indicates that the index is out-of-bounds. |
211 | | inline MUST_USE_RESULT Maybe<bool> ParseArrayIndex(Environment* env, |
212 | | Local<Value> arg, |
213 | | size_t def, |
214 | 70 | size_t* ret) { |
215 | 70 | if (arg->IsUndefined()) { |
216 | 0 | *ret = def; |
217 | 0 | return Just(true); |
218 | 0 | } |
219 | | |
220 | 70 | int64_t tmp_i; |
221 | 70 | if (!arg->IntegerValue(env->context()).To(&tmp_i)) |
222 | 0 | return Nothing<bool>(); |
223 | | |
224 | 70 | if (tmp_i < 0) |
225 | 0 | return Just(false); |
226 | | |
227 | | // Check that the result fits in a size_t. |
228 | | // coverity[pointless_expression] |
229 | 70 | if (static_cast<uint64_t>(tmp_i) > std::numeric_limits<size_t>::max()) |
230 | 0 | return Just(false); |
231 | | |
232 | 70 | *ret = static_cast<size_t>(tmp_i); |
233 | 70 | return Just(true); |
234 | 70 | } |
235 | | |
236 | | } // anonymous namespace |
237 | | |
238 | | // Buffer methods |
239 | | |
240 | 35 | bool HasInstance(Local<Value> val) { |
241 | 35 | return val->IsArrayBufferView(); |
242 | 35 | } |
243 | | |
244 | | |
245 | 0 | bool HasInstance(Local<Object> obj) { |
246 | 0 | return obj->IsArrayBufferView(); |
247 | 0 | } |
248 | | |
249 | | |
250 | 0 | char* Data(Local<Value> val) { |
251 | 0 | CHECK(val->IsArrayBufferView()); |
252 | 0 | Local<ArrayBufferView> ui = val.As<ArrayBufferView>(); |
253 | 0 | return static_cast<char*>(ui->Buffer()->Data()) + ui->ByteOffset(); |
254 | 0 | } |
255 | | |
256 | | |
257 | 0 | char* Data(Local<Object> obj) { |
258 | 0 | return Data(obj.As<Value>()); |
259 | 0 | } |
260 | | |
261 | | |
262 | 0 | size_t Length(Local<Value> val) { |
263 | 0 | CHECK(val->IsArrayBufferView()); |
264 | 0 | Local<ArrayBufferView> ui = val.As<ArrayBufferView>(); |
265 | 0 | return ui->ByteLength(); |
266 | 0 | } |
267 | | |
268 | | |
269 | 0 | size_t Length(Local<Object> obj) { |
270 | 0 | CHECK(obj->IsArrayBufferView()); |
271 | 0 | Local<ArrayBufferView> ui = obj.As<ArrayBufferView>(); |
272 | 0 | return ui->ByteLength(); |
273 | 0 | } |
274 | | |
275 | | |
276 | | MaybeLocal<Uint8Array> New(Environment* env, |
277 | | Local<ArrayBuffer> ab, |
278 | | size_t byte_offset, |
279 | 0 | size_t length) { |
280 | 0 | CHECK(!env->buffer_prototype_object().IsEmpty()); |
281 | 0 | Local<Uint8Array> ui = Uint8Array::New(ab, byte_offset, length); |
282 | 0 | if (ui->SetPrototypeV2(env->context(), env->buffer_prototype_object()) |
283 | 0 | .IsNothing()) { |
284 | 0 | return MaybeLocal<Uint8Array>(); |
285 | 0 | } |
286 | 0 | return ui; |
287 | 0 | } |
288 | | |
289 | | MaybeLocal<Uint8Array> New(Isolate* isolate, |
290 | | Local<ArrayBuffer> ab, |
291 | | size_t byte_offset, |
292 | 0 | size_t length) { |
293 | 0 | Environment* env = Environment::GetCurrent(isolate); |
294 | 0 | if (env == nullptr) { |
295 | 0 | THROW_ERR_BUFFER_CONTEXT_NOT_AVAILABLE(isolate); |
296 | 0 | return MaybeLocal<Uint8Array>(); |
297 | 0 | } |
298 | 0 | return New(env, ab, byte_offset, length); |
299 | 0 | } |
300 | | |
301 | | |
302 | | MaybeLocal<Object> New(Isolate* isolate, |
303 | | Local<String> string, |
304 | 0 | enum encoding enc) { |
305 | 0 | EscapableHandleScope scope(isolate); |
306 | |
|
307 | 0 | size_t length; |
308 | 0 | if (!StringBytes::Size(isolate, string, enc).To(&length)) |
309 | 0 | return Local<Object>(); |
310 | 0 | size_t actual = 0; |
311 | 0 | std::unique_ptr<BackingStore> store; |
312 | |
|
313 | 0 | if (length > 0) { |
314 | 0 | store = ArrayBuffer::NewBackingStore(isolate, length); |
315 | |
|
316 | 0 | if (!store) [[unlikely]] { |
317 | 0 | THROW_ERR_MEMORY_ALLOCATION_FAILED(isolate); |
318 | 0 | return Local<Object>(); |
319 | 0 | } |
320 | | |
321 | 0 | actual = StringBytes::Write( |
322 | 0 | isolate, |
323 | 0 | static_cast<char*>(store->Data()), |
324 | 0 | length, |
325 | 0 | string, |
326 | 0 | enc); |
327 | 0 | CHECK(actual <= length); |
328 | | |
329 | 0 | if (actual > 0) [[likely]] { |
330 | 0 | if (actual < length) { |
331 | 0 | std::unique_ptr<BackingStore> old_store = std::move(store); |
332 | 0 | store = ArrayBuffer::NewBackingStore( |
333 | 0 | isolate, actual, BackingStoreInitializationMode::kUninitialized); |
334 | 0 | memcpy(store->Data(), old_store->Data(), actual); |
335 | 0 | } |
336 | 0 | Local<ArrayBuffer> buf = ArrayBuffer::New(isolate, std::move(store)); |
337 | 0 | Local<Object> obj; |
338 | 0 | if (!New(isolate, buf, 0, actual).ToLocal(&obj)) [[unlikely]] { |
339 | 0 | return {}; |
340 | 0 | } |
341 | 0 | return scope.Escape(obj); |
342 | 0 | } |
343 | 0 | } |
344 | | |
345 | 0 | return scope.EscapeMaybe(New(isolate, 0)); |
346 | 0 | } |
347 | | |
348 | | |
349 | 0 | MaybeLocal<Object> New(Isolate* isolate, size_t length) { |
350 | 0 | EscapableHandleScope handle_scope(isolate); |
351 | 0 | Local<Object> obj; |
352 | 0 | Environment* env = Environment::GetCurrent(isolate); |
353 | 0 | if (env == nullptr) { |
354 | 0 | THROW_ERR_BUFFER_CONTEXT_NOT_AVAILABLE(isolate); |
355 | 0 | return MaybeLocal<Object>(); |
356 | 0 | } |
357 | 0 | if (Buffer::New(env, length).ToLocal(&obj)) |
358 | 0 | return handle_scope.Escape(obj); |
359 | 0 | return Local<Object>(); |
360 | 0 | } |
361 | | |
362 | | |
363 | 0 | MaybeLocal<Object> New(Environment* env, size_t length) { |
364 | 0 | Isolate* isolate(env->isolate()); |
365 | 0 | EscapableHandleScope scope(isolate); |
366 | | |
367 | | // V8 currently only allows a maximum Typed Array index of max Smi. |
368 | 0 | if (length > kMaxLength) { |
369 | 0 | isolate->ThrowException(ERR_BUFFER_TOO_LARGE(isolate)); |
370 | 0 | return Local<Object>(); |
371 | 0 | } |
372 | | |
373 | 0 | Local<ArrayBuffer> ab; |
374 | 0 | { |
375 | 0 | std::unique_ptr<BackingStore> bs = ArrayBuffer::NewBackingStore( |
376 | 0 | isolate, length, BackingStoreInitializationMode::kUninitialized); |
377 | |
|
378 | 0 | CHECK(bs); |
379 | | |
380 | 0 | ab = ArrayBuffer::New(isolate, std::move(bs)); |
381 | 0 | } |
382 | | |
383 | 0 | MaybeLocal<Object> obj = |
384 | 0 | New(env, ab, 0, ab->ByteLength()) |
385 | 0 | .FromMaybe(Local<Uint8Array>()); |
386 | |
|
387 | 0 | return scope.EscapeMaybe(obj); |
388 | 0 | } |
389 | | |
390 | | |
391 | 0 | MaybeLocal<Object> Copy(Isolate* isolate, const char* data, size_t length) { |
392 | 0 | EscapableHandleScope handle_scope(isolate); |
393 | 0 | Environment* env = Environment::GetCurrent(isolate); |
394 | 0 | if (env == nullptr) { |
395 | 0 | THROW_ERR_BUFFER_CONTEXT_NOT_AVAILABLE(isolate); |
396 | 0 | return MaybeLocal<Object>(); |
397 | 0 | } |
398 | 0 | Local<Object> obj; |
399 | 0 | if (Buffer::Copy(env, data, length).ToLocal(&obj)) |
400 | 0 | return handle_scope.Escape(obj); |
401 | 0 | return Local<Object>(); |
402 | 0 | } |
403 | | |
404 | | |
405 | 0 | MaybeLocal<Object> Copy(Environment* env, const char* data, size_t length) { |
406 | 0 | Isolate* isolate(env->isolate()); |
407 | 0 | EscapableHandleScope scope(isolate); |
408 | | |
409 | | // V8 currently only allows a maximum Typed Array index of max Smi. |
410 | 0 | if (length > kMaxLength) { |
411 | 0 | isolate->ThrowException(ERR_BUFFER_TOO_LARGE(isolate)); |
412 | 0 | return Local<Object>(); |
413 | 0 | } |
414 | | |
415 | 0 | std::unique_ptr<BackingStore> bs = ArrayBuffer::NewBackingStore( |
416 | 0 | isolate, length, BackingStoreInitializationMode::kUninitialized); |
417 | |
|
418 | 0 | CHECK(bs); |
419 | | |
420 | 0 | if (length > 0) memcpy(bs->Data(), data, length); |
421 | |
|
422 | 0 | Local<ArrayBuffer> ab = ArrayBuffer::New(isolate, std::move(bs)); |
423 | |
|
424 | 0 | MaybeLocal<Object> obj = |
425 | 0 | New(env, ab, 0, ab->ByteLength()) |
426 | 0 | .FromMaybe(Local<Uint8Array>()); |
427 | |
|
428 | 0 | return scope.EscapeMaybe(obj); |
429 | 0 | } |
430 | | |
431 | | |
432 | | MaybeLocal<Object> New(Isolate* isolate, |
433 | | char* data, |
434 | | size_t length, |
435 | | FreeCallback callback, |
436 | 0 | void* hint) { |
437 | 0 | EscapableHandleScope handle_scope(isolate); |
438 | 0 | Environment* env = Environment::GetCurrent(isolate); |
439 | 0 | if (env == nullptr) { |
440 | 0 | callback(data, hint); |
441 | 0 | THROW_ERR_BUFFER_CONTEXT_NOT_AVAILABLE(isolate); |
442 | 0 | return MaybeLocal<Object>(); |
443 | 0 | } |
444 | 0 | return handle_scope.EscapeMaybe( |
445 | 0 | Buffer::New(env, data, length, callback, hint)); |
446 | 0 | } |
447 | | |
448 | | |
449 | | MaybeLocal<Object> New(Environment* env, |
450 | | char* data, |
451 | | size_t length, |
452 | | FreeCallback callback, |
453 | 0 | void* hint) { |
454 | 0 | EscapableHandleScope scope(env->isolate()); |
455 | |
|
456 | 0 | if (length > kMaxLength) { |
457 | 0 | env->isolate()->ThrowException(ERR_BUFFER_TOO_LARGE(env->isolate())); |
458 | 0 | callback(data, hint); |
459 | 0 | return Local<Object>(); |
460 | 0 | } |
461 | | |
462 | 0 | Local<ArrayBuffer> ab = |
463 | 0 | CallbackInfo::CreateTrackedArrayBuffer(env, data, length, callback, hint); |
464 | 0 | if (ab->SetPrivate(env->context(), |
465 | 0 | env->untransferable_object_private_symbol(), |
466 | 0 | True(env->isolate())).IsNothing()) { |
467 | 0 | return Local<Object>(); |
468 | 0 | } |
469 | 0 | MaybeLocal<Uint8Array> maybe_ui = Buffer::New(env, ab, 0, length); |
470 | |
|
471 | 0 | Local<Uint8Array> ui; |
472 | 0 | if (!maybe_ui.ToLocal(&ui)) |
473 | 0 | return MaybeLocal<Object>(); |
474 | | |
475 | 0 | return scope.Escape(ui); |
476 | 0 | } |
477 | | |
478 | | // Warning: This function needs `data` to be allocated with malloc() and not |
479 | | // necessarily isolate's ArrayBuffer::Allocator. |
480 | 0 | MaybeLocal<Object> New(Isolate* isolate, char* data, size_t length) { |
481 | 0 | EscapableHandleScope handle_scope(isolate); |
482 | 0 | Environment* env = Environment::GetCurrent(isolate); |
483 | 0 | if (env == nullptr) { |
484 | 0 | free(data); |
485 | 0 | THROW_ERR_BUFFER_CONTEXT_NOT_AVAILABLE(isolate); |
486 | 0 | return MaybeLocal<Object>(); |
487 | 0 | } |
488 | 0 | Local<Object> obj; |
489 | 0 | if (Buffer::New(env, data, length).ToLocal(&obj)) |
490 | 0 | return handle_scope.Escape(obj); |
491 | 0 | return Local<Object>(); |
492 | 0 | } |
493 | | |
494 | | // The contract for this function is that `data` is allocated with malloc() |
495 | | // and not necessarily isolate's ArrayBuffer::Allocator. |
496 | | MaybeLocal<Object> New(Environment* env, |
497 | | char* data, |
498 | 0 | size_t length) { |
499 | 0 | if (length > 0) { |
500 | 0 | CHECK_NOT_NULL(data); |
501 | | // V8 currently only allows a maximum Typed Array index of max Smi. |
502 | 0 | if (length > kMaxLength) { |
503 | 0 | Isolate* isolate(env->isolate()); |
504 | 0 | isolate->ThrowException(ERR_BUFFER_TOO_LARGE(isolate)); |
505 | 0 | free(data); |
506 | 0 | return Local<Object>(); |
507 | 0 | } |
508 | 0 | } |
509 | | |
510 | | #if defined(V8_ENABLE_SANDBOX) |
511 | | // When v8 sandbox is enabled, external backing stores are not supported |
512 | | // since all arraybuffer allocations are expected to be done by the isolate. |
513 | | // Since this violates the contract of this function, let's free the data and |
514 | | // throw an error. |
515 | | free(data); |
516 | | THROW_ERR_OPERATION_FAILED( |
517 | | env->isolate(), |
518 | | "Wrapping external data is not supported when the v8 sandbox is enabled"); |
519 | | return MaybeLocal<Object>(); |
520 | | #else |
521 | 0 | EscapableHandleScope handle_scope(env->isolate()); |
522 | |
|
523 | 0 | auto free_callback = [](void* data, size_t length, void* deleter_data) { |
524 | 0 | free(data); |
525 | 0 | }; |
526 | 0 | std::unique_ptr<BackingStore> bs = |
527 | 0 | ArrayBuffer::NewBackingStore(data, length, free_callback, nullptr); |
528 | |
|
529 | 0 | Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), std::move(bs)); |
530 | |
|
531 | 0 | Local<Object> obj; |
532 | 0 | if (Buffer::New(env, ab, 0, length).ToLocal(&obj)) |
533 | 0 | return handle_scope.Escape(obj); |
534 | 0 | return Local<Object>(); |
535 | 0 | #endif |
536 | 0 | } |
537 | | |
538 | | namespace { |
539 | | |
540 | | template <encoding encoding> |
541 | 0 | void StringSlice(const FunctionCallbackInfo<Value>& args) { |
542 | 0 | Environment* env = Environment::GetCurrent(args); |
543 | 0 | Isolate* isolate = env->isolate(); |
544 | |
|
545 | 0 | THROW_AND_RETURN_UNLESS_BUFFER(env, args.This()); |
546 | 0 | ArrayBufferViewContents<char> buffer(args.This()); |
547 | |
|
548 | 0 | if (buffer.length() == 0) |
549 | 0 | return args.GetReturnValue().SetEmptyString(); |
550 | | |
551 | 0 | size_t start = 0; |
552 | 0 | size_t end = 0; |
553 | 0 | THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[0], 0, &start)); |
554 | 0 | THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[1], buffer.length(), &end)); |
555 | 0 | if (end < start) end = start; |
556 | 0 | THROW_AND_RETURN_IF_OOB(Just(end <= buffer.length())); |
557 | 0 | size_t length = end - start; |
558 | |
|
559 | 0 | Local<Value> ret; |
560 | 0 | if (StringBytes::Encode(isolate, buffer.data() + start, length, encoding) |
561 | 0 | .ToLocal(&ret)) { |
562 | 0 | args.GetReturnValue().Set(ret); |
563 | 0 | } |
564 | 0 | } Unexecuted instantiation: node_buffer.cc:void node::Buffer::(anonymous namespace)::StringSlice<(node::encoding)0>(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: node_buffer.cc:void node::Buffer::(anonymous namespace)::StringSlice<(node::encoding)2>(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: node_buffer.cc:void node::Buffer::(anonymous namespace)::StringSlice<(node::encoding)7>(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: node_buffer.cc:void node::Buffer::(anonymous namespace)::StringSlice<(node::encoding)4>(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: node_buffer.cc:void node::Buffer::(anonymous namespace)::StringSlice<(node::encoding)5>(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: node_buffer.cc:void node::Buffer::(anonymous namespace)::StringSlice<(node::encoding)3>(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: node_buffer.cc:void node::Buffer::(anonymous namespace)::StringSlice<(node::encoding)1>(v8::FunctionCallbackInfo<v8::Value> const&) |
565 | | |
566 | | void CopyImpl(Local<Value> source_obj, |
567 | | Local<Value> target_obj, |
568 | | const uint32_t target_start, |
569 | | const uint32_t source_start, |
570 | 0 | const uint32_t to_copy) { |
571 | 0 | ArrayBufferViewContents<char> source(source_obj); |
572 | 0 | SPREAD_BUFFER_ARG(target_obj, target); |
573 | |
|
574 | 0 | memmove(target_data + target_start, source.data() + source_start, to_copy); |
575 | 0 | } |
576 | | |
577 | | // Assume caller has properly validated args. |
578 | 0 | void SlowCopy(const FunctionCallbackInfo<Value>& args) { |
579 | 0 | Local<Value> source_obj = args[0]; |
580 | 0 | Local<Value> target_obj = args[1]; |
581 | 0 | const uint32_t target_start = args[2].As<Uint32>()->Value(); |
582 | 0 | const uint32_t source_start = args[3].As<Uint32>()->Value(); |
583 | 0 | const uint32_t to_copy = args[4].As<Uint32>()->Value(); |
584 | |
|
585 | 0 | CopyImpl(source_obj, target_obj, target_start, source_start, to_copy); |
586 | |
|
587 | 0 | args.GetReturnValue().Set(to_copy); |
588 | 0 | } |
589 | | |
590 | | // Assume caller has properly validated args. |
591 | | uint32_t FastCopy(Local<Value> receiver, |
592 | | Local<Value> source_obj, |
593 | | Local<Value> target_obj, |
594 | | uint32_t target_start, |
595 | | uint32_t source_start, |
596 | | uint32_t to_copy, |
597 | | // NOLINTNEXTLINE(runtime/references) |
598 | 0 | FastApiCallbackOptions& options) { |
599 | 0 | HandleScope scope(options.isolate); |
600 | |
|
601 | 0 | CopyImpl(source_obj, target_obj, target_start, source_start, to_copy); |
602 | |
|
603 | 0 | return to_copy; |
604 | 0 | } |
605 | | |
606 | | static CFunction fast_copy(CFunction::Make(FastCopy)); |
607 | | |
608 | 0 | void Fill(const FunctionCallbackInfo<Value>& args) { |
609 | 0 | Environment* env = Environment::GetCurrent(args); |
610 | 0 | Local<Context> ctx = env->context(); |
611 | |
|
612 | 0 | THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); |
613 | 0 | SPREAD_BUFFER_ARG(args[0], ts_obj); |
614 | |
|
615 | 0 | size_t start = 0; |
616 | 0 | THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[2], 0, &start)); |
617 | 0 | size_t end; |
618 | 0 | THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[3], 0, &end)); |
619 | | |
620 | 0 | size_t fill_length = end - start; |
621 | 0 | Local<String> str_obj; |
622 | 0 | size_t str_length; |
623 | 0 | enum encoding enc; |
624 | | |
625 | | // OOB Check. Throw the error in JS. |
626 | 0 | if (start > end || fill_length + start > ts_obj_length) |
627 | 0 | return args.GetReturnValue().Set(-2); |
628 | | |
629 | | // First check if Buffer has been passed. |
630 | 0 | if (Buffer::HasInstance(args[1])) { |
631 | 0 | SPREAD_BUFFER_ARG(args[1], fill_obj); |
632 | 0 | str_length = fill_obj_length; |
633 | 0 | memcpy( |
634 | 0 | ts_obj_data + start, fill_obj_data, std::min(str_length, fill_length)); |
635 | 0 | goto start_fill; |
636 | 0 | } |
637 | | |
638 | | // Then coerce everything that's not a string. |
639 | 0 | if (!args[1]->IsString()) { |
640 | 0 | uint32_t val; |
641 | 0 | if (!args[1]->Uint32Value(ctx).To(&val)) return; |
642 | 0 | int value = val & 255; |
643 | 0 | memset(ts_obj_data + start, value, fill_length); |
644 | 0 | return; |
645 | 0 | } |
646 | | |
647 | 0 | if (!args[1]->ToString(env->context()).ToLocal(&str_obj)) { |
648 | 0 | return; |
649 | 0 | } |
650 | 0 | enc = ParseEncoding(env->isolate(), args[4], UTF8); |
651 | | |
652 | | // Can't use StringBytes::Write() in all cases. For example if attempting |
653 | | // to write a two byte character into a one byte Buffer. |
654 | 0 | if (enc == UTF8) { |
655 | 0 | str_length = str_obj->Utf8LengthV2(env->isolate()); |
656 | 0 | node::Utf8Value str(env->isolate(), args[1]); |
657 | 0 | memcpy(ts_obj_data + start, *str, std::min(str_length, fill_length)); |
658 | |
|
659 | 0 | } else if (enc == UCS2) { |
660 | 0 | str_length = str_obj->Length() * sizeof(uint16_t); |
661 | 0 | node::TwoByteValue str(env->isolate(), args[1]); |
662 | | if constexpr (IsBigEndian()) |
663 | | CHECK(nbytes::SwapBytes16(reinterpret_cast<char*>(&str[0]), str_length)); |
664 | |
|
665 | 0 | memcpy(ts_obj_data + start, *str, std::min(str_length, fill_length)); |
666 | |
|
667 | 0 | } else { |
668 | | // Write initial String to Buffer, then use that memory to copy remainder |
669 | | // of string. Correct the string length for cases like HEX where less than |
670 | | // the total string length is written. |
671 | 0 | str_length = StringBytes::Write( |
672 | 0 | env->isolate(), ts_obj_data + start, fill_length, str_obj, enc); |
673 | 0 | } |
674 | |
|
675 | 0 | start_fill: |
676 | |
|
677 | 0 | if (str_length >= fill_length) |
678 | 0 | return; |
679 | | |
680 | | // If str_length is zero, then either an empty buffer was provided, or Write() |
681 | | // indicated that no bytes could be written. If no bytes could be written, |
682 | | // then return -1 because the fill value is invalid. This will trigger a throw |
683 | | // in JavaScript. Silently failing should be avoided because it can lead to |
684 | | // buffers with unexpected contents. |
685 | 0 | if (str_length == 0) |
686 | 0 | return args.GetReturnValue().Set(-1); |
687 | | |
688 | 0 | size_t in_there = str_length; |
689 | 0 | char* ptr = ts_obj_data + start + str_length; |
690 | |
|
691 | 0 | while (in_there < fill_length - in_there) { |
692 | 0 | memcpy(ptr, ts_obj_data + start, in_there); |
693 | 0 | ptr += in_there; |
694 | 0 | in_there *= 2; |
695 | 0 | } |
696 | |
|
697 | 0 | if (in_there < fill_length) { |
698 | 0 | memcpy(ptr, ts_obj_data + start, fill_length - in_there); |
699 | 0 | } |
700 | 0 | } |
701 | | |
702 | | |
703 | | template <encoding encoding> |
704 | 0 | void StringWrite(const FunctionCallbackInfo<Value>& args) { |
705 | 0 | Environment* env = Environment::GetCurrent(args); |
706 | |
|
707 | 0 | THROW_AND_RETURN_UNLESS_BUFFER(env, args.This()); |
708 | 0 | SPREAD_BUFFER_ARG(args.This(), ts_obj); |
709 | |
|
710 | 0 | THROW_AND_RETURN_IF_NOT_STRING(env, args[0], "argument"); |
711 | | |
712 | 0 | Local<String> str; |
713 | 0 | if (!args[0]->ToString(env->context()).ToLocal(&str)) { |
714 | 0 | return; |
715 | 0 | } |
716 | | |
717 | 0 | size_t offset = 0; |
718 | 0 | size_t max_length = 0; |
719 | |
|
720 | 0 | THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[1], 0, &offset)); |
721 | 0 | if (offset > ts_obj_length) { |
722 | 0 | return node::THROW_ERR_BUFFER_OUT_OF_BOUNDS( |
723 | 0 | env, "\"offset\" is outside of buffer bounds"); |
724 | 0 | } |
725 | | |
726 | 0 | THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[2], ts_obj_length - offset, |
727 | 0 | &max_length)); |
728 | | |
729 | 0 | max_length = std::min(ts_obj_length - offset, max_length); |
730 | |
|
731 | 0 | if (max_length == 0) |
732 | 0 | return args.GetReturnValue().Set(0); |
733 | | |
734 | 0 | uint32_t written = StringBytes::Write( |
735 | 0 | env->isolate(), ts_obj_data + offset, max_length, str, encoding); |
736 | 0 | args.GetReturnValue().Set(written); |
737 | 0 | } Unexecuted instantiation: node_buffer.cc:void node::Buffer::(anonymous namespace)::StringWrite<(node::encoding)2>(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: node_buffer.cc:void node::Buffer::(anonymous namespace)::StringWrite<(node::encoding)7>(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: node_buffer.cc:void node::Buffer::(anonymous namespace)::StringWrite<(node::encoding)5>(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: node_buffer.cc:void node::Buffer::(anonymous namespace)::StringWrite<(node::encoding)3>(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: node_buffer.cc:void node::Buffer::(anonymous namespace)::StringWrite<(node::encoding)0>(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: node_buffer.cc:void node::Buffer::(anonymous namespace)::StringWrite<(node::encoding)4>(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: node_buffer.cc:void node::Buffer::(anonymous namespace)::StringWrite<(node::encoding)1>(v8::FunctionCallbackInfo<v8::Value> const&) |
738 | | |
739 | 0 | void SlowByteLengthUtf8(const FunctionCallbackInfo<Value>& args) { |
740 | 0 | CHECK(args[0]->IsString()); |
741 | | |
742 | | // Fast case: avoid StringBytes on UTF8 string. Jump to v8. |
743 | 0 | size_t result = args[0].As<String>()->Utf8LengthV2(args.GetIsolate()); |
744 | 0 | args.GetReturnValue().Set(static_cast<uint64_t>(result)); |
745 | 0 | } |
746 | | |
747 | | uint32_t FastByteLengthUtf8( |
748 | | Local<Value> receiver, |
749 | | Local<Value> sourceValue, |
750 | 0 | FastApiCallbackOptions& options) { // NOLINT(runtime/references) |
751 | 0 | TRACK_V8_FAST_API_CALL("Buffer::FastByteLengthUtf8"); |
752 | 0 | auto isolate = options.isolate; |
753 | 0 | HandleScope handleScope(isolate); |
754 | 0 | CHECK(sourceValue->IsString()); |
755 | 0 | Local<String> sourceStr = sourceValue.As<String>(); |
756 | |
|
757 | 0 | if (!sourceStr->IsExternalOneByte()) { |
758 | 0 | return sourceStr->Utf8LengthV2(isolate); |
759 | 0 | } |
760 | 0 | auto source = sourceStr->GetExternalOneByteStringResource(); |
761 | | // For short inputs, the function call overhead to simdutf is maybe |
762 | | // not worth it, reserve simdutf for long strings. |
763 | 0 | if (source->length() > 128) { |
764 | 0 | return simdutf::utf8_length_from_latin1(source->data(), source->length()); |
765 | 0 | } |
766 | | |
767 | 0 | uint32_t length = source->length(); |
768 | 0 | const auto input = reinterpret_cast<const uint8_t*>(source->data()); |
769 | |
|
770 | 0 | uint32_t answer = length; |
771 | 0 | uint32_t i = 0; |
772 | |
|
773 | 0 | auto pop = [](uint64_t v) { |
774 | 0 | return static_cast<size_t>(((v >> 7) & UINT64_C(0x0101010101010101)) * |
775 | 0 | UINT64_C(0x0101010101010101) >> |
776 | 0 | 56); |
777 | 0 | }; |
778 | |
|
779 | 0 | for (; i + 32 <= length; i += 32) { |
780 | 0 | uint64_t v; |
781 | 0 | memcpy(&v, input + i, 8); |
782 | 0 | answer += pop(v); |
783 | 0 | memcpy(&v, input + i + 8, 8); |
784 | 0 | answer += pop(v); |
785 | 0 | memcpy(&v, input + i + 16, 8); |
786 | 0 | answer += pop(v); |
787 | 0 | memcpy(&v, input + i + 24, 8); |
788 | 0 | answer += pop(v); |
789 | 0 | } |
790 | 0 | for (; i + 8 <= length; i += 8) { |
791 | 0 | uint64_t v; |
792 | 0 | memcpy(&v, input + i, 8); |
793 | 0 | answer += pop(v); |
794 | 0 | } |
795 | 0 | for (; i + 1 <= length; i += 1) { |
796 | 0 | answer += input[i] >> 7; |
797 | 0 | } |
798 | |
|
799 | 0 | return answer; |
800 | 0 | } |
801 | | |
802 | | static CFunction fast_byte_length_utf8(CFunction::Make(FastByteLengthUtf8)); |
803 | | |
804 | | // Normalize val to be an integer in the range of [1, -1] since |
805 | | // implementations of memcmp() can vary by platform. |
806 | 0 | static int normalizeCompareVal(int val, size_t a_length, size_t b_length) { |
807 | 0 | if (val == 0) { |
808 | 0 | if (a_length > b_length) |
809 | 0 | return 1; |
810 | 0 | else if (a_length < b_length) |
811 | 0 | return -1; |
812 | 0 | } else { |
813 | 0 | if (val > 0) |
814 | 0 | return 1; |
815 | 0 | else |
816 | 0 | return -1; |
817 | 0 | } |
818 | 0 | return val; |
819 | 0 | } |
820 | | |
821 | 0 | void CompareOffset(const FunctionCallbackInfo<Value> &args) { |
822 | 0 | Environment* env = Environment::GetCurrent(args); |
823 | |
|
824 | 0 | THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); |
825 | 0 | THROW_AND_RETURN_UNLESS_BUFFER(env, args[1]); |
826 | 0 | ArrayBufferViewContents<char> source(args[0]); |
827 | 0 | ArrayBufferViewContents<char> target(args[1]); |
828 | |
|
829 | 0 | size_t target_start = 0; |
830 | 0 | size_t source_start = 0; |
831 | 0 | size_t source_end = 0; |
832 | 0 | size_t target_end = 0; |
833 | |
|
834 | 0 | THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[2], 0, &target_start)); |
835 | 0 | THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[3], 0, &source_start)); |
836 | 0 | THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[4], target.length(), |
837 | 0 | &target_end)); |
838 | 0 | THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[5], source.length(), |
839 | 0 | &source_end)); |
840 | | |
841 | 0 | if (source_start > source.length()) |
842 | 0 | return THROW_ERR_OUT_OF_RANGE( |
843 | 0 | env, "The value of \"sourceStart\" is out of range."); |
844 | 0 | if (target_start > target.length()) |
845 | 0 | return THROW_ERR_OUT_OF_RANGE( |
846 | 0 | env, "The value of \"targetStart\" is out of range."); |
847 | | |
848 | 0 | CHECK_LE(source_start, source_end); |
849 | 0 | CHECK_LE(target_start, target_end); |
850 | | |
851 | 0 | size_t to_cmp = |
852 | 0 | std::min(std::min(source_end - source_start, target_end - target_start), |
853 | 0 | source.length() - source_start); |
854 | |
|
855 | 0 | int val = normalizeCompareVal(to_cmp > 0 ? |
856 | 0 | memcmp(source.data() + source_start, |
857 | 0 | target.data() + target_start, |
858 | 0 | to_cmp) : 0, |
859 | 0 | source_end - source_start, |
860 | 0 | target_end - target_start); |
861 | |
|
862 | 0 | args.GetReturnValue().Set(val); |
863 | 0 | } |
864 | | |
865 | 0 | int32_t CompareImpl(Local<Value> a_obj, Local<Value> b_obj) { |
866 | 0 | ArrayBufferViewContents<char> a(a_obj); |
867 | 0 | ArrayBufferViewContents<char> b(b_obj); |
868 | |
|
869 | 0 | size_t cmp_length = std::min(a.length(), b.length()); |
870 | |
|
871 | 0 | return normalizeCompareVal( |
872 | 0 | cmp_length > 0 ? memcmp(a.data(), b.data(), cmp_length) : 0, |
873 | 0 | a.length(), |
874 | 0 | b.length()); |
875 | 0 | } |
876 | | |
877 | 0 | void Compare(const FunctionCallbackInfo<Value> &args) { |
878 | 0 | Environment* env = Environment::GetCurrent(args); |
879 | 0 | THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); |
880 | 0 | THROW_AND_RETURN_UNLESS_BUFFER(env, args[1]); |
881 | | |
882 | 0 | int val = CompareImpl(args[0], args[1]); |
883 | |
|
884 | 0 | args.GetReturnValue().Set(val); |
885 | 0 | } |
886 | | |
887 | | int32_t FastCompare(Local<Value>, |
888 | | Local<Value> a_obj, |
889 | | Local<Value> b_obj, |
890 | | // NOLINTNEXTLINE(runtime/references) |
891 | 0 | FastApiCallbackOptions& options) { |
892 | 0 | HandleScope scope(options.isolate); |
893 | |
|
894 | 0 | return CompareImpl(a_obj, b_obj); |
895 | 0 | } |
896 | | |
897 | | static CFunction fast_compare(CFunction::Make(FastCompare)); |
898 | | |
899 | | // Computes the offset for starting an indexOf or lastIndexOf search. |
900 | | // Returns either a valid offset in [0...<length - 1>], ie inside the Buffer, |
901 | | // or -1 to signal that there is no possible match. |
902 | | int64_t IndexOfOffset(size_t length, |
903 | | int64_t offset_i64, |
904 | | int64_t needle_length, |
905 | 0 | bool is_forward) { |
906 | 0 | int64_t length_i64 = static_cast<int64_t>(length); |
907 | 0 | if (offset_i64 < 0) { |
908 | 0 | if (offset_i64 + length_i64 >= 0) { |
909 | | // Negative offsets count backwards from the end of the buffer. |
910 | 0 | return length_i64 + offset_i64; |
911 | 0 | } else if (is_forward || needle_length == 0) { |
912 | | // indexOf from before the start of the buffer: search the whole buffer. |
913 | 0 | return 0; |
914 | 0 | } else { |
915 | | // lastIndexOf from before the start of the buffer: no match. |
916 | 0 | return -1; |
917 | 0 | } |
918 | 0 | } else { |
919 | 0 | if (offset_i64 + needle_length <= length_i64) { |
920 | | // Valid positive offset. |
921 | 0 | return offset_i64; |
922 | 0 | } else if (needle_length == 0) { |
923 | | // Out of buffer bounds, but empty needle: point to end of buffer. |
924 | 0 | return length_i64; |
925 | 0 | } else if (is_forward) { |
926 | | // indexOf from past the end of the buffer: no match. |
927 | 0 | return -1; |
928 | 0 | } else { |
929 | | // lastIndexOf from past the end of the buffer: search the whole buffer. |
930 | 0 | return length_i64 - 1; |
931 | 0 | } |
932 | 0 | } |
933 | 0 | } |
934 | | |
935 | 0 | void IndexOfString(const FunctionCallbackInfo<Value>& args) { |
936 | 0 | Environment* env = Environment::GetCurrent(args); |
937 | 0 | Isolate* isolate = env->isolate(); |
938 | |
|
939 | 0 | CHECK(args[1]->IsString()); |
940 | 0 | CHECK(args[2]->IsNumber()); |
941 | 0 | CHECK(args[3]->IsInt32()); |
942 | 0 | CHECK(args[4]->IsBoolean()); |
943 | | |
944 | 0 | enum encoding enc = static_cast<enum encoding>(args[3].As<Int32>()->Value()); |
945 | |
|
946 | 0 | THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); |
947 | 0 | ArrayBufferViewContents<char> buffer(args[0]); |
948 | |
|
949 | 0 | Local<String> needle = args[1].As<String>(); |
950 | 0 | int64_t offset_i64 = args[2].As<Integer>()->Value(); |
951 | 0 | bool is_forward = args[4]->IsTrue(); |
952 | |
|
953 | 0 | const char* haystack = buffer.data(); |
954 | | // Round down to the nearest multiple of 2 in case of UCS2. |
955 | 0 | const size_t haystack_length = (enc == UCS2) ? |
956 | 0 | buffer.length() &~ 1 : buffer.length(); // NOLINT(whitespace/operators) |
957 | |
|
958 | 0 | size_t needle_length; |
959 | 0 | if (!StringBytes::Size(isolate, needle, enc).To(&needle_length)) return; |
960 | | |
961 | 0 | int64_t opt_offset = IndexOfOffset(haystack_length, |
962 | 0 | offset_i64, |
963 | 0 | needle_length, |
964 | 0 | is_forward); |
965 | |
|
966 | 0 | if (needle_length == 0) { |
967 | | // Match String#indexOf() and String#lastIndexOf() behavior. |
968 | 0 | args.GetReturnValue().Set(static_cast<double>(opt_offset)); |
969 | 0 | return; |
970 | 0 | } |
971 | | |
972 | 0 | if (haystack_length == 0) { |
973 | 0 | return args.GetReturnValue().Set(-1); |
974 | 0 | } |
975 | | |
976 | 0 | if (opt_offset <= -1) { |
977 | 0 | return args.GetReturnValue().Set(-1); |
978 | 0 | } |
979 | 0 | size_t offset = static_cast<size_t>(opt_offset); |
980 | 0 | CHECK_LT(offset, haystack_length); |
981 | 0 | if ((is_forward && needle_length + offset > haystack_length) || |
982 | 0 | needle_length > haystack_length) { |
983 | 0 | return args.GetReturnValue().Set(-1); |
984 | 0 | } |
985 | | |
986 | 0 | size_t result = haystack_length; |
987 | |
|
988 | 0 | if (enc == UCS2) { |
989 | 0 | TwoByteValue needle_value(isolate, needle); |
990 | 0 | if (haystack_length < 2 || needle_value.length() < 1) { |
991 | 0 | return args.GetReturnValue().Set(-1); |
992 | 0 | } |
993 | | |
994 | | if constexpr (IsBigEndian()) { |
995 | | StringBytes::InlineDecoder decoder; |
996 | | if (decoder.Decode(env, needle, enc).IsNothing()) return; |
997 | | const uint16_t* decoded_string = |
998 | | reinterpret_cast<const uint16_t*>(decoder.out()); |
999 | | |
1000 | | if (decoded_string == nullptr) |
1001 | | return args.GetReturnValue().Set(-1); |
1002 | | |
1003 | | result = nbytes::SearchString(reinterpret_cast<const uint16_t*>(haystack), |
1004 | | haystack_length / 2, |
1005 | | decoded_string, |
1006 | | decoder.size() / 2, |
1007 | | offset / 2, |
1008 | | is_forward); |
1009 | 0 | } else { |
1010 | 0 | result = nbytes::SearchString(reinterpret_cast<const uint16_t*>(haystack), |
1011 | 0 | haystack_length / 2, |
1012 | 0 | needle_value.out(), |
1013 | 0 | needle_value.length(), |
1014 | 0 | offset / 2, |
1015 | 0 | is_forward); |
1016 | 0 | } |
1017 | 0 | result *= 2; |
1018 | 0 | } else if (enc == UTF8) { |
1019 | 0 | Utf8Value needle_value(isolate, needle); |
1020 | 0 | if (*needle_value == nullptr) |
1021 | 0 | return args.GetReturnValue().Set(-1); |
1022 | 0 | CHECK_GE(needle_length, needle_value.length()); |
1023 | | |
1024 | 0 | result = nbytes::SearchString( |
1025 | 0 | reinterpret_cast<const uint8_t*>(haystack), |
1026 | 0 | haystack_length, |
1027 | 0 | reinterpret_cast<const uint8_t*>(needle_value.out()), |
1028 | 0 | needle_length, |
1029 | 0 | offset, |
1030 | 0 | is_forward); |
1031 | 0 | } else if (enc == LATIN1) { |
1032 | 0 | uint8_t* needle_data = node::UncheckedMalloc<uint8_t>(needle_length); |
1033 | 0 | if (needle_data == nullptr) { |
1034 | 0 | return args.GetReturnValue().Set(-1); |
1035 | 0 | } |
1036 | 0 | StringBytes::Write(isolate, |
1037 | 0 | reinterpret_cast<char*>(needle_data), |
1038 | 0 | needle_length, |
1039 | 0 | needle, |
1040 | 0 | enc); |
1041 | |
|
1042 | 0 | result = nbytes::SearchString(reinterpret_cast<const uint8_t*>(haystack), |
1043 | 0 | haystack_length, |
1044 | 0 | needle_data, |
1045 | 0 | needle_length, |
1046 | 0 | offset, |
1047 | 0 | is_forward); |
1048 | 0 | free(needle_data); |
1049 | 0 | } |
1050 | | |
1051 | 0 | args.GetReturnValue().Set( |
1052 | 0 | result == haystack_length ? -1 : static_cast<int>(result)); |
1053 | 0 | } |
1054 | | |
1055 | 0 | void IndexOfBuffer(const FunctionCallbackInfo<Value>& args) { |
1056 | 0 | CHECK(args[1]->IsObject()); |
1057 | 0 | CHECK(args[2]->IsNumber()); |
1058 | 0 | CHECK(args[3]->IsInt32()); |
1059 | 0 | CHECK(args[4]->IsBoolean()); |
1060 | | |
1061 | 0 | enum encoding enc = static_cast<enum encoding>(args[3].As<Int32>()->Value()); |
1062 | |
|
1063 | 0 | Environment* env = Environment::GetCurrent(args); |
1064 | 0 | THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); |
1065 | 0 | THROW_AND_RETURN_UNLESS_BUFFER(env, args[1]); |
1066 | 0 | ArrayBufferViewContents<char> haystack_contents(args[0]); |
1067 | 0 | ArrayBufferViewContents<char> needle_contents(args[1]); |
1068 | 0 | int64_t offset_i64 = args[2].As<Integer>()->Value(); |
1069 | 0 | bool is_forward = args[4]->IsTrue(); |
1070 | |
|
1071 | 0 | const char* haystack = haystack_contents.data(); |
1072 | 0 | const size_t haystack_length = haystack_contents.length(); |
1073 | 0 | const char* needle = needle_contents.data(); |
1074 | 0 | const size_t needle_length = needle_contents.length(); |
1075 | |
|
1076 | 0 | int64_t opt_offset = IndexOfOffset(haystack_length, |
1077 | 0 | offset_i64, |
1078 | 0 | needle_length, |
1079 | 0 | is_forward); |
1080 | |
|
1081 | 0 | if (needle_length == 0) { |
1082 | | // Match String#indexOf() and String#lastIndexOf() behavior. |
1083 | 0 | args.GetReturnValue().Set(static_cast<double>(opt_offset)); |
1084 | 0 | return; |
1085 | 0 | } |
1086 | | |
1087 | 0 | if (haystack_length == 0) { |
1088 | 0 | return args.GetReturnValue().Set(-1); |
1089 | 0 | } |
1090 | | |
1091 | 0 | if (opt_offset <= -1) { |
1092 | 0 | return args.GetReturnValue().Set(-1); |
1093 | 0 | } |
1094 | 0 | size_t offset = static_cast<size_t>(opt_offset); |
1095 | 0 | CHECK_LT(offset, haystack_length); |
1096 | 0 | if ((is_forward && needle_length + offset > haystack_length) || |
1097 | 0 | needle_length > haystack_length) { |
1098 | 0 | return args.GetReturnValue().Set(-1); |
1099 | 0 | } |
1100 | | |
1101 | 0 | size_t result = haystack_length; |
1102 | |
|
1103 | 0 | if (enc == UCS2) { |
1104 | 0 | if (haystack_length < 2 || needle_length < 2) { |
1105 | 0 | return args.GetReturnValue().Set(-1); |
1106 | 0 | } |
1107 | 0 | result = nbytes::SearchString(reinterpret_cast<const uint16_t*>(haystack), |
1108 | 0 | haystack_length / 2, |
1109 | 0 | reinterpret_cast<const uint16_t*>(needle), |
1110 | 0 | needle_length / 2, |
1111 | 0 | offset / 2, |
1112 | 0 | is_forward); |
1113 | 0 | result *= 2; |
1114 | 0 | } else { |
1115 | 0 | result = nbytes::SearchString(reinterpret_cast<const uint8_t*>(haystack), |
1116 | 0 | haystack_length, |
1117 | 0 | reinterpret_cast<const uint8_t*>(needle), |
1118 | 0 | needle_length, |
1119 | 0 | offset, |
1120 | 0 | is_forward); |
1121 | 0 | } |
1122 | | |
1123 | 0 | args.GetReturnValue().Set( |
1124 | 0 | result == haystack_length ? -1 : static_cast<int>(result)); |
1125 | 0 | } |
1126 | | |
1127 | | int32_t IndexOfNumberImpl(Local<Value> buffer_obj, |
1128 | | const uint32_t needle, |
1129 | | const int64_t offset_i64, |
1130 | 0 | const bool is_forward) { |
1131 | 0 | ArrayBufferViewContents<uint8_t> buffer(buffer_obj); |
1132 | 0 | const uint8_t* buffer_data = buffer.data(); |
1133 | 0 | const size_t buffer_length = buffer.length(); |
1134 | 0 | int64_t opt_offset = IndexOfOffset(buffer_length, offset_i64, 1, is_forward); |
1135 | 0 | if (opt_offset <= -1 || buffer_length == 0) { |
1136 | 0 | return -1; |
1137 | 0 | } |
1138 | 0 | size_t offset = static_cast<size_t>(opt_offset); |
1139 | 0 | CHECK_LT(offset, buffer_length); |
1140 | | |
1141 | 0 | const void* ptr; |
1142 | 0 | if (is_forward) { |
1143 | 0 | ptr = memchr(buffer_data + offset, needle, buffer_length - offset); |
1144 | 0 | } else { |
1145 | 0 | ptr = nbytes::stringsearch::MemrchrFill(buffer_data, needle, offset + 1); |
1146 | 0 | } |
1147 | 0 | const uint8_t* ptr_uint8 = static_cast<const uint8_t*>(ptr); |
1148 | 0 | return ptr != nullptr ? static_cast<int32_t>(ptr_uint8 - buffer_data) : -1; |
1149 | 0 | } |
1150 | | |
1151 | 0 | void SlowIndexOfNumber(const FunctionCallbackInfo<Value>& args) { |
1152 | 0 | CHECK(args[1]->IsUint32()); |
1153 | 0 | CHECK(args[2]->IsNumber()); |
1154 | 0 | CHECK(args[3]->IsBoolean()); |
1155 | | |
1156 | 0 | THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); |
1157 | | |
1158 | 0 | Local<Value> buffer_obj = args[0]; |
1159 | 0 | uint32_t needle = args[1].As<Uint32>()->Value(); |
1160 | 0 | int64_t offset_i64 = args[2].As<Integer>()->Value(); |
1161 | 0 | bool is_forward = args[3]->IsTrue(); |
1162 | |
|
1163 | 0 | args.GetReturnValue().Set( |
1164 | 0 | IndexOfNumberImpl(buffer_obj, needle, offset_i64, is_forward)); |
1165 | 0 | } |
1166 | | |
1167 | | int32_t FastIndexOfNumber(Local<Value>, |
1168 | | Local<Value> buffer_obj, |
1169 | | uint32_t needle, |
1170 | | int64_t offset_i64, |
1171 | | bool is_forward, |
1172 | | // NOLINTNEXTLINE(runtime/references) |
1173 | 0 | FastApiCallbackOptions& options) { |
1174 | 0 | HandleScope scope(options.isolate); |
1175 | 0 | return IndexOfNumberImpl(buffer_obj, needle, offset_i64, is_forward); |
1176 | 0 | } |
1177 | | |
1178 | | static CFunction fast_index_of_number(CFunction::Make(FastIndexOfNumber)); |
1179 | | |
1180 | 0 | void Swap16(const FunctionCallbackInfo<Value>& args) { |
1181 | 0 | Environment* env = Environment::GetCurrent(args); |
1182 | 0 | THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); |
1183 | 0 | SPREAD_BUFFER_ARG(args[0], ts_obj); |
1184 | 0 | CHECK(nbytes::SwapBytes16(ts_obj_data, ts_obj_length)); |
1185 | 0 | args.GetReturnValue().Set(args[0]); |
1186 | 0 | } |
1187 | | |
1188 | | |
1189 | 0 | void Swap32(const FunctionCallbackInfo<Value>& args) { |
1190 | 0 | Environment* env = Environment::GetCurrent(args); |
1191 | 0 | THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); |
1192 | 0 | SPREAD_BUFFER_ARG(args[0], ts_obj); |
1193 | 0 | CHECK(nbytes::SwapBytes32(ts_obj_data, ts_obj_length)); |
1194 | 0 | args.GetReturnValue().Set(args[0]); |
1195 | 0 | } |
1196 | | |
1197 | | |
1198 | 0 | void Swap64(const FunctionCallbackInfo<Value>& args) { |
1199 | 0 | Environment* env = Environment::GetCurrent(args); |
1200 | 0 | THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); |
1201 | 0 | SPREAD_BUFFER_ARG(args[0], ts_obj); |
1202 | 0 | CHECK(nbytes::SwapBytes64(ts_obj_data, ts_obj_length)); |
1203 | 0 | args.GetReturnValue().Set(args[0]); |
1204 | 0 | } |
1205 | | |
1206 | 0 | static void IsUtf8(const FunctionCallbackInfo<Value>& args) { |
1207 | 0 | Environment* env = Environment::GetCurrent(args); |
1208 | 0 | CHECK_EQ(args.Length(), 1); |
1209 | 0 | CHECK(args[0]->IsTypedArray() || args[0]->IsArrayBuffer() || |
1210 | 0 | args[0]->IsSharedArrayBuffer()); |
1211 | 0 | ArrayBufferViewContents<char> abv(args[0]); |
1212 | |
|
1213 | 0 | if (abv.WasDetached()) { |
1214 | 0 | return node::THROW_ERR_INVALID_STATE( |
1215 | 0 | env, "Cannot validate on a detached buffer"); |
1216 | 0 | } |
1217 | | |
1218 | 0 | args.GetReturnValue().Set(simdutf::validate_utf8(abv.data(), abv.length())); |
1219 | 0 | } |
1220 | | |
1221 | 0 | static void IsAscii(const FunctionCallbackInfo<Value>& args) { |
1222 | 0 | Environment* env = Environment::GetCurrent(args); |
1223 | 0 | CHECK_EQ(args.Length(), 1); |
1224 | 0 | CHECK(args[0]->IsTypedArray() || args[0]->IsArrayBuffer() || |
1225 | 0 | args[0]->IsSharedArrayBuffer()); |
1226 | 0 | ArrayBufferViewContents<char> abv(args[0]); |
1227 | |
|
1228 | 0 | if (abv.WasDetached()) { |
1229 | 0 | return node::THROW_ERR_INVALID_STATE( |
1230 | 0 | env, "Cannot validate on a detached buffer"); |
1231 | 0 | } |
1232 | | |
1233 | 0 | args.GetReturnValue().Set(simdutf::validate_ascii(abv.data(), abv.length())); |
1234 | 0 | } |
1235 | | |
1236 | 35 | void SetBufferPrototype(const FunctionCallbackInfo<Value>& args) { |
1237 | 35 | Realm* realm = Realm::GetCurrent(args); |
1238 | | |
1239 | | // TODO(legendecas): Remove this check once the binding supports sub-realms. |
1240 | 35 | CHECK_EQ(realm->kind(), Realm::Kind::kPrincipal); |
1241 | | |
1242 | 35 | CHECK(args[0]->IsObject()); |
1243 | 35 | Local<Object> proto = args[0].As<Object>(); |
1244 | 35 | realm->set_buffer_prototype_object(proto); |
1245 | 35 | } |
1246 | | |
1247 | 35 | void GetZeroFillToggle(const FunctionCallbackInfo<Value>& args) { |
1248 | 35 | Environment* env = Environment::GetCurrent(args); |
1249 | 35 | NodeArrayBufferAllocator* allocator = env->isolate_data()->node_allocator(); |
1250 | 35 | Local<ArrayBuffer> ab; |
1251 | | // It can be a nullptr when running inside an isolate where we |
1252 | | // do not own the ArrayBuffer allocator. |
1253 | 35 | if (allocator == nullptr || env->isolate_data()->is_building_snapshot()) { |
1254 | | // Create a dummy Uint32Array - the JS land can only toggle the C++ land |
1255 | | // setting when the allocator uses our toggle. With this the toggle in JS |
1256 | | // land results in no-ops. |
1257 | | // When building a snapshot, just use a dummy toggle as well to avoid |
1258 | | // introducing the dynamic external reference. We'll re-initialize the |
1259 | | // toggle with a real one connected to the C++ allocator after snapshot |
1260 | | // deserialization. |
1261 | | |
1262 | 35 | ab = ArrayBuffer::New(env->isolate(), sizeof(uint32_t)); |
1263 | 35 | } else { |
1264 | | // TODO(joyeecheung): save ab->GetBackingStore()->Data() in the Node.js |
1265 | | // array buffer allocator and include it into the C++ toggle while the |
1266 | | // Environment is still alive. |
1267 | 0 | uint32_t* zero_fill_field = allocator->zero_fill_field(); |
1268 | 0 | std::unique_ptr<BackingStore> backing = |
1269 | 0 | ArrayBuffer::NewBackingStore(zero_fill_field, |
1270 | 0 | sizeof(*zero_fill_field), |
1271 | 0 | [](void*, size_t, void*) {}, |
1272 | 0 | nullptr); |
1273 | 0 | ab = ArrayBuffer::New(env->isolate(), std::move(backing)); |
1274 | 0 | } |
1275 | | |
1276 | 35 | if (ab->SetPrivate(env->context(), |
1277 | 35 | env->untransferable_object_private_symbol(), |
1278 | 35 | True(env->isolate())) |
1279 | 35 | .IsNothing()) { |
1280 | 0 | return; |
1281 | 0 | } |
1282 | | |
1283 | 35 | args.GetReturnValue().Set(Uint32Array::New(ab, 0, 1)); |
1284 | 35 | } |
1285 | | |
1286 | 0 | static void Btoa(const FunctionCallbackInfo<Value>& args) { |
1287 | 0 | CHECK_EQ(args.Length(), 1); |
1288 | 0 | Environment* env = Environment::GetCurrent(args); |
1289 | 0 | THROW_AND_RETURN_IF_NOT_STRING(env, args[0], "argument"); |
1290 | | |
1291 | 0 | Local<String> input = args[0].As<String>(); |
1292 | 0 | MaybeStackBuffer<char> buffer; |
1293 | 0 | size_t written; |
1294 | |
|
1295 | 0 | if (input->IsExternalOneByte()) { // 8-bit case |
1296 | 0 | auto ext = input->GetExternalOneByteStringResource(); |
1297 | 0 | size_t expected_length = simdutf::base64_length_from_binary(ext->length()); |
1298 | 0 | buffer.AllocateSufficientStorage(expected_length + 1); |
1299 | 0 | buffer.SetLengthAndZeroTerminate(expected_length); |
1300 | 0 | written = |
1301 | 0 | simdutf::binary_to_base64(ext->data(), ext->length(), buffer.out()); |
1302 | 0 | } else if (input->IsOneByte()) { |
1303 | 0 | MaybeStackBuffer<uint8_t> stack_buf(input->Length()); |
1304 | 0 | input->WriteOneByteV2(env->isolate(), 0, input->Length(), stack_buf.out()); |
1305 | |
|
1306 | 0 | size_t expected_length = |
1307 | 0 | simdutf::base64_length_from_binary(input->Length()); |
1308 | 0 | buffer.AllocateSufficientStorage(expected_length + 1); |
1309 | 0 | buffer.SetLengthAndZeroTerminate(expected_length); |
1310 | 0 | written = |
1311 | 0 | simdutf::binary_to_base64(reinterpret_cast<const char*>(*stack_buf), |
1312 | 0 | input->Length(), |
1313 | 0 | buffer.out()); |
1314 | 0 | } else { |
1315 | 0 | TwoByteValue value(env->isolate(), input); |
1316 | 0 | MaybeStackBuffer<char> stack_buf(value.length()); |
1317 | 0 | size_t out_len = simdutf::convert_utf16_to_latin1( |
1318 | 0 | reinterpret_cast<const char16_t*>(value.out()), |
1319 | 0 | value.length(), |
1320 | 0 | stack_buf.out()); |
1321 | 0 | if (out_len == 0) { // error |
1322 | 0 | return args.GetReturnValue().Set(-1); |
1323 | 0 | } |
1324 | 0 | size_t expected_length = simdutf::base64_length_from_binary(out_len); |
1325 | 0 | buffer.AllocateSufficientStorage(expected_length + 1); |
1326 | 0 | buffer.SetLengthAndZeroTerminate(expected_length); |
1327 | 0 | written = simdutf::binary_to_base64(*stack_buf, out_len, buffer.out()); |
1328 | 0 | } |
1329 | | |
1330 | 0 | auto value = OneByteString( |
1331 | 0 | env->isolate(), reinterpret_cast<const uint8_t*>(buffer.out()), written); |
1332 | |
|
1333 | 0 | return args.GetReturnValue().Set(value); |
1334 | 0 | } |
1335 | | |
1336 | | // In case of success, the decoded string is returned. |
1337 | | // In case of error, a negative value is returned: |
1338 | | // * -1 indicates a single character remained, |
1339 | | // * -2 indicates an invalid character, |
1340 | | // * -3 indicates a possible overflow (i.e., more than 2 GB output). |
1341 | 0 | static void Atob(const FunctionCallbackInfo<Value>& args) { |
1342 | 0 | CHECK_EQ(args.Length(), 1); |
1343 | 0 | Environment* env = Environment::GetCurrent(args); |
1344 | 0 | THROW_AND_RETURN_IF_NOT_STRING(env, args[0], "argument"); |
1345 | | |
1346 | 0 | Local<String> input = args[0].As<String>(); |
1347 | 0 | MaybeStackBuffer<char> buffer; |
1348 | 0 | simdutf::result result; |
1349 | |
|
1350 | 0 | if (input->IsExternalOneByte()) { // 8-bit case |
1351 | 0 | auto ext = input->GetExternalOneByteStringResource(); |
1352 | 0 | size_t expected_length = |
1353 | 0 | simdutf::maximal_binary_length_from_base64(ext->data(), ext->length()); |
1354 | 0 | buffer.AllocateSufficientStorage(expected_length); |
1355 | 0 | buffer.SetLength(expected_length); |
1356 | 0 | result = simdutf::base64_to_binary( |
1357 | 0 | ext->data(), ext->length(), buffer.out(), simdutf::base64_default); |
1358 | 0 | } else if (input->IsOneByte()) { |
1359 | 0 | MaybeStackBuffer<uint8_t> stack_buf(input->Length()); |
1360 | 0 | input->WriteOneByteV2( |
1361 | 0 | args.GetIsolate(), 0, input->Length(), stack_buf.out()); |
1362 | 0 | const char* data = reinterpret_cast<const char*>(*stack_buf); |
1363 | 0 | size_t expected_length = |
1364 | 0 | simdutf::maximal_binary_length_from_base64(data, input->Length()); |
1365 | 0 | buffer.AllocateSufficientStorage(expected_length); |
1366 | 0 | buffer.SetLength(expected_length); |
1367 | 0 | result = simdutf::base64_to_binary(data, input->Length(), buffer.out()); |
1368 | 0 | } else { // 16-bit case |
1369 | 0 | TwoByteValue value(env->isolate(), input); |
1370 | 0 | auto data = reinterpret_cast<const char16_t*>(value.out()); |
1371 | 0 | size_t expected_length = |
1372 | 0 | simdutf::maximal_binary_length_from_base64(data, value.length()); |
1373 | 0 | buffer.AllocateSufficientStorage(expected_length); |
1374 | 0 | buffer.SetLength(expected_length); |
1375 | 0 | result = simdutf::base64_to_binary(data, value.length(), buffer.out()); |
1376 | 0 | } |
1377 | |
|
1378 | 0 | if (result.error == simdutf::error_code::SUCCESS) { |
1379 | 0 | auto value = OneByteString(env->isolate(), |
1380 | 0 | reinterpret_cast<const uint8_t*>(buffer.out()), |
1381 | 0 | result.count); |
1382 | 0 | return args.GetReturnValue().Set(value); |
1383 | 0 | } |
1384 | | |
1385 | | // Default value is: "possible overflow" |
1386 | 0 | int32_t error_code = -3; |
1387 | |
|
1388 | 0 | if (result.error == simdutf::error_code::INVALID_BASE64_CHARACTER) { |
1389 | 0 | error_code = -2; |
1390 | 0 | } else if (result.error == simdutf::error_code::BASE64_INPUT_REMAINDER) { |
1391 | 0 | error_code = -1; |
1392 | 0 | } |
1393 | |
|
1394 | 0 | args.GetReturnValue().Set(error_code); |
1395 | 0 | } |
1396 | | |
1397 | | namespace { |
1398 | | |
1399 | 0 | std::pair<void*, size_t> DecomposeBufferToParts(Local<Value> buffer) { |
1400 | 0 | void* pointer; |
1401 | 0 | size_t byte_length; |
1402 | 0 | if (buffer->IsArrayBuffer()) { |
1403 | 0 | Local<ArrayBuffer> ab = buffer.As<ArrayBuffer>(); |
1404 | 0 | pointer = ab->Data(); |
1405 | 0 | byte_length = ab->ByteLength(); |
1406 | 0 | } else if (buffer->IsSharedArrayBuffer()) { |
1407 | 0 | Local<SharedArrayBuffer> ab = buffer.As<SharedArrayBuffer>(); |
1408 | 0 | pointer = ab->Data(); |
1409 | 0 | byte_length = ab->ByteLength(); |
1410 | 0 | } else { |
1411 | 0 | UNREACHABLE(); // Caller must validate. |
1412 | 0 | } |
1413 | 0 | return {pointer, byte_length}; |
1414 | 0 | } |
1415 | | |
1416 | | } // namespace |
1417 | | |
1418 | 0 | void CopyArrayBuffer(const FunctionCallbackInfo<Value>& args) { |
1419 | | // args[0] == Destination ArrayBuffer |
1420 | | // args[1] == Destination ArrayBuffer Offset |
1421 | | // args[2] == Source ArrayBuffer |
1422 | | // args[3] == Source ArrayBuffer Offset |
1423 | | // args[4] == bytesToCopy |
1424 | |
|
1425 | 0 | CHECK(args[0]->IsArrayBuffer() || args[0]->IsSharedArrayBuffer()); |
1426 | 0 | CHECK(args[1]->IsUint32()); |
1427 | 0 | CHECK(args[2]->IsArrayBuffer() || args[2]->IsSharedArrayBuffer()); |
1428 | 0 | CHECK(args[3]->IsUint32()); |
1429 | 0 | CHECK(args[4]->IsUint32()); |
1430 | | |
1431 | 0 | void* destination; |
1432 | 0 | size_t destination_byte_length; |
1433 | 0 | std::tie(destination, destination_byte_length) = |
1434 | 0 | DecomposeBufferToParts(args[0]); |
1435 | |
|
1436 | 0 | void* source; |
1437 | 0 | size_t source_byte_length; |
1438 | 0 | std::tie(source, source_byte_length) = DecomposeBufferToParts(args[2]); |
1439 | |
|
1440 | 0 | uint32_t destination_offset = args[1].As<Uint32>()->Value(); |
1441 | 0 | uint32_t source_offset = args[3].As<Uint32>()->Value(); |
1442 | 0 | size_t bytes_to_copy = args[4].As<Uint32>()->Value(); |
1443 | |
|
1444 | 0 | CHECK_GE(destination_byte_length - destination_offset, bytes_to_copy); |
1445 | 0 | CHECK_GE(source_byte_length - source_offset, bytes_to_copy); |
1446 | | |
1447 | 0 | uint8_t* dest = static_cast<uint8_t*>(destination) + destination_offset; |
1448 | 0 | uint8_t* src = static_cast<uint8_t*>(source) + source_offset; |
1449 | 0 | memcpy(dest, src, bytes_to_copy); |
1450 | 0 | } |
1451 | | |
1452 | | template <encoding encoding> |
1453 | | uint32_t WriteOneByteString(const char* src, |
1454 | | uint32_t src_len, |
1455 | | char* dst, |
1456 | 0 | uint32_t dst_len) { |
1457 | 0 | if (dst_len == 0) { |
1458 | 0 | return 0; |
1459 | 0 | } |
1460 | | |
1461 | 0 | if constexpr (encoding == UTF8) { |
1462 | 0 | return simdutf::convert_latin1_to_utf8_safe(src, src_len, dst, dst_len); |
1463 | 0 | } else if constexpr (encoding == LATIN1 || encoding == ASCII) { |
1464 | 0 | const auto size = std::min(src_len, dst_len); |
1465 | 0 | memcpy(dst, src, size); |
1466 | 0 | return size; |
1467 | | } else { |
1468 | | // TODO(ronag): Add support for more encoding. |
1469 | | UNREACHABLE(); |
1470 | | } |
1471 | 0 | } Unexecuted instantiation: node_buffer.cc:unsigned int node::Buffer::(anonymous namespace)::WriteOneByteString<(node::encoding)0>(char const*, unsigned int, char*, unsigned int) Unexecuted instantiation: node_buffer.cc:unsigned int node::Buffer::(anonymous namespace)::WriteOneByteString<(node::encoding)4>(char const*, unsigned int, char*, unsigned int) Unexecuted instantiation: node_buffer.cc:unsigned int node::Buffer::(anonymous namespace)::WriteOneByteString<(node::encoding)1>(char const*, unsigned int, char*, unsigned int) |
1472 | | |
1473 | | template <encoding encoding> |
1474 | 35 | void SlowWriteString(const FunctionCallbackInfo<Value>& args) { |
1475 | 35 | Environment* env = Environment::GetCurrent(args); |
1476 | | |
1477 | 35 | THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); |
1478 | 70 | SPREAD_BUFFER_ARG(args[0], ts_obj); |
1479 | | |
1480 | 35 | THROW_AND_RETURN_IF_NOT_STRING(env, args[1], "argument"); |
1481 | | |
1482 | 35 | Local<String> str; |
1483 | 35 | if (!args[1]->ToString(env->context()).ToLocal(&str)) { |
1484 | 0 | return; |
1485 | 0 | } |
1486 | | |
1487 | 35 | size_t offset = 0; |
1488 | 35 | size_t max_length = 0; |
1489 | | |
1490 | 35 | THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[2], 0, &offset)); |
1491 | 35 | THROW_AND_RETURN_IF_OOB( |
1492 | 35 | ParseArrayIndex(env, args[3], ts_obj_length - offset, &max_length)); |
1493 | | |
1494 | 35 | max_length = std::min(ts_obj_length - offset, max_length); |
1495 | | |
1496 | 35 | if (max_length == 0) return args.GetReturnValue().Set(0); |
1497 | | |
1498 | 35 | uint32_t written = 0; |
1499 | | |
1500 | 35 | if ((encoding == UTF8 || encoding == LATIN1 || encoding == ASCII) && |
1501 | 35 | str->IsExternalOneByte()) { |
1502 | 0 | const auto src = str->GetExternalOneByteStringResource(); |
1503 | 0 | written = WriteOneByteString<encoding>( |
1504 | 0 | src->data(), src->length(), ts_obj_data + offset, max_length); |
1505 | 35 | } else { |
1506 | 35 | written = StringBytes::Write( |
1507 | 35 | env->isolate(), ts_obj_data + offset, max_length, str, encoding); |
1508 | 35 | } |
1509 | | |
1510 | 35 | args.GetReturnValue().Set(written); |
1511 | 35 | } Unexecuted instantiation: node_buffer.cc:void node::Buffer::(anonymous namespace)::SlowWriteString<(node::encoding)4>(v8::FunctionCallbackInfo<v8::Value> const&) node_buffer.cc:void node::Buffer::(anonymous namespace)::SlowWriteString<(node::encoding)1>(v8::FunctionCallbackInfo<v8::Value> const&) Line | Count | Source | 1474 | 35 | void SlowWriteString(const FunctionCallbackInfo<Value>& args) { | 1475 | 35 | Environment* env = Environment::GetCurrent(args); | 1476 | | | 1477 | 35 | THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); | 1478 | 70 | SPREAD_BUFFER_ARG(args[0], ts_obj); | 1479 | | | 1480 | 35 | THROW_AND_RETURN_IF_NOT_STRING(env, args[1], "argument"); | 1481 | | | 1482 | 35 | Local<String> str; | 1483 | 35 | if (!args[1]->ToString(env->context()).ToLocal(&str)) { | 1484 | 0 | return; | 1485 | 0 | } | 1486 | | | 1487 | 35 | size_t offset = 0; | 1488 | 35 | size_t max_length = 0; | 1489 | | | 1490 | 35 | THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[2], 0, &offset)); | 1491 | 35 | THROW_AND_RETURN_IF_OOB( | 1492 | 35 | ParseArrayIndex(env, args[3], ts_obj_length - offset, &max_length)); | 1493 | | | 1494 | 35 | max_length = std::min(ts_obj_length - offset, max_length); | 1495 | | | 1496 | 35 | if (max_length == 0) return args.GetReturnValue().Set(0); | 1497 | | | 1498 | 35 | uint32_t written = 0; | 1499 | | | 1500 | 35 | if ((encoding == UTF8 || encoding == LATIN1 || encoding == ASCII) && | 1501 | 35 | str->IsExternalOneByte()) { | 1502 | 0 | const auto src = str->GetExternalOneByteStringResource(); | 1503 | 0 | written = WriteOneByteString<encoding>( | 1504 | 0 | src->data(), src->length(), ts_obj_data + offset, max_length); | 1505 | 35 | } else { | 1506 | 35 | written = StringBytes::Write( | 1507 | 35 | env->isolate(), ts_obj_data + offset, max_length, str, encoding); | 1508 | 35 | } | 1509 | | | 1510 | 35 | args.GetReturnValue().Set(written); | 1511 | 35 | } |
|
1512 | | |
1513 | | template <encoding encoding> |
1514 | | uint32_t FastWriteString(Local<Value> receiver, |
1515 | | Local<Value> dst_obj, |
1516 | | const FastOneByteString& src, |
1517 | | uint32_t offset, |
1518 | | uint32_t max_length, |
1519 | | // NOLINTNEXTLINE(runtime/references) |
1520 | 0 | FastApiCallbackOptions& options) { |
1521 | | // Just a heads up... this is a v8 fast api function. The use of |
1522 | | // FastOneByteString has some caveats. Specifically, a GC occurring |
1523 | | // between the time the FastOneByteString is created and the time |
1524 | | // we use it below can cause the FastOneByteString to become invalid |
1525 | | // and produce garbage data. This is not a problem here because we |
1526 | | // are not performing any allocations or other operations that would |
1527 | | // trigger a GC before the FastOneByteString is used. Take care when |
1528 | | // modifying this code to ensure that no operations would trigger a GC. |
1529 | 0 | HandleScope handle_scope(options.isolate); |
1530 | 0 | SPREAD_BUFFER_ARG(dst_obj, dst); |
1531 | 0 | CHECK(offset <= dst_length); |
1532 | 0 | CHECK(dst_length - offset <= std::numeric_limits<uint32_t>::max()); |
1533 | 0 | TRACK_V8_FAST_API_CALL("buffer.writeString"); |
1534 | |
|
1535 | 0 | return WriteOneByteString<encoding>( |
1536 | 0 | src.data, |
1537 | 0 | src.length, |
1538 | 0 | reinterpret_cast<char*>(dst_data + offset), |
1539 | 0 | std::min<uint32_t>(dst_length - offset, max_length)); |
1540 | 0 | } Unexecuted instantiation: node_buffer.cc:unsigned int node::Buffer::(anonymous namespace)::FastWriteString<(node::encoding)0>(v8::Local<v8::Value>, v8::Local<v8::Value>, v8::FastOneByteString const&, unsigned int, unsigned int, v8::FastApiCallbackOptions&) Unexecuted instantiation: node_buffer.cc:unsigned int node::Buffer::(anonymous namespace)::FastWriteString<(node::encoding)4>(v8::Local<v8::Value>, v8::Local<v8::Value>, v8::FastOneByteString const&, unsigned int, unsigned int, v8::FastApiCallbackOptions&) Unexecuted instantiation: node_buffer.cc:unsigned int node::Buffer::(anonymous namespace)::FastWriteString<(node::encoding)1>(v8::Local<v8::Value>, v8::Local<v8::Value>, v8::FastOneByteString const&, unsigned int, unsigned int, v8::FastApiCallbackOptions&) |
1541 | | |
1542 | | static const CFunction fast_write_string_ascii( |
1543 | | CFunction::Make(FastWriteString<ASCII>)); |
1544 | | static const CFunction fast_write_string_latin1( |
1545 | | CFunction::Make(FastWriteString<LATIN1>)); |
1546 | | static const CFunction fast_write_string_utf8( |
1547 | | CFunction::Make(FastWriteString<UTF8>)); |
1548 | | |
1549 | | void Initialize(Local<Object> target, |
1550 | | Local<Value> unused, |
1551 | | Local<Context> context, |
1552 | 35 | void* priv) { |
1553 | 35 | Environment* env = Environment::GetCurrent(context); |
1554 | 35 | Isolate* isolate = env->isolate(); |
1555 | | |
1556 | 35 | SetMethodNoSideEffect(context, target, "atob", Atob); |
1557 | 35 | SetMethodNoSideEffect(context, target, "btoa", Btoa); |
1558 | | |
1559 | 35 | SetMethod(context, target, "setBufferPrototype", SetBufferPrototype); |
1560 | | |
1561 | 35 | SetFastMethodNoSideEffect(context, |
1562 | 35 | target, |
1563 | 35 | "byteLengthUtf8", |
1564 | 35 | SlowByteLengthUtf8, |
1565 | 35 | &fast_byte_length_utf8); |
1566 | 35 | SetFastMethod(context, target, "copy", SlowCopy, &fast_copy); |
1567 | 35 | SetFastMethodNoSideEffect(context, target, "compare", Compare, &fast_compare); |
1568 | 35 | SetMethodNoSideEffect(context, target, "compareOffset", CompareOffset); |
1569 | 35 | SetMethod(context, target, "fill", Fill); |
1570 | 35 | SetMethodNoSideEffect(context, target, "indexOfBuffer", IndexOfBuffer); |
1571 | 35 | SetFastMethodNoSideEffect(context, |
1572 | 35 | target, |
1573 | 35 | "indexOfNumber", |
1574 | 35 | SlowIndexOfNumber, |
1575 | 35 | &fast_index_of_number); |
1576 | 35 | SetMethodNoSideEffect(context, target, "indexOfString", IndexOfString); |
1577 | | |
1578 | 35 | SetMethod(context, target, "copyArrayBuffer", CopyArrayBuffer); |
1579 | | |
1580 | 35 | SetMethod(context, target, "swap16", Swap16); |
1581 | 35 | SetMethod(context, target, "swap32", Swap32); |
1582 | 35 | SetMethod(context, target, "swap64", Swap64); |
1583 | | |
1584 | 35 | SetMethodNoSideEffect(context, target, "isUtf8", IsUtf8); |
1585 | 35 | SetMethodNoSideEffect(context, target, "isAscii", IsAscii); |
1586 | | |
1587 | 35 | target |
1588 | 35 | ->Set(context, |
1589 | 35 | FIXED_ONE_BYTE_STRING(isolate, "kMaxLength"), |
1590 | 35 | Number::New(isolate, kMaxLength)) |
1591 | 35 | .Check(); |
1592 | | |
1593 | 35 | target |
1594 | 35 | ->Set(context, |
1595 | 35 | FIXED_ONE_BYTE_STRING(isolate, "kStringMaxLength"), |
1596 | 35 | Integer::New(isolate, String::kMaxLength)) |
1597 | 35 | .Check(); |
1598 | | |
1599 | 35 | SetMethodNoSideEffect(context, target, "asciiSlice", StringSlice<ASCII>); |
1600 | 35 | SetMethodNoSideEffect(context, target, "base64Slice", StringSlice<BASE64>); |
1601 | 35 | SetMethodNoSideEffect( |
1602 | 35 | context, target, "base64urlSlice", StringSlice<BASE64URL>); |
1603 | 35 | SetMethodNoSideEffect(context, target, "latin1Slice", StringSlice<LATIN1>); |
1604 | 35 | SetMethodNoSideEffect(context, target, "hexSlice", StringSlice<HEX>); |
1605 | 35 | SetMethodNoSideEffect(context, target, "ucs2Slice", StringSlice<UCS2>); |
1606 | 35 | SetMethodNoSideEffect(context, target, "utf8Slice", StringSlice<UTF8>); |
1607 | | |
1608 | 35 | SetMethod(context, target, "base64Write", StringWrite<BASE64>); |
1609 | 35 | SetMethod(context, target, "base64urlWrite", StringWrite<BASE64URL>); |
1610 | 35 | SetMethod(context, target, "hexWrite", StringWrite<HEX>); |
1611 | 35 | SetMethod(context, target, "ucs2Write", StringWrite<UCS2>); |
1612 | | |
1613 | 35 | SetFastMethod(context, |
1614 | 35 | target, |
1615 | 35 | "asciiWriteStatic", |
1616 | 35 | SlowWriteString<ASCII>, |
1617 | 35 | &fast_write_string_ascii); |
1618 | 35 | SetFastMethod(context, |
1619 | 35 | target, |
1620 | 35 | "latin1WriteStatic", |
1621 | 35 | SlowWriteString<LATIN1>, |
1622 | 35 | &fast_write_string_latin1); |
1623 | 35 | SetFastMethod(context, |
1624 | 35 | target, |
1625 | 35 | "utf8WriteStatic", |
1626 | 35 | SlowWriteString<UTF8>, |
1627 | 35 | &fast_write_string_utf8); |
1628 | | |
1629 | 35 | SetMethod(context, target, "getZeroFillToggle", GetZeroFillToggle); |
1630 | 35 | } |
1631 | | |
1632 | | } // anonymous namespace |
1633 | | |
1634 | 0 | void RegisterExternalReferences(ExternalReferenceRegistry* registry) { |
1635 | 0 | registry->Register(SetBufferPrototype); |
1636 | |
|
1637 | 0 | registry->Register(SlowByteLengthUtf8); |
1638 | 0 | registry->Register(fast_byte_length_utf8); |
1639 | 0 | registry->Register(SlowCopy); |
1640 | 0 | registry->Register(fast_copy); |
1641 | 0 | registry->Register(Compare); |
1642 | 0 | registry->Register(fast_compare); |
1643 | 0 | registry->Register(CompareOffset); |
1644 | 0 | registry->Register(Fill); |
1645 | 0 | registry->Register(IndexOfBuffer); |
1646 | 0 | registry->Register(SlowIndexOfNumber); |
1647 | 0 | registry->Register(fast_index_of_number); |
1648 | 0 | registry->Register(IndexOfString); |
1649 | |
|
1650 | 0 | registry->Register(Swap16); |
1651 | 0 | registry->Register(Swap32); |
1652 | 0 | registry->Register(Swap64); |
1653 | |
|
1654 | 0 | registry->Register(IsUtf8); |
1655 | 0 | registry->Register(IsAscii); |
1656 | |
|
1657 | 0 | registry->Register(StringSlice<ASCII>); |
1658 | 0 | registry->Register(StringSlice<BASE64>); |
1659 | 0 | registry->Register(StringSlice<BASE64URL>); |
1660 | 0 | registry->Register(StringSlice<LATIN1>); |
1661 | 0 | registry->Register(StringSlice<HEX>); |
1662 | 0 | registry->Register(StringSlice<UCS2>); |
1663 | 0 | registry->Register(StringSlice<UTF8>); |
1664 | |
|
1665 | 0 | registry->Register(SlowWriteString<ASCII>); |
1666 | 0 | registry->Register(SlowWriteString<LATIN1>); |
1667 | 0 | registry->Register(SlowWriteString<UTF8>); |
1668 | 0 | registry->Register(fast_write_string_ascii); |
1669 | 0 | registry->Register(fast_write_string_latin1); |
1670 | 0 | registry->Register(fast_write_string_utf8); |
1671 | 0 | registry->Register(StringWrite<ASCII>); |
1672 | 0 | registry->Register(StringWrite<BASE64>); |
1673 | 0 | registry->Register(StringWrite<BASE64URL>); |
1674 | 0 | registry->Register(StringWrite<LATIN1>); |
1675 | 0 | registry->Register(StringWrite<HEX>); |
1676 | 0 | registry->Register(StringWrite<UCS2>); |
1677 | 0 | registry->Register(StringWrite<UTF8>); |
1678 | 0 | registry->Register(GetZeroFillToggle); |
1679 | |
|
1680 | 0 | registry->Register(CopyArrayBuffer); |
1681 | |
|
1682 | 0 | registry->Register(Atob); |
1683 | 0 | registry->Register(Btoa); |
1684 | 0 | } |
1685 | | |
1686 | | } // namespace Buffer |
1687 | | } // namespace node |
1688 | | |
1689 | | NODE_BINDING_CONTEXT_AWARE_INTERNAL(buffer, node::Buffer::Initialize) |
1690 | | NODE_BINDING_EXTERNAL_REFERENCE(buffer, |
1691 | | node::Buffer::RegisterExternalReferences) |