Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/io/nsBinaryStream.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
/**
8
 * This file contains implementations of the nsIBinaryInputStream and
9
 * nsIBinaryOutputStream interfaces.  Together, these interfaces allows reading
10
 * and writing of primitive data types (integers, floating-point values,
11
 * booleans, etc.) to a stream in a binary, untagged, fixed-endianness format.
12
 * This might be used, for example, to implement network protocols or to
13
 * produce architecture-neutral binary disk files, i.e. ones that can be read
14
 * and written by both big-endian and little-endian platforms.  Output is
15
 * written in big-endian order (high-order byte first), as this is traditional
16
 * network order.
17
 *
18
 * @See nsIBinaryInputStream
19
 * @See nsIBinaryOutputStream
20
 */
21
#include <algorithm>
22
#include <string.h>
23
24
#include "nsBinaryStream.h"
25
26
#include "mozilla/EndianUtils.h"
27
#include "mozilla/PodOperations.h"
28
#include "mozilla/RefPtr.h"
29
#include "mozilla/UniquePtr.h"
30
31
#include "nsCRT.h"
32
#include "nsString.h"
33
#include "nsISerializable.h"
34
#include "nsIClassInfo.h"
35
#include "nsComponentManagerUtils.h"
36
#include "nsIURI.h" // for NS_IURI_IID
37
#include "nsIX509Cert.h" // for NS_IX509CERT_IID
38
39
#include "jsfriendapi.h"
40
41
using mozilla::MakeUnique;
42
using mozilla::PodCopy;
43
using mozilla::UniquePtr;
44
45
already_AddRefed<nsIObjectOutputStream>
46
NS_NewObjectOutputStream(nsIOutputStream* aOutputStream)
47
0
{
48
0
  MOZ_ASSERT(aOutputStream);
49
0
  auto stream = mozilla::MakeRefPtr<nsBinaryOutputStream>();
50
0
51
0
  MOZ_ALWAYS_SUCCEEDS(stream->SetOutputStream(aOutputStream));
52
0
  return stream.forget();
53
0
}
54
55
already_AddRefed<nsIObjectInputStream>
56
NS_NewObjectInputStream(nsIInputStream* aInputStream)
57
0
{
58
0
  MOZ_ASSERT(aInputStream);
59
0
  auto stream = mozilla::MakeRefPtr<nsBinaryInputStream>();
60
0
61
0
  MOZ_ALWAYS_SUCCEEDS(stream->SetInputStream(aInputStream));
62
0
  return stream.forget();
63
0
}
64
65
NS_IMPL_ISUPPORTS(nsBinaryOutputStream,
66
                  nsIObjectOutputStream,
67
                  nsIBinaryOutputStream,
68
                  nsIOutputStream)
69
70
NS_IMETHODIMP
71
nsBinaryOutputStream::Flush()
72
0
{
73
0
  if (NS_WARN_IF(!mOutputStream)) {
74
0
    return NS_ERROR_UNEXPECTED;
75
0
  }
76
0
  return mOutputStream->Flush();
77
0
}
78
79
NS_IMETHODIMP
80
nsBinaryOutputStream::Close()
81
0
{
82
0
  if (NS_WARN_IF(!mOutputStream)) {
83
0
    return NS_ERROR_UNEXPECTED;
84
0
  }
85
0
  return mOutputStream->Close();
86
0
}
87
88
NS_IMETHODIMP
89
nsBinaryOutputStream::Write(const char* aBuf, uint32_t aCount,
90
                            uint32_t* aActualBytes)
91
0
{
92
0
  if (NS_WARN_IF(!mOutputStream)) {
93
0
    return NS_ERROR_UNEXPECTED;
94
0
  }
95
0
  return mOutputStream->Write(aBuf, aCount, aActualBytes);
96
0
}
97
98
NS_IMETHODIMP
99
nsBinaryOutputStream::WriteFrom(nsIInputStream* aInStr, uint32_t aCount,
100
                                uint32_t* aResult)
101
0
{
102
0
  MOZ_ASSERT_UNREACHABLE("WriteFrom");
103
0
  return NS_ERROR_NOT_IMPLEMENTED;
104
0
}
105
106
NS_IMETHODIMP
107
nsBinaryOutputStream::WriteSegments(nsReadSegmentFun aReader, void* aClosure,
108
                                    uint32_t aCount, uint32_t* aResult)
