VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/xpcom/io/nsBinaryStream.cpp@ 28970

Last change on this file since 28970 was 1, checked in by vboxsync, 55 years ago

import

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.8 KB
Line 
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 *
15 * The Original Code is Mozilla Communicator client code, released
16 * March 31, 1998.
17 *
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998-1999
21 * the Initial Developer. All Rights Reserved.
22 *
23 * Contributor(s):
24 *
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
36 *
37 * ***** END LICENSE BLOCK ***** */
38
39/**
40 * This file contains implementations of the nsIBinaryInputStream and
41 * nsIBinaryOutputStream interfaces. Together, these interfaces allows reading
42 * and writing of primitive data types (integers, floating-point values,
43 * booleans, etc.) to a stream in a binary, untagged, fixed-endianness format.
44 * This might be used, for example, to implement network protocols or to
45 * produce architecture-neutral binary disk files, i.e. ones that can be read
46 * and written by both big-endian and little-endian platforms. Output is
47 * written in big-endian order (high-order byte first), as this is traditional
48 * network order.
49 *
50 * @See nsIBinaryInputStream
51 * @See nsIBinaryOutputStream
52 */
53#include <string.h>
54#include "nsBinaryStream.h"
55#include "nsCRT.h"
56#include "nsIStreamBufferAccess.h"
57#include "nsMemory.h"
58#include "prlong.h"
59#include "nsGenericFactory.h"
60
61NS_IMPL_ISUPPORTS3(nsBinaryOutputStream, nsIObjectOutputStream, nsIBinaryOutputStream, nsIOutputStream)
62
63NS_IMETHODIMP
64nsBinaryOutputStream::Flush() { return mOutputStream->Flush(); }
65
66NS_IMETHODIMP
67nsBinaryOutputStream::Close() { return mOutputStream->Close(); }
68
69NS_IMETHODIMP
70nsBinaryOutputStream::Write(const char *aBuf, PRUint32 aCount, PRUint32 *aActualBytes)
71{
72 return mOutputStream->Write(aBuf, aCount, aActualBytes);
73}
74
75NS_IMETHODIMP
76nsBinaryOutputStream::WriteFrom(nsIInputStream *inStr, PRUint32 count, PRUint32 *_retval)
77{
78 NS_NOTREACHED("WriteFrom");
79 return NS_ERROR_NOT_IMPLEMENTED;
80}
81
82NS_IMETHODIMP
83nsBinaryOutputStream::WriteSegments(nsReadSegmentFun reader, void * closure, PRUint32 count, PRUint32 *_retval)
84{
85 NS_NOTREACHED("WriteSegments");
86 return NS_ERROR_NOT_IMPLEMENTED;
87}
88
89NS_IMETHODIMP
90nsBinaryOutputStream::IsNonBlocking(PRBool *aNonBlocking)
91{
92 return mOutputStream->IsNonBlocking(aNonBlocking);
93}
94
95nsresult
96nsBinaryOutputStream::WriteFully(const char *aBuf, PRUint32 aCount)
97{
98 nsresult rv;
99 PRUint32 bytesWritten;
100
101 rv = mOutputStream->Write(aBuf, aCount, &bytesWritten);
102 if (NS_FAILED(rv)) return rv;
103 if (bytesWritten != aCount)
104 return NS_ERROR_FAILURE;
105 return NS_OK;
106}
107
108NS_IMETHODIMP
109nsBinaryOutputStream::SetOutputStream(nsIOutputStream *aOutputStream)
110{
111 NS_ENSURE_ARG_POINTER(aOutputStream);
112 mOutputStream = aOutputStream;
113 mBufferAccess = do_QueryInterface(aOutputStream);
114 return NS_OK;
115}
116
117NS_IMETHODIMP
118nsBinaryOutputStream::WriteBoolean(PRBool aBoolean)
119{
120 return Write8(aBoolean);
121}
122
123NS_IMETHODIMP
124nsBinaryOutputStream::Write8(PRUint8 aByte)
125{
126 return WriteFully((const char*)&aByte, sizeof aByte);
127}
128
129NS_IMETHODIMP
130nsBinaryOutputStream::Write16(PRUint16 a16)
131{
132 a16 = NS_SWAP16(a16);
133 return WriteFully((const char*)&a16, sizeof a16);
134}
135
136NS_IMETHODIMP
137nsBinaryOutputStream::Write32(PRUint32 a32)
138{
139 a32 = NS_SWAP32(a32);
140 return WriteFully((const char*)&a32, sizeof a32);
141}
142
143NS_IMETHODIMP
144nsBinaryOutputStream::Write64(PRUint64 a64)
145{
146 nsresult rv;
147 PRUint32 bytesWritten;
148
149 a64 = NS_SWAP64(a64);
150 rv = Write(NS_REINTERPRET_CAST(char*, &a64), sizeof a64, &bytesWritten);
151 if (NS_FAILED(rv)) return rv;
152 if (bytesWritten != sizeof a64)
153 return NS_ERROR_FAILURE;
154 return rv;
155}
156
157NS_IMETHODIMP
158nsBinaryOutputStream::WriteFloat(float aFloat)
159{
160 NS_ASSERTION(sizeof(float) == sizeof (PRUint32),
161 "False assumption about sizeof(float)");
162 return Write32(*NS_REINTERPRET_CAST(PRUint32*, &aFloat));
163}
164
165NS_IMETHODIMP
166nsBinaryOutputStream::WriteDouble(double aDouble)
167{
168 NS_ASSERTION(sizeof(double) == sizeof(PRUint64),
169 "False assumption about sizeof(double)");
170 return Write64(*NS_REINTERPRET_CAST(PRUint64*, &aDouble));
171}
172
173NS_IMETHODIMP
174nsBinaryOutputStream::WriteStringZ(const char *aString)
175{
176 PRUint32 length;
177 nsresult rv;
178
179 length = strlen(aString);
180 rv = Write32(length);
181 if (NS_FAILED(rv)) return rv;
182 return WriteFully(aString, length);
183}
184
185NS_IMETHODIMP
186nsBinaryOutputStream::WriteWStringZ(const PRUnichar* aString)
187{
188 PRUint32 length, byteCount;
189 nsresult rv;
190
191 length = nsCRT::strlen(aString);
192 rv = Write32(length);
193 if (NS_FAILED(rv)) return rv;
194
195 if (length == 0)
196 return NS_OK;
197 byteCount = length * sizeof(PRUnichar);
198
199#ifdef IS_BIG_ENDIAN
200 rv = WriteBytes(NS_REINTERPRET_CAST(const char*, aString), byteCount);
201#else
202 // XXX use WriteSegments here to avoid copy!
203 PRUnichar *copy, temp[64];
204 if (length <= 64) {
205 copy = temp;
206 } else {
207 copy = NS_REINTERPRET_CAST(PRUnichar*, nsMemory::Alloc(byteCount));
208 if (!copy)
209 return NS_ERROR_OUT_OF_MEMORY;
210 }
211 NS_ASSERTION((PRUptrdiff(aString) & 0x1) == 0, "aString not properly aligned");
212 for (PRUint32 i = 0; i < length; i++)
213 copy[i] = NS_SWAP16(aString[i]);
214 rv = WriteBytes(NS_REINTERPRET_CAST(const char*, copy), byteCount);
215 if (copy != temp)
216 nsMemory::Free(copy);
217#endif
218
219 return rv;
220}
221
222NS_IMETHODIMP
223nsBinaryOutputStream::WriteUtf8Z(const PRUnichar* aString)
224{
225 NS_NOTREACHED("WriteUtf8Z");
226 return NS_ERROR_NOT_IMPLEMENTED;
227}
228
229NS_IMETHODIMP
230nsBinaryOutputStream::WriteBytes(const char *aString, PRUint32 aLength)
231{
232 nsresult rv;
233 PRUint32 bytesWritten;
234
235 rv = Write(aString, aLength, &bytesWritten);
236 if (NS_FAILED(rv)) return rv;
237 if (bytesWritten != aLength)
238 return NS_ERROR_FAILURE;
239 return rv;
240}
241
242NS_IMETHODIMP
243nsBinaryOutputStream::WriteByteArray(PRUint8 *aBytes, PRUint32 aLength)
244{
245 return WriteBytes(NS_REINTERPRET_CAST(char *, aBytes), aLength);
246}
247
248NS_IMETHODIMP
249nsBinaryOutputStream::WriteObject(nsISupports* aObject, PRBool aIsStrongRef)
250{
251 NS_NOTREACHED("WriteObject");
252 return NS_ERROR_NOT_IMPLEMENTED;
253}
254
255NS_IMETHODIMP
256nsBinaryOutputStream::WriteSingleRefObject(nsISupports* aObject)
257{
258 NS_NOTREACHED("WriteSingleRefObject");
259 return NS_ERROR_NOT_IMPLEMENTED;
260}
261
262NS_IMETHODIMP
263nsBinaryOutputStream::WriteCompoundObject(nsISupports* aObject,
264 const nsIID& aIID,
265 PRBool aIsStrongRef)
266{
267 NS_NOTREACHED("WriteCompoundObject");
268 return NS_ERROR_NOT_IMPLEMENTED;
269}
270
271NS_IMETHODIMP
272nsBinaryOutputStream::WriteID(const nsIID& aIID)
273{
274 NS_NOTREACHED("WriteID");
275 return NS_ERROR_NOT_IMPLEMENTED;
276}
277
278NS_IMETHODIMP_(char*)
279nsBinaryOutputStream::GetBuffer(PRUint32 aLength, PRUint32 aAlignMask)
280{
281 if (mBufferAccess)
282 return mBufferAccess->GetBuffer(aLength, aAlignMask);
283 return nsnull;
284}
285
286NS_IMETHODIMP_(void)
287nsBinaryOutputStream::PutBuffer(char* aBuffer, PRUint32 aLength)
288{
289 if (mBufferAccess)
290 mBufferAccess->PutBuffer(aBuffer, aLength);
291}
292
293NS_IMPL_ISUPPORTS3(nsBinaryInputStream, nsIObjectInputStream, nsIBinaryInputStream, nsIInputStream)
294
295NS_IMETHODIMP
296nsBinaryInputStream::Available(PRUint32* aResult)
297{
298 return mInputStream->Available(aResult);
299}
300
301NS_IMETHODIMP
302nsBinaryInputStream::Read(char* aBuffer, PRUint32 aCount, PRUint32 *aNumRead)
303{
304 return mInputStream->Read(aBuffer, aCount, aNumRead);
305}
306
307
308// when forwarding ReadSegments to mInputStream, we need to make sure
309// 'this' is being passed to the writer each time. To do this, we need
310// a thunking function which keeps the real input stream around.
311
312// the closure wrapper
313struct ReadSegmentsClosure {
314 nsIInputStream* mRealInputStream;
315 void* mRealClosure;
316 nsWriteSegmentFun mRealWriter;
317};
318
319// the thunking function
320static NS_METHOD
321ReadSegmentForwardingThunk(nsIInputStream* aStream,
322 void *aClosure,
323 const char* aFromSegment,
324 PRUint32 aToOffset,
325 PRUint32 aCount,
326 PRUint32 *aWriteCount)
327{
328 ReadSegmentsClosure* thunkClosure =
329 NS_REINTERPRET_CAST(ReadSegmentsClosure*, aClosure);
330
331 return thunkClosure->mRealWriter(thunkClosure->mRealInputStream,
332 thunkClosure->mRealClosure,
333 aFromSegment, aToOffset,
334 aCount, aWriteCount);
335}
336
337
338NS_IMETHODIMP
339nsBinaryInputStream::ReadSegments(nsWriteSegmentFun writer, void * closure, PRUint32 count, PRUint32 *_retval)
340{
341 ReadSegmentsClosure thunkClosure = { this, closure, writer };
342
343 return mInputStream->ReadSegments(ReadSegmentForwardingThunk, &thunkClosure, count, _retval);
344}
345
346NS_IMETHODIMP
347nsBinaryInputStream::IsNonBlocking(PRBool *aNonBlocking)
348{
349 return mInputStream->IsNonBlocking(aNonBlocking);
350}
351
352NS_IMETHODIMP
353nsBinaryInputStream::Close() { return mInputStream->Close(); }
354
355NS_IMETHODIMP
356nsBinaryInputStream::SetInputStream(nsIInputStream *aInputStream)
357{
358 NS_ENSURE_ARG_POINTER(aInputStream);
359 mInputStream = aInputStream;
360 mBufferAccess = do_QueryInterface(aInputStream);
361 return NS_OK;
362}
363
364NS_IMETHODIMP
365nsBinaryInputStream::ReadBoolean(PRBool* aBoolean)
366{
367 PRUint8 byteResult;
368 nsresult rv = Read8(&byteResult);
369 *aBoolean = byteResult;
370 return rv;
371}
372
373NS_IMETHODIMP
374nsBinaryInputStream::Read8(PRUint8* aByte)
375{
376 nsresult rv;
377 PRUint32 bytesRead;
378
379 rv = Read(NS_REINTERPRET_CAST(char*, aByte), sizeof(*aByte), &bytesRead);
380 if (NS_FAILED(rv)) return rv;
381 if (bytesRead != 1)
382 return NS_ERROR_FAILURE;
383 return rv;
384}
385
386NS_IMETHODIMP
387nsBinaryInputStream::Read16(PRUint16* a16)
388{
389 nsresult rv;
390 PRUint32 bytesRead;
391
392 rv = Read(NS_REINTERPRET_CAST(char*, a16), sizeof *a16, &bytesRead);
393 if (NS_FAILED(rv)) return rv;
394 if (bytesRead != sizeof *a16)
395 return NS_ERROR_FAILURE;
396 *a16 = NS_SWAP16(*a16);
397 return rv;
398}
399
400NS_IMETHODIMP
401nsBinaryInputStream::Read32(PRUint32* a32)
402{
403 nsresult rv;
404 PRUint32 bytesRead;
405
406 rv = Read(NS_REINTERPRET_CAST(char*, a32), sizeof *a32, &bytesRead);
407 if (NS_FAILED(rv)) return rv;
408 if (bytesRead != sizeof *a32)
409 return NS_ERROR_FAILURE;
410 *a32 = NS_SWAP32(*a32);
411 return rv;
412}
413
414NS_IMETHODIMP
415nsBinaryInputStream::Read64(PRUint64* a64)
416{
417 nsresult rv;
418 PRUint32 bytesRead;
419
420 rv = Read(NS_REINTERPRET_CAST(char*, a64), sizeof *a64, &bytesRead);
421 if (NS_FAILED(rv)) return rv;
422 if (bytesRead != sizeof *a64)
423 return NS_ERROR_FAILURE;
424 *a64 = NS_SWAP64(*a64);
425 return rv;
426}
427
428NS_IMETHODIMP
429nsBinaryInputStream::ReadFloat(float* aFloat)
430{
431 NS_ASSERTION(sizeof(float) == sizeof (PRUint32),
432 "False assumption about sizeof(float)");
433 return Read32(NS_REINTERPRET_CAST(PRUint32*, aFloat));
434}
435
436NS_IMETHODIMP
437nsBinaryInputStream::ReadDouble(double* aDouble)
438{
439 NS_ASSERTION(sizeof(double) == sizeof(PRUint64),
440 "False assumption about sizeof(double)");
441 return Read64(NS_REINTERPRET_CAST(PRUint64*, aDouble));
442}
443
444static NS_METHOD
445WriteSegmentToCString(nsIInputStream* aStream,
446 void *aClosure,
447 const char* aFromSegment,
448 PRUint32 aToOffset,
449 PRUint32 aCount,
450 PRUint32 *aWriteCount)
451{
452 nsACString* outString = NS_STATIC_CAST(nsACString*,aClosure);
453
454 outString->Append(aFromSegment, aCount);
455
456 *aWriteCount = aCount;
457
458 return NS_OK;
459}
460
461NS_IMETHODIMP
462nsBinaryInputStream::ReadCString(nsACString& aString)
463{
464 nsresult rv;
465 PRUint32 length, bytesRead;
466
467 rv = Read32(&length);
468 if (NS_FAILED(rv)) return rv;
469
470 aString.Truncate();
471 rv = ReadSegments(WriteSegmentToCString, &aString, length, &bytesRead);
472 if (NS_FAILED(rv)) return rv;
473
474 if (bytesRead != length)
475 return NS_ERROR_FAILURE;
476
477 return NS_OK;
478}
479
480
481// sometimes, WriteSegmentToString will be handed an odd-number of
482// bytes, which means we only have half of the last PRUnichar
483struct WriteStringClosure {
484 PRUnichar *mWriteCursor;
485 PRPackedBool mHasCarryoverByte;
486 char mCarryoverByte;
487};
488
489// there are a few cases we have to account for here:
490// * even length buffer, no carryover - easy, just append
491// * odd length buffer, no carryover - the last byte needs to be saved
492// for carryover
493// * odd length buffer, with carryover - first byte needs to be used
494// with the carryover byte, and
495// the rest of the even length
496// buffer is appended as normal
497// * even length buffer, with carryover - the first byte needs to be
498// used with the previous carryover byte.
499// this gives you an odd length buffer,
500// so you have to save the last byte for
501// the next carryover
502
503
504// same version of the above, but with correct casting and endian swapping
505static NS_METHOD
506WriteSegmentToString(nsIInputStream* aStream,
507 void *aClosure,
508 const char* aFromSegment,
509 PRUint32 aToOffset,
510 PRUint32 aCount,
511 PRUint32 *aWriteCount)
512{
513 NS_PRECONDITION(aCount > 0, "Why are we being told to write 0 bytes?");
514 NS_PRECONDITION(sizeof(PRUnichar) == 2, "We can't handle other sizes!");
515
516 WriteStringClosure* closure = NS_STATIC_CAST(WriteStringClosure*,aClosure);
517 PRUnichar *cursor = closure->mWriteCursor;
518
519 // we're always going to consume the whole buffer no matter what
520 // happens, so take care of that right now.. that allows us to
521 // tweak aCount later. Do NOT move this!
522 *aWriteCount = aCount;
523
524 // if the last Write had an odd-number of bytes read, then
525 if (closure->mHasCarryoverByte) {
526 // re-create the two-byte sequence we want to work with
527 char bytes[2] = { closure->mCarryoverByte, *aFromSegment };
528 *cursor = *(PRUnichar*)bytes;
529 // Now the little endianness dance
530#ifdef IS_LITTLE_ENDIAN
531 *cursor = (PRUnichar) NS_SWAP16(*cursor);
532#endif
533 ++cursor;
534
535 // now skip past the first byte of the buffer.. code from here
536 // can assume normal operations, but should not assume aCount
537 // is relative to the ORIGINAL buffer
538 ++aFromSegment;
539 --aCount;
540
541 closure->mHasCarryoverByte = PR_FALSE;
542 }
543
544 // this array is possibly unaligned... be careful how we access it!
545 const PRUnichar *unicodeSegment =
546 NS_REINTERPRET_CAST(const PRUnichar*, aFromSegment);
547
548 // calculate number of full characters in segment (aCount could be odd!)
549 PRUint32 segmentLength = aCount / sizeof(PRUnichar);
550
551 // copy all data into our aligned buffer. byte swap if necessary.
552 memcpy(cursor, unicodeSegment, segmentLength * sizeof(PRUnichar));
553 PRUnichar *end = cursor + segmentLength;
554#ifdef IS_LITTLE_ENDIAN
555 for (; cursor < end; ++cursor)
556 *cursor = (PRUnichar) NS_SWAP16(*cursor);
557#endif
558 closure->mWriteCursor = end;
559
560 // remember this is the modifed aCount and aFromSegment,
561 // so that will take into account the fact that we might have
562 // skipped the first byte in the buffer
563 if (aCount % sizeof(PRUnichar) != 0) {
564 // we must have had a carryover byte, that we'll need the next
565 // time around
566 closure->mCarryoverByte = aFromSegment[aCount - 1];
567 closure->mHasCarryoverByte = PR_TRUE;
568 }
569
570 return NS_OK;
571}
572
573
574NS_IMETHODIMP
575nsBinaryInputStream::ReadString(nsAString& aString)
576{
577 nsresult rv;
578 PRUint32 length, bytesRead;
579
580 rv = Read32(&length);
581 if (NS_FAILED(rv)) return rv;
582
583 // pre-allocate output buffer, and get direct access to buffer...
584 aString.SetLength(length);
585 nsAString::iterator start;
586 aString.BeginWriting(start);
587
588 WriteStringClosure closure;
589 closure.mWriteCursor = start.get();
590 closure.mHasCarryoverByte = PR_FALSE;
591
592 rv = ReadSegments(WriteSegmentToString, &closure,
593 length*sizeof(PRUnichar), &bytesRead);
594 if (NS_FAILED(rv)) return rv;
595
596 NS_ASSERTION(!closure.mHasCarryoverByte, "some strange stream corruption!");
597
598 if (bytesRead != length*sizeof(PRUnichar))
599 return NS_ERROR_FAILURE;
600
601 return NS_OK;
602}
603
604NS_IMETHODIMP
605nsBinaryInputStream::ReadBytes(PRUint32 aLength, char* *_rval)
606{
607 nsresult rv;
608 PRUint32 bytesRead;
609 char* s;
610
611 s = NS_REINTERPRET_CAST(char*, nsMemory::Alloc(aLength));
612 if (!s)
613 return NS_ERROR_OUT_OF_MEMORY;
614
615 rv = Read(s, aLength, &bytesRead);
616 if (NS_FAILED(rv)) {
617 nsMemory::Free(s);
618 return rv;
619 }
620 if (bytesRead != aLength) {
621 nsMemory::Free(s);
622 return NS_ERROR_FAILURE;
623 }
624
625 *_rval = s;
626 return NS_OK;
627}
628
629NS_IMETHODIMP
630nsBinaryInputStream::ReadByteArray(PRUint32 aLength, PRUint8* *_rval)
631{
632 return ReadBytes(aLength, NS_REINTERPRET_CAST(char **, _rval));
633}
634
635NS_IMETHODIMP
636nsBinaryInputStream::ReadObject(PRBool aIsStrongRef, nsISupports* *aObject)
637{
638 NS_NOTREACHED("ReadObject");
639 return NS_ERROR_NOT_IMPLEMENTED;
640}
641
642NS_IMETHODIMP
643nsBinaryInputStream::ReadID(nsID *aResult)
644{
645 NS_NOTREACHED("ReadID");
646 return NS_ERROR_NOT_IMPLEMENTED;
647}
648
649NS_IMETHODIMP_(char*)
650nsBinaryInputStream::GetBuffer(PRUint32 aLength, PRUint32 aAlignMask)
651{
652 if (mBufferAccess)
653 return mBufferAccess->GetBuffer(aLength, aAlignMask);
654 return nsnull;
655}
656
657NS_IMETHODIMP_(void)
658nsBinaryInputStream::PutBuffer(char* aBuffer, PRUint32 aLength)
659{
660 if (mBufferAccess)
661 mBufferAccess->PutBuffer(aBuffer, aLength);
662}
663
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette