xpcom/io/nsBinaryStream.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial