• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

melchor629 / node-flac-bindings / 15650914751

14 Jun 2025 10:05AM UTC coverage: 91.005% (-0.2%) from 91.222%
15650914751

push

github

melchor629
fix: variable i refactor

161 of 206 branches covered (78.16%)

Branch coverage included in aggregate %.

3 of 3 new or added lines in 1 file covered. (100.0%)

12 existing lines in 3 files now uncovered.

5019 of 5486 relevant lines covered (91.49%)

37227.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.25
/src/decoder/decoder-builder.cpp
1
#include "../mappings/mappings.hpp"
2
#include "decoder.hpp"
3

4
namespace flac_bindings {
5

6
  Function StreamDecoderBuilder::init(Napi::Env env, FlacAddon& addon) {
21✔
7
    EscapableHandleScope scope(env);
21✔
8

9
    auto constructor = DefineClass(
21✔
10
      env,
11
      "StreamDecoderBuilder",
12
      {
13
        InstanceMethod("getState", &StreamDecoderBuilder::getState),
21✔
14
        InstanceMethod("getMd5Checking", &StreamDecoderBuilder::getMd5Checking),
21✔
15
        InstanceMethod("getDecodeChainedStream", &StreamDecoderBuilder::getDecodeChainedStream),
21✔
16

17
        InstanceMethod("setOggSerialNumber", &StreamDecoderBuilder::setOggSerialNumber),
21✔
18
        InstanceMethod("setMd5Checking", &StreamDecoderBuilder::setMd5Checking),
21✔
19
        InstanceMethod("setMetadataRespond", &StreamDecoderBuilder::setMetadataRespond),
21✔
20
        InstanceMethod("setMetadataRespondAll", &StreamDecoderBuilder::setMetadataRespondAll),
21✔
21
        InstanceMethod(
21✔
22
          "setMetadataRespondApplication",
23
          &StreamDecoderBuilder::setMetadataRespondApplication),
24
        InstanceMethod("setMetadataIgnore", &StreamDecoderBuilder::setMetadataIgnore),
21✔
25
        InstanceMethod("setMetadataIgnoreAll", &StreamDecoderBuilder::setMetadataIgnoreAll),
21✔
26
        InstanceMethod(
21✔
27
          "setMetadataIgnoreApplication",
28
          &StreamDecoderBuilder::setMetadataIgnoreApplication),
29
        InstanceMethod("setDecodeChainedStream", &StreamDecoderBuilder::setDecodeChainedStream),
21✔
30

31
        InstanceMethod("buildWithStream", &StreamDecoderBuilder::buildWithStream),
21✔
32
        InstanceMethod("buildWithOggStream", &StreamDecoderBuilder::buildWithOggStream),
21✔
33
        InstanceMethod("buildWithFile", &StreamDecoderBuilder::buildWithFile),
21✔
34
        InstanceMethod("buildWithOggFile", &StreamDecoderBuilder::buildWithOggFile),
21✔
35

36
        InstanceMethod("buildWithStreamAsync", &StreamDecoderBuilder::buildWithStreamAsync),
21✔
37
        InstanceMethod("buildWithOggStreamAsync", &StreamDecoderBuilder::buildWithOggStreamAsync),
21✔
38
        InstanceMethod("buildWithFileAsync", &StreamDecoderBuilder::buildWithFileAsync),
21✔
39
        InstanceMethod("buildWithOggFileAsync", &StreamDecoderBuilder::buildWithOggFileAsync),
21✔
40
      });
41

42
    constructor.Freeze();
21✔
43
    addon.decoderBuilderConstructor = Persistent(constructor);
21✔
44

45
    return scope.Escape(constructor).As<Function>();
42✔
46
  }
21✔
47

48
  StreamDecoderBuilder::StreamDecoderBuilder(const CallbackInfo& info):
59✔
49
      ObjectWrap<StreamDecoderBuilder>(info) {
59✔
50
    dec = FLAC__stream_decoder_new();
59✔
51
    if (dec == nullptr) {
59✔
52
      throw Error::New(info.Env(), "Could not allocate memory");
×
53
    }
54
  }
59✔
55

UNCOV
56
  StreamDecoderBuilder::~StreamDecoderBuilder() {
×
UNCOV
57
    if (dec != nullptr) {
×
UNCOV
58
      FLAC__stream_decoder_delete(dec);
×
59
    }
UNCOV
60
  }
×
61

62
  // -- getters --
63

64
  Napi::Value StreamDecoderBuilder::getState(const CallbackInfo& info) {
×
65
    checkIfBuilt(info.Env());
×
66

67
    auto state = FLAC__stream_decoder_get_state(dec);
×
68
    return numberToJs(info.Env(), state);
×
69
  }
70

71
  Napi::Value StreamDecoderBuilder::getMd5Checking(const CallbackInfo& info) {
×
72
    checkIfBuilt(info.Env());
×
73

74
    auto md5Checking = FLAC__stream_decoder_get_md5_checking(dec);
×
75
    return booleanToJs(info.Env(), md5Checking);
×
76
  }
77

78
  Napi::Value StreamDecoderBuilder::getDecodeChainedStream(const CallbackInfo& info) {
×
79
    checkIfBuilt(info.Env());
×
80

81
#if FLAC_API_VERSION_CURRENT >= 14
82
    auto is_chained_stream = FLAC__stream_decoder_get_decode_chained_stream(dec);
×
83
    return booleanToJs(info.Env(), is_chained_stream);
×
84
#else
85
    return booleanToJs(info.Env(), false);
86
#endif
87
  }
88

89
  // -- setters --
90

91
  Napi::Value StreamDecoderBuilder::setOggSerialNumber(const CallbackInfo& info) {
1✔
92
    checkIfBuilt(info.Env());
1✔
93

94
    auto oggSerialNumber = numberFromJs<long>(info[0]);
1✔
95
    FLAC__stream_decoder_set_ogg_serial_number(dec, oggSerialNumber);
1✔
96
    return info.This();
1✔
97
  }
98

99
  Napi::Value StreamDecoderBuilder::setMd5Checking(const CallbackInfo& info) {
1✔
100
    checkIfBuilt(info.Env());
1✔
101

102
    auto check = booleanFromJs<FLAC__bool>(info[0]);
1✔
103
    FLAC__stream_decoder_set_md5_checking(dec, check);
1✔
104
    return info.This();
1✔
105
  }
106

107
  Napi::Value StreamDecoderBuilder::setMetadataRespond(const CallbackInfo& info) {
1✔
108
    checkIfBuilt(info.Env());
1✔
109

110
    auto metadataType = numberFromJs<FLAC__MetadataType>(info[0]);
1✔
111
    FLAC__stream_decoder_set_metadata_respond(dec, metadataType);
1✔
112
    return info.This();
1✔
113
  }
114

115
  Napi::Value StreamDecoderBuilder::setMetadataRespondAll(const CallbackInfo& info) {
2✔
116
    checkIfBuilt(info.Env());
2✔
117

118
    FLAC__stream_decoder_set_metadata_respond_all(dec);
2✔
119
    return info.This();
2✔
120
  }
121

122
  Napi::Value StreamDecoderBuilder::setMetadataRespondApplication(const CallbackInfo& info) {
×
123
    checkIfBuilt(info.Env());
×
124

125
    FLAC__byte* id;
126
    size_t size;
127
    std::tie(id, size) = pointer::fromBuffer<FLAC__byte>(info[0]);
×
128

129
    if (size < 4) {
×
130
      throw Error::New(info.Env(), "ID buffer must be 4 bytes length");
×
131
    }
132

133
    FLAC__stream_decoder_set_metadata_respond_application(dec, id);
×
134
    return info.This();
×
135
  }
136

137
  Napi::Value StreamDecoderBuilder::setMetadataIgnore(const CallbackInfo& info) {
1✔
138
    checkIfBuilt(info.Env());
1✔
139

140
    auto metadataType = numberFromJs<FLAC__MetadataType>(info[0]);
1✔
141
    FLAC__stream_decoder_set_metadata_ignore(dec, metadataType);
1✔
142
    return info.This();
1✔
143
  }
144

145
  Napi::Value StreamDecoderBuilder::setMetadataIgnoreAll(const CallbackInfo& info) {
×
146
    checkIfBuilt(info.Env());
×
147

148
    FLAC__stream_decoder_set_metadata_ignore_all(dec);
×
149
    return info.This();
×
150
  }
151

152
  Napi::Value StreamDecoderBuilder::setMetadataIgnoreApplication(const CallbackInfo& info) {
×
153
    checkIfBuilt(info.Env());
×
154

155
    FLAC__byte* id;
156
    size_t size;
157
    std::tie(id, size) = pointer::fromBuffer<FLAC__byte>(info[0]);
×
158

159
    if (size < 4) {
×
160
      throw Error::New(info.Env(), "ID buffer must be 4 bytes length");
×
161
    }
162

163
    FLAC__stream_decoder_set_metadata_ignore_application(dec, id);
×
164
    return info.This();
×
165
  }
166

167
  Napi::Value StreamDecoderBuilder::setDecodeChainedStream(const CallbackInfo& info) {
12✔
168
    checkIfBuilt(info.Env());
12✔
169

170
#if FLAC_API_VERSION_CURRENT >= 14
171
    auto value = booleanFromJs<FLAC__bool>(info[0]);
12✔
172
    FLAC__stream_decoder_set_decode_chained_stream(dec, value);
12✔
173
#endif
174
    return info.This();
12✔
175
  }
176

177
  // -- builder methods --
178

179
  Napi::Value StreamDecoderBuilder::buildWithStream(const CallbackInfo& info) {
3✔
180
    EscapableHandleScope scope(info.Env());
3✔
181
    checkIfBuilt(info.Env());
3✔
182

183
    auto ctx = std::make_shared<DecoderWorkContext>(dec, DecoderWorkContext::ExecutionMode::Sync);
3✔
184
    maybeFunctionIntoRef(ctx->readCbk, info[0]);
3✔
185
    maybeFunctionIntoRef(ctx->seekCbk, info[1]);
3✔
186
    maybeFunctionIntoRef(ctx->tellCbk, info[2]);
3✔
187
    maybeFunctionIntoRef(ctx->lengthCbk, info[3]);
3✔
188
    maybeFunctionIntoRef(ctx->eofCbk, info[4]);
3✔
189
    maybeFunctionIntoRef(ctx->writeCbk, info[5]);
3✔
190
    maybeFunctionIntoRef(ctx->metadataCbk, info[6]);
3✔
191
    maybeFunctionIntoRef(ctx->errorCbk, info[7]);
3✔
192

193
    auto ret = FLAC__stream_decoder_init_stream(
24✔
194
      dec,
195
      !ctx->readCbk.IsEmpty() ? StreamDecoder::readCallback : nullptr,
3✔
196
      !ctx->seekCbk.IsEmpty() ? StreamDecoder::seekCallback : nullptr,
3✔
197
      !ctx->tellCbk.IsEmpty() ? StreamDecoder::tellCallback : nullptr,
3✔
198
      !ctx->lengthCbk.IsEmpty() ? StreamDecoder::lengthCallback : nullptr,
3✔
199
      !ctx->eofCbk.IsEmpty() ? StreamDecoder::eofCallback : nullptr,
3✔
200
      !ctx->writeCbk.IsEmpty() ? StreamDecoder::writeCallback : nullptr,
3✔
201
      !ctx->metadataCbk.IsEmpty() ? StreamDecoder::metadataCallback : nullptr,
3✔
202
      !ctx->errorCbk.IsEmpty() ? StreamDecoder::errorCallback : nullptr,
3✔
203
      ctx.get());
3✔
204

205
    checkInitStatus(info.Env(), ret);
3✔
206
    return scope.Escape(createDecoder(info.Env(), info.This(), ctx));
6✔
207
  }
3✔
208

209
  Napi::Value StreamDecoderBuilder::buildWithOggStream(const CallbackInfo& info) {
5✔
210
    EscapableHandleScope scope(info.Env());
5✔
211
    checkIfBuilt(info.Env());
5✔
212

213
    auto ctx = std::make_shared<DecoderWorkContext>(dec, DecoderWorkContext::ExecutionMode::Sync);
5✔
214
    maybeFunctionIntoRef(ctx->readCbk, info[0]);
5✔
215
    maybeFunctionIntoRef(ctx->seekCbk, info[1]);
5✔
216
    maybeFunctionIntoRef(ctx->tellCbk, info[2]);
5✔
217
    maybeFunctionIntoRef(ctx->lengthCbk, info[3]);
5✔
218
    maybeFunctionIntoRef(ctx->eofCbk, info[4]);
5✔
219
    maybeFunctionIntoRef(ctx->writeCbk, info[5]);
5✔
220
    maybeFunctionIntoRef(ctx->metadataCbk, info[6]);
5✔
221
    maybeFunctionIntoRef(ctx->errorCbk, info[7]);
5✔
222

223
    auto ret = FLAC__stream_decoder_init_ogg_stream(
40✔
224
      dec,
225
      !ctx->readCbk.IsEmpty() ? StreamDecoder::readCallback : nullptr,
5✔
226
      !ctx->seekCbk.IsEmpty() ? StreamDecoder::seekCallback : nullptr,
5✔
227
      !ctx->tellCbk.IsEmpty() ? StreamDecoder::tellCallback : nullptr,
5✔
228
      !ctx->lengthCbk.IsEmpty() ? StreamDecoder::lengthCallback : nullptr,
5✔
229
      !ctx->eofCbk.IsEmpty() ? StreamDecoder::eofCallback : nullptr,
5✔
230
      !ctx->writeCbk.IsEmpty() ? StreamDecoder::writeCallback : nullptr,
5✔
231
      !ctx->metadataCbk.IsEmpty() ? StreamDecoder::metadataCallback : nullptr,
5✔
232
      !ctx->errorCbk.IsEmpty() ? StreamDecoder::errorCallback : nullptr,
5✔
233
      ctx.get());
5✔
234

235
    checkInitStatus(info.Env(), ret);
5✔
236
    return scope.Escape(createDecoder(info.Env(), info.This(), ctx));
10✔
237
  }
5✔
238

239
  Napi::Value StreamDecoderBuilder::buildWithFile(const CallbackInfo& info) {
6✔
240
    EscapableHandleScope scope(info.Env());
6✔
241
    checkIfBuilt(info.Env());
6✔
242

243
    auto path = stringFromJs(info[0]);
6✔
244
    auto ctx = std::make_shared<DecoderWorkContext>(dec, DecoderWorkContext::ExecutionMode::Sync);
6✔
245
    maybeFunctionIntoRef(ctx->writeCbk, info[1]);
6✔
246
    maybeFunctionIntoRef(ctx->metadataCbk, info[2]);
6✔
247
    maybeFunctionIntoRef(ctx->errorCbk, info[3]);
6✔
248

249
    auto ret = FLAC__stream_decoder_init_file(
18✔
250
      dec,
251
      path.c_str(),
252
      !ctx->writeCbk.IsEmpty() ? StreamDecoder::writeCallback : nullptr,
6✔
253
      !ctx->metadataCbk.IsEmpty() ? StreamDecoder::metadataCallback : nullptr,
6✔
254
      !ctx->errorCbk.IsEmpty() ? StreamDecoder::errorCallback : nullptr,
6✔
255
      ctx.get());
6✔
256

257
    checkInitStatus(info.Env(), ret);
6✔
258
    return scope.Escape(createDecoder(info.Env(), info.This(), ctx));
12✔
259
  }
6✔
260

261
  Napi::Value StreamDecoderBuilder::buildWithOggFile(const CallbackInfo& info) {
3✔
262
    EscapableHandleScope scope(info.Env());
3✔
263
    checkIfBuilt(info.Env());
3✔
264

265
    auto path = stringFromJs(info[0]);
3✔
266
    auto ctx = std::make_shared<DecoderWorkContext>(dec, DecoderWorkContext::ExecutionMode::Sync);
3✔
267
    maybeFunctionIntoRef(ctx->writeCbk, info[1]);
3✔
268
    maybeFunctionIntoRef(ctx->metadataCbk, info[2]);
3✔
269
    maybeFunctionIntoRef(ctx->errorCbk, info[3]);
3✔
270

271
    auto ret = FLAC__stream_decoder_init_ogg_file(
9✔
272
      dec,
273
      path.c_str(),
274
      !ctx->writeCbk.IsEmpty() ? StreamDecoder::writeCallback : nullptr,
3✔
275
      !ctx->metadataCbk.IsEmpty() ? StreamDecoder::metadataCallback : nullptr,
3✔
276
      !ctx->errorCbk.IsEmpty() ? StreamDecoder::errorCallback : nullptr,
3✔
277
      ctx.get());
3✔
278

279
    checkInitStatus(info.Env(), ret);
3✔
280
    return scope.Escape(createDecoder(info.Env(), info.This(), ctx));
6✔
281
  }
3✔
282

283
  // -- async builders --
284

285
  Napi::Value StreamDecoderBuilder::buildWithStreamAsync(const CallbackInfo& info) {
12✔
286
    EscapableHandleScope scope(info.Env());
12✔
287
    checkIfBuilt(info.Env());
12✔
288

289
    auto ctx = std::make_shared<DecoderWorkContext>(dec, DecoderWorkContext::ExecutionMode::Async);
12✔
290
    maybeFunctionIntoRef(ctx->readCbk, info[0]);
12✔
291
    maybeFunctionIntoRef(ctx->seekCbk, info[1]);
12✔
292
    maybeFunctionIntoRef(ctx->tellCbk, info[2]);
12✔
293
    maybeFunctionIntoRef(ctx->lengthCbk, info[3]);
12✔
294
    maybeFunctionIntoRef(ctx->eofCbk, info[4]);
12✔
295
    maybeFunctionIntoRef(ctx->writeCbk, info[5]);
12✔
296
    maybeFunctionIntoRef(ctx->metadataCbk, info[6]);
12✔
297
    maybeFunctionIntoRef(ctx->errorCbk, info[7]);
12✔
298

299
    // why no mutex? JS runs in a single thread, and the check has already been done
300
    AsyncDecoderWork* work = AsyncDecoderWork::forInitStream({info.This()}, ctx, *this);
12✔
301
    workInProgress = true;
12✔
302
    ctx->workInProgress = true;
12✔
303
    work->Queue();
12✔
304
    return scope.Escape(work->getPromise());
24✔
305
  }
12✔
306

307
  Napi::Value StreamDecoderBuilder::buildWithOggStreamAsync(const CallbackInfo& info) {
9✔
308
    EscapableHandleScope scope(info.Env());
9✔
309
    checkIfBuilt(info.Env());
9✔
310

311
    auto ctx = std::make_shared<DecoderWorkContext>(dec, DecoderWorkContext::ExecutionMode::Async);
9✔
312
    maybeFunctionIntoRef(ctx->readCbk, info[0]);
9✔
313
    maybeFunctionIntoRef(ctx->seekCbk, info[1]);
9✔
314
    maybeFunctionIntoRef(ctx->tellCbk, info[2]);
9✔
315
    maybeFunctionIntoRef(ctx->lengthCbk, info[3]);
9✔
316
    maybeFunctionIntoRef(ctx->eofCbk, info[4]);
9✔
317
    maybeFunctionIntoRef(ctx->writeCbk, info[5]);
9✔
318
    maybeFunctionIntoRef(ctx->metadataCbk, info[6]);
9✔
319
    maybeFunctionIntoRef(ctx->errorCbk, info[7]);
9✔
320

321
    AsyncDecoderWork* work = AsyncDecoderWork::forInitOggStream({info.This()}, ctx, *this);
9✔
322
    workInProgress = true;
9✔
323
    ctx->workInProgress = true;
9✔
324
    work->Queue();
9✔
325
    return scope.Escape(work->getPromise());
18✔
326
  }
9✔
327

328
  Napi::Value StreamDecoderBuilder::buildWithFileAsync(const CallbackInfo& info) {
15✔
329
    EscapableHandleScope scope(info.Env());
15✔
330
    checkIfBuilt(info.Env());
15✔
331

332
    auto path = stringFromJs(info[0]);
15✔
333
    auto ctx = std::make_shared<DecoderWorkContext>(dec, DecoderWorkContext::ExecutionMode::Async);
15✔
334
    maybeFunctionIntoRef(ctx->writeCbk, info[1]);
15✔
335
    maybeFunctionIntoRef(ctx->metadataCbk, info[2]);
15✔
336
    maybeFunctionIntoRef(ctx->errorCbk, info[3]);
15✔
337

338
    AsyncDecoderWork* work = AsyncDecoderWork::forInitFile({info.This()}, path, ctx, *this);
15✔
339
    workInProgress = true;
15✔
340
    ctx->workInProgress = true;
15✔
341
    work->Queue();
15✔
342
    return scope.Escape(work->getPromise());
30✔
343
  }
15✔
344

345
  Napi::Value StreamDecoderBuilder::buildWithOggFileAsync(const CallbackInfo& info) {
6✔
346
    EscapableHandleScope scope(info.Env());
6✔
347
    checkIfBuilt(info.Env());
6✔
348

349
    auto path = stringFromJs(info[0]);
6✔
350
    auto ctx = std::make_shared<DecoderWorkContext>(dec, DecoderWorkContext::ExecutionMode::Async);
6✔
351
    maybeFunctionIntoRef(ctx->writeCbk, info[1]);
6✔
352
    maybeFunctionIntoRef(ctx->metadataCbk, info[2]);
6✔
353
    maybeFunctionIntoRef(ctx->errorCbk, info[3]);
6✔
354

355
    AsyncDecoderWork* work = AsyncDecoderWork::forInitOggFile({info.This()}, path, ctx, *this);
6✔
356
    workInProgress = true;
6✔
357
    ctx->workInProgress = true;
6✔
358
    work->Queue();
6✔
359
    return scope.Escape(work->getPromise());
12✔
360
  }
6✔
361

362
  // -- helpers --
363

364
  Napi::Value StreamDecoderBuilder::createDecoder(
58✔
365
    Napi::Env env,
366
    Napi::Value self,
367
    std::shared_ptr<DecoderWorkContext> ctx) {
368
    EscapableHandleScope scope(env);
58✔
369
    auto addon = env.GetInstanceData<FlacAddon>();
58✔
370

371
    auto decoderJs = addon->decoderConstructor.New({self});
58✔
372

373
    auto decoder = ObjectWrap<StreamDecoder>::Unwrap(decoderJs);
58✔
374
    decoder->dec = dec;
58✔
375
    decoder->ctx = ctx;
58✔
376

377
    // decoder is build, cannot be used in builder
378
    dec = nullptr;
58✔
379
    workInProgress = false;
58✔
380

381
    return scope.Escape(decoderJs);
116✔
382
  }
58✔
383

384
  void StreamDecoderBuilder::checkInitStatus(Napi::Env env, FLAC__StreamDecoderInitStatus status) {
59✔
385
    // set work to false, because when this method is called, no more background work is being done
386
    workInProgress = false;
59✔
387

388
    if (status != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
59✔
389
      const char* statusString;
390
      // remove prefix FLAC__STREAM_DECODER_INIT_STATUS_
391
      statusString = FLAC__StreamDecoderInitStatusString[status] + 33;
1✔
392

393
      auto error = Error::New(env, "Decoder initialization failed: "s + statusString);
1✔
394
      error.Set("status", numberToJs(env, status));
1✔
395
      error.Set("statusString", String::New(env, statusString));
1✔
396
      throw error;
1✔
397
    }
1✔
398
  }
58✔
399

400
  void StreamDecoderBuilder::checkIfBuilt(Napi::Env env) {
77✔
401
    // if null, means one of the buildWith* methods have been called (see above)
402
    if (dec == nullptr) {
77✔
403
      throw Error::New(env, "Decoder has been built - cannot call any method");
×
404
    }
405

406
    // if workInProgress is true, then fail too
407
    if (workInProgress.load()) {
77✔
408
      throw Error::New(env, "There is a pending Promise running, wait until is resolved");
×
409
    }
410
  }
77✔
411

412
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc