michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /** michael@0: * This file contains implementations of the nsIBinaryInputStream and michael@0: * nsIBinaryOutputStream interfaces. Together, these interfaces allows reading michael@0: * and writing of primitive data types (integers, floating-point values, michael@0: * booleans, etc.) to a stream in a binary, untagged, fixed-endianness format. michael@0: * This might be used, for example, to implement network protocols or to michael@0: * produce architecture-neutral binary disk files, i.e. ones that can be read michael@0: * and written by both big-endian and little-endian platforms. Output is michael@0: * written in big-endian order (high-order byte first), as this is traditional michael@0: * network order. michael@0: * michael@0: * @See nsIBinaryInputStream michael@0: * @See nsIBinaryOutputStream michael@0: */ michael@0: #include michael@0: #include michael@0: michael@0: #include "nsBinaryStream.h" michael@0: michael@0: #include "mozilla/Endian.h" michael@0: #include "mozilla/PodOperations.h" michael@0: #include "mozilla/Scoped.h" michael@0: michael@0: #include "nsCRT.h" michael@0: #include "nsString.h" michael@0: #include "nsISerializable.h" michael@0: #include "nsIClassInfo.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsIURI.h" // for NS_IURI_IID michael@0: michael@0: #include "jsfriendapi.h" michael@0: michael@0: using mozilla::PodCopy; michael@0: using mozilla::ScopedDeleteArray; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsBinaryOutputStream, nsIObjectOutputStream, nsIBinaryOutputStream, nsIOutputStream) michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryOutputStream::Flush() michael@0: { michael@0: if (NS_WARN_IF(!mOutputStream)) michael@0: return NS_ERROR_UNEXPECTED; michael@0: return mOutputStream->Flush(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryOutputStream::Close() michael@0: { michael@0: if (NS_WARN_IF(!mOutputStream)) michael@0: return NS_ERROR_UNEXPECTED; michael@0: return mOutputStream->Close(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryOutputStream::Write(const char *aBuf, uint32_t aCount, uint32_t *aActualBytes) michael@0: { michael@0: if (NS_WARN_IF(!mOutputStream)) michael@0: return NS_ERROR_UNEXPECTED; michael@0: return mOutputStream->Write(aBuf, aCount, aActualBytes); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryOutputStream::WriteFrom(nsIInputStream *inStr, uint32_t count, uint32_t *_retval) michael@0: { michael@0: NS_NOTREACHED("WriteFrom"); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryOutputStream::WriteSegments(nsReadSegmentFun reader, void * closure, uint32_t count, uint32_t *_retval) michael@0: { michael@0: NS_NOTREACHED("WriteSegments"); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryOutputStream::IsNonBlocking(bool *aNonBlocking) michael@0: { michael@0: if (NS_WARN_IF(!mOutputStream)) michael@0: return NS_ERROR_UNEXPECTED; michael@0: return mOutputStream->IsNonBlocking(aNonBlocking); michael@0: } michael@0: michael@0: nsresult michael@0: nsBinaryOutputStream::WriteFully(const char *aBuf, uint32_t aCount) michael@0: { michael@0: if (NS_WARN_IF(!mOutputStream)) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: nsresult rv; michael@0: uint32_t bytesWritten; michael@0: michael@0: rv = mOutputStream->Write(aBuf, aCount, &bytesWritten); michael@0: if (NS_FAILED(rv)) return rv; michael@0: if (bytesWritten != aCount) michael@0: return NS_ERROR_FAILURE; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryOutputStream::SetOutputStream(nsIOutputStream *aOutputStream) michael@0: { michael@0: if (NS_WARN_IF(!aOutputStream)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: mOutputStream = aOutputStream; michael@0: mBufferAccess = do_QueryInterface(aOutputStream); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryOutputStream::WriteBoolean(bool aBoolean) michael@0: { michael@0: return Write8(aBoolean); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryOutputStream::Write8(uint8_t aByte) michael@0: { michael@0: return WriteFully((const char*)&aByte, sizeof aByte); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryOutputStream::Write16(uint16_t a16) michael@0: { michael@0: a16 = mozilla::NativeEndian::swapToBigEndian(a16); michael@0: return WriteFully((const char*)&a16, sizeof a16); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryOutputStream::Write32(uint32_t a32) michael@0: { michael@0: a32 = mozilla::NativeEndian::swapToBigEndian(a32); michael@0: return WriteFully((const char*)&a32, sizeof a32); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryOutputStream::Write64(uint64_t a64) michael@0: { michael@0: nsresult rv; michael@0: uint32_t bytesWritten; michael@0: michael@0: a64 = mozilla::NativeEndian::swapToBigEndian(a64); michael@0: rv = Write(reinterpret_cast(&a64), sizeof a64, &bytesWritten); michael@0: if (NS_FAILED(rv)) return rv; michael@0: if (bytesWritten != sizeof a64) michael@0: return NS_ERROR_FAILURE; michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryOutputStream::WriteFloat(float aFloat) michael@0: { michael@0: NS_ASSERTION(sizeof(float) == sizeof (uint32_t), michael@0: "False assumption about sizeof(float)"); michael@0: return Write32(*reinterpret_cast(&aFloat)); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryOutputStream::WriteDouble(double aDouble) michael@0: { michael@0: NS_ASSERTION(sizeof(double) == sizeof(uint64_t), michael@0: "False assumption about sizeof(double)"); michael@0: return Write64(*reinterpret_cast(&aDouble)); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryOutputStream::WriteStringZ(const char *aString) michael@0: { michael@0: uint32_t length; michael@0: nsresult rv; michael@0: michael@0: length = strlen(aString); michael@0: rv = Write32(length); michael@0: if (NS_FAILED(rv)) return rv; michael@0: return WriteFully(aString, length); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryOutputStream::WriteWStringZ(const char16_t* aString) michael@0: { michael@0: uint32_t length, byteCount; michael@0: nsresult rv; michael@0: michael@0: length = NS_strlen(aString); michael@0: rv = Write32(length); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (length == 0) michael@0: return NS_OK; michael@0: byteCount = length * sizeof(char16_t); michael@0: michael@0: #ifdef IS_BIG_ENDIAN michael@0: rv = WriteBytes(reinterpret_cast(aString), byteCount); michael@0: #else michael@0: // XXX use WriteSegments here to avoid copy! michael@0: char16_t *copy, temp[64]; michael@0: if (length <= 64) { michael@0: copy = temp; michael@0: } else { michael@0: copy = reinterpret_cast(moz_malloc(byteCount)); michael@0: if (!copy) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: NS_ASSERTION((uintptr_t(aString) & 0x1) == 0, "aString not properly aligned"); michael@0: mozilla::NativeEndian::copyAndSwapToBigEndian(copy, aString, length); michael@0: rv = WriteBytes(reinterpret_cast(copy), byteCount); michael@0: if (copy != temp) michael@0: moz_free(copy); michael@0: #endif michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryOutputStream::WriteUtf8Z(const char16_t* aString) michael@0: { michael@0: return WriteStringZ(NS_ConvertUTF16toUTF8(aString).get()); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryOutputStream::WriteBytes(const char *aString, uint32_t aLength) michael@0: { michael@0: nsresult rv; michael@0: uint32_t bytesWritten; michael@0: michael@0: rv = Write(aString, aLength, &bytesWritten); michael@0: if (NS_FAILED(rv)) return rv; michael@0: if (bytesWritten != aLength) michael@0: return NS_ERROR_FAILURE; michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryOutputStream::WriteByteArray(uint8_t *aBytes, uint32_t aLength) michael@0: { michael@0: return WriteBytes(reinterpret_cast(aBytes), aLength); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryOutputStream::WriteObject(nsISupports* aObject, bool aIsStrongRef) michael@0: { michael@0: return WriteCompoundObject(aObject, NS_GET_IID(nsISupports), michael@0: aIsStrongRef); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryOutputStream::WriteSingleRefObject(nsISupports* aObject) michael@0: { michael@0: return WriteCompoundObject(aObject, NS_GET_IID(nsISupports), michael@0: true); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryOutputStream::WriteCompoundObject(nsISupports* aObject, michael@0: const nsIID& aIID, michael@0: bool aIsStrongRef) michael@0: { michael@0: nsCOMPtr classInfo = do_QueryInterface(aObject); michael@0: nsCOMPtr serializable = do_QueryInterface(aObject); michael@0: michael@0: // Can't deal with weak refs michael@0: if (NS_WARN_IF(!aIsStrongRef)) michael@0: return NS_ERROR_UNEXPECTED; michael@0: if (NS_WARN_IF(!classInfo) || NS_WARN_IF(!serializable)) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: nsCID cid; michael@0: nsresult rv = classInfo->GetClassIDNoAlloc(&cid); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = WriteID(cid); michael@0: } else { michael@0: nsCID *cidptr = nullptr; michael@0: rv = classInfo->GetClassID(&cidptr); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: return rv; michael@0: } michael@0: michael@0: rv = WriteID(*cidptr); michael@0: michael@0: NS_Free(cidptr); michael@0: } michael@0: michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: rv = WriteID(aIID); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: return serializable->Write(this); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryOutputStream::WriteID(const nsIID& aIID) michael@0: { michael@0: nsresult rv = Write32(aIID.m0); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: rv = Write16(aIID.m1); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: rv = Write16(aIID.m2); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: for (int i = 0; i < 8; ++i) { michael@0: rv = Write8(aIID.m3[i]); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(char*) michael@0: nsBinaryOutputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask) michael@0: { michael@0: if (mBufferAccess) michael@0: return mBufferAccess->GetBuffer(aLength, aAlignMask); michael@0: return nullptr; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(void) michael@0: nsBinaryOutputStream::PutBuffer(char* aBuffer, uint32_t aLength) michael@0: { michael@0: if (mBufferAccess) michael@0: mBufferAccess->PutBuffer(aBuffer, aLength); michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsBinaryInputStream, nsIObjectInputStream, nsIBinaryInputStream, nsIInputStream) michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryInputStream::Available(uint64_t* aResult) michael@0: { michael@0: if (NS_WARN_IF(!mInputStream)) michael@0: return NS_ERROR_UNEXPECTED; michael@0: return mInputStream->Available(aResult); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t *aNumRead) michael@0: { michael@0: if (NS_WARN_IF(!mInputStream)) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: // mInputStream might give us short reads, so deal with that. michael@0: uint32_t totalRead = 0; michael@0: michael@0: uint32_t bytesRead; michael@0: do { michael@0: nsresult rv = mInputStream->Read(aBuffer, aCount, &bytesRead); michael@0: if (rv == NS_BASE_STREAM_WOULD_BLOCK && totalRead != 0) { michael@0: // We already read some data. Return it. michael@0: break; michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: totalRead += bytesRead; michael@0: aBuffer += bytesRead; michael@0: aCount -= bytesRead; michael@0: } while (aCount != 0 && bytesRead != 0); michael@0: michael@0: *aNumRead = totalRead; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: // when forwarding ReadSegments to mInputStream, we need to make sure michael@0: // 'this' is being passed to the writer each time. To do this, we need michael@0: // a thunking function which keeps the real input stream around. michael@0: michael@0: // the closure wrapper michael@0: struct ReadSegmentsClosure { michael@0: nsIInputStream* mRealInputStream; michael@0: void* mRealClosure; michael@0: nsWriteSegmentFun mRealWriter; michael@0: nsresult mRealResult; michael@0: uint32_t mBytesRead; // to properly implement aToOffset michael@0: }; michael@0: michael@0: // the thunking function michael@0: static NS_METHOD michael@0: ReadSegmentForwardingThunk(nsIInputStream* aStream, michael@0: void *aClosure, michael@0: const char* aFromSegment, michael@0: uint32_t aToOffset, michael@0: uint32_t aCount, michael@0: uint32_t *aWriteCount) michael@0: { michael@0: ReadSegmentsClosure* thunkClosure = michael@0: reinterpret_cast(aClosure); michael@0: michael@0: NS_ASSERTION(NS_SUCCEEDED(thunkClosure->mRealResult), michael@0: "How did this get to be a failure status?"); michael@0: michael@0: thunkClosure->mRealResult = michael@0: thunkClosure->mRealWriter(thunkClosure->mRealInputStream, michael@0: thunkClosure->mRealClosure, michael@0: aFromSegment, michael@0: thunkClosure->mBytesRead + aToOffset, michael@0: aCount, aWriteCount); michael@0: michael@0: return thunkClosure->mRealResult; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryInputStream::ReadSegments(nsWriteSegmentFun writer, void * closure, uint32_t count, uint32_t *_retval) michael@0: { michael@0: if (NS_WARN_IF(!mInputStream)) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: ReadSegmentsClosure thunkClosure = { this, closure, writer, NS_OK, 0 }; michael@0: michael@0: // mInputStream might give us short reads, so deal with that. michael@0: uint32_t bytesRead; michael@0: do { michael@0: nsresult rv = mInputStream->ReadSegments(ReadSegmentForwardingThunk, michael@0: &thunkClosure, michael@0: count, &bytesRead); michael@0: michael@0: if (rv == NS_BASE_STREAM_WOULD_BLOCK && thunkClosure.mBytesRead != 0) { michael@0: // We already read some data. Return it. michael@0: break; michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: thunkClosure.mBytesRead += bytesRead; michael@0: count -= bytesRead; michael@0: } while (count != 0 && bytesRead != 0 && michael@0: NS_SUCCEEDED(thunkClosure.mRealResult)); michael@0: michael@0: *_retval = thunkClosure.mBytesRead; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryInputStream::IsNonBlocking(bool *aNonBlocking) michael@0: { michael@0: if (NS_WARN_IF(!mInputStream)) michael@0: return NS_ERROR_UNEXPECTED; michael@0: return mInputStream->IsNonBlocking(aNonBlocking); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryInputStream::Close() michael@0: { michael@0: if (NS_WARN_IF(!mInputStream)) michael@0: return NS_ERROR_UNEXPECTED; michael@0: return mInputStream->Close(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryInputStream::SetInputStream(nsIInputStream *aInputStream) michael@0: { michael@0: if (NS_WARN_IF(!aInputStream)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: mInputStream = aInputStream; michael@0: mBufferAccess = do_QueryInterface(aInputStream); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryInputStream::ReadBoolean(bool* aBoolean) michael@0: { michael@0: uint8_t byteResult; michael@0: nsresult rv = Read8(&byteResult); michael@0: if (NS_FAILED(rv)) return rv; michael@0: *aBoolean = !!byteResult; michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryInputStream::Read8(uint8_t* aByte) michael@0: { michael@0: nsresult rv; michael@0: uint32_t bytesRead; michael@0: michael@0: rv = Read(reinterpret_cast(aByte), sizeof(*aByte), &bytesRead); michael@0: if (NS_FAILED(rv)) return rv; michael@0: if (bytesRead != 1) michael@0: return NS_ERROR_FAILURE; michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryInputStream::Read16(uint16_t* a16) michael@0: { michael@0: nsresult rv; michael@0: uint32_t bytesRead; michael@0: michael@0: rv = Read(reinterpret_cast(a16), sizeof *a16, &bytesRead); michael@0: if (NS_FAILED(rv)) return rv; michael@0: if (bytesRead != sizeof *a16) michael@0: return NS_ERROR_FAILURE; michael@0: *a16 = mozilla::NativeEndian::swapFromBigEndian(*a16); michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryInputStream::Read32(uint32_t* a32) michael@0: { michael@0: nsresult rv; michael@0: uint32_t bytesRead; michael@0: michael@0: rv = Read(reinterpret_cast(a32), sizeof *a32, &bytesRead); michael@0: if (NS_FAILED(rv)) return rv; michael@0: if (bytesRead != sizeof *a32) michael@0: return NS_ERROR_FAILURE; michael@0: *a32 = mozilla::NativeEndian::swapFromBigEndian(*a32); michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryInputStream::Read64(uint64_t* a64) michael@0: { michael@0: nsresult rv; michael@0: uint32_t bytesRead; michael@0: michael@0: rv = Read(reinterpret_cast(a64), sizeof *a64, &bytesRead); michael@0: if (NS_FAILED(rv)) return rv; michael@0: if (bytesRead != sizeof *a64) michael@0: return NS_ERROR_FAILURE; michael@0: *a64 = mozilla::NativeEndian::swapFromBigEndian(*a64); michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryInputStream::ReadFloat(float* aFloat) michael@0: { michael@0: NS_ASSERTION(sizeof(float) == sizeof (uint32_t), michael@0: "False assumption about sizeof(float)"); michael@0: return Read32(reinterpret_cast(aFloat)); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryInputStream::ReadDouble(double* aDouble) michael@0: { michael@0: NS_ASSERTION(sizeof(double) == sizeof(uint64_t), michael@0: "False assumption about sizeof(double)"); michael@0: return Read64(reinterpret_cast(aDouble)); michael@0: } michael@0: michael@0: static NS_METHOD michael@0: WriteSegmentToCString(nsIInputStream* aStream, michael@0: void *aClosure, michael@0: const char* aFromSegment, michael@0: uint32_t aToOffset, michael@0: uint32_t aCount, michael@0: uint32_t *aWriteCount) michael@0: { michael@0: nsACString* outString = static_cast(aClosure); michael@0: michael@0: outString->Append(aFromSegment, aCount); michael@0: michael@0: *aWriteCount = aCount; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryInputStream::ReadCString(nsACString& aString) michael@0: { michael@0: nsresult rv; michael@0: uint32_t length, bytesRead; michael@0: michael@0: rv = Read32(&length); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: aString.Truncate(); michael@0: rv = ReadSegments(WriteSegmentToCString, &aString, length, &bytesRead); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (bytesRead != length) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: // sometimes, WriteSegmentToString will be handed an odd-number of michael@0: // bytes, which means we only have half of the last char16_t michael@0: struct WriteStringClosure { michael@0: char16_t *mWriteCursor; michael@0: bool mHasCarryoverByte; michael@0: char mCarryoverByte; michael@0: }; michael@0: michael@0: // there are a few cases we have to account for here: michael@0: // * even length buffer, no carryover - easy, just append michael@0: // * odd length buffer, no carryover - the last byte needs to be saved michael@0: // for carryover michael@0: // * odd length buffer, with carryover - first byte needs to be used michael@0: // with the carryover byte, and michael@0: // the rest of the even length michael@0: // buffer is appended as normal michael@0: // * even length buffer, with carryover - the first byte needs to be michael@0: // used with the previous carryover byte. michael@0: // this gives you an odd length buffer, michael@0: // so you have to save the last byte for michael@0: // the next carryover michael@0: michael@0: michael@0: // same version of the above, but with correct casting and endian swapping michael@0: static NS_METHOD michael@0: WriteSegmentToString(nsIInputStream* aStream, michael@0: void *aClosure, michael@0: const char* aFromSegment, michael@0: uint32_t aToOffset, michael@0: uint32_t aCount, michael@0: uint32_t *aWriteCount) michael@0: { michael@0: NS_PRECONDITION(aCount > 0, "Why are we being told to write 0 bytes?"); michael@0: NS_PRECONDITION(sizeof(char16_t) == 2, "We can't handle other sizes!"); michael@0: michael@0: WriteStringClosure* closure = static_cast(aClosure); michael@0: char16_t *cursor = closure->mWriteCursor; michael@0: michael@0: // we're always going to consume the whole buffer no matter what michael@0: // happens, so take care of that right now.. that allows us to michael@0: // tweak aCount later. Do NOT move this! michael@0: *aWriteCount = aCount; michael@0: michael@0: // if the last Write had an odd-number of bytes read, then michael@0: if (closure->mHasCarryoverByte) { michael@0: // re-create the two-byte sequence we want to work with michael@0: char bytes[2] = { closure->mCarryoverByte, *aFromSegment }; michael@0: *cursor = *(char16_t*)bytes; michael@0: // Now the little endianness dance michael@0: mozilla::NativeEndian::swapToBigEndianInPlace(cursor, 1); michael@0: ++cursor; michael@0: michael@0: // now skip past the first byte of the buffer.. code from here michael@0: // can assume normal operations, but should not assume aCount michael@0: // is relative to the ORIGINAL buffer michael@0: ++aFromSegment; michael@0: --aCount; michael@0: michael@0: closure->mHasCarryoverByte = false; michael@0: } michael@0: michael@0: // this array is possibly unaligned... be careful how we access it! michael@0: const char16_t *unicodeSegment = michael@0: reinterpret_cast(aFromSegment); michael@0: michael@0: // calculate number of full characters in segment (aCount could be odd!) michael@0: uint32_t segmentLength = aCount / sizeof(char16_t); michael@0: michael@0: // copy all data into our aligned buffer. byte swap if necessary. michael@0: // cursor may be unaligned, so we cannot use copyAndSwapToBigEndian directly michael@0: memcpy(cursor, unicodeSegment, segmentLength * sizeof(char16_t)); michael@0: char16_t *end = cursor + segmentLength; michael@0: mozilla::NativeEndian::swapToBigEndianInPlace(cursor, segmentLength); michael@0: closure->mWriteCursor = end; michael@0: michael@0: // remember this is the modifed aCount and aFromSegment, michael@0: // so that will take into account the fact that we might have michael@0: // skipped the first byte in the buffer michael@0: if (aCount % sizeof(char16_t) != 0) { michael@0: // we must have had a carryover byte, that we'll need the next michael@0: // time around michael@0: closure->mCarryoverByte = aFromSegment[aCount - 1]; michael@0: closure->mHasCarryoverByte = true; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryInputStream::ReadString(nsAString& aString) michael@0: { michael@0: nsresult rv; michael@0: uint32_t length, bytesRead; michael@0: michael@0: rv = Read32(&length); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (length == 0) { michael@0: aString.Truncate(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // pre-allocate output buffer, and get direct access to buffer... michael@0: if (!aString.SetLength(length, mozilla::fallible_t())) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: nsAString::iterator start; michael@0: aString.BeginWriting(start); michael@0: michael@0: WriteStringClosure closure; michael@0: closure.mWriteCursor = start.get(); michael@0: closure.mHasCarryoverByte = false; michael@0: michael@0: rv = ReadSegments(WriteSegmentToString, &closure, michael@0: length*sizeof(char16_t), &bytesRead); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: NS_ASSERTION(!closure.mHasCarryoverByte, "some strange stream corruption!"); michael@0: michael@0: if (bytesRead != length*sizeof(char16_t)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryInputStream::ReadBytes(uint32_t aLength, char* *_rval) michael@0: { michael@0: nsresult rv; michael@0: uint32_t bytesRead; michael@0: char* s; michael@0: michael@0: s = reinterpret_cast(moz_malloc(aLength)); michael@0: if (!s) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: rv = Read(s, aLength, &bytesRead); michael@0: if (NS_FAILED(rv)) { michael@0: moz_free(s); michael@0: return rv; michael@0: } michael@0: if (bytesRead != aLength) { michael@0: moz_free(s); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: *_rval = s; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryInputStream::ReadByteArray(uint32_t aLength, uint8_t* *_rval) michael@0: { michael@0: return ReadBytes(aLength, reinterpret_cast(_rval)); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryInputStream::ReadArrayBuffer(uint32_t aLength, JS::Handle aBuffer, JSContext* cx) michael@0: { michael@0: if (!aBuffer.isObject()) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: JS::RootedObject buffer(cx, &aBuffer.toObject()); michael@0: if (!JS_IsArrayBufferObject(buffer)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: uint32_t bufferLength = JS_GetArrayBufferByteLength(buffer); michael@0: if (bufferLength < aLength) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: char* data = reinterpret_cast(JS_GetStableArrayBufferData(cx, buffer)); michael@0: if (!data) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: uint32_t bufSize = std::min(aLength, 4096); michael@0: ScopedDeleteArray buf(new char[bufSize]); michael@0: michael@0: uint32_t remaining = aLength; michael@0: do { michael@0: // Read data into temporary buffer. michael@0: uint32_t bytesRead; michael@0: uint32_t amount = std::min(remaining, bufSize); michael@0: nsresult rv = Read(buf, amount, &bytesRead); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: return rv; michael@0: } michael@0: MOZ_ASSERT(bytesRead <= amount); michael@0: michael@0: if (bytesRead == 0) { michael@0: break; michael@0: } michael@0: michael@0: // Copy data into actual buffer. michael@0: if (bufferLength != JS_GetArrayBufferByteLength(buffer)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: PodCopy(data, buf.get(), bytesRead); michael@0: michael@0: remaining -= bytesRead; michael@0: data += bytesRead; michael@0: } while (remaining > 0); michael@0: michael@0: return remaining > 0 ? NS_ERROR_FAILURE : NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryInputStream::ReadObject(bool aIsStrongRef, nsISupports* *aObject) michael@0: { michael@0: nsCID cid; michael@0: nsIID iid; michael@0: nsresult rv = ReadID(&cid); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: rv = ReadID(&iid); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: // HACK: Intercept old (pre-gecko6) nsIURI IID, and replace with michael@0: // the updated IID, so that we're QI'ing to an actual interface. michael@0: // (As soon as we drop support for upgrading from pre-gecko6, we can michael@0: // remove this chunk.) michael@0: static const nsIID oldURIiid = michael@0: { 0x7a22cc0, 0xce5, 0x11d3, michael@0: { 0x93, 0x31, 0x0, 0x10, 0x4b, 0xa0, 0xfd, 0x40 }}; michael@0: michael@0: // hackaround for bug 670542 michael@0: static const nsIID oldURIiid2 = michael@0: { 0xd6d04c36, 0x0fa4, 0x4db3, michael@0: { 0xbe, 0x05, 0x4a, 0x18, 0x39, 0x71, 0x03, 0xe2 }}; michael@0: michael@0: // hackaround for bug 682031 michael@0: static const nsIID oldURIiid3 = michael@0: { 0x12120b20, 0x0929, 0x40e9, michael@0: { 0x88, 0xcf, 0x6e, 0x08, 0x76, 0x6e, 0x8b, 0x23 }}; michael@0: michael@0: if (iid.Equals(oldURIiid) || michael@0: iid.Equals(oldURIiid2) || michael@0: iid.Equals(oldURIiid3)) { michael@0: const nsIID newURIiid = NS_IURI_IID; michael@0: iid = newURIiid; michael@0: } michael@0: // END HACK michael@0: michael@0: nsCOMPtr object = do_CreateInstance(cid, &rv); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: nsCOMPtr serializable = do_QueryInterface(object); michael@0: if (NS_WARN_IF(!serializable)) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: rv = serializable->Read(this); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: return object->QueryInterface(iid, reinterpret_cast(aObject)); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBinaryInputStream::ReadID(nsID *aResult) michael@0: { michael@0: nsresult rv = Read32(&aResult->m0); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: rv = Read16(&aResult->m1); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: rv = Read16(&aResult->m2); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: for (int i = 0; i < 8; ++i) { michael@0: rv = Read8(&aResult->m3[i]); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(char*) michael@0: nsBinaryInputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask) michael@0: { michael@0: if (mBufferAccess) michael@0: return mBufferAccess->GetBuffer(aLength, aAlignMask); michael@0: return nullptr; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(void) michael@0: nsBinaryInputStream::PutBuffer(char* aBuffer, uint32_t aLength) michael@0: { michael@0: if (mBufferAccess) michael@0: mBufferAccess->PutBuffer(aBuffer, aLength); michael@0: }