109
0
{
110
0
  MOZ_ASSERT_UNREACHABLE("WriteSegments");
111
0
  return NS_ERROR_NOT_IMPLEMENTED;
112
0
}
113
114
NS_IMETHODIMP
115
nsBinaryOutputStream::IsNonBlocking(bool* aNonBlocking)
116
0
{
117
0
  if (NS_WARN_IF(!mOutputStream)) {
118
0
    return NS_ERROR_UNEXPECTED;
119
0
  }
120
0
  return mOutputStream->IsNonBlocking(aNonBlocking);
121
0
}
122
123
nsresult
124
nsBinaryOutputStream::WriteFully(const char* aBuf, uint32_t aCount)
125
0
{
126
0
  if (NS_WARN_IF(!mOutputStream)) {
127
0
    return NS_ERROR_UNEXPECTED;
128
0
  }
129
0
130
0
  nsresult rv;
131
0
  uint32_t bytesWritten;
132
0
133
0
  rv = mOutputStream->Write(aBuf, aCount, &bytesWritten);
134
0
  if (NS_FAILED(rv)) {
135
0
    return rv;
136
0
  }
137
0
  if (bytesWritten != aCount) {
138
0
    return NS_ERROR_FAILURE;
139
0
  }
140
0
  return NS_OK;
141
0
}
142
143
NS_IMETHODIMP
144
nsBinaryOutputStream::SetOutputStream(nsIOutputStream* aOutputStream)
145
0
{
146
0
  if (NS_WARN_IF(!aOutputStream)) {
147
0
    return NS_ERROR_INVALID_ARG;
148
0
  }
149
0
  mOutputStream = aOutputStream;
150
0
  mBufferAccess = do_QueryInterface(aOutputStream);
151
0
  return NS_OK;
152
0
}
153
154
NS_IMETHODIMP
155
nsBinaryOutputStream::WriteBoolean(bool aBoolean)
156
0
{
157
0
  return Write8(aBoolean);
158
0
}
159
160
NS_IMETHODIMP
161
nsBinaryOutputStream::Write8(uint8_t aByte)
162
0
{
163
0
  return WriteFully((const char*)&aByte, sizeof(aByte));
164
0
}
165
166
NS_IMETHODIMP
167
nsBinaryOutputStream::Write16(uint16_t aNum)
168
0
{
169
0
  aNum = mozilla::NativeEndian::swapToBigEndian(aNum);
170
0
  return WriteFully((const char*)&aNum, sizeof(aNum));
171
0
}
172
173
NS_IMETHODIMP
174
nsBinaryOutputStream::Write32(uint32_t aNum)
175
0
{
176
0
  aNum = mozilla::NativeEndian::swapToBigEndian(aNum);
177
0
  return WriteFully((const char*)&aNum, sizeof(aNum));
178
0
}
179
180
NS_IMETHODIMP
181
nsBinaryOutputStream::Write64(uint64_t aNum)
182
0
{
183
0
  nsresult rv;
184
0
  uint32_t bytesWritten;
185
0
186
0
  aNum = mozilla::NativeEndian::swapToBigEndian(aNum);
187
0
  rv = Write(reinterpret_cast<char*>(&aNum), sizeof(aNum), &bytesWritten);
188
0
  if (NS_FAILED(rv)) {
189
0
    return rv;
190
0
  }
191
0
  if (bytesWritten != sizeof(aNum)) {
192
0
    return NS_ERROR_FAILURE;
193
0
  }
194
0
  return rv;
195
0
}
196
197
NS_IMETHODIMP
198
nsBinaryOutputStream::WriteFloat(float aFloat)
199
0
{
200
0
  static_assert(sizeof(float) == sizeof(uint32_t),
201
0
                "False assumption about sizeof(float)");
202
0
  return Write32(*reinterpret_cast<uint32_t*>(&aFloat));
203
0
}
204
205
NS_IMETHODIMP
206
nsBinaryOutputStream::WriteDouble(double aDouble)
207
0
{
208
0
  static_assert(sizeof(double) == sizeof(uint64_t),
209
0
               "False assumption about sizeof(double)");
210
0
  return Write64(*reinterpret_cast<uint64_t*>(&aDouble));
211
0
}
212
213
NS_IMETHODIMP
214
nsBinaryOutputStream::WriteStringZ(const char* aString)
215
0
{
216
0
  uint32_t length;
217
0
  nsresult rv;
218
0
219
0
  length = strlen(aString);
220
0
  rv = Write32(length);
221
0
  if (NS_FAILED(rv)) {
222
0
    return rv;
223
0
  }
224
0
  return WriteFully(aString, length);
225
0
}
226
227
NS_IMETHODIMP
228
nsBinaryOutputStream::WriteWStringZ(const char16_t* aString)
229
0
{
230
0
  uint32_t length, byteCount;
231
0
  nsresult rv;
232
0
233
0
  length = NS_strlen(aString);
234
0
  rv = Write32(length);
235
0
  if (NS_FAILED(rv)) {
236
0
    return rv;
237
0
  }
238
0
239
0
  if (length == 0) {
240
0
    return NS_OK;
241
0
  }
242
0
  byteCount = length * sizeof(char16_t);
243
0
244
#ifdef IS_BIG_ENDIAN
245
  rv = WriteBytes(reinterpret_cast<const char*>(aString), byteCount);
246
#else
247
  // XXX use WriteSegments here to avoid copy!
248
0
  char16_t* copy;
249
0
  char16_t temp[64];
250
0
  if (length <= 64) {
251
0
    copy = temp;
252
0
  } else {
253
0
    copy = reinterpret_cast<char16_t*>(malloc(byteCount));
254
0
    if (!copy) {
255
0
      return NS_ERROR_OUT_OF_MEMORY;
256
0
    }
257
0
  }
258
0
  NS_ASSERTION((uintptr_t(aString) & 0x1) == 0, "aString not properly aligned");
259
0
  mozilla::NativeEndian::copyAndSwapToBigEndian(copy, aString, length);
260
0
  rv = WriteBytes(reinterpret_cast<const char*>(copy), byteCount);
261
0
  if (copy != temp) {
262
0
    free(copy);
263
0
  }
264
0
#endif
265
0
266
0
  return rv;
267
0
}
268
269
NS_IMETHODIMP
270
nsBinaryOutputStream::WriteUtf8Z(const char16_t* aString)
271
0
{
272
0
  return WriteStringZ(NS_ConvertUTF16toUTF8(aString).get());
273
0
}
274
275
NS_IMETHODIMP
276
nsBinaryOutputStream::WriteBytes(const char* aString, uint32_t aLength)
277
0
{
278
0
  nsresult rv;
279
0
  uint32_t bytesWritten;
280
0
281
0
  rv = Write(aString, aLength, &bytesWritten);
282
0
  if (NS_FAILED(rv)) {
283
0
    return rv;
284
0
  }
285
0
  if (bytesWritten != aLength) {
286
0
    return NS_ERROR_FAILURE;
287
0
  }
288
0
  return rv;
289
0
}
290
291
NS_IMETHODIMP
292
nsBinaryOutputStream::WriteByteArray(uint8_t* aBytes, uint32_t aLength)
293
0
{
294
0
  return WriteBytes(reinterpret_cast<char*>(aBytes), aLength);
295
0
}
296
297
NS_IMETHODIMP
298
nsBinaryOutputStream::WriteObject(nsISupports* aObject, bool aIsStrongRef)
299
0
{
300
0
  return WriteCompoundObject(aObject, NS_GET_IID(nsISupports),
301
0
                             aIsStrongRef);
302
0
}
303
304
NS_IMETHODIMP
305
nsBinaryOutputStream::WriteSingleRefObject(nsISupports* aObject)
306
0
{
307
0
  return WriteCompoundObject(aObject, NS_GET_IID(nsISupports),
308
0
                             true);
309
0
}
310
311
NS_IMETHODIMP
312
nsBinaryOutputStream::WriteCompoundObject(nsISupports* aObject,
313
                                          const nsIID& aIID,
314
                                          bool aIsStrongRef)
315
0
{
316
0
  nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(aObject);
317
0
  nsCOMPtr<nsISerializable> serializable = do_QueryInterface(aObject);
318
0
319
0
  // Can't deal with weak refs
320
0
  if (NS_WARN_IF(!aIsStrongRef)) {
321
0
    return NS_ERROR_UNEXPECTED;
322
0
  }
323
0
  if (NS_WARN_IF(!classInfo) || NS_WARN_IF(!serializable)) {
324
0
    return NS_ERROR_NOT_AVAILABLE;
325
0
  }
326
0
327
0
  nsCID cid;
328
0
  nsresult rv = classInfo->GetClassIDNoAlloc(&cid);
329
0
  if (NS_SUCCEEDED(rv)) {
330
0
    rv = WriteID(cid);
331
0
  } else {
332
0
    nsCID* cidptr = nullptr;
333
0
    rv = classInfo->GetClassID(&cidptr);
334
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
335
0
      return rv;
336
0
    }
337
0
338
0
    rv = WriteID(*cidptr);
339
0
340
0
    free(cidptr);
341
0
  }
342
0
343
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
344
0
    return rv;
345
0
  }
346
0
347
0
  rv = WriteID(aIID);
348
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
349
0
    return rv;
350
0
  }
351
0
352
0
  return serializable->Write(this);
353
0
}
354
355
NS_IMETHODIMP
356
nsBinaryOutputStream::WriteID(const nsIID& aIID)
357
0
{
358
0
  nsresult rv = Write32(aIID.m0);
359
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
360
0
    return rv;
361
0
  }
362
0
363
0
  rv = Write16(aIID.m1);
364
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
365
0
    return rv;
366
0
  }
367
0
368
0
  rv = Write16(aIID.m2);
369
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
370
0
    return rv;
371
0
  }
372
0
373
0
  rv = WriteBytes(reinterpret_cast<const char*>(&aIID.m3[0]), sizeof(aIID.m3));
374
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
375
0
      return rv;
376
0
  }
377
0
378
0
  return NS_OK;
379
0
}
380
381
NS_IMETHODIMP_(char*)
382
nsBinaryOutputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask)
383
0
{
384
0
  if (mBufferAccess) {
385
0
    return mBufferAccess->GetBuffer(aLength, aAlignMask);
386
0
  }
387
0
  return nullptr;
388
0
}
389
390
NS_IMETHODIMP_(void)
391
nsBinaryOutputStream::PutBuffer(char* aBuffer, uint32_t aLength)
392
0
{
393
0
  if (mBufferAccess) {
394
0
    mBufferAccess->PutBuffer(aBuffer, aLength);
395
0
  }
396
0
}
397
398
NS_IMPL_ISUPPORTS(nsBinaryInputStream,
399
                  nsIObjectInputStream,
400
                  nsIBinaryInputStream,
401
                  nsIInputStream)
