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