mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
3b40268cc1
This is semantically similar to the existing available() method, however will not block, and doesn't need to do the work to actually determine the number of available bytes. As part of this patch, I also fixed one available() implementation which was incorrectly throwing NS_BASE_STREAM_WOULD_BLOCK. Differential Revision: https://phabricator.services.mozilla.com/D170697
1008 lines
27 KiB
C++
1008 lines
27 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/**
|
|
* This file contains implementations of the nsIBinaryInputStream and
|
|
* nsIBinaryOutputStream interfaces. Together, these interfaces allows reading
|
|
* and writing of primitive data types (integers, floating-point values,
|
|
* booleans, etc.) to a stream in a binary, untagged, fixed-endianness format.
|
|
* This might be used, for example, to implement network protocols or to
|
|
* produce architecture-neutral binary disk files, i.e. ones that can be read
|
|
* and written by both big-endian and little-endian platforms. Output is
|
|
* written in big-endian order (high-order byte first), as this is traditional
|
|
* network order.
|
|
*
|
|
* @See nsIBinaryInputStream
|
|
* @See nsIBinaryOutputStream
|
|
*/
|
|
#include <algorithm>
|
|
#include <string.h>
|
|
|
|
#include "nsBinaryStream.h"
|
|
|
|
#include "mozilla/EndianUtils.h"
|
|
#include "mozilla/PodOperations.h"
|
|
#include "mozilla/RefPtr.h"
|
|
#include "mozilla/Span.h"
|
|
#include "mozilla/UniquePtr.h"
|
|
|
|
#include "nsCRT.h"
|
|
#include "nsString.h"
|
|
#include "nsISerializable.h"
|
|
#include "nsIClassInfo.h"
|
|
#include "nsComponentManagerUtils.h"
|
|
#include "nsIURI.h" // for NS_IURI_IID
|
|
#include "nsIX509Cert.h" // for NS_IX509CERT_IID
|
|
|
|
#include "js/ArrayBuffer.h" // JS::{GetArrayBuffer{,ByteLength},IsArrayBufferObject}
|
|
#include "js/GCAPI.h" // JS::AutoCheckCannotGC
|
|
#include "js/RootingAPI.h" // JS::{Handle,Rooted}
|
|
#include "js/Value.h" // JS::Value
|
|
|
|
using mozilla::AsBytes;
|
|
using mozilla::MakeUnique;
|
|
using mozilla::PodCopy;
|
|
using mozilla::Span;
|
|
using mozilla::UniquePtr;
|
|
|
|
already_AddRefed<nsIObjectOutputStream> NS_NewObjectOutputStream(
|
|
nsIOutputStream* aOutputStream) {
|
|
MOZ_ASSERT(aOutputStream);
|
|
auto stream = mozilla::MakeRefPtr<nsBinaryOutputStream>();
|
|
|
|
MOZ_ALWAYS_SUCCEEDS(stream->SetOutputStream(aOutputStream));
|
|
return stream.forget();
|
|
}
|
|
|
|
already_AddRefed<nsIObjectInputStream> NS_NewObjectInputStream(
|
|
nsIInputStream* aInputStream) {
|
|
MOZ_ASSERT(aInputStream);
|
|
auto stream = mozilla::MakeRefPtr<nsBinaryInputStream>();
|
|
|
|
MOZ_ALWAYS_SUCCEEDS(stream->SetInputStream(aInputStream));
|
|
return stream.forget();
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS(nsBinaryOutputStream, nsIObjectOutputStream,
|
|
nsIBinaryOutputStream, nsIOutputStream)
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryOutputStream::Flush() {
|
|
if (NS_WARN_IF(!mOutputStream)) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
return mOutputStream->Flush();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryOutputStream::Close() {
|
|
if (NS_WARN_IF(!mOutputStream)) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
return mOutputStream->Close();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryOutputStream::StreamStatus() {
|
|
if (NS_WARN_IF(!mOutputStream)) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
return mOutputStream->StreamStatus();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryOutputStream::Write(const char* aBuf, uint32_t aCount,
|
|
uint32_t* aActualBytes) {
|
|
if (NS_WARN_IF(!mOutputStream)) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
return mOutputStream->Write(aBuf, aCount, aActualBytes);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryOutputStream::WriteFrom(nsIInputStream* aInStr, uint32_t aCount,
|
|
uint32_t* aResult) {
|
|
MOZ_ASSERT_UNREACHABLE("WriteFrom");
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryOutputStream::WriteSegments(nsReadSegmentFun aReader, void* aClosure,
|
|
uint32_t aCount, uint32_t* aResult) {
|
|
MOZ_ASSERT_UNREACHABLE("WriteSegments");
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryOutputStream::IsNonBlocking(bool* aNonBlocking) {
|
|
if (NS_WARN_IF(!mOutputStream)) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
return mOutputStream->IsNonBlocking(aNonBlocking);
|
|
}
|
|
|
|
nsresult nsBinaryOutputStream::WriteFully(const char* aBuf, uint32_t aCount) {
|
|
if (NS_WARN_IF(!mOutputStream)) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
nsresult rv;
|
|
uint32_t bytesWritten;
|
|
|
|
rv = mOutputStream->Write(aBuf, aCount, &bytesWritten);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
if (bytesWritten != aCount) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryOutputStream::SetOutputStream(nsIOutputStream* aOutputStream) {
|
|
if (NS_WARN_IF(!aOutputStream)) {
|
|
return NS_ERROR_INVALID_ARG;
|
|
}
|
|
mOutputStream = aOutputStream;
|
|
mBufferAccess = do_QueryInterface(aOutputStream);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryOutputStream::WriteBoolean(bool aBoolean) { return Write8(aBoolean); }
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryOutputStream::Write8(uint8_t aByte) {
|
|
return WriteFully((const char*)&aByte, sizeof(aByte));
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryOutputStream::Write16(uint16_t aNum) {
|
|
aNum = mozilla::NativeEndian::swapToBigEndian(aNum);
|
|
return WriteFully((const char*)&aNum, sizeof(aNum));
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryOutputStream::Write32(uint32_t aNum) {
|
|
aNum = mozilla::NativeEndian::swapToBigEndian(aNum);
|
|
return WriteFully((const char*)&aNum, sizeof(aNum));
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryOutputStream::Write64(uint64_t aNum) {
|
|
nsresult rv;
|
|
uint32_t bytesWritten;
|
|
|
|
aNum = mozilla::NativeEndian::swapToBigEndian(aNum);
|
|
rv = Write(reinterpret_cast<char*>(&aNum), sizeof(aNum), &bytesWritten);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
if (bytesWritten != sizeof(aNum)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryOutputStream::WriteFloat(float aFloat) {
|
|
static_assert(sizeof(float) == sizeof(uint32_t),
|
|
"False assumption about sizeof(float)");
|
|
return Write32(*reinterpret_cast<uint32_t*>(&aFloat));
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryOutputStream::WriteDouble(double aDouble) {
|
|
static_assert(sizeof(double) == sizeof(uint64_t),
|
|
"False assumption about sizeof(double)");
|
|
return Write64(*reinterpret_cast<uint64_t*>(&aDouble));
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryOutputStream::WriteStringZ(const char* aString) {
|
|
uint32_t length;
|
|
nsresult rv;
|
|
|
|
length = strlen(aString);
|
|
rv = Write32(length);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
return WriteFully(aString, length);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryOutputStream::WriteWStringZ(const char16_t* aString) {
|
|
uint32_t length = NS_strlen(aString);
|
|
nsresult rv = Write32(length);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
if (length == 0) {
|
|
return NS_OK;
|
|
}
|
|
|
|
#ifdef IS_BIG_ENDIAN
|
|
rv = WriteBytes(AsBytes(Span(aString, length)));
|
|
#else
|
|
// XXX use WriteSegments here to avoid copy!
|
|
char16_t* copy;
|
|
char16_t temp[64];
|
|
if (length <= 64) {
|
|
copy = temp;
|
|
} else {
|
|
copy = static_cast<char16_t*>(malloc(length * sizeof(char16_t)));
|
|
if (!copy) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
NS_ASSERTION((uintptr_t(aString) & 0x1) == 0, "aString not properly aligned");
|
|
mozilla::NativeEndian::copyAndSwapToBigEndian(copy, aString, length);
|
|
rv = WriteBytes(AsBytes(Span(copy, length)));
|
|
if (copy != temp) {
|
|
free(copy);
|
|
}
|
|
#endif
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryOutputStream::WriteUtf8Z(const char16_t* aString) {
|
|
return WriteStringZ(NS_ConvertUTF16toUTF8(aString).get());
|
|
}
|
|
|
|
nsresult nsBinaryOutputStream::WriteBytes(Span<const uint8_t> aBytes) {
|
|
nsresult rv;
|
|
uint32_t bytesWritten;
|
|
|
|
rv = Write(reinterpret_cast<const char*>(aBytes.Elements()), aBytes.Length(),
|
|
&bytesWritten);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
if (bytesWritten != aBytes.Length()) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryOutputStream::WriteBytesFromJS(const char* aString, uint32_t aLength) {
|
|
return WriteBytes(AsBytes(Span(aString, aLength)));
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryOutputStream::WriteByteArray(const nsTArray<uint8_t>& aByteArray) {
|
|
return WriteBytes(aByteArray);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryOutputStream::WriteObject(nsISupports* aObject, bool aIsStrongRef) {
|
|
return WriteCompoundObject(aObject, NS_GET_IID(nsISupports), aIsStrongRef);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryOutputStream::WriteSingleRefObject(nsISupports* aObject) {
|
|
return WriteCompoundObject(aObject, NS_GET_IID(nsISupports), true);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryOutputStream::WriteCompoundObject(nsISupports* aObject,
|
|
const nsIID& aIID,
|
|
bool aIsStrongRef) {
|
|
nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(aObject);
|
|
nsCOMPtr<nsISerializable> serializable = do_QueryInterface(aObject);
|
|
|
|
// Can't deal with weak refs
|
|
if (NS_WARN_IF(!aIsStrongRef)) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
if (NS_WARN_IF(!classInfo) || NS_WARN_IF(!serializable)) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
nsCID cid;
|
|
nsresult rv = classInfo->GetClassIDNoAlloc(&cid);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
rv = WriteID(cid);
|
|
} else {
|
|
nsCID* cidptr = nullptr;
|
|
rv = classInfo->GetClassID(&cidptr);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
rv = WriteID(*cidptr);
|
|
|
|
free(cidptr);
|
|
}
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
rv = WriteID(aIID);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
return serializable->Write(this);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryOutputStream::WriteID(const nsIID& aIID) {
|
|
nsresult rv = Write32(aIID.m0);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
rv = Write16(aIID.m1);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
rv = Write16(aIID.m2);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
rv = WriteBytes(aIID.m3);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP_(char*)
|
|
nsBinaryOutputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask) {
|
|
if (mBufferAccess) {
|
|
return mBufferAccess->GetBuffer(aLength, aAlignMask);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
NS_IMETHODIMP_(void)
|
|
nsBinaryOutputStream::PutBuffer(char* aBuffer, uint32_t aLength) {
|
|
if (mBufferAccess) {
|
|
mBufferAccess->PutBuffer(aBuffer, aLength);
|
|
}
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS(nsBinaryInputStream, nsIObjectInputStream,
|
|
nsIBinaryInputStream, nsIInputStream)
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryInputStream::Available(uint64_t* aResult) {
|
|
if (NS_WARN_IF(!mInputStream)) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
return mInputStream->Available(aResult);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryInputStream::StreamStatus() {
|
|
if (NS_WARN_IF(!mInputStream)) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
return mInputStream->StreamStatus();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aNumRead) {
|
|
if (NS_WARN_IF(!mInputStream)) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
// mInputStream might give us short reads, so deal with that.
|
|
uint32_t totalRead = 0;
|
|
|
|
uint32_t bytesRead;
|
|
do {
|
|
nsresult rv = mInputStream->Read(aBuffer, aCount, &bytesRead);
|
|
if (rv == NS_BASE_STREAM_WOULD_BLOCK && totalRead != 0) {
|
|
// We already read some data. Return it.
|
|
break;
|
|
}
|
|
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
totalRead += bytesRead;
|
|
aBuffer += bytesRead;
|
|
aCount -= bytesRead;
|
|
} while (aCount != 0 && bytesRead != 0);
|
|
|
|
*aNumRead = totalRead;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// when forwarding ReadSegments to mInputStream, we need to make sure
|
|
// 'this' is being passed to the writer each time. To do this, we need
|
|
// a thunking function which keeps the real input stream around.
|
|
|
|
// the closure wrapper
|
|
struct MOZ_STACK_CLASS ReadSegmentsClosure {
|
|
nsCOMPtr<nsIInputStream> mRealInputStream;
|
|
void* mRealClosure;
|
|
nsWriteSegmentFun mRealWriter;
|
|
nsresult mRealResult;
|
|
uint32_t mBytesRead; // to properly implement aToOffset
|
|
};
|
|
|
|
// the thunking function
|
|
static nsresult ReadSegmentForwardingThunk(nsIInputStream* aStream,
|
|
void* aClosure,
|
|
const char* aFromSegment,
|
|
uint32_t aToOffset, uint32_t aCount,
|
|
uint32_t* aWriteCount) {
|
|
ReadSegmentsClosure* thunkClosure =
|
|
reinterpret_cast<ReadSegmentsClosure*>(aClosure);
|
|
|
|
NS_ASSERTION(NS_SUCCEEDED(thunkClosure->mRealResult),
|
|
"How did this get to be a failure status?");
|
|
|
|
thunkClosure->mRealResult = thunkClosure->mRealWriter(
|
|
thunkClosure->mRealInputStream, thunkClosure->mRealClosure, aFromSegment,
|
|
thunkClosure->mBytesRead + aToOffset, aCount, aWriteCount);
|
|
|
|
return thunkClosure->mRealResult;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
|
|
uint32_t aCount, uint32_t* aResult) {
|
|
if (NS_WARN_IF(!mInputStream)) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
ReadSegmentsClosure thunkClosure = {this, aClosure, aWriter, NS_OK, 0};
|
|
|
|
// mInputStream might give us short reads, so deal with that.
|
|
uint32_t bytesRead;
|
|
do {
|
|
nsresult rv = mInputStream->ReadSegments(ReadSegmentForwardingThunk,
|
|
&thunkClosure, aCount, &bytesRead);
|
|
|
|
if (rv == NS_BASE_STREAM_WOULD_BLOCK && thunkClosure.mBytesRead != 0) {
|
|
// We already read some data. Return it.
|
|
break;
|
|
}
|
|
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
thunkClosure.mBytesRead += bytesRead;
|
|
aCount -= bytesRead;
|
|
} while (aCount != 0 && bytesRead != 0 &&
|
|
NS_SUCCEEDED(thunkClosure.mRealResult));
|
|
|
|
*aResult = thunkClosure.mBytesRead;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryInputStream::IsNonBlocking(bool* aNonBlocking) {
|
|
if (NS_WARN_IF(!mInputStream)) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
return mInputStream->IsNonBlocking(aNonBlocking);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryInputStream::Close() {
|
|
if (NS_WARN_IF(!mInputStream)) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
return mInputStream->Close();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryInputStream::SetInputStream(nsIInputStream* aInputStream) {
|
|
if (NS_WARN_IF(!aInputStream)) {
|
|
return NS_ERROR_INVALID_ARG;
|
|
}
|
|
mInputStream = aInputStream;
|
|
mBufferAccess = do_QueryInterface(aInputStream);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryInputStream::ReadBoolean(bool* aBoolean) {
|
|
uint8_t byteResult;
|
|
nsresult rv = Read8(&byteResult);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
*aBoolean = !!byteResult;
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryInputStream::Read8(uint8_t* aByte) {
|
|
nsresult rv;
|
|
uint32_t bytesRead;
|
|
|
|
rv = Read(reinterpret_cast<char*>(aByte), sizeof(*aByte), &bytesRead);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
if (bytesRead != 1) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryInputStream::Read16(uint16_t* aNum) {
|
|
uint32_t bytesRead;
|
|
nsresult rv = Read(reinterpret_cast<char*>(aNum), sizeof(*aNum), &bytesRead);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
if (bytesRead != sizeof(*aNum)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
*aNum = mozilla::NativeEndian::swapFromBigEndian(*aNum);
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryInputStream::Read32(uint32_t* aNum) {
|
|
uint32_t bytesRead;
|
|
nsresult rv = Read(reinterpret_cast<char*>(aNum), sizeof(*aNum), &bytesRead);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
if (bytesRead != sizeof(*aNum)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
*aNum = mozilla::NativeEndian::swapFromBigEndian(*aNum);
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryInputStream::Read64(uint64_t* aNum) {
|
|
uint32_t bytesRead;
|
|
nsresult rv = Read(reinterpret_cast<char*>(aNum), sizeof(*aNum), &bytesRead);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
if (bytesRead != sizeof(*aNum)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
*aNum = mozilla::NativeEndian::swapFromBigEndian(*aNum);
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryInputStream::ReadFloat(float* aFloat) {
|
|
static_assert(sizeof(float) == sizeof(uint32_t),
|
|
"False assumption about sizeof(float)");
|
|
return Read32(reinterpret_cast<uint32_t*>(aFloat));
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryInputStream::ReadDouble(double* aDouble) {
|
|
static_assert(sizeof(double) == sizeof(uint64_t),
|
|
"False assumption about sizeof(double)");
|
|
return Read64(reinterpret_cast<uint64_t*>(aDouble));
|
|
}
|
|
|
|
static nsresult WriteSegmentToCString(nsIInputStream* aStream, void* aClosure,
|
|
const char* aFromSegment,
|
|
uint32_t aToOffset, uint32_t aCount,
|
|
uint32_t* aWriteCount) {
|
|
nsACString* outString = static_cast<nsACString*>(aClosure);
|
|
|
|
outString->Append(aFromSegment, aCount);
|
|
|
|
*aWriteCount = aCount;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryInputStream::ReadCString(nsACString& aString) {
|
|
nsresult rv;
|
|
uint32_t length, bytesRead;
|
|
|
|
rv = Read32(&length);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
aString.Truncate();
|
|
rv = ReadSegments(WriteSegmentToCString, &aString, length, &bytesRead);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
if (bytesRead != length) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// sometimes, WriteSegmentToString will be handed an odd-number of
|
|
// bytes, which means we only have half of the last char16_t
|
|
struct WriteStringClosure {
|
|
char16_t* mWriteCursor;
|
|
bool mHasCarryoverByte;
|
|
char mCarryoverByte;
|
|
};
|
|
|
|
// there are a few cases we have to account for here:
|
|
// * even length buffer, no carryover - easy, just append
|
|
// * odd length buffer, no carryover - the last byte needs to be saved
|
|
// for carryover
|
|
// * odd length buffer, with carryover - first byte needs to be used
|
|
// with the carryover byte, and
|
|
// the rest of the even length
|
|
// buffer is appended as normal
|
|
// * even length buffer, with carryover - the first byte needs to be
|
|
// used with the previous carryover byte.
|
|
// this gives you an odd length buffer,
|
|
// so you have to save the last byte for
|
|
// the next carryover
|
|
|
|
// same version of the above, but with correct casting and endian swapping
|
|
static nsresult WriteSegmentToString(nsIInputStream* aStream, void* aClosure,
|
|
const char* aFromSegment,
|
|
uint32_t aToOffset, uint32_t aCount,
|
|
uint32_t* aWriteCount) {
|
|
MOZ_ASSERT(aCount > 0, "Why are we being told to write 0 bytes?");
|
|
static_assert(sizeof(char16_t) == 2, "We can't handle other sizes!");
|
|
|
|
WriteStringClosure* closure = static_cast<WriteStringClosure*>(aClosure);
|
|
char16_t* cursor = closure->mWriteCursor;
|
|
|
|
// we're always going to consume the whole buffer no matter what
|
|
// happens, so take care of that right now.. that allows us to
|
|
// tweak aCount later. Do NOT move this!
|
|
*aWriteCount = aCount;
|
|
|
|
// if the last Write had an odd-number of bytes read, then
|
|
if (closure->mHasCarryoverByte) {
|
|
// re-create the two-byte sequence we want to work with
|
|
char bytes[2] = {closure->mCarryoverByte, *aFromSegment};
|
|
*cursor = *(char16_t*)bytes;
|
|
// Now the little endianness dance
|
|
mozilla::NativeEndian::swapToBigEndianInPlace(cursor, 1);
|
|
++cursor;
|
|
|
|
// now skip past the first byte of the buffer.. code from here
|
|
// can assume normal operations, but should not assume aCount
|
|
// is relative to the ORIGINAL buffer
|
|
++aFromSegment;
|
|
--aCount;
|
|
|
|
closure->mHasCarryoverByte = false;
|
|
}
|
|
|
|
// this array is possibly unaligned... be careful how we access it!
|
|
const char16_t* unicodeSegment =
|
|
reinterpret_cast<const char16_t*>(aFromSegment);
|
|
|
|
// calculate number of full characters in segment (aCount could be odd!)
|
|
uint32_t segmentLength = aCount / sizeof(char16_t);
|
|
|
|
// copy all data into our aligned buffer. byte swap if necessary.
|
|
// cursor may be unaligned, so we cannot use copyAndSwapToBigEndian directly
|
|
memcpy(cursor, unicodeSegment, segmentLength * sizeof(char16_t));
|
|
char16_t* end = cursor + segmentLength;
|
|
mozilla::NativeEndian::swapToBigEndianInPlace(cursor, segmentLength);
|
|
closure->mWriteCursor = end;
|
|
|
|
// remember this is the modifed aCount and aFromSegment,
|
|
// so that will take into account the fact that we might have
|
|
// skipped the first byte in the buffer
|
|
if (aCount % sizeof(char16_t) != 0) {
|
|
// we must have had a carryover byte, that we'll need the next
|
|
// time around
|
|
closure->mCarryoverByte = aFromSegment[aCount - 1];
|
|
closure->mHasCarryoverByte = true;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryInputStream::ReadString(nsAString& aString) {
|
|
nsresult rv;
|
|
uint32_t length, bytesRead;
|
|
|
|
rv = Read32(&length);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
if (length == 0) {
|
|
aString.Truncate();
|
|
return NS_OK;
|
|
}
|
|
|
|
// pre-allocate output buffer, and get direct access to buffer...
|
|
if (!aString.SetLength(length, mozilla::fallible)) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
WriteStringClosure closure;
|
|
closure.mWriteCursor = aString.BeginWriting();
|
|
closure.mHasCarryoverByte = false;
|
|
|
|
rv = ReadSegments(WriteSegmentToString, &closure, length * sizeof(char16_t),
|
|
&bytesRead);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
NS_ASSERTION(!closure.mHasCarryoverByte, "some strange stream corruption!");
|
|
|
|
if (bytesRead != length * sizeof(char16_t)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsBinaryInputStream::ReadBytesToBuffer(uint32_t aLength,
|
|
uint8_t* aBuffer) {
|
|
uint32_t bytesRead;
|
|
nsresult rv = Read(reinterpret_cast<char*>(aBuffer), aLength, &bytesRead);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
if (bytesRead != aLength) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryInputStream::ReadBytes(uint32_t aLength, char** aResult) {
|
|
char* s = static_cast<char*>(malloc(aLength));
|
|
if (!s) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
nsresult rv = ReadBytesToBuffer(aLength, reinterpret_cast<uint8_t*>(s));
|
|
if (NS_FAILED(rv)) {
|
|
free(s);
|
|
return rv;
|
|
}
|
|
|
|
*aResult = s;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryInputStream::ReadByteArray(uint32_t aLength,
|
|
nsTArray<uint8_t>& aResult) {
|
|
if (!aResult.SetLength(aLength, mozilla::fallible)) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
nsresult rv = ReadBytesToBuffer(aLength, aResult.Elements());
|
|
if (NS_FAILED(rv)) {
|
|
aResult.Clear();
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryInputStream::ReadArrayBuffer(uint64_t aLength,
|
|
JS::Handle<JS::Value> aBuffer,
|
|
JSContext* aCx, uint64_t* aReadLength) {
|
|
if (!aBuffer.isObject()) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
JS::Rooted<JSObject*> buffer(aCx, &aBuffer.toObject());
|
|
if (!JS::IsArrayBufferObject(buffer)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
size_t bufferLength = JS::GetArrayBufferByteLength(buffer);
|
|
if (bufferLength < aLength) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
uint32_t bufSize = std::min<uint64_t>(aLength, 4096);
|
|
UniquePtr<char[]> buf = MakeUnique<char[]>(bufSize);
|
|
|
|
uint64_t pos = 0;
|
|
*aReadLength = 0;
|
|
do {
|
|
// Read data into temporary buffer.
|
|
uint32_t bytesRead;
|
|
uint32_t amount = std::min<uint64_t>(aLength - pos, bufSize);
|
|
nsresult rv = Read(buf.get(), amount, &bytesRead);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
MOZ_ASSERT(bytesRead <= amount);
|
|
|
|
if (bytesRead == 0) {
|
|
break;
|
|
}
|
|
|
|
// Copy data into actual buffer.
|
|
|
|
JS::AutoCheckCannotGC nogc;
|
|
bool isShared;
|
|
if (bufferLength != JS::GetArrayBufferByteLength(buffer)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
char* data = reinterpret_cast<char*>(
|
|
JS::GetArrayBufferData(buffer, &isShared, nogc));
|
|
MOZ_ASSERT(!isShared); // Implied by JS::GetArrayBufferData()
|
|
if (!data) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
*aReadLength += bytesRead;
|
|
PodCopy(data + pos, buf.get(), bytesRead);
|
|
|
|
pos += bytesRead;
|
|
} while (pos < aLength);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryInputStream::ReadObject(bool aIsStrongRef, nsISupports** aObject) {
|
|
nsCID cid;
|
|
nsIID iid;
|
|
nsresult rv = ReadID(&cid);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
rv = ReadID(&iid);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
// HACK: Intercept old (pre-gecko6) nsIURI IID, and replace with
|
|
// the updated IID, so that we're QI'ing to an actual interface.
|
|
// (As soon as we drop support for upgrading from pre-gecko6, we can
|
|
// remove this chunk.)
|
|
static const nsIID oldURIiid = {
|
|
0x7a22cc0,
|
|
0xce5,
|
|
0x11d3,
|
|
{0x93, 0x31, 0x0, 0x10, 0x4b, 0xa0, 0xfd, 0x40}};
|
|
|
|
// hackaround for bug 670542
|
|
static const nsIID oldURIiid2 = {
|
|
0xd6d04c36,
|
|
0x0fa4,
|
|
0x4db3,
|
|
{0xbe, 0x05, 0x4a, 0x18, 0x39, 0x71, 0x03, 0xe2}};
|
|
|
|
// hackaround for bug 682031
|
|
static const nsIID oldURIiid3 = {
|
|
0x12120b20,
|
|
0x0929,
|
|
0x40e9,
|
|
{0x88, 0xcf, 0x6e, 0x08, 0x76, 0x6e, 0x8b, 0x23}};
|
|
|
|
// hackaround for bug 1195415
|
|
static const nsIID oldURIiid4 = {
|
|
0x395fe045,
|
|
0x7d18,
|
|
0x4adb,
|
|
{0xa3, 0xfd, 0xaf, 0x98, 0xc8, 0xa1, 0xaf, 0x11}};
|
|
|
|
if (iid.Equals(oldURIiid) || iid.Equals(oldURIiid2) ||
|
|
iid.Equals(oldURIiid3) || iid.Equals(oldURIiid4)) {
|
|
const nsIID newURIiid = NS_IURI_IID;
|
|
iid = newURIiid;
|
|
}
|
|
|
|
// Hack around bug 1508939
|
|
// The old CSP serialization can't be handled cleanly when
|
|
// it's embedded in an old style principal
|
|
static const nsIID oldCSPiid = {
|
|
0xb3c4c0ae,
|
|
0xbd5e,
|
|
0x4cad,
|
|
{0x87, 0xe0, 0x8d, 0x21, 0x0d, 0xbb, 0x3f, 0x9f}};
|
|
if (iid.Equals(oldCSPiid)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
// END HACK
|
|
|
|
// HACK: Service workers store resource security info on disk in the dom
|
|
// Cache API. When the uuid of the nsIX509Cert interface changes
|
|
// these serialized objects cannot be loaded any more. This hack
|
|
// works around this issue.
|
|
|
|
// hackaround for bug 1247580 (FF45 to FF46 transition)
|
|
static const nsIID oldCertIID = {
|
|
0xf8ed8364,
|
|
0xced9,
|
|
0x4c6e,
|
|
{0x86, 0xba, 0x48, 0xaf, 0x53, 0xc3, 0x93, 0xe6}};
|
|
|
|
if (iid.Equals(oldCertIID)) {
|
|
const nsIID newCertIID = NS_IX509CERT_IID;
|
|
iid = newCertIID;
|
|
}
|
|
// END HACK
|
|
|
|
nsCOMPtr<nsISupports> object = do_CreateInstance(cid, &rv);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
nsCOMPtr<nsISerializable> serializable = do_QueryInterface(object);
|
|
if (NS_WARN_IF(!serializable)) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
rv = serializable->Read(this);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
return object->QueryInterface(iid, reinterpret_cast<void**>(aObject));
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBinaryInputStream::ReadID(nsID* aResult) {
|
|
nsresult rv = Read32(&aResult->m0);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
rv = Read16(&aResult->m1);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
rv = Read16(&aResult->m2);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
const uint32_t toRead = sizeof(aResult->m3);
|
|
uint32_t bytesRead = 0;
|
|
rv = Read(reinterpret_cast<char*>(&aResult->m3[0]), toRead, &bytesRead);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
if (bytesRead != toRead) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP_(char*)
|
|
nsBinaryInputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask) {
|
|
if (mBufferAccess) {
|
|
return mBufferAccess->GetBuffer(aLength, aAlignMask);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
NS_IMETHODIMP_(void)
|
|
nsBinaryInputStream::PutBuffer(char* aBuffer, uint32_t aLength) {
|
|
if (mBufferAccess) {
|
|
mBufferAccess->PutBuffer(aBuffer, aLength);
|
|
}
|
|
}
|