402
403
NS_IMETHODIMP
404
nsBinaryInputStream::Available(uint64_t* aResult)
405
0
{
406
0
  if (NS_WARN_IF(!mInputStream)) {
407
0
    return NS_ERROR_UNEXPECTED;
408
0
  }
409
0
  return mInputStream->Available(aResult);
410
0
}
411
412
NS_IMETHODIMP
413
nsBinaryInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aNumRead)
414
0
{
415
0
  if (NS_WARN_IF(!mInputStream)) {
416
0
    return NS_ERROR_UNEXPECTED;
417
0
  }
418
0
419
0
  // mInputStream might give us short reads, so deal with that.
420
0
  uint32_t totalRead = 0;
421
0
422
0
  uint32_t bytesRead;
423
0
  do {
424
0
    nsresult rv = mInputStream->Read(aBuffer, aCount, &bytesRead);
425
0
    if (rv == NS_BASE_STREAM_WOULD_BLOCK && totalRead != 0) {
426
0
      // We already read some data.  Return it.
427
0
      break;
428
0
    }
429
0
430
0
    if (NS_FAILED(rv)) {
431
0
      return rv;
432
0
    }
433
0
434
0
    totalRead += bytesRead;
435
0
    aBuffer += bytesRead;
436
0
    aCount -= bytesRead;
437
0
  } while (aCount != 0 && bytesRead != 0);
438
0
439
0
  *aNumRead = totalRead;
440
0
441
0
  return NS_OK;
442
0
}
443
444
445
// when forwarding ReadSegments to mInputStream, we need to make sure
446
// 'this' is being passed to the writer each time. To do this, we need
447
// a thunking function which keeps the real input stream around.
448
449
// the closure wrapper
450
struct MOZ_STACK_CLASS ReadSegmentsClosure
451
{
452
  nsCOMPtr<nsIInputStream> mRealInputStream;
453
  void* mRealClosure;
454
  nsWriteSegmentFun mRealWriter;
455
  nsresult mRealResult;
456
  uint32_t mBytesRead;  // to properly implement aToOffset
457
};
458
459
// the thunking function
460
static nsresult
461
ReadSegmentForwardingThunk(nsIInputStream* aStream,
462
                           void* aClosure,
463
                           const char* aFromSegment,
464
                           uint32_t aToOffset,
465
                           uint32_t aCount,
466
                           uint32_t* aWriteCount)
467
0
{
468
0
  ReadSegmentsClosure* thunkClosure =
469
0
    reinterpret_cast<ReadSegmentsClosure*>(aClosure);
470
0
471
0
  NS_ASSERTION(NS_SUCCEEDED(thunkClosure->mRealResult),
472
0
               "How did this get to be a failure status?");
473
0
474
0
  thunkClosure->mRealResult =
475
0
    thunkClosure->mRealWriter(thunkClosure->mRealInputStream,
476
0
                              thunkClosure->mRealClosure,
477
0
                              aFromSegment,
478
0
                              thunkClosure->mBytesRead + aToOffset,
479
0
                              aCount, aWriteCount);
480
0
481
0
  return thunkClosure->mRealResult;
482
0
}
483
484
485
NS_IMETHODIMP
486
nsBinaryInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
487
                                  uint32_t aCount, uint32_t* aResult)
