1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* vim:set ts=4 sts=4 sw=4 cin et: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
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
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * mcmullen@netscape.com (original author)
25 : * warren@netscape.com
26 : * alecf@netscape.com
27 : * scc@mozilla.org
28 : * david.gardiner@unisa.edu.au
29 : * fur@netscape.com
30 : * norris@netscape.com
31 : * pinkerton@netscape.com
32 : * davidm@netscape.com
33 : * sfraser@netscape.com
34 : * darin@netscape.com
35 : * bzbarsky@mit.edu
36 : *
37 : * Alternatively, the contents of this file may be used under the terms of
38 : * either of the GNU General Public License Version 2 or later (the "GPL"),
39 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
40 : * in which case the provisions of the GPL or the LGPL are applicable instead
41 : * of those above. If you wish to allow use of your version of this file only
42 : * under the terms of either the GPL or the LGPL, and not to allow others to
43 : * use your version of this file under the terms of the MPL, indicate your
44 : * decision by deleting the provisions above and replace them with the notice
45 : * and other provisions required by the GPL or the LGPL. If you do not delete
46 : * the provisions above, a recipient may use your version of this file under
47 : * the terms of any one of the MPL, the GPL or the LGPL.
48 : *
49 : * ***** END LICENSE BLOCK ***** */
50 :
51 : /**
52 : * Based on original code from nsIStringStream.cpp
53 : */
54 :
55 : #include "IPC/IPCMessageUtils.h"
56 :
57 : #include "nsStringStream.h"
58 : #include "nsStreamUtils.h"
59 : #include "nsReadableUtils.h"
60 : #include "nsISeekableStream.h"
61 : #include "nsISupportsPrimitives.h"
62 : #include "nsCRT.h"
63 : #include "prerror.h"
64 : #include "plstr.h"
65 : #include "nsIClassInfoImpl.h"
66 : #include "nsIIPCSerializable.h"
67 :
68 : //-----------------------------------------------------------------------------
69 : // nsIStringInputStream implementation
70 : //-----------------------------------------------------------------------------
71 :
72 : class nsStringInputStream : public nsIStringInputStream
73 : , public nsISeekableStream
74 : , public nsISupportsCString
75 : , public nsIIPCSerializable
76 : {
77 : public:
78 : NS_DECL_ISUPPORTS
79 : NS_DECL_NSIINPUTSTREAM
80 : NS_DECL_NSISTRINGINPUTSTREAM
81 : NS_DECL_NSISEEKABLESTREAM
82 : NS_DECL_NSISUPPORTSPRIMITIVE
83 : NS_DECL_NSISUPPORTSCSTRING
84 : NS_DECL_NSIIPCSERIALIZABLE
85 :
86 202200 : nsStringInputStream()
87 202200 : {
88 202200 : Clear();
89 202200 : }
90 :
91 : private:
92 202188 : ~nsStringInputStream()
93 202188 : {}
94 :
95 793930 : PRUint32 Length() const
96 : {
97 793930 : return mData.Length();
98 : }
99 :
100 399109 : PRUint32 LengthRemaining() const
101 : {
102 399109 : return Length() - mOffset;
103 : }
104 :
105 202477 : void Clear()
106 : {
107 202477 : mData.SetIsVoid(true);
108 202477 : }
109 :
110 399111 : bool Closed()
111 : {
112 399111 : return mData.IsVoid();
113 : }
114 :
115 : nsDependentCSubstring mData;
116 : PRUint32 mOffset;
117 : };
118 :
119 : // This class needs to support threadsafe refcounting since people often
120 : // allocate a string stream, and then read it from a background thread.
121 615610 : NS_IMPL_THREADSAFE_ADDREF(nsStringInputStream)
122 817786 : NS_IMPL_THREADSAFE_RELEASE(nsStringInputStream)
123 :
124 : NS_IMPL_CLASSINFO(nsStringInputStream, NULL, nsIClassInfo::THREADSAFE,
125 : NS_STRINGINPUTSTREAM_CID)
126 422760 : NS_IMPL_QUERY_INTERFACE5_CI(nsStringInputStream,
127 : nsIStringInputStream,
128 : nsIInputStream,
129 : nsISupportsCString,
130 : nsISeekableStream,
131 1833 : nsIIPCSerializable)
132 776 : NS_IMPL_CI_INTERFACE_GETTER5(nsStringInputStream,
133 : nsIStringInputStream,
134 : nsIInputStream,
135 : nsISupportsCString,
136 : nsISeekableStream,
137 776 : nsIIPCSerializable)
138 :
139 : /////////
140 : // nsISupportsCString implementation
141 : /////////
142 :
143 : NS_IMETHODIMP
144 0 : nsStringInputStream::GetType(PRUint16 *type)
145 : {
146 0 : *type = TYPE_CSTRING;
147 0 : return NS_OK;
148 : }
149 :
150 : NS_IMETHODIMP
151 0 : nsStringInputStream::GetData(nsACString &data)
152 : {
153 : // The stream doesn't have any data when it is closed. We could fake it
154 : // and return an empty string here, but it seems better to keep this return
155 : // value consistent with the behavior of the other 'getter' methods.
156 0 : NS_ENSURE_TRUE(!Closed(), NS_BASE_STREAM_CLOSED);
157 :
158 0 : data.Assign(mData);
159 0 : return NS_OK;
160 : }
161 :
162 : NS_IMETHODIMP
163 1119 : nsStringInputStream::SetData(const nsACString &data)
164 : {
165 1119 : mData.Assign(data);
166 1119 : mOffset = 0;
167 1119 : return NS_OK;
168 : }
169 :
170 : NS_IMETHODIMP
171 0 : nsStringInputStream::ToString(char **result)
172 : {
173 : // NOTE: This method may result in data loss, so we do not implement it.
174 0 : return NS_ERROR_NOT_IMPLEMENTED;
175 : }
176 :
177 : /////////
178 : // nsIStringInputStream implementation
179 : /////////
180 :
181 : NS_IMETHODIMP
182 709 : nsStringInputStream::SetData(const char *data, PRInt32 dataLen)
183 : {
184 709 : NS_ENSURE_ARG_POINTER(data);
185 709 : mData.Assign(data, dataLen);
186 709 : mOffset = 0;
187 709 : return NS_OK;
188 : }
189 :
190 : NS_IMETHODIMP
191 919 : nsStringInputStream::AdoptData(char *data, PRInt32 dataLen)
192 : {
193 919 : NS_ENSURE_ARG_POINTER(data);
194 919 : mData.Adopt(data, dataLen);
195 919 : mOffset = 0;
196 919 : return NS_OK;
197 : }
198 :
199 : NS_IMETHODIMP
200 204001 : nsStringInputStream::ShareData(const char *data, PRInt32 dataLen)
201 : {
202 204001 : NS_ENSURE_ARG_POINTER(data);
203 :
204 204001 : if (dataLen < 0)
205 2 : dataLen = strlen(data);
206 :
207 204001 : mData.Rebind(data, dataLen);
208 204001 : mOffset = 0;
209 204001 : return NS_OK;
210 : }
211 :
212 : /////////
213 : // nsIInputStream implementation
214 : /////////
215 :
216 : NS_IMETHODIMP
217 277 : nsStringInputStream::Close()
218 : {
219 277 : Clear();
220 277 : return NS_OK;
221 : }
222 :
223 : NS_IMETHODIMP
224 4290 : nsStringInputStream::Available(PRUint32 *aLength)
225 : {
226 4290 : NS_ASSERTION(aLength, "null ptr");
227 :
228 4290 : if (Closed())
229 0 : return NS_BASE_STREAM_CLOSED;
230 :
231 4290 : *aLength = LengthRemaining();
232 4290 : return NS_OK;
233 : }
234 :
235 : NS_IMETHODIMP
236 386550 : nsStringInputStream::Read(char* aBuf, PRUint32 aCount, PRUint32 *aReadCount)
237 : {
238 386550 : NS_ASSERTION(aBuf, "null ptr");
239 386550 : return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, aReadCount);
240 : }
241 :
242 : NS_IMETHODIMP
243 394819 : nsStringInputStream::ReadSegments(nsWriteSegmentFun writer, void *closure,
244 : PRUint32 aCount, PRUint32 *result)
245 : {
246 394819 : NS_ASSERTION(result, "null ptr");
247 394819 : NS_ASSERTION(Length() >= mOffset, "bad stream state");
248 :
249 394819 : if (Closed())
250 0 : return NS_BASE_STREAM_CLOSED;
251 :
252 : // We may be at end-of-file
253 394819 : PRUint32 maxCount = LengthRemaining();
254 394819 : if (maxCount == 0) {
255 8961 : *result = 0;
256 8961 : return NS_OK;
257 : }
258 :
259 385858 : if (aCount > maxCount)
260 9092 : aCount = maxCount;
261 385858 : nsresult rv = writer(this, closure, mData.BeginReading() + mOffset, 0, aCount, result);
262 385858 : if (NS_SUCCEEDED(rv)) {
263 384126 : NS_ASSERTION(*result <= aCount,
264 : "writer should not write more than we asked it to write");
265 384126 : mOffset += *result;
266 : }
267 :
268 : // errors returned from the writer end here!
269 385858 : return NS_OK;
270 : }
271 :
272 : NS_IMETHODIMP
273 1 : nsStringInputStream::IsNonBlocking(bool *aNonBlocking)
274 : {
275 1 : *aNonBlocking = true;
276 1 : return NS_OK;
277 : }
278 :
279 : /////////
280 : // nsISeekableStream implementation
281 : /////////
282 :
283 : NS_IMETHODIMP
284 2 : nsStringInputStream::Seek(PRInt32 whence, PRInt64 offset)
285 : {
286 2 : if (Closed())
287 0 : return NS_BASE_STREAM_CLOSED;
288 :
289 : // Compute new stream position. The given offset may be a negative value.
290 :
291 2 : PRInt64 newPos = offset;
292 2 : switch (whence) {
293 : case NS_SEEK_SET:
294 2 : break;
295 : case NS_SEEK_CUR:
296 0 : newPos += mOffset;
297 0 : break;
298 : case NS_SEEK_END:
299 0 : newPos += Length();
300 0 : break;
301 : default:
302 0 : NS_ERROR("invalid whence");
303 0 : return NS_ERROR_INVALID_ARG;
304 : }
305 :
306 2 : NS_ENSURE_ARG(newPos >= 0);
307 2 : NS_ENSURE_ARG(newPos <= Length());
308 :
309 2 : mOffset = (PRUint32)newPos;
310 2 : return NS_OK;
311 : }
312 :
313 : NS_IMETHODIMP
314 0 : nsStringInputStream::Tell(PRInt64* outWhere)
315 : {
316 0 : if (Closed())
317 0 : return NS_BASE_STREAM_CLOSED;
318 :
319 0 : *outWhere = mOffset;
320 0 : return NS_OK;
321 : }
322 :
323 : NS_IMETHODIMP
324 0 : nsStringInputStream::SetEOF()
325 : {
326 0 : if (Closed())
327 0 : return NS_BASE_STREAM_CLOSED;
328 :
329 0 : mOffset = Length();
330 0 : return NS_OK;
331 : }
332 :
333 : /////////
334 : // nsIIPCSerializable implementation
335 : /////////
336 :
337 : bool
338 0 : nsStringInputStream::Read(const IPC::Message *aMsg, void **aIter)
339 : {
340 : using IPC::ReadParam;
341 :
342 0 : nsCString value;
343 :
344 0 : if (!ReadParam(aMsg, aIter, &value))
345 0 : return false;
346 :
347 0 : nsresult rv = SetData(value);
348 0 : if (NS_FAILED(rv))
349 0 : return false;
350 :
351 0 : return true;
352 : }
353 :
354 : void
355 0 : nsStringInputStream::Write(IPC::Message *aMsg)
356 : {
357 : using IPC::WriteParam;
358 :
359 0 : WriteParam(aMsg, static_cast<const nsCString&>(PromiseFlatCString(mData)));
360 0 : }
361 :
362 : nsresult
363 3818 : NS_NewByteInputStream(nsIInputStream** aStreamResult,
364 : const char* aStringToRead, PRInt32 aLength,
365 : nsAssignmentType aAssignment)
366 : {
367 3818 : NS_PRECONDITION(aStreamResult, "null out ptr");
368 :
369 3818 : nsStringInputStream* stream = new nsStringInputStream();
370 3818 : if (! stream)
371 0 : return NS_ERROR_OUT_OF_MEMORY;
372 :
373 3818 : NS_ADDREF(stream);
374 :
375 : nsresult rv;
376 3818 : switch (aAssignment) {
377 : case NS_ASSIGNMENT_COPY:
378 0 : rv = stream->SetData(aStringToRead, aLength);
379 0 : break;
380 : case NS_ASSIGNMENT_DEPEND:
381 3818 : rv = stream->ShareData(aStringToRead, aLength);
382 3818 : break;
383 : case NS_ASSIGNMENT_ADOPT:
384 0 : rv = stream->AdoptData(const_cast<char*>(aStringToRead), aLength);
385 0 : break;
386 : default:
387 0 : NS_ERROR("invalid assignment type");
388 0 : rv = NS_ERROR_INVALID_ARG;
389 : }
390 :
391 3818 : if (NS_FAILED(rv)) {
392 0 : NS_RELEASE(stream);
393 0 : return rv;
394 : }
395 :
396 3818 : *aStreamResult = stream;
397 3818 : return NS_OK;
398 : }
399 :
400 : nsresult
401 0 : NS_NewStringInputStream(nsIInputStream** aStreamResult,
402 : const nsAString& aStringToRead)
403 : {
404 0 : NS_LossyConvertUTF16toASCII data(aStringToRead); // truncates high-order bytes
405 0 : return NS_NewCStringInputStream(aStreamResult, data);
406 : }
407 :
408 : nsresult
409 273 : NS_NewCStringInputStream(nsIInputStream** aStreamResult,
410 : const nsACString& aStringToRead)
411 : {
412 273 : NS_PRECONDITION(aStreamResult, "null out ptr");
413 :
414 273 : nsStringInputStream* stream = new nsStringInputStream();
415 273 : if (! stream)
416 0 : return NS_ERROR_OUT_OF_MEMORY;
417 :
418 273 : NS_ADDREF(stream);
419 :
420 273 : stream->SetData(aStringToRead);
421 :
422 273 : *aStreamResult = stream;
423 273 : return NS_OK;
424 : }
425 :
426 : // factory method for constructing a nsStringInputStream object
427 : nsresult
428 198109 : nsStringInputStreamConstructor(nsISupports *outer, REFNSIID iid, void **result)
429 : {
430 198109 : *result = nsnull;
431 :
432 198109 : NS_ENSURE_TRUE(!outer, NS_ERROR_NO_AGGREGATION);
433 :
434 198109 : nsStringInputStream *inst = new nsStringInputStream();
435 198109 : if (!inst)
436 0 : return NS_ERROR_OUT_OF_MEMORY;
437 :
438 198109 : NS_ADDREF(inst);
439 198109 : nsresult rv = inst->QueryInterface(iid, result);
440 198109 : NS_RELEASE(inst);
441 :
442 198109 : return rv;
443 : }
|