GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage-daily/nodes/benchmark/out/../src/node_blob.cc Lines: 161 196 82.1 %
Date: 2021-06-02 04:11:51 Branches: 59 108 54.6 %

Line Branch Exec Source
1
#include "node_blob.h"
2
#include "async_wrap-inl.h"
3
#include "base_object-inl.h"
4
#include "env-inl.h"
5
#include "memory_tracker-inl.h"
6
#include "node_errors.h"
7
#include "node_external_reference.h"
8
#include "threadpoolwork-inl.h"
9
#include "v8.h"
10
11
#include <algorithm>
12
13
namespace node {
14
15
using v8::Array;
16
using v8::ArrayBuffer;
17
using v8::ArrayBufferView;
18
using v8::BackingStore;
19
using v8::Context;
20
using v8::EscapableHandleScope;
21
using v8::Function;
22
using v8::FunctionCallbackInfo;
23
using v8::FunctionTemplate;
24
using v8::HandleScope;
25
using v8::Local;
26
using v8::MaybeLocal;
27
using v8::Number;
28
using v8::Object;
29
using v8::Uint32;
30
using v8::Undefined;
31
using v8::Value;
32
33
469
void Blob::Initialize(Environment* env, v8::Local<v8::Object> target) {
34
469
  env->SetMethod(target, "createBlob", New);
35
469
  FixedSizeBlobCopyJob::Initialize(env, target);
36
469
}
37
38
85
Local<FunctionTemplate> Blob::GetConstructorTemplate(Environment* env) {
39
85
  Local<FunctionTemplate> tmpl = env->blob_constructor_template();
40
85
  if (tmpl.IsEmpty()) {
41
5
    tmpl = FunctionTemplate::New(env->isolate());
42
15
    tmpl->InstanceTemplate()->SetInternalFieldCount(
43
5
        BaseObject::kInternalFieldCount);
44
10
    tmpl->Inherit(BaseObject::GetConstructorTemplate(env));
45
10
    tmpl->SetClassName(
46
5
        FIXED_ONE_BYTE_STRING(env->isolate(), "Blob"));
47
5
    env->SetProtoMethod(tmpl, "toArrayBuffer", ToArrayBuffer);
48
5
    env->SetProtoMethod(tmpl, "slice", ToSlice);
49
5
    env->set_blob_constructor_template(tmpl);
50
  }
51
85
  return tmpl;
52
}
53
54
35
bool Blob::HasInstance(Environment* env, v8::Local<v8::Value> object) {
55
70
  return GetConstructorTemplate(env)->HasInstance(object);
56
}
57
58
50
BaseObjectPtr<Blob> Blob::Create(
59
    Environment* env,
60
    const std::vector<BlobEntry> store,
61
    size_t length) {
62
63
100
  HandleScope scope(env->isolate());
64
65
  Local<Function> ctor;
66
150
  if (!GetConstructorTemplate(env)->GetFunction(env->context()).ToLocal(&ctor))
67
    return BaseObjectPtr<Blob>();
68
69
  Local<Object> obj;
70
150
  if (!ctor->NewInstance(env->context()).ToLocal(&obj))
71
    return BaseObjectPtr<Blob>();
72
73
50
  return MakeBaseObject<Blob>(env, obj, store, length);
74
}
75
76
36
void Blob::New(const FunctionCallbackInfo<Value>& args) {
77
36
  Environment* env = Environment::GetCurrent(args);
78
72
  CHECK(args[0]->IsArray());  // sources
79
72
  CHECK(args[1]->IsUint32());  // length
80
81
72
  std::vector<BlobEntry> entries;
82
83
108
  size_t length = args[1].As<Uint32>()->Value();
84
36
  size_t len = 0;
85
72
  Local<Array> ary = args[0].As<Array>();
86
150
  for (size_t n = 0; n < ary->Length(); n++) {
87
    Local<Value> entry;
88
117
    if (!ary->Get(env->context(), n).ToLocal(&entry))
89
      return;
90

39
    CHECK(entry->IsArrayBufferView() || Blob::HasInstance(env, entry));
91
39
    if (entry->IsArrayBufferView()) {
92
38
      Local<ArrayBufferView> view = entry.As<ArrayBufferView>();
93
38
      CHECK_EQ(view->ByteOffset(), 0);
94
114
      std::shared_ptr<BackingStore> store = view->Buffer()->GetBackingStore();
95
38
      size_t byte_length = view->ByteLength();
96
76
      view->Buffer()->Detach();  // The Blob will own the backing store now.
97
38
      entries.emplace_back(BlobEntry{std::move(store), byte_length, 0});
98
38
      len += byte_length;
99
    } else {
100
      Blob* blob;
101
1
      ASSIGN_OR_RETURN_UNWRAP(&blob, entry);
102
2
      auto source = blob->entries();
103
1
      entries.insert(entries.end(), source.begin(), source.end());
104
1
      len += blob->length();
105
    }
106
  }
107
36
  CHECK_EQ(length, len);
108
109
72
  BaseObjectPtr<Blob> blob = Create(env, entries, length);
110
36
  if (blob)
111
108
    args.GetReturnValue().Set(blob->object());
112
}
113
114
void Blob::ToArrayBuffer(const FunctionCallbackInfo<Value>& args) {
115
  Environment* env = Environment::GetCurrent(args);
116
  Blob* blob;
117
  ASSIGN_OR_RETURN_UNWRAP(&blob, args.Holder());
118
  Local<Value> ret;
119
  if (blob->GetArrayBuffer(env).ToLocal(&ret))
120
    args.GetReturnValue().Set(ret);
121
}
122
123
13
void Blob::ToSlice(const FunctionCallbackInfo<Value>& args) {
124
13
  Environment* env = Environment::GetCurrent(args);
125
  Blob* blob;
126
13
  ASSIGN_OR_RETURN_UNWRAP(&blob, args.Holder());
127
26
  CHECK(args[0]->IsUint32());
128
26
  CHECK(args[1]->IsUint32());
129
39
  size_t start = args[0].As<Uint32>()->Value();
130
39
  size_t end = args[1].As<Uint32>()->Value();
131
26
  BaseObjectPtr<Blob> slice = blob->Slice(env, start, end);
132
13
  if (slice)
133
39
    args.GetReturnValue().Set(slice->object());
134
}
135
136
void Blob::MemoryInfo(MemoryTracker* tracker) const {
137
  tracker->TrackFieldWithSize("store", length_);
138
}
139
140
MaybeLocal<Value> Blob::GetArrayBuffer(Environment* env) {
141
  EscapableHandleScope scope(env->isolate());
142
  size_t len = length();
143
  std::shared_ptr<BackingStore> store =
144
      ArrayBuffer::NewBackingStore(env->isolate(), len);
145
  if (len > 0) {
146
    unsigned char* dest = static_cast<unsigned char*>(store->Data());
147
    size_t total = 0;
148
    for (const auto& entry : entries()) {
149
      unsigned char* src = static_cast<unsigned char*>(entry.store->Data());
150
      src += entry.offset;
151
      memcpy(dest, src, entry.length);
152
      dest += entry.length;
153
      total += entry.length;
154
      CHECK_LE(total, len);
155
    }
156
  }
157
158
  return scope.Escape(ArrayBuffer::New(env->isolate(), store));
159
}
160
161
13
BaseObjectPtr<Blob> Blob::Slice(Environment* env, size_t start, size_t end) {
162
13
  CHECK_LE(start, length());
163
13
  CHECK_LE(end, length());
164
13
  CHECK_LE(start, end);
165
166
26
  std::vector<BlobEntry> slices;
167
13
  size_t total = end - start;
168
13
  size_t remaining = total;
169
170
13
  if (total == 0) return Create(env, slices, 0);
171
172
14
  for (const auto& entry : entries()) {
173
14
    if (start + entry.offset > entry.store->ByteLength()) {
174
      start -= entry.length;
175
      continue;
176
    }
177
178
14
    size_t offset = entry.offset + start;
179
14
    size_t len = std::min(remaining, entry.store->ByteLength() - offset);
180
14
    slices.emplace_back(BlobEntry{entry.store, len, offset});
181
182
14
    remaining -= len;
183
14
    start = 0;
184
185
14
    if (remaining == 0)
186
9
      break;
187
  }
188
189
9
  return Create(env, slices, total);
190
}
191
192
50
Blob::Blob(
193
    Environment* env,
194
    v8::Local<v8::Object> obj,
195
    const std::vector<BlobEntry>& store,
196
50
    size_t length)
197
    : BaseObject(env, obj),
198
      store_(store),
199
50
      length_(length) {
200
50
  MakeWeak();
201
50
}
202
203
BaseObjectPtr<BaseObject>
204
1
Blob::BlobTransferData::Deserialize(
205
    Environment* env,
206
    Local<Context> context,
207
    std::unique_ptr<worker::TransferData> self) {
208
2
  if (context != env->context()) {
209
    THROW_ERR_MESSAGE_TARGET_CONTEXT_UNAVAILABLE(env);
210
    return {};
211
  }
212
1
  return Blob::Create(env, store_, length_);
213
}
214
215
1
BaseObject::TransferMode Blob::GetTransferMode() const {
216
1
  return BaseObject::TransferMode::kCloneable;
217
}
218
219
1
std::unique_ptr<worker::TransferData> Blob::CloneForMessaging() const {
220
1
  return std::make_unique<BlobTransferData>(store_, length_);
221
}
222
223
34
FixedSizeBlobCopyJob::FixedSizeBlobCopyJob(
224
    Environment* env,
225
    Local<Object> object,
226
    Blob* blob,
227
34
    FixedSizeBlobCopyJob::Mode mode)
228
    : AsyncWrap(env, object, AsyncWrap::PROVIDER_FIXEDSIZEBLOBCOPY),
229
      ThreadPoolWork(env),
230
34
      mode_(mode) {
231
34
  if (mode == FixedSizeBlobCopyJob::Mode::SYNC) MakeWeak();
232
34
  source_ = blob->entries();
233
34
  length_ = blob->length();
234
34
}
235
236
1
void FixedSizeBlobCopyJob::AfterThreadPoolWork(int status) {
237
1
  Environment* env = AsyncWrap::env();
238
1
  CHECK_EQ(mode_, Mode::ASYNC);
239

1
  CHECK(status == 0 || status == UV_ECANCELED);
240
2
  std::unique_ptr<FixedSizeBlobCopyJob> ptr(this);
241
2
  HandleScope handle_scope(env->isolate());
242
1
  Context::Scope context_scope(env->context());
243
3
  Local<Value> args[2];
244
245
1
  if (status == UV_ECANCELED) {
246
    args[0] = Number::New(env->isolate(), status),
247
    args[1] = Undefined(env->isolate());
248
  } else {
249
2
    args[0] = Undefined(env->isolate());
250
2
    args[1] = ArrayBuffer::New(env->isolate(), destination_);
251
  }
252
253
1
  ptr->MakeCallback(env->ondone_string(), arraysize(args), args);
254
1
}
255
256
34
void FixedSizeBlobCopyJob::DoThreadPoolWork() {
257
34
  unsigned char* dest = static_cast<unsigned char*>(destination_->Data());
258
34
  if (length_ > 0) {
259
29
    size_t total = 0;
260
72
    for (const auto& entry : source_) {
261
43
      unsigned char* src = static_cast<unsigned char*>(entry.store->Data());
262
43
      src += entry.offset;
263
43
      memcpy(dest, src, entry.length);
264
43
      dest += entry.length;
265
43
      total += entry.length;
266
43
      CHECK_LE(total, length_);
267
    }
268
  }
269
34
}
270
271
void FixedSizeBlobCopyJob::MemoryInfo(MemoryTracker* tracker) const {
272
  tracker->TrackFieldWithSize("source", length_);
273
  tracker->TrackFieldWithSize(
274
      "destination",
275
      destination_ ? destination_->ByteLength() : 0);
276
}
277
278
469
void FixedSizeBlobCopyJob::Initialize(Environment* env, Local<Object> target) {
279
469
  v8::Local<v8::FunctionTemplate> job = env->NewFunctionTemplate(New);
280
938
  job->Inherit(AsyncWrap::GetConstructorTemplate(env));
281
1407
  job->InstanceTemplate()->SetInternalFieldCount(
282
469
      AsyncWrap::kInternalFieldCount);
283
469
  env->SetProtoMethod(job, "run", Run);
284
469
  env->SetConstructorFunction(target, "FixedSizeBlobCopyJob", job);
285
469
}
286
287
34
void FixedSizeBlobCopyJob::New(const FunctionCallbackInfo<Value>& args) {
288
  static constexpr size_t kMaxSyncLength = 4096;
289
  static constexpr size_t kMaxEntryCount = 4;
290
291
34
  Environment* env = Environment::GetCurrent(args);
292
34
  CHECK(args.IsConstructCall());
293
68
  CHECK(args[0]->IsObject());
294
34
  CHECK(Blob::HasInstance(env, args[0]));
295
296
  Blob* blob;
297
34
  ASSIGN_OR_RETURN_UNWRAP(&blob, args[0]);
298
299
  // This is a fairly arbitrary heuristic. We want to avoid deferring to
300
  // the threadpool if the amount of data being copied is small and there
301
  // aren't that many entries to copy.
302
  FixedSizeBlobCopyJob::Mode mode =
303
102
      (blob->length() < kMaxSyncLength &&
304

169
       blob->entries().size() < kMaxEntryCount) ?
305
          FixedSizeBlobCopyJob::Mode::SYNC :
306
34
          FixedSizeBlobCopyJob::Mode::ASYNC;
307
308
34
  new FixedSizeBlobCopyJob(env, args.This(), blob, mode);
309
}
310
311
34
void FixedSizeBlobCopyJob::Run(const FunctionCallbackInfo<Value>& args) {
312
34
  Environment* env = Environment::GetCurrent(args);
313
  FixedSizeBlobCopyJob* job;
314
35
  ASSIGN_OR_RETURN_UNWRAP(&job, args.Holder());
315
34
  job->destination_ =
316
68
      ArrayBuffer::NewBackingStore(env->isolate(), job->length_);
317
34
  if (job->mode() == FixedSizeBlobCopyJob::Mode::ASYNC)
318
1
    return job->ScheduleWork();
319
320
33
  job->DoThreadPoolWork();
321
99
  args.GetReturnValue().Set(
322
33
      ArrayBuffer::New(env->isolate(), job->destination_));
323
}
324
325
4774
void FixedSizeBlobCopyJob::RegisterExternalReferences(
326
    ExternalReferenceRegistry* registry) {
327
4774
  registry->Register(New);
328
4774
  registry->Register(Run);
329
4774
}
330
331
4774
void Blob::RegisterExternalReferences(ExternalReferenceRegistry* registry) {
332
4774
  registry->Register(Blob::New);
333
4774
  registry->Register(Blob::ToArrayBuffer);
334
4774
  registry->Register(Blob::ToSlice);
335
4774
}
336
337

14520
}  // namespace node