488
0
{
489
0
  if (NS_WARN_IF(!mInputStream)) {
490
0
    return NS_ERROR_UNEXPECTED;
491
0
  }
492
0
493
0
  ReadSegmentsClosure thunkClosure = { this, aClosure, aWriter, NS_OK, 0 };
494
0
495
0
  // mInputStream might give us short reads, so deal with that.
496
0
  uint32_t bytesRead;
497
0
  do {
498
0
    nsresult rv = mInputStream->ReadSegments(ReadSegmentForwardingThunk,
499
0
                                             &thunkClosure,
500
0
                                             aCount, &bytesRead);
501
0
502
0
    if (rv == NS_BASE_STREAM_WOULD_BLOCK && thunkClosure.mBytesRead != 0) {
503
0
      // We already read some data.  Return it.
504
0
      break;
505
0
    }
506
0
507
0
    if (NS_FAILED(rv)) {
508
0
      return rv;
509
0
    }
510
0
511
0
    thunkClosure.mBytesRead += bytesRead;
512
0
    aCount -= bytesRead;
513
0
  } while (aCount != 0 && bytesRead != 0 &&
514
0
           NS_SUCCEEDED(thunkClosure.mRealResult));
515
0
516
0
  *aResult = thunkClosure.mBytesRead;
517
0
518
0
  return NS_OK;
519
0
}
520
521
NS_IMETHODIMP
522
nsBinaryInputStream::IsNonBlocking(bool* aNonBlocking)
523
0
{
524
0
  if (NS_WARN_IF(!mInputStream)) {
525
0
    return NS_ERROR_UNEXPECTED;
526
0
  }
527
0
  return mInputStream->IsNonBlocking(aNonBlocking);
528
0
}
529
530
NS_IMETHODIMP
531
nsBinaryInputStream::Close()
532
0
{
533
0
  if (NS_WARN_IF(!mInputStream)) {
534
0
    return NS_ERROR_UNEXPECTED;
535
0
  }
536
0
  return mInputStream->Close();
537
0
}
538
539
NS_IMETHODIMP
540
nsBinaryInputStream::SetInputStream(nsIInputStream* aInputStream)
541
0
{
542
0
  if (NS_WARN_IF(!aInputStream)) {
543
0
    return NS_ERROR_INVALID_ARG;
544
0
  }
545
0
  mInputStream = aInputStream;
546
0
  mBufferAccess = do_QueryInterface(aInputStream);
547
0
  return NS_OK;
548
0
}
549
550
NS_IMETHODIMP
551
nsBinaryInputStream::ReadBoolean(bool* aBoolean)
552
0
{
553
0
  uint8_t byteResult;
554
0
  nsresult rv = Read8(&byteResult);
555
0
  if (NS_FAILED(rv)) {
556
0
    return rv;
557
0
  }
558
0
  *aBoolean = !!byteResult;
559
0
  return rv;
560
0
}
561
562
NS_IMETHODIMP
563
nsBinaryInputStream::Read8(uint8_t* aByte)
564
0
{
565
0
  nsresult rv;
566
0
  uint32_t bytesRead;
567
0
568
0
  rv = Read(reinterpret_cast<char*>(aByte), sizeof(*aByte), &bytesRead);
569
0
  if (NS_FAILED(rv)) {
570
0
    return rv;
571
0
  }
572
0
  if (bytesRead != 1) {
573
0
    return NS_ERROR_FAILURE;
574
0
  }
575
0
  return rv;
576
0
}
577
578
NS_IMETHODIMP
579
nsBinaryInputStream::Read16(uint16_t* aNum)
580
0
{
581
0
  uint32_t bytesRead;
582
0
  nsresult rv = Read(reinterpret_cast<char*>(aNum), sizeof(*aNum), &bytesRead);
583
0
  if (NS_FAILED(rv)) {
584
0
    return rv;
585
0
  }
586
0
  if (bytesRead != sizeof(*aNum)) {
587
0
    return NS_ERROR_FAILURE;
588
0
  }
589
0
  *aNum = mozilla::NativeEndian::swapFromBigEndian(*aNum);
590
0
  return rv;
591
0
}
592
593
NS_IMETHODIMP
594
nsBinaryInputStream::Read32(uint32_t* aNum)
595
0
{
596
0
  uint32_t bytesRead;
597
0
  nsresult rv = Read(reinterpret_cast<char*>(aNum), sizeof(*aNum), &bytesRead);
598
0
  if (NS_FAILED(rv)) {
599
0
    return rv;
600
0
  }
601
0
  if (bytesRead != sizeof(*aNum)) {
602
0
    return NS_ERROR_FAILURE;
603
0
  }
604
0
  *aNum = mozilla::NativeEndian::swapFromBigEndian(*aNum);
605
0
  return rv;
606
0
}
607
608
NS_IMETHODIMP
609
nsBinaryInputStream::Read64(uint64_t* aNum)
610
0
{
611
0
  uint32_t bytesRead;
612
0
  nsresult rv = Read(reinterpret_cast<char*>(aNum), sizeof(*aNum), &bytesRead);
613
0
  if (NS_FAILED(rv)) {
614
0
    return rv;
615
0
  }
616
0
  if (bytesRead != sizeof(*aNum)) {
617
0
    return NS_ERROR_FAILURE;
618
0
  }
619
0
  *aNum = mozilla::NativeEndian::swapFromBigEndian(*aNum);
620
0
  return rv;
621
0
}
622
623
NS_IMETHODIMP
624
nsBinaryInputStream::ReadFloat(float* aFloat)
625
0
{
626
0
  static_assert(sizeof(float) == sizeof(uint32_t),
627
0
                "False assumption about sizeof(float)");
628
0
  return Read32(reinterpret_cast<uint32_t*>(aFloat));
629
0
}
630
631
NS_IMETHODIMP
632
nsBinaryInputStream::ReadDouble(double* aDouble)
633
0
{
634
0
  static_assert(sizeof(double) == sizeof(uint64_t),
635
0
               "False assumption about sizeof(double)");
636
0
  return Read64(reinterpret_cast<uint64_t*>(aDouble));
637
0
}
638
639
static nsresult
640
WriteSegmentToCString(nsIInputStream* aStream,
641
                      void* aClosure,
642
                      const char* aFromSegment,
643
                      uint32_t aToOffset,
644
                      uint32_t aCount,
645
                      uint32_t* aWriteCount)
646
0
{
647
0
  nsACString* outString = static_cast<nsACString*>(aClosure);
648
0
649
0
  outString->Append(aFromSegment, aCount);
650
0
651
0
  *aWriteCount = aCount;
652
0
653
0
  return NS_OK;
654
0
}
655
656
NS_IMETHODIMP
657
nsBinaryInputStream::ReadCString(nsACString& aString)
658
0
{
659
0
  nsresult rv;
660
0
  uint32_t length, bytesRead;
661
0
662
0
  rv = Read32(&length);
663
0
  if (NS_FAILED(rv)) {
664
0
    return rv;
665
0
  }
666
0
667
0
  aString.Truncate();
668
0
  rv = ReadSegments(WriteSegmentToCString, &aString, length, &bytesRead);
669
0
  if (NS_FAILED(rv)) {
670
0
    return rv;
671
0
  }
672
0
673
0
  if (bytesRead != length) {
674
0
    return NS_ERROR_FAILURE;
675
0
  }
676
0
677
0
  return NS_OK;
678
0
}
679
680
681
// sometimes, WriteSegmentToString will be handed an odd-number of
682
// bytes, which means we only have half of the last char16_t
683
struct WriteStringClosure
684
{
685
  char16_t* mWriteCursor;
686
  bool mHasCarryoverByte;
687
  char mCarryoverByte;
688
};
689
690
// there are a few cases we have to account for here:
691
// * even length buffer, no carryover - easy, just append
692
// * odd length buffer, no carryover - the last byte needs to be saved
693
//                                     for carryover
694
// * odd length buffer, with carryover - first byte needs to be used
695
//                              with the carryover byte, and
696
//                              the rest of the even length
697
//                              buffer is appended as normal
698
// * even length buffer, with carryover - the first byte needs to be
699
//                              used with the previous carryover byte.
700
//                              this gives you an odd length buffer,
701
//                              so you have to save the last byte for
702
//                              the next carryover
703
704
705
// same version of the above, but with correct casting and endian swapping
706
static nsresult
707
WriteSegmentToString(nsIInputStream* aStream,
708
                     void* aClosure,
709
                     const char* aFromSegment,
710
                     uint32_t aToOffset,
711
                     uint32_t aCount,
712
                     uint32_t* aWriteCount)
713
0
{
714
0
  MOZ_ASSERT(aCount > 0, "Why are we being told to write 0 bytes?");
715
0
  static_assert(sizeof(char16_t) == 2, "We can't handle other sizes!");
716
0
717
0
  WriteStringClosure* closure = static_cast<WriteStringClosure*>(aClosure);
718
0
  char16_t* cursor = closure->mWriteCursor;
719
0
720
0
  // we're always going to consume the whole buffer no matter what
721
0
  // happens, so take care of that right now.. that allows us to
722
0
  // tweak aCount later. Do NOT move this!
723
0
  *aWriteCount = aCount;
724
0
725
0
  // if the last Write had an odd-number of bytes read, then
726
0
  if (closure->mHasCarryoverByte) {
727
0
    // re-create the two-byte sequence we want to work with
728
0
    char bytes[2] = { closure->mCarryoverByte, *aFromSegment };
729
0
    *cursor = *(char16_t*)bytes;
730
0
    // Now the little endianness dance
731
0
    mozilla::NativeEndian::swapToBigEndianInPlace(cursor, 1);
732
0
    ++cursor;
733
0
734
0
    // now skip past the first byte of the buffer.. code from here
735
0
    // can assume normal operations, but should not assume aCount
736
0
    // is relative to the ORIGINAL buffer
737
0
    ++aFromSegment;
738
0
    --aCount;
739
0
740
0
    closure->mHasCarryoverByte = false;
741
0
  }
742
0
743
0
  // this array is possibly unaligned... be careful how we access it!
744
0
  const char16_t* unicodeSegment =
745
0
    reinterpret_cast<const char16_t*>(aFromSegment);
746
0
747
0
  // calculate number of full characters in segment (aCount could be odd!)
748
0
  uint32_t segmentLength = aCount / sizeof(char16_t);
749
0
750
0
  // copy all data into our aligned buffer.  byte swap if necessary.
751
0
  // cursor may be unaligned, so we cannot use copyAndSwapToBigEndian directly
752
0
  memcpy(cursor, unicodeSegment, segmentLength * sizeof(char16_t));
753
0
  char16_t* end = cursor + segmentLength;
754
0
  mozilla::NativeEndian::swapToBigEndianInPlace(cursor, segmentLength);
755
0
  closure->mWriteCursor = end;
756
0
757
0
  // remember this is the modifed aCount and aFromSegment,
758
0
  // so that will take into account the fact that we might have
759
0
  // skipped the first byte in the buffer
760
0
  if (aCount % sizeof(char16_t) != 0) {
761
0
    // we must have had a carryover byte, that we'll need the next
762
0
    // time around
763
0
    closure->mCarryoverByte = aFromSegment[aCount - 1];
764
0
    closure->mHasCarryoverByte = true;
765
0
  }
766
0
767
0
  return NS_OK;
768
0
}
769
770
771
NS_IMETHODIMP
772
nsBinaryInputStream::ReadString(nsAString& aString)
773
0
{
774
0
  nsresult rv;
775
0
  uint32_t length, bytesRead;
776
0
777
0
  rv = Read32(&length);
778
0
  if (NS_FAILED(rv)) {
779
0
    return rv;
780
0
  }
781
0
782
0
  if (length == 0) {
783
0
    aString.Truncate();
784
0
    return NS_OK;
785
0
  }
786
0
787
0
  // pre-allocate output buffer, and get direct access to buffer...
788
0
  if (!aString.SetLength(length, mozilla::fallible)) {
789
0
    return NS_ERROR_OUT_OF_MEMORY;
790
0
  }
791
0
792
0
  WriteStringClosure closure;
793
0
  closure.mWriteCursor = aString.BeginWriting();
794
0
  closure.mHasCarryoverByte = false;
795
0
796
0
  rv = ReadSegments(WriteSegmentToString, &closure,
797
0
                    length * sizeof(char16_t), &bytesRead);
798
0
  if (NS_FAILED(rv)) {
799
0
    return rv;
800
0
  }
801
0
802
0
  NS_ASSERTION(!closure.mHasCarryoverByte, "some strange stream corruption!");
803
0
804
0
  if (bytesRead != length * sizeof(char16_t)) {
805
0
    return NS_ERROR_FAILURE;
806
0
  }
807
0
808
0
  return NS_OK;
809
0
}
810
811
NS_IMETHODIMP
812
nsBinaryInputStream::ReadBytes(uint32_t aLength, char** aResult)
813
0
{
814
0
  nsresult rv;
815
0
  uint32_t bytesRead;
816
0
  char* s;
817
0
818
0
  s = reinterpret_cast<char*>(malloc(aLength));
819
0
  if (!s) {
820
0
    return NS_ERROR_OUT_OF_MEMORY;
821
0
  }
822
0
823
0
  rv = Read(s, aLength, &bytesRead);
824
0
  if (NS_FAILED(rv)) {
825
0
    free(s);
826
0
    return rv;
827
0
  }
828
0
  if (bytesRead != aLength) {
829
0
    free(s);
830
0
    return NS_ERROR_FAILURE;
831
0
  }
832
0
833
0
  *aResult = s;
834
0
  return NS_OK;
835
0
}
836
837
NS_IMETHODIMP
838
nsBinaryInputStream::ReadByteArray(uint32_t aLength, uint8_t** aResult)
839
0
{
840
0
  return ReadBytes(aLength, reinterpret_cast<char**>(aResult));
841
0
}
842
843
NS_IMETHODIMP
844
nsBinaryInputStream::ReadArrayBuffer(uint32_t aLength,
845
                                     JS::Handle<JS::Value> aBuffer,
846
                                     JSContext* aCx, uint32_t* aReadLength)
