1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpcom/io/nsBinaryStream.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,896 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/** 1.10 + * This file contains implementations of the nsIBinaryInputStream and 1.11 + * nsIBinaryOutputStream interfaces. Together, these interfaces allows reading 1.12 + * and writing of primitive data types (integers, floating-point values, 1.13 + * booleans, etc.) to a stream in a binary, untagged, fixed-endianness format. 1.14 + * This might be used, for example, to implement network protocols or to 1.15 + * produce architecture-neutral binary disk files, i.e. ones that can be read 1.16 + * and written by both big-endian and little-endian platforms. Output is 1.17 + * written in big-endian order (high-order byte first), as this is traditional 1.18 + * network order. 1.19 + * 1.20 + * @See nsIBinaryInputStream 1.21 + * @See nsIBinaryOutputStream 1.22 + */ 1.23 +#include <algorithm> 1.24 +#include <string.h> 1.25 + 1.26 +#include "nsBinaryStream.h" 1.27 + 1.28 +#include "mozilla/Endian.h" 1.29 +#include "mozilla/PodOperations.h" 1.30 +#include "mozilla/Scoped.h" 1.31 + 1.32 +#include "nsCRT.h" 1.33 +#include "nsString.h" 1.34 +#include "nsISerializable.h" 1.35 +#include "nsIClassInfo.h" 1.36 +#include "nsComponentManagerUtils.h" 1.37 +#include "nsIURI.h" // for NS_IURI_IID 1.38 + 1.39 +#include "jsfriendapi.h" 1.40 + 1.41 +using mozilla::PodCopy; 1.42 +using mozilla::ScopedDeleteArray; 1.43 + 1.44 +NS_IMPL_ISUPPORTS(nsBinaryOutputStream, nsIObjectOutputStream, nsIBinaryOutputStream, nsIOutputStream) 1.45 + 1.46 +NS_IMETHODIMP 1.47 +nsBinaryOutputStream::Flush() 1.48 +{ 1.49 + if (NS_WARN_IF(!mOutputStream)) 1.50 + return NS_ERROR_UNEXPECTED; 1.51 + return mOutputStream->Flush(); 1.52 +} 1.53 + 1.54 +NS_IMETHODIMP 1.55 +nsBinaryOutputStream::Close() 1.56 +{ 1.57 + if (NS_WARN_IF(!mOutputStream)) 1.58 + return NS_ERROR_UNEXPECTED; 1.59 + return mOutputStream->Close(); 1.60 +} 1.61 + 1.62 +NS_IMETHODIMP 1.63 +nsBinaryOutputStream::Write(const char *aBuf, uint32_t aCount, uint32_t *aActualBytes) 1.64 +{ 1.65 + if (NS_WARN_IF(!mOutputStream)) 1.66 + return NS_ERROR_UNEXPECTED; 1.67 + return mOutputStream->Write(aBuf, aCount, aActualBytes); 1.68 +} 1.69 + 1.70 +NS_IMETHODIMP 1.71 +nsBinaryOutputStream::WriteFrom(nsIInputStream *inStr, uint32_t count, uint32_t *_retval) 1.72 +{ 1.73 + NS_NOTREACHED("WriteFrom"); 1.74 + return NS_ERROR_NOT_IMPLEMENTED; 1.75 +} 1.76 + 1.77 +NS_IMETHODIMP 1.78 +nsBinaryOutputStream::WriteSegments(nsReadSegmentFun reader, void * closure, uint32_t count, uint32_t *_retval) 1.79 +{ 1.80 + NS_NOTREACHED("WriteSegments"); 1.81 + return NS_ERROR_NOT_IMPLEMENTED; 1.82 +} 1.83 + 1.84 +NS_IMETHODIMP 1.85 +nsBinaryOutputStream::IsNonBlocking(bool *aNonBlocking) 1.86 +{ 1.87 + if (NS_WARN_IF(!mOutputStream)) 1.88 + return NS_ERROR_UNEXPECTED; 1.89 + return mOutputStream->IsNonBlocking(aNonBlocking); 1.90 +} 1.91 + 1.92 +nsresult 1.93 +nsBinaryOutputStream::WriteFully(const char *aBuf, uint32_t aCount) 1.94 +{ 1.95 + if (NS_WARN_IF(!mOutputStream)) 1.96 + return NS_ERROR_UNEXPECTED; 1.97 + 1.98 + nsresult rv; 1.99 + uint32_t bytesWritten; 1.100 + 1.101 + rv = mOutputStream->Write(aBuf, aCount, &bytesWritten); 1.102 + if (NS_FAILED(rv)) return rv; 1.103 + if (bytesWritten != aCount) 1.104 + return NS_ERROR_FAILURE; 1.105 + return NS_OK; 1.106 +} 1.107 + 1.108 +NS_IMETHODIMP 1.109 +nsBinaryOutputStream::SetOutputStream(nsIOutputStream *aOutputStream) 1.110 +{ 1.111 + if (NS_WARN_IF(!aOutputStream)) 1.112 + return NS_ERROR_INVALID_ARG; 1.113 + mOutputStream = aOutputStream; 1.114 + mBufferAccess = do_QueryInterface(aOutputStream); 1.115 + return NS_OK; 1.116 +} 1.117 + 1.118 +NS_IMETHODIMP 1.119 +nsBinaryOutputStream::WriteBoolean(bool aBoolean) 1.120 +{ 1.121 + return Write8(aBoolean); 1.122 +} 1.123 + 1.124 +NS_IMETHODIMP 1.125 +nsBinaryOutputStream::Write8(uint8_t aByte) 1.126 +{ 1.127 + return WriteFully((const char*)&aByte, sizeof aByte); 1.128 +} 1.129 + 1.130 +NS_IMETHODIMP 1.131 +nsBinaryOutputStream::Write16(uint16_t a16) 1.132 +{ 1.133 + a16 = mozilla::NativeEndian::swapToBigEndian(a16); 1.134 + return WriteFully((const char*)&a16, sizeof a16); 1.135 +} 1.136 + 1.137 +NS_IMETHODIMP 1.138 +nsBinaryOutputStream::Write32(uint32_t a32) 1.139 +{ 1.140 + a32 = mozilla::NativeEndian::swapToBigEndian(a32); 1.141 + return WriteFully((const char*)&a32, sizeof a32); 1.142 +} 1.143 + 1.144 +NS_IMETHODIMP 1.145 +nsBinaryOutputStream::Write64(uint64_t a64) 1.146 +{ 1.147 + nsresult rv; 1.148 + uint32_t bytesWritten; 1.149 + 1.150 + a64 = mozilla::NativeEndian::swapToBigEndian(a64); 1.151 + rv = Write(reinterpret_cast<char*>(&a64), sizeof a64, &bytesWritten); 1.152 + if (NS_FAILED(rv)) return rv; 1.153 + if (bytesWritten != sizeof a64) 1.154 + return NS_ERROR_FAILURE; 1.155 + return rv; 1.156 +} 1.157 + 1.158 +NS_IMETHODIMP 1.159 +nsBinaryOutputStream::WriteFloat(float aFloat) 1.160 +{ 1.161 + NS_ASSERTION(sizeof(float) == sizeof (uint32_t), 1.162 + "False assumption about sizeof(float)"); 1.163 + return Write32(*reinterpret_cast<uint32_t*>(&aFloat)); 1.164 +} 1.165 + 1.166 +NS_IMETHODIMP 1.167 +nsBinaryOutputStream::WriteDouble(double aDouble) 1.168 +{ 1.169 + NS_ASSERTION(sizeof(double) == sizeof(uint64_t), 1.170 + "False assumption about sizeof(double)"); 1.171 + return Write64(*reinterpret_cast<uint64_t*>(&aDouble)); 1.172 +} 1.173 + 1.174 +NS_IMETHODIMP 1.175 +nsBinaryOutputStream::WriteStringZ(const char *aString) 1.176 +{ 1.177 + uint32_t length; 1.178 + nsresult rv; 1.179 + 1.180 + length = strlen(aString); 1.181 + rv = Write32(length); 1.182 + if (NS_FAILED(rv)) return rv; 1.183 + return WriteFully(aString, length); 1.184 +} 1.185 + 1.186 +NS_IMETHODIMP 1.187 +nsBinaryOutputStream::WriteWStringZ(const char16_t* aString) 1.188 +{ 1.189 + uint32_t length, byteCount; 1.190 + nsresult rv; 1.191 + 1.192 + length = NS_strlen(aString); 1.193 + rv = Write32(length); 1.194 + if (NS_FAILED(rv)) return rv; 1.195 + 1.196 + if (length == 0) 1.197 + return NS_OK; 1.198 + byteCount = length * sizeof(char16_t); 1.199 + 1.200 +#ifdef IS_BIG_ENDIAN 1.201 + rv = WriteBytes(reinterpret_cast<const char*>(aString), byteCount); 1.202 +#else 1.203 + // XXX use WriteSegments here to avoid copy! 1.204 + char16_t *copy, temp[64]; 1.205 + if (length <= 64) { 1.206 + copy = temp; 1.207 + } else { 1.208 + copy = reinterpret_cast<char16_t*>(moz_malloc(byteCount)); 1.209 + if (!copy) 1.210 + return NS_ERROR_OUT_OF_MEMORY; 1.211 + } 1.212 + NS_ASSERTION((uintptr_t(aString) & 0x1) == 0, "aString not properly aligned"); 1.213 + mozilla::NativeEndian::copyAndSwapToBigEndian(copy, aString, length); 1.214 + rv = WriteBytes(reinterpret_cast<const char*>(copy), byteCount); 1.215 + if (copy != temp) 1.216 + moz_free(copy); 1.217 +#endif 1.218 + 1.219 + return rv; 1.220 +} 1.221 + 1.222 +NS_IMETHODIMP 1.223 +nsBinaryOutputStream::WriteUtf8Z(const char16_t* aString) 1.224 +{ 1.225 + return WriteStringZ(NS_ConvertUTF16toUTF8(aString).get()); 1.226 +} 1.227 + 1.228 +NS_IMETHODIMP 1.229 +nsBinaryOutputStream::WriteBytes(const char *aString, uint32_t aLength) 1.230 +{ 1.231 + nsresult rv; 1.232 + uint32_t bytesWritten; 1.233 + 1.234 + rv = Write(aString, aLength, &bytesWritten); 1.235 + if (NS_FAILED(rv)) return rv; 1.236 + if (bytesWritten != aLength) 1.237 + return NS_ERROR_FAILURE; 1.238 + return rv; 1.239 +} 1.240 + 1.241 +NS_IMETHODIMP 1.242 +nsBinaryOutputStream::WriteByteArray(uint8_t *aBytes, uint32_t aLength) 1.243 +{ 1.244 + return WriteBytes(reinterpret_cast<char *>(aBytes), aLength); 1.245 +} 1.246 + 1.247 +NS_IMETHODIMP 1.248 +nsBinaryOutputStream::WriteObject(nsISupports* aObject, bool aIsStrongRef) 1.249 +{ 1.250 + return WriteCompoundObject(aObject, NS_GET_IID(nsISupports), 1.251 + aIsStrongRef); 1.252 +} 1.253 + 1.254 +NS_IMETHODIMP 1.255 +nsBinaryOutputStream::WriteSingleRefObject(nsISupports* aObject) 1.256 +{ 1.257 + return WriteCompoundObject(aObject, NS_GET_IID(nsISupports), 1.258 + true); 1.259 +} 1.260 + 1.261 +NS_IMETHODIMP 1.262 +nsBinaryOutputStream::WriteCompoundObject(nsISupports* aObject, 1.263 + const nsIID& aIID, 1.264 + bool aIsStrongRef) 1.265 +{ 1.266 + nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(aObject); 1.267 + nsCOMPtr<nsISerializable> serializable = do_QueryInterface(aObject); 1.268 + 1.269 + // Can't deal with weak refs 1.270 + if (NS_WARN_IF(!aIsStrongRef)) 1.271 + return NS_ERROR_UNEXPECTED; 1.272 + if (NS_WARN_IF(!classInfo) || NS_WARN_IF(!serializable)) 1.273 + return NS_ERROR_NOT_AVAILABLE; 1.274 + 1.275 + nsCID cid; 1.276 + nsresult rv = classInfo->GetClassIDNoAlloc(&cid); 1.277 + if (NS_SUCCEEDED(rv)) { 1.278 + rv = WriteID(cid); 1.279 + } else { 1.280 + nsCID *cidptr = nullptr; 1.281 + rv = classInfo->GetClassID(&cidptr); 1.282 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.283 + return rv; 1.284 + } 1.285 + 1.286 + rv = WriteID(*cidptr); 1.287 + 1.288 + NS_Free(cidptr); 1.289 + } 1.290 + 1.291 + if (NS_WARN_IF(NS_FAILED(rv))) 1.292 + return rv; 1.293 + 1.294 + rv = WriteID(aIID); 1.295 + if (NS_WARN_IF(NS_FAILED(rv))) 1.296 + return rv; 1.297 + 1.298 + return serializable->Write(this); 1.299 +} 1.300 + 1.301 +NS_IMETHODIMP 1.302 +nsBinaryOutputStream::WriteID(const nsIID& aIID) 1.303 +{ 1.304 + nsresult rv = Write32(aIID.m0); 1.305 + if (NS_WARN_IF(NS_FAILED(rv))) 1.306 + return rv; 1.307 + 1.308 + rv = Write16(aIID.m1); 1.309 + if (NS_WARN_IF(NS_FAILED(rv))) 1.310 + return rv; 1.311 + 1.312 + rv = Write16(aIID.m2); 1.313 + if (NS_WARN_IF(NS_FAILED(rv))) 1.314 + return rv; 1.315 + 1.316 + for (int i = 0; i < 8; ++i) { 1.317 + rv = Write8(aIID.m3[i]); 1.318 + if (NS_WARN_IF(NS_FAILED(rv))) 1.319 + return rv; 1.320 + } 1.321 + 1.322 + return NS_OK; 1.323 +} 1.324 + 1.325 +NS_IMETHODIMP_(char*) 1.326 +nsBinaryOutputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask) 1.327 +{ 1.328 + if (mBufferAccess) 1.329 + return mBufferAccess->GetBuffer(aLength, aAlignMask); 1.330 + return nullptr; 1.331 +} 1.332 + 1.333 +NS_IMETHODIMP_(void) 1.334 +nsBinaryOutputStream::PutBuffer(char* aBuffer, uint32_t aLength) 1.335 +{ 1.336 + if (mBufferAccess) 1.337 + mBufferAccess->PutBuffer(aBuffer, aLength); 1.338 +} 1.339 + 1.340 +NS_IMPL_ISUPPORTS(nsBinaryInputStream, nsIObjectInputStream, nsIBinaryInputStream, nsIInputStream) 1.341 + 1.342 +NS_IMETHODIMP 1.343 +nsBinaryInputStream::Available(uint64_t* aResult) 1.344 +{ 1.345 + if (NS_WARN_IF(!mInputStream)) 1.346 + return NS_ERROR_UNEXPECTED; 1.347 + return mInputStream->Available(aResult); 1.348 +} 1.349 + 1.350 +NS_IMETHODIMP 1.351 +nsBinaryInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t *aNumRead) 1.352 +{ 1.353 + if (NS_WARN_IF(!mInputStream)) 1.354 + return NS_ERROR_UNEXPECTED; 1.355 + 1.356 + // mInputStream might give us short reads, so deal with that. 1.357 + uint32_t totalRead = 0; 1.358 + 1.359 + uint32_t bytesRead; 1.360 + do { 1.361 + nsresult rv = mInputStream->Read(aBuffer, aCount, &bytesRead); 1.362 + if (rv == NS_BASE_STREAM_WOULD_BLOCK && totalRead != 0) { 1.363 + // We already read some data. Return it. 1.364 + break; 1.365 + } 1.366 + 1.367 + if (NS_FAILED(rv)) { 1.368 + return rv; 1.369 + } 1.370 + 1.371 + totalRead += bytesRead; 1.372 + aBuffer += bytesRead; 1.373 + aCount -= bytesRead; 1.374 + } while (aCount != 0 && bytesRead != 0); 1.375 + 1.376 + *aNumRead = totalRead; 1.377 + 1.378 + return NS_OK; 1.379 +} 1.380 + 1.381 + 1.382 +// when forwarding ReadSegments to mInputStream, we need to make sure 1.383 +// 'this' is being passed to the writer each time. To do this, we need 1.384 +// a thunking function which keeps the real input stream around. 1.385 + 1.386 +// the closure wrapper 1.387 +struct ReadSegmentsClosure { 1.388 + nsIInputStream* mRealInputStream; 1.389 + void* mRealClosure; 1.390 + nsWriteSegmentFun mRealWriter; 1.391 + nsresult mRealResult; 1.392 + uint32_t mBytesRead; // to properly implement aToOffset 1.393 +}; 1.394 + 1.395 +// the thunking function 1.396 +static NS_METHOD 1.397 +ReadSegmentForwardingThunk(nsIInputStream* aStream, 1.398 + void *aClosure, 1.399 + const char* aFromSegment, 1.400 + uint32_t aToOffset, 1.401 + uint32_t aCount, 1.402 + uint32_t *aWriteCount) 1.403 +{ 1.404 + ReadSegmentsClosure* thunkClosure = 1.405 + reinterpret_cast<ReadSegmentsClosure*>(aClosure); 1.406 + 1.407 + NS_ASSERTION(NS_SUCCEEDED(thunkClosure->mRealResult), 1.408 + "How did this get to be a failure status?"); 1.409 + 1.410 + thunkClosure->mRealResult = 1.411 + thunkClosure->mRealWriter(thunkClosure->mRealInputStream, 1.412 + thunkClosure->mRealClosure, 1.413 + aFromSegment, 1.414 + thunkClosure->mBytesRead + aToOffset, 1.415 + aCount, aWriteCount); 1.416 + 1.417 + return thunkClosure->mRealResult; 1.418 +} 1.419 + 1.420 + 1.421 +NS_IMETHODIMP 1.422 +nsBinaryInputStream::ReadSegments(nsWriteSegmentFun writer, void * closure, uint32_t count, uint32_t *_retval) 1.423 +{ 1.424 + if (NS_WARN_IF(!mInputStream)) 1.425 + return NS_ERROR_UNEXPECTED; 1.426 + 1.427 + ReadSegmentsClosure thunkClosure = { this, closure, writer, NS_OK, 0 }; 1.428 + 1.429 + // mInputStream might give us short reads, so deal with that. 1.430 + uint32_t bytesRead; 1.431 + do { 1.432 + nsresult rv = mInputStream->ReadSegments(ReadSegmentForwardingThunk, 1.433 + &thunkClosure, 1.434 + count, &bytesRead); 1.435 + 1.436 + if (rv == NS_BASE_STREAM_WOULD_BLOCK && thunkClosure.mBytesRead != 0) { 1.437 + // We already read some data. Return it. 1.438 + break; 1.439 + } 1.440 + 1.441 + if (NS_FAILED(rv)) { 1.442 + return rv; 1.443 + } 1.444 + 1.445 + thunkClosure.mBytesRead += bytesRead; 1.446 + count -= bytesRead; 1.447 + } while (count != 0 && bytesRead != 0 && 1.448 + NS_SUCCEEDED(thunkClosure.mRealResult)); 1.449 + 1.450 + *_retval = thunkClosure.mBytesRead; 1.451 + 1.452 + return NS_OK; 1.453 +} 1.454 + 1.455 +NS_IMETHODIMP 1.456 +nsBinaryInputStream::IsNonBlocking(bool *aNonBlocking) 1.457 +{ 1.458 + if (NS_WARN_IF(!mInputStream)) 1.459 + return NS_ERROR_UNEXPECTED; 1.460 + return mInputStream->IsNonBlocking(aNonBlocking); 1.461 +} 1.462 + 1.463 +NS_IMETHODIMP 1.464 +nsBinaryInputStream::Close() 1.465 +{ 1.466 + if (NS_WARN_IF(!mInputStream)) 1.467 + return NS_ERROR_UNEXPECTED; 1.468 + return mInputStream->Close(); 1.469 +} 1.470 + 1.471 +NS_IMETHODIMP 1.472 +nsBinaryInputStream::SetInputStream(nsIInputStream *aInputStream) 1.473 +{ 1.474 + if (NS_WARN_IF(!aInputStream)) 1.475 + return NS_ERROR_INVALID_ARG; 1.476 + mInputStream = aInputStream; 1.477 + mBufferAccess = do_QueryInterface(aInputStream); 1.478 + return NS_OK; 1.479 +} 1.480 + 1.481 +NS_IMETHODIMP 1.482 +nsBinaryInputStream::ReadBoolean(bool* aBoolean) 1.483 +{ 1.484 + uint8_t byteResult; 1.485 + nsresult rv = Read8(&byteResult); 1.486 + if (NS_FAILED(rv)) return rv; 1.487 + *aBoolean = !!byteResult; 1.488 + return rv; 1.489 +} 1.490 + 1.491 +NS_IMETHODIMP 1.492 +nsBinaryInputStream::Read8(uint8_t* aByte) 1.493 +{ 1.494 + nsresult rv; 1.495 + uint32_t bytesRead; 1.496 + 1.497 + rv = Read(reinterpret_cast<char*>(aByte), sizeof(*aByte), &bytesRead); 1.498 + if (NS_FAILED(rv)) return rv; 1.499 + if (bytesRead != 1) 1.500 + return NS_ERROR_FAILURE; 1.501 + return rv; 1.502 +} 1.503 + 1.504 +NS_IMETHODIMP 1.505 +nsBinaryInputStream::Read16(uint16_t* a16) 1.506 +{ 1.507 + nsresult rv; 1.508 + uint32_t bytesRead; 1.509 + 1.510 + rv = Read(reinterpret_cast<char*>(a16), sizeof *a16, &bytesRead); 1.511 + if (NS_FAILED(rv)) return rv; 1.512 + if (bytesRead != sizeof *a16) 1.513 + return NS_ERROR_FAILURE; 1.514 + *a16 = mozilla::NativeEndian::swapFromBigEndian(*a16); 1.515 + return rv; 1.516 +} 1.517 + 1.518 +NS_IMETHODIMP 1.519 +nsBinaryInputStream::Read32(uint32_t* a32) 1.520 +{ 1.521 + nsresult rv; 1.522 + uint32_t bytesRead; 1.523 + 1.524 + rv = Read(reinterpret_cast<char*>(a32), sizeof *a32, &bytesRead); 1.525 + if (NS_FAILED(rv)) return rv; 1.526 + if (bytesRead != sizeof *a32) 1.527 + return NS_ERROR_FAILURE; 1.528 + *a32 = mozilla::NativeEndian::swapFromBigEndian(*a32); 1.529 + return rv; 1.530 +} 1.531 + 1.532 +NS_IMETHODIMP 1.533 +nsBinaryInputStream::Read64(uint64_t* a64) 1.534 +{ 1.535 + nsresult rv; 1.536 + uint32_t bytesRead; 1.537 + 1.538 + rv = Read(reinterpret_cast<char*>(a64), sizeof *a64, &bytesRead); 1.539 + if (NS_FAILED(rv)) return rv; 1.540 + if (bytesRead != sizeof *a64) 1.541 + return NS_ERROR_FAILURE; 1.542 + *a64 = mozilla::NativeEndian::swapFromBigEndian(*a64); 1.543 + return rv; 1.544 +} 1.545 + 1.546 +NS_IMETHODIMP 1.547 +nsBinaryInputStream::ReadFloat(float* aFloat) 1.548 +{ 1.549 + NS_ASSERTION(sizeof(float) == sizeof (uint32_t), 1.550 + "False assumption about sizeof(float)"); 1.551 + return Read32(reinterpret_cast<uint32_t*>(aFloat)); 1.552 +} 1.553 + 1.554 +NS_IMETHODIMP 1.555 +nsBinaryInputStream::ReadDouble(double* aDouble) 1.556 +{ 1.557 + NS_ASSERTION(sizeof(double) == sizeof(uint64_t), 1.558 + "False assumption about sizeof(double)"); 1.559 + return Read64(reinterpret_cast<uint64_t*>(aDouble)); 1.560 +} 1.561 + 1.562 +static NS_METHOD 1.563 +WriteSegmentToCString(nsIInputStream* aStream, 1.564 + void *aClosure, 1.565 + const char* aFromSegment, 1.566 + uint32_t aToOffset, 1.567 + uint32_t aCount, 1.568 + uint32_t *aWriteCount) 1.569 +{ 1.570 + nsACString* outString = static_cast<nsACString*>(aClosure); 1.571 + 1.572 + outString->Append(aFromSegment, aCount); 1.573 + 1.574 + *aWriteCount = aCount; 1.575 + 1.576 + return NS_OK; 1.577 +} 1.578 + 1.579 +NS_IMETHODIMP 1.580 +nsBinaryInputStream::ReadCString(nsACString& aString) 1.581 +{ 1.582 + nsresult rv; 1.583 + uint32_t length, bytesRead; 1.584 + 1.585 + rv = Read32(&length); 1.586 + if (NS_FAILED(rv)) return rv; 1.587 + 1.588 + aString.Truncate(); 1.589 + rv = ReadSegments(WriteSegmentToCString, &aString, length, &bytesRead); 1.590 + if (NS_FAILED(rv)) return rv; 1.591 + 1.592 + if (bytesRead != length) 1.593 + return NS_ERROR_FAILURE; 1.594 + 1.595 + return NS_OK; 1.596 +} 1.597 + 1.598 + 1.599 +// sometimes, WriteSegmentToString will be handed an odd-number of 1.600 +// bytes, which means we only have half of the last char16_t 1.601 +struct WriteStringClosure { 1.602 + char16_t *mWriteCursor; 1.603 + bool mHasCarryoverByte; 1.604 + char mCarryoverByte; 1.605 +}; 1.606 + 1.607 +// there are a few cases we have to account for here: 1.608 +// * even length buffer, no carryover - easy, just append 1.609 +// * odd length buffer, no carryover - the last byte needs to be saved 1.610 +// for carryover 1.611 +// * odd length buffer, with carryover - first byte needs to be used 1.612 +// with the carryover byte, and 1.613 +// the rest of the even length 1.614 +// buffer is appended as normal 1.615 +// * even length buffer, with carryover - the first byte needs to be 1.616 +// used with the previous carryover byte. 1.617 +// this gives you an odd length buffer, 1.618 +// so you have to save the last byte for 1.619 +// the next carryover 1.620 + 1.621 + 1.622 +// same version of the above, but with correct casting and endian swapping 1.623 +static NS_METHOD 1.624 +WriteSegmentToString(nsIInputStream* aStream, 1.625 + void *aClosure, 1.626 + const char* aFromSegment, 1.627 + uint32_t aToOffset, 1.628 + uint32_t aCount, 1.629 + uint32_t *aWriteCount) 1.630 +{ 1.631 + NS_PRECONDITION(aCount > 0, "Why are we being told to write 0 bytes?"); 1.632 + NS_PRECONDITION(sizeof(char16_t) == 2, "We can't handle other sizes!"); 1.633 + 1.634 + WriteStringClosure* closure = static_cast<WriteStringClosure*>(aClosure); 1.635 + char16_t *cursor = closure->mWriteCursor; 1.636 + 1.637 + // we're always going to consume the whole buffer no matter what 1.638 + // happens, so take care of that right now.. that allows us to 1.639 + // tweak aCount later. Do NOT move this! 1.640 + *aWriteCount = aCount; 1.641 + 1.642 + // if the last Write had an odd-number of bytes read, then 1.643 + if (closure->mHasCarryoverByte) { 1.644 + // re-create the two-byte sequence we want to work with 1.645 + char bytes[2] = { closure->mCarryoverByte, *aFromSegment }; 1.646 + *cursor = *(char16_t*)bytes; 1.647 + // Now the little endianness dance 1.648 + mozilla::NativeEndian::swapToBigEndianInPlace(cursor, 1); 1.649 + ++cursor; 1.650 + 1.651 + // now skip past the first byte of the buffer.. code from here 1.652 + // can assume normal operations, but should not assume aCount 1.653 + // is relative to the ORIGINAL buffer 1.654 + ++aFromSegment; 1.655 + --aCount; 1.656 + 1.657 + closure->mHasCarryoverByte = false; 1.658 + } 1.659 + 1.660 + // this array is possibly unaligned... be careful how we access it! 1.661 + const char16_t *unicodeSegment = 1.662 + reinterpret_cast<const char16_t*>(aFromSegment); 1.663 + 1.664 + // calculate number of full characters in segment (aCount could be odd!) 1.665 + uint32_t segmentLength = aCount / sizeof(char16_t); 1.666 + 1.667 + // copy all data into our aligned buffer. byte swap if necessary. 1.668 + // cursor may be unaligned, so we cannot use copyAndSwapToBigEndian directly 1.669 + memcpy(cursor, unicodeSegment, segmentLength * sizeof(char16_t)); 1.670 + char16_t *end = cursor + segmentLength; 1.671 + mozilla::NativeEndian::swapToBigEndianInPlace(cursor, segmentLength); 1.672 + closure->mWriteCursor = end; 1.673 + 1.674 + // remember this is the modifed aCount and aFromSegment, 1.675 + // so that will take into account the fact that we might have 1.676 + // skipped the first byte in the buffer 1.677 + if (aCount % sizeof(char16_t) != 0) { 1.678 + // we must have had a carryover byte, that we'll need the next 1.679 + // time around 1.680 + closure->mCarryoverByte = aFromSegment[aCount - 1]; 1.681 + closure->mHasCarryoverByte = true; 1.682 + } 1.683 + 1.684 + return NS_OK; 1.685 +} 1.686 + 1.687 + 1.688 +NS_IMETHODIMP 1.689 +nsBinaryInputStream::ReadString(nsAString& aString) 1.690 +{ 1.691 + nsresult rv; 1.692 + uint32_t length, bytesRead; 1.693 + 1.694 + rv = Read32(&length); 1.695 + if (NS_FAILED(rv)) return rv; 1.696 + 1.697 + if (length == 0) { 1.698 + aString.Truncate(); 1.699 + return NS_OK; 1.700 + } 1.701 + 1.702 + // pre-allocate output buffer, and get direct access to buffer... 1.703 + if (!aString.SetLength(length, mozilla::fallible_t())) 1.704 + return NS_ERROR_OUT_OF_MEMORY; 1.705 + 1.706 + nsAString::iterator start; 1.707 + aString.BeginWriting(start); 1.708 + 1.709 + WriteStringClosure closure; 1.710 + closure.mWriteCursor = start.get(); 1.711 + closure.mHasCarryoverByte = false; 1.712 + 1.713 + rv = ReadSegments(WriteSegmentToString, &closure, 1.714 + length*sizeof(char16_t), &bytesRead); 1.715 + if (NS_FAILED(rv)) return rv; 1.716 + 1.717 + NS_ASSERTION(!closure.mHasCarryoverByte, "some strange stream corruption!"); 1.718 + 1.719 + if (bytesRead != length*sizeof(char16_t)) 1.720 + return NS_ERROR_FAILURE; 1.721 + 1.722 + return NS_OK; 1.723 +} 1.724 + 1.725 +NS_IMETHODIMP 1.726 +nsBinaryInputStream::ReadBytes(uint32_t aLength, char* *_rval) 1.727 +{ 1.728 + nsresult rv; 1.729 + uint32_t bytesRead; 1.730 + char* s; 1.731 + 1.732 + s = reinterpret_cast<char*>(moz_malloc(aLength)); 1.733 + if (!s) 1.734 + return NS_ERROR_OUT_OF_MEMORY; 1.735 + 1.736 + rv = Read(s, aLength, &bytesRead); 1.737 + if (NS_FAILED(rv)) { 1.738 + moz_free(s); 1.739 + return rv; 1.740 + } 1.741 + if (bytesRead != aLength) { 1.742 + moz_free(s); 1.743 + return NS_ERROR_FAILURE; 1.744 + } 1.745 + 1.746 + *_rval = s; 1.747 + return NS_OK; 1.748 +} 1.749 + 1.750 +NS_IMETHODIMP 1.751 +nsBinaryInputStream::ReadByteArray(uint32_t aLength, uint8_t* *_rval) 1.752 +{ 1.753 + return ReadBytes(aLength, reinterpret_cast<char **>(_rval)); 1.754 +} 1.755 + 1.756 +NS_IMETHODIMP 1.757 +nsBinaryInputStream::ReadArrayBuffer(uint32_t aLength, JS::Handle<JS::Value> aBuffer, JSContext* cx) 1.758 +{ 1.759 + if (!aBuffer.isObject()) { 1.760 + return NS_ERROR_FAILURE; 1.761 + } 1.762 + JS::RootedObject buffer(cx, &aBuffer.toObject()); 1.763 + if (!JS_IsArrayBufferObject(buffer)) { 1.764 + return NS_ERROR_FAILURE; 1.765 + } 1.766 + 1.767 + uint32_t bufferLength = JS_GetArrayBufferByteLength(buffer); 1.768 + if (bufferLength < aLength) { 1.769 + return NS_ERROR_FAILURE; 1.770 + } 1.771 + 1.772 + char* data = reinterpret_cast<char*>(JS_GetStableArrayBufferData(cx, buffer)); 1.773 + if (!data) { 1.774 + return NS_ERROR_FAILURE; 1.775 + } 1.776 + 1.777 + uint32_t bufSize = std::min<uint32_t>(aLength, 4096); 1.778 + ScopedDeleteArray<char> buf(new char[bufSize]); 1.779 + 1.780 + uint32_t remaining = aLength; 1.781 + do { 1.782 + // Read data into temporary buffer. 1.783 + uint32_t bytesRead; 1.784 + uint32_t amount = std::min(remaining, bufSize); 1.785 + nsresult rv = Read(buf, amount, &bytesRead); 1.786 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.787 + return rv; 1.788 + } 1.789 + MOZ_ASSERT(bytesRead <= amount); 1.790 + 1.791 + if (bytesRead == 0) { 1.792 + break; 1.793 + } 1.794 + 1.795 + // Copy data into actual buffer. 1.796 + if (bufferLength != JS_GetArrayBufferByteLength(buffer)) { 1.797 + return NS_ERROR_FAILURE; 1.798 + } 1.799 + PodCopy(data, buf.get(), bytesRead); 1.800 + 1.801 + remaining -= bytesRead; 1.802 + data += bytesRead; 1.803 + } while (remaining > 0); 1.804 + 1.805 + return remaining > 0 ? NS_ERROR_FAILURE : NS_OK; 1.806 +} 1.807 + 1.808 +NS_IMETHODIMP 1.809 +nsBinaryInputStream::ReadObject(bool aIsStrongRef, nsISupports* *aObject) 1.810 +{ 1.811 + nsCID cid; 1.812 + nsIID iid; 1.813 + nsresult rv = ReadID(&cid); 1.814 + if (NS_WARN_IF(NS_FAILED(rv))) 1.815 + return rv; 1.816 + 1.817 + rv = ReadID(&iid); 1.818 + if (NS_WARN_IF(NS_FAILED(rv))) 1.819 + return rv; 1.820 + 1.821 + // HACK: Intercept old (pre-gecko6) nsIURI IID, and replace with 1.822 + // the updated IID, so that we're QI'ing to an actual interface. 1.823 + // (As soon as we drop support for upgrading from pre-gecko6, we can 1.824 + // remove this chunk.) 1.825 + static const nsIID oldURIiid = 1.826 + { 0x7a22cc0, 0xce5, 0x11d3, 1.827 + { 0x93, 0x31, 0x0, 0x10, 0x4b, 0xa0, 0xfd, 0x40 }}; 1.828 + 1.829 + // hackaround for bug 670542 1.830 + static const nsIID oldURIiid2 = 1.831 + { 0xd6d04c36, 0x0fa4, 0x4db3, 1.832 + { 0xbe, 0x05, 0x4a, 0x18, 0x39, 0x71, 0x03, 0xe2 }}; 1.833 + 1.834 + // hackaround for bug 682031 1.835 + static const nsIID oldURIiid3 = 1.836 + { 0x12120b20, 0x0929, 0x40e9, 1.837 + { 0x88, 0xcf, 0x6e, 0x08, 0x76, 0x6e, 0x8b, 0x23 }}; 1.838 + 1.839 + if (iid.Equals(oldURIiid) || 1.840 + iid.Equals(oldURIiid2) || 1.841 + iid.Equals(oldURIiid3)) { 1.842 + const nsIID newURIiid = NS_IURI_IID; 1.843 + iid = newURIiid; 1.844 + } 1.845 + // END HACK 1.846 + 1.847 + nsCOMPtr<nsISupports> object = do_CreateInstance(cid, &rv); 1.848 + if (NS_WARN_IF(NS_FAILED(rv))) 1.849 + return rv; 1.850 + 1.851 + nsCOMPtr<nsISerializable> serializable = do_QueryInterface(object); 1.852 + if (NS_WARN_IF(!serializable)) 1.853 + return NS_ERROR_UNEXPECTED; 1.854 + 1.855 + rv = serializable->Read(this); 1.856 + if (NS_WARN_IF(NS_FAILED(rv))) 1.857 + return rv; 1.858 + 1.859 + return object->QueryInterface(iid, reinterpret_cast<void**>(aObject)); 1.860 +} 1.861 + 1.862 +NS_IMETHODIMP 1.863 +nsBinaryInputStream::ReadID(nsID *aResult) 1.864 +{ 1.865 + nsresult rv = Read32(&aResult->m0); 1.866 + if (NS_WARN_IF(NS_FAILED(rv))) 1.867 + return rv; 1.868 + 1.869 + rv = Read16(&aResult->m1); 1.870 + if (NS_WARN_IF(NS_FAILED(rv))) 1.871 + return rv; 1.872 + 1.873 + rv = Read16(&aResult->m2); 1.874 + if (NS_WARN_IF(NS_FAILED(rv))) 1.875 + return rv; 1.876 + 1.877 + for (int i = 0; i < 8; ++i) { 1.878 + rv = Read8(&aResult->m3[i]); 1.879 + if (NS_WARN_IF(NS_FAILED(rv))) 1.880 + return rv; 1.881 + } 1.882 + 1.883 + return NS_OK; 1.884 +} 1.885 + 1.886 +NS_IMETHODIMP_(char*) 1.887 +nsBinaryInputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask) 1.888 +{ 1.889 + if (mBufferAccess) 1.890 + return mBufferAccess->GetBuffer(aLength, aAlignMask); 1.891 + return nullptr; 1.892 +} 1.893 + 1.894 +NS_IMETHODIMP_(void) 1.895 +nsBinaryInputStream::PutBuffer(char* aBuffer, uint32_t aLength) 1.896 +{ 1.897 + if (mBufferAccess) 1.898 + mBufferAccess->PutBuffer(aBuffer, aLength); 1.899 +}