847
0
{
848
0
  if (!aBuffer.isObject()) {
849
0
    return NS_ERROR_FAILURE;
850
0
  }
851
0
  JS::RootedObject buffer(aCx, &aBuffer.toObject());
852
0
  if (!JS_IsArrayBufferObject(buffer)) {
853
0
    return NS_ERROR_FAILURE;
854
0
  }
855
0
856
0
  uint32_t bufferLength = JS_GetArrayBufferByteLength(buffer);
857
0
  if (bufferLength < aLength) {
858
0
    return NS_ERROR_FAILURE;
859
0
  }
860
0
861
0
  uint32_t bufSize = std::min<uint32_t>(aLength, 4096);
862
0
  UniquePtr<char[]> buf = MakeUnique<char[]>(bufSize);
863
0
864
0
  uint32_t pos = 0;
865
0
  *aReadLength = 0;
866
0
  do {
867
0
    // Read data into temporary buffer.
868
0
    uint32_t bytesRead;
869
0
    uint32_t amount = std::min(aLength - pos, bufSize);
870
0
    nsresult rv = Read(buf.get(), amount, &bytesRead);
871
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
872
0
      return rv;
873
0
    }
874
0
    MOZ_ASSERT(bytesRead <= amount);
875
0
876
0
    if (bytesRead == 0) {
877
0
      break;
878
0
    }
879
0
880
0
    // Copy data into actual buffer.
881
0
882
0
    JS::AutoCheckCannotGC nogc;
883
0
    bool isShared;
884
0
    if (bufferLength != JS_GetArrayBufferByteLength(buffer)) {
885
0
      return NS_ERROR_FAILURE;
886
0
    }
887
0
888
0
    char* data = reinterpret_cast<char*>(JS_GetArrayBufferData(buffer, &isShared, nogc));
889
0
    MOZ_ASSERT(!isShared);      // Implied by JS_GetArrayBufferData()
890
0
    if (!data) {
891
0
      return NS_ERROR_FAILURE;
892
0
    }
893
0
894
0
    *aReadLength += bytesRead;
895
0
    PodCopy(data + pos, buf.get(), bytesRead);
896
0
897
0
    pos += bytesRead;
898
0
  } while (pos < aLength);
899
0
900
0
  return NS_OK;
901
0
}
902
903
NS_IMETHODIMP
904
nsBinaryInputStream::ReadObject(bool aIsStrongRef, nsISupports** aObject)
905
0
{
906
0
  nsCID cid;
907
0
  nsIID iid;
908
0
  nsresult rv = ReadID(&cid);
909
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
910
0
    return rv;
911
0
  }
912
0
913
0
  rv = ReadID(&iid);
914
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
915
0
    return rv;
916
0
  }
917
0
918
0
  // HACK: Intercept old (pre-gecko6) nsIURI IID, and replace with
919
0
  // the updated IID, so that we're QI'ing to an actual interface.
920
0
  // (As soon as we drop support for upgrading from pre-gecko6, we can
921
0
  // remove this chunk.)
922
0
  static const nsIID oldURIiid = {
923
0
    0x7a22cc0, 0xce5, 0x11d3,
924
0
    { 0x93, 0x31, 0x0, 0x10, 0x4b, 0xa0, 0xfd, 0x40 }
925
0
  };
926
0
927
0
  // hackaround for bug 670542
928
0
  static const nsIID oldURIiid2 = {
929
0
    0xd6d04c36, 0x0fa4, 0x4db3,
930
0
    { 0xbe, 0x05, 0x4a, 0x18, 0x39, 0x71, 0x03, 0xe2 }
931
0
  };
932
0
933
0
  // hackaround for bug 682031
934
0
  static const nsIID oldURIiid3 = {
935
0
    0x12120b20, 0x0929, 0x40e9,
936
0
    { 0x88, 0xcf, 0x6e, 0x08, 0x76, 0x6e, 0x8b, 0x23 }
937
0
  };
938
0
939
0
  // hackaround for bug 1195415
940
0
  static const nsIID oldURIiid4 = {
941
0
    0x395fe045, 0x7d18, 0x4adb,
942
0
    { 0xa3, 0xfd, 0xaf, 0x98, 0xc8, 0xa1, 0xaf, 0x11 }
943
0
  };
944
0
945
0
  if (iid.Equals(oldURIiid) ||
946
0
      iid.Equals(oldURIiid2) ||
947
0
      iid.Equals(oldURIiid3) ||
948
0
      iid.Equals(oldURIiid4)) {
949
0
    const nsIID newURIiid = NS_IURI_IID;
950
0
    iid = newURIiid;
951
0
  }
952
0
  // END HACK
953
0
954
0
  // HACK:  Service workers store resource security info on disk in the dom
955
0
  //        Cache API.  When the uuid of the nsIX509Cert interface changes
956
0
  //        these serialized objects cannot be loaded any more.  This hack
957
0
  //        works around this issue.
958
0
959
0
  // hackaround for bug 1247580 (FF45 to FF46 transition)
960
0
  static const nsIID oldCertIID = {
961
0
    0xf8ed8364, 0xced9, 0x4c6e,
962
0
    { 0x86, 0xba, 0x48, 0xaf, 0x53, 0xc3, 0x93, 0xe6 }
963
0
  };
964
0
965
0
  if (iid.Equals(oldCertIID)) {
966
0
    const nsIID newCertIID = NS_IX509CERT_IID;
967
0
    iid = newCertIID;
968
0
  }
969
0
  // END HACK
970
0
971
0
  nsCOMPtr<nsISupports> object = do_CreateInstance(cid, &rv);
972
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
973
0
    return rv;
974
0
  }
975
0
976
0
  nsCOMPtr<nsISerializable> serializable = do_QueryInterface(object);
977
0
  if (NS_WARN_IF(!serializable)) {
978
0
    return NS_ERROR_UNEXPECTED;
979
0
  }
980
0
981
0
  rv = serializable->Read(this);
982
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
983
0
    return rv;
984
0
  }
985
0
986
0
  return object->QueryInterface(iid, reinterpret_cast<void**>(aObject));
987
0
}
988
989
NS_IMETHODIMP
990
nsBinaryInputStream::ReadID(nsID* aResult)
991
0
{
992
0
  nsresult rv = Read32(&aResult->m0);
993
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
994
0
    return rv;
995
0
  }
996
0
997
0
  rv = Read16(&aResult->m1);
998
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
999
0
    return rv;
1000
0
  }
1001
0
1002
0
  rv = Read16(&aResult->m2);
1003
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1004
0
    return rv;
1005
0
  }
1006
0
1007
0
  const uint32_t toRead = sizeof(aResult->m3);
1008
0
  uint32_t bytesRead = 0;
1009
0
  rv = Read(reinterpret_cast<char*>(&aResult->m3[0]), toRead, &bytesRead);
1010
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1011
0
    return rv;
1012
0
  }
1013
0
  if (bytesRead != toRead) {
1014
0
    return NS_ERROR_FAILURE;
1015
0
  }
1016
0
1017
0
  return NS_OK;
1018
0
}
1019
1020
NS_IMETHODIMP_(char*)
1021
nsBinaryInputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask)
1022
0
{
1023
0
  if (mBufferAccess) {
1024
0
    return mBufferAccess->GetBuffer(aLength, aAlignMask);
1025
0
  }
1026
0
  return nullptr;
1027
0
}
1028
1029
NS_IMETHODIMP_(void)
1030
nsBinaryInputStream::PutBuffer(char* aBuffer, uint32_t aLength)
1031
0
{
1032
0
  if (mBufferAccess) {
1033
0
    mBufferAccess->PutBuffer(aBuffer, aLength);
1034
0
  }
1035
0
}