xpcom/io/nsStorageStream.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 /* vim:set ts=4 sts=4 sw=4 cin et: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 /*
michael@0 8 * The storage stream provides an internal buffer that can be filled by a
michael@0 9 * client using a single output stream. One or more independent input streams
michael@0 10 * can be created to read the data out non-destructively. The implementation
michael@0 11 * uses a segmented buffer internally to avoid realloc'ing of large buffers,
michael@0 12 * with the attendant performance loss and heap fragmentation.
michael@0 13 */
michael@0 14
michael@0 15 #include "nsAlgorithm.h"
michael@0 16 #include "nsStorageStream.h"
michael@0 17 #include "nsSegmentedBuffer.h"
michael@0 18 #include "nsStreamUtils.h"
michael@0 19 #include "nsCOMPtr.h"
michael@0 20 #include "nsIInputStream.h"
michael@0 21 #include "nsISeekableStream.h"
michael@0 22 #include "prlog.h"
michael@0 23 #include "mozilla/Attributes.h"
michael@0 24 #include "mozilla/Likely.h"
michael@0 25 #include "mozilla/MathAlgorithms.h"
michael@0 26
michael@0 27 #if defined(PR_LOGGING)
michael@0 28 //
michael@0 29 // Log module for StorageStream logging...
michael@0 30 //
michael@0 31 // To enable logging (see prlog.h for full details):
michael@0 32 //
michael@0 33 // set NSPR_LOG_MODULES=StorageStreamLog:5
michael@0 34 // set NSPR_LOG_FILE=nspr.log
michael@0 35 //
michael@0 36 // this enables PR_LOG_DEBUG level information and places all output in
michael@0 37 // the file nspr.log
michael@0 38 //
michael@0 39 static PRLogModuleInfo*
michael@0 40 GetStorageStreamLog()
michael@0 41 {
michael@0 42 static PRLogModuleInfo *sLog;
michael@0 43 if (!sLog)
michael@0 44 sLog = PR_NewLogModule("nsStorageStream");
michael@0 45 return sLog;
michael@0 46 }
michael@0 47 #endif
michael@0 48 #ifdef LOG
michael@0 49 #undef LOG
michael@0 50 #endif
michael@0 51 #define LOG(args) PR_LOG(GetStorageStreamLog(), PR_LOG_DEBUG, args)
michael@0 52
michael@0 53 nsStorageStream::nsStorageStream()
michael@0 54 : mSegmentedBuffer(0), mSegmentSize(0), mWriteInProgress(false),
michael@0 55 mLastSegmentNum(-1), mWriteCursor(0), mSegmentEnd(0), mLogicalLength(0)
michael@0 56 {
michael@0 57 LOG(("Creating nsStorageStream [%p].\n", this));
michael@0 58 }
michael@0 59
michael@0 60 nsStorageStream::~nsStorageStream()
michael@0 61 {
michael@0 62 delete mSegmentedBuffer;
michael@0 63 }
michael@0 64
michael@0 65 NS_IMPL_ISUPPORTS(nsStorageStream,
michael@0 66 nsIStorageStream,
michael@0 67 nsIOutputStream)
michael@0 68
michael@0 69 NS_IMETHODIMP
michael@0 70 nsStorageStream::Init(uint32_t segmentSize, uint32_t maxSize)
michael@0 71 {
michael@0 72 mSegmentedBuffer = new nsSegmentedBuffer();
michael@0 73 if (!mSegmentedBuffer)
michael@0 74 return NS_ERROR_OUT_OF_MEMORY;
michael@0 75
michael@0 76 mSegmentSize = segmentSize;
michael@0 77 mSegmentSizeLog2 = mozilla::FloorLog2(segmentSize);
michael@0 78
michael@0 79 // Segment size must be a power of two
michael@0 80 if (mSegmentSize != ((uint32_t)1 << mSegmentSizeLog2))
michael@0 81 return NS_ERROR_INVALID_ARG;
michael@0 82
michael@0 83 return mSegmentedBuffer->Init(segmentSize, maxSize);
michael@0 84 }
michael@0 85
michael@0 86 NS_IMETHODIMP
michael@0 87 nsStorageStream::GetOutputStream(int32_t aStartingOffset,
michael@0 88 nsIOutputStream * *aOutputStream)
michael@0 89 {
michael@0 90 if (NS_WARN_IF(!aOutputStream))
michael@0 91 return NS_ERROR_INVALID_ARG;
michael@0 92 if (NS_WARN_IF(!mSegmentedBuffer))
michael@0 93 return NS_ERROR_NOT_INITIALIZED;
michael@0 94
michael@0 95 if (mWriteInProgress)
michael@0 96 return NS_ERROR_NOT_AVAILABLE;
michael@0 97
michael@0 98 nsresult rv = Seek(aStartingOffset);
michael@0 99 if (NS_FAILED(rv)) return rv;
michael@0 100
michael@0 101 // Enlarge the last segment in the buffer so that it is the same size as
michael@0 102 // all the other segments in the buffer. (It may have been realloc'ed
michael@0 103 // smaller in the Close() method.)
michael@0 104 if (mLastSegmentNum >= 0)
michael@0 105 if (mSegmentedBuffer->ReallocLastSegment(mSegmentSize)) {
michael@0 106 // Need to re-Seek, since realloc changed segment base pointer
michael@0 107 rv = Seek(aStartingOffset);
michael@0 108 if (NS_FAILED(rv)) return rv;
michael@0 109 }
michael@0 110
michael@0 111 NS_ADDREF(this);
michael@0 112 *aOutputStream = static_cast<nsIOutputStream*>(this);
michael@0 113 mWriteInProgress = true;
michael@0 114 return NS_OK;
michael@0 115 }
michael@0 116
michael@0 117 NS_IMETHODIMP
michael@0 118 nsStorageStream::Close()
michael@0 119 {
michael@0 120 if (NS_WARN_IF(!mSegmentedBuffer))
michael@0 121 return NS_ERROR_NOT_INITIALIZED;
michael@0 122
michael@0 123 mWriteInProgress = false;
michael@0 124
michael@0 125 int32_t segmentOffset = SegOffset(mLogicalLength);
michael@0 126
michael@0 127 // Shrink the final segment in the segmented buffer to the minimum size
michael@0 128 // needed to contain the data, so as to conserve memory.
michael@0 129 if (segmentOffset)
michael@0 130 mSegmentedBuffer->ReallocLastSegment(segmentOffset);
michael@0 131
michael@0 132 mWriteCursor = 0;
michael@0 133 mSegmentEnd = 0;
michael@0 134
michael@0 135 LOG(("nsStorageStream [%p] Close mWriteCursor=%x mSegmentEnd=%x\n",
michael@0 136 this, mWriteCursor, mSegmentEnd));
michael@0 137
michael@0 138 return NS_OK;
michael@0 139 }
michael@0 140
michael@0 141 NS_IMETHODIMP
michael@0 142 nsStorageStream::Flush()
michael@0 143 {
michael@0 144 return NS_OK;
michael@0 145 }
michael@0 146
michael@0 147 NS_IMETHODIMP
michael@0 148 nsStorageStream::Write(const char *aBuffer, uint32_t aCount, uint32_t *aNumWritten)
michael@0 149 {
michael@0 150 if (NS_WARN_IF(!aNumWritten) || NS_WARN_IF(!aBuffer))
michael@0 151 return NS_ERROR_INVALID_ARG;
michael@0 152 if (NS_WARN_IF(!mSegmentedBuffer))
michael@0 153 return NS_ERROR_NOT_INITIALIZED;
michael@0 154
michael@0 155 const char* readCursor;
michael@0 156 uint32_t count, availableInSegment, remaining;
michael@0 157 nsresult rv = NS_OK;
michael@0 158
michael@0 159 LOG(("nsStorageStream [%p] Write mWriteCursor=%x mSegmentEnd=%x aCount=%d\n",
michael@0 160 this, mWriteCursor, mSegmentEnd, aCount));
michael@0 161
michael@0 162 remaining = aCount;
michael@0 163 readCursor = aBuffer;
michael@0 164 // If no segments have been created yet, create one even if we don't have
michael@0 165 // to write any data; this enables creating an input stream which reads from
michael@0 166 // the very end of the data for any amount of data in the stream (i.e.
michael@0 167 // this stream contains N bytes of data and newInputStream(N) is called),
michael@0 168 // even for N=0 (with the caveat that we require .write("", 0) be called to
michael@0 169 // initialize internal buffers).
michael@0 170 bool firstTime = mSegmentedBuffer->GetSegmentCount() == 0;
michael@0 171 while (remaining || MOZ_UNLIKELY(firstTime)) {
michael@0 172 firstTime = false;
michael@0 173 availableInSegment = mSegmentEnd - mWriteCursor;
michael@0 174 if (!availableInSegment) {
michael@0 175 mWriteCursor = mSegmentedBuffer->AppendNewSegment();
michael@0 176 if (!mWriteCursor) {
michael@0 177 mSegmentEnd = 0;
michael@0 178 rv = NS_ERROR_OUT_OF_MEMORY;
michael@0 179 goto out;
michael@0 180 }
michael@0 181 mLastSegmentNum++;
michael@0 182 mSegmentEnd = mWriteCursor + mSegmentSize;
michael@0 183 availableInSegment = mSegmentEnd - mWriteCursor;
michael@0 184 LOG(("nsStorageStream [%p] Write (new seg) mWriteCursor=%x mSegmentEnd=%x\n",
michael@0 185 this, mWriteCursor, mSegmentEnd));
michael@0 186 }
michael@0 187
michael@0 188 count = XPCOM_MIN(availableInSegment, remaining);
michael@0 189 memcpy(mWriteCursor, readCursor, count);
michael@0 190 remaining -= count;
michael@0 191 readCursor += count;
michael@0 192 mWriteCursor += count;
michael@0 193 LOG(("nsStorageStream [%p] Writing mWriteCursor=%x mSegmentEnd=%x count=%d\n",
michael@0 194 this, mWriteCursor, mSegmentEnd, count));
michael@0 195 };
michael@0 196
michael@0 197 out:
michael@0 198 *aNumWritten = aCount - remaining;
michael@0 199 mLogicalLength += *aNumWritten;
michael@0 200
michael@0 201 LOG(("nsStorageStream [%p] Wrote mWriteCursor=%x mSegmentEnd=%x numWritten=%d\n",
michael@0 202 this, mWriteCursor, mSegmentEnd, *aNumWritten));
michael@0 203 return rv;
michael@0 204 }
michael@0 205
michael@0 206 NS_IMETHODIMP
michael@0 207 nsStorageStream::WriteFrom(nsIInputStream *inStr, uint32_t count, uint32_t *_retval)
michael@0 208 {
michael@0 209 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 210 }
michael@0 211
michael@0 212 NS_IMETHODIMP
michael@0 213 nsStorageStream::WriteSegments(nsReadSegmentFun reader, void * closure, uint32_t count, uint32_t *_retval)
michael@0 214 {
michael@0 215 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 216 }
michael@0 217
michael@0 218 NS_IMETHODIMP
michael@0 219 nsStorageStream::IsNonBlocking(bool *aNonBlocking)
michael@0 220 {
michael@0 221 *aNonBlocking = false;
michael@0 222 return NS_OK;
michael@0 223 }
michael@0 224
michael@0 225 NS_IMETHODIMP
michael@0 226 nsStorageStream::GetLength(uint32_t *aLength)
michael@0 227 {
michael@0 228 *aLength = mLogicalLength;
michael@0 229 return NS_OK;
michael@0 230 }
michael@0 231
michael@0 232 // Truncate the buffer by deleting the end segments
michael@0 233 NS_IMETHODIMP
michael@0 234 nsStorageStream::SetLength(uint32_t aLength)
michael@0 235 {
michael@0 236 if (NS_WARN_IF(!mSegmentedBuffer))
michael@0 237 return NS_ERROR_NOT_INITIALIZED;
michael@0 238
michael@0 239 if (mWriteInProgress)
michael@0 240 return NS_ERROR_NOT_AVAILABLE;
michael@0 241
michael@0 242 if (aLength > mLogicalLength)
michael@0 243 return NS_ERROR_INVALID_ARG;
michael@0 244
michael@0 245 int32_t newLastSegmentNum = SegNum(aLength);
michael@0 246 int32_t segmentOffset = SegOffset(aLength);
michael@0 247 if (segmentOffset == 0)
michael@0 248 newLastSegmentNum--;
michael@0 249
michael@0 250 while (newLastSegmentNum < mLastSegmentNum) {
michael@0 251 mSegmentedBuffer->DeleteLastSegment();
michael@0 252 mLastSegmentNum--;
michael@0 253 }
michael@0 254
michael@0 255 mLogicalLength = aLength;
michael@0 256 return NS_OK;
michael@0 257 }
michael@0 258
michael@0 259 NS_IMETHODIMP
michael@0 260 nsStorageStream::GetWriteInProgress(bool *aWriteInProgress)
michael@0 261 {
michael@0 262 *aWriteInProgress = mWriteInProgress;
michael@0 263 return NS_OK;
michael@0 264 }
michael@0 265
michael@0 266 NS_METHOD
michael@0 267 nsStorageStream::Seek(int32_t aPosition)
michael@0 268 {
michael@0 269 if (NS_WARN_IF(!mSegmentedBuffer))
michael@0 270 return NS_ERROR_NOT_INITIALIZED;
michael@0 271
michael@0 272 // An argument of -1 means "seek to end of stream"
michael@0 273 if (aPosition == -1)
michael@0 274 aPosition = mLogicalLength;
michael@0 275
michael@0 276 // Seeking beyond the buffer end is illegal
michael@0 277 if ((uint32_t)aPosition > mLogicalLength)
michael@0 278 return NS_ERROR_INVALID_ARG;
michael@0 279
michael@0 280 // Seeking backwards in the write stream results in truncation
michael@0 281 SetLength(aPosition);
michael@0 282
michael@0 283 // Special handling for seek to start-of-buffer
michael@0 284 if (aPosition == 0) {
michael@0 285 mWriteCursor = 0;
michael@0 286 mSegmentEnd = 0;
michael@0 287 LOG(("nsStorageStream [%p] Seek mWriteCursor=%x mSegmentEnd=%x\n",
michael@0 288 this, mWriteCursor, mSegmentEnd));
michael@0 289 return NS_OK;
michael@0 290 }
michael@0 291
michael@0 292 // Segment may have changed, so reset pointers
michael@0 293 mWriteCursor = mSegmentedBuffer->GetSegment(mLastSegmentNum);
michael@0 294 NS_ASSERTION(mWriteCursor, "null mWriteCursor");
michael@0 295 mSegmentEnd = mWriteCursor + mSegmentSize;
michael@0 296
michael@0 297 // Adjust write cursor for current segment offset. This test is necessary
michael@0 298 // because SegNum may reference the next-to-be-allocated segment, in which
michael@0 299 // case we need to be pointing at the end of the last segment.
michael@0 300 int32_t segmentOffset = SegOffset(aPosition);
michael@0 301 if (segmentOffset == 0 && (SegNum(aPosition) > (uint32_t) mLastSegmentNum))
michael@0 302 mWriteCursor = mSegmentEnd;
michael@0 303 else
michael@0 304 mWriteCursor += segmentOffset;
michael@0 305
michael@0 306 LOG(("nsStorageStream [%p] Seek mWriteCursor=%x mSegmentEnd=%x\n",
michael@0 307 this, mWriteCursor, mSegmentEnd));
michael@0 308 return NS_OK;
michael@0 309 }
michael@0 310
michael@0 311 ////////////////////////////////////////////////////////////////////////////////
michael@0 312
michael@0 313 // There can be many nsStorageInputStreams for a single nsStorageStream
michael@0 314 class nsStorageInputStream MOZ_FINAL : public nsIInputStream
michael@0 315 , public nsISeekableStream
michael@0 316 {
michael@0 317 public:
michael@0 318 nsStorageInputStream(nsStorageStream *aStorageStream, uint32_t aSegmentSize)
michael@0 319 : mStorageStream(aStorageStream), mReadCursor(0),
michael@0 320 mSegmentEnd(0), mSegmentNum(0),
michael@0 321 mSegmentSize(aSegmentSize), mLogicalCursor(0),
michael@0 322 mStatus(NS_OK)
michael@0 323 {
michael@0 324 NS_ADDREF(mStorageStream);
michael@0 325 }
michael@0 326
michael@0 327 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 328 NS_DECL_NSIINPUTSTREAM
michael@0 329 NS_DECL_NSISEEKABLESTREAM
michael@0 330
michael@0 331 private:
michael@0 332 ~nsStorageInputStream()
michael@0 333 {
michael@0 334 NS_IF_RELEASE(mStorageStream);
michael@0 335 }
michael@0 336
michael@0 337 protected:
michael@0 338 NS_METHOD Seek(uint32_t aPosition);
michael@0 339
michael@0 340 friend class nsStorageStream;
michael@0 341
michael@0 342 private:
michael@0 343 nsStorageStream* mStorageStream;
michael@0 344 uint32_t mReadCursor; // Next memory location to read byte, or 0
michael@0 345 uint32_t mSegmentEnd; // One byte past end of current buffer segment
michael@0 346 uint32_t mSegmentNum; // Segment number containing read cursor
michael@0 347 uint32_t mSegmentSize; // All segments, except the last, are of this size
michael@0 348 uint32_t mLogicalCursor; // Logical offset into stream
michael@0 349 nsresult mStatus;
michael@0 350
michael@0 351 uint32_t SegNum(uint32_t aPosition) {return aPosition >> mStorageStream->mSegmentSizeLog2;}
michael@0 352 uint32_t SegOffset(uint32_t aPosition) {return aPosition & (mSegmentSize - 1);}
michael@0 353 };
michael@0 354
michael@0 355 NS_IMPL_ISUPPORTS(nsStorageInputStream,
michael@0 356 nsIInputStream,
michael@0 357 nsISeekableStream)
michael@0 358
michael@0 359 NS_IMETHODIMP
michael@0 360 nsStorageStream::NewInputStream(int32_t aStartingOffset, nsIInputStream* *aInputStream)
michael@0 361 {
michael@0 362 if (NS_WARN_IF(!mSegmentedBuffer))
michael@0 363 return NS_ERROR_NOT_INITIALIZED;
michael@0 364
michael@0 365 nsStorageInputStream *inputStream = new nsStorageInputStream(this, mSegmentSize);
michael@0 366 if (!inputStream)
michael@0 367 return NS_ERROR_OUT_OF_MEMORY;
michael@0 368
michael@0 369 NS_ADDREF(inputStream);
michael@0 370
michael@0 371 nsresult rv = inputStream->Seek(aStartingOffset);
michael@0 372 if (NS_FAILED(rv)) {
michael@0 373 NS_RELEASE(inputStream);
michael@0 374 return rv;
michael@0 375 }
michael@0 376
michael@0 377 *aInputStream = inputStream;
michael@0 378 return NS_OK;
michael@0 379 }
michael@0 380
michael@0 381 NS_IMETHODIMP
michael@0 382 nsStorageInputStream::Close()
michael@0 383 {
michael@0 384 mStatus = NS_BASE_STREAM_CLOSED;
michael@0 385 return NS_OK;
michael@0 386 }
michael@0 387
michael@0 388 NS_IMETHODIMP
michael@0 389 nsStorageInputStream::Available(uint64_t *aAvailable)
michael@0 390 {
michael@0 391 if (NS_FAILED(mStatus))
michael@0 392 return mStatus;
michael@0 393
michael@0 394 *aAvailable = mStorageStream->mLogicalLength - mLogicalCursor;
michael@0 395 return NS_OK;
michael@0 396 }
michael@0 397
michael@0 398 NS_IMETHODIMP
michael@0 399 nsStorageInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t *aNumRead)
michael@0 400 {
michael@0 401 return ReadSegments(NS_CopySegmentToBuffer, aBuffer, aCount, aNumRead);
michael@0 402 }
michael@0 403
michael@0 404 NS_IMETHODIMP
michael@0 405 nsStorageInputStream::ReadSegments(nsWriteSegmentFun writer, void * closure, uint32_t aCount, uint32_t *aNumRead)
michael@0 406 {
michael@0 407 *aNumRead = 0;
michael@0 408 if (mStatus == NS_BASE_STREAM_CLOSED)
michael@0 409 return NS_OK;
michael@0 410 if (NS_FAILED(mStatus))
michael@0 411 return mStatus;
michael@0 412
michael@0 413 uint32_t count, availableInSegment, remainingCapacity, bytesConsumed;
michael@0 414 nsresult rv;
michael@0 415
michael@0 416 remainingCapacity = aCount;
michael@0 417 while (remainingCapacity) {
michael@0 418 availableInSegment = mSegmentEnd - mReadCursor;
michael@0 419 if (!availableInSegment) {
michael@0 420 uint32_t available = mStorageStream->mLogicalLength - mLogicalCursor;
michael@0 421 if (!available)
michael@0 422 goto out;
michael@0 423
michael@0 424 mSegmentNum++;
michael@0 425 mReadCursor = 0;
michael@0 426 mSegmentEnd = XPCOM_MIN(mSegmentSize, available);
michael@0 427 availableInSegment = mSegmentEnd;
michael@0 428 }
michael@0 429 const char *cur = mStorageStream->mSegmentedBuffer->GetSegment(mSegmentNum);
michael@0 430
michael@0 431 count = XPCOM_MIN(availableInSegment, remainingCapacity);
michael@0 432 rv = writer(this, closure, cur + mReadCursor, aCount - remainingCapacity,
michael@0 433 count, &bytesConsumed);
michael@0 434 if (NS_FAILED(rv) || (bytesConsumed == 0))
michael@0 435 break;
michael@0 436 remainingCapacity -= bytesConsumed;
michael@0 437 mReadCursor += bytesConsumed;
michael@0 438 mLogicalCursor += bytesConsumed;
michael@0 439 };
michael@0 440
michael@0 441 out:
michael@0 442 *aNumRead = aCount - remainingCapacity;
michael@0 443
michael@0 444 bool isWriteInProgress = false;
michael@0 445 if (NS_FAILED(mStorageStream->GetWriteInProgress(&isWriteInProgress)))
michael@0 446 isWriteInProgress = false;
michael@0 447
michael@0 448 if (*aNumRead == 0 && isWriteInProgress)
michael@0 449 return NS_BASE_STREAM_WOULD_BLOCK;
michael@0 450
michael@0 451 return NS_OK;
michael@0 452 }
michael@0 453
michael@0 454 NS_IMETHODIMP
michael@0 455 nsStorageInputStream::IsNonBlocking(bool *aNonBlocking)
michael@0 456 {
michael@0 457 // TODO: This class should implement nsIAsyncInputStream so that callers
michael@0 458 // have some way of dealing with NS_BASE_STREAM_WOULD_BLOCK errors.
michael@0 459
michael@0 460 *aNonBlocking = true;
michael@0 461 return NS_OK;
michael@0 462 }
michael@0 463
michael@0 464 NS_IMETHODIMP
michael@0 465 nsStorageInputStream::Seek(int32_t aWhence, int64_t aOffset)
michael@0 466 {
michael@0 467 if (NS_FAILED(mStatus))
michael@0 468 return mStatus;
michael@0 469
michael@0 470 int64_t pos = aOffset;
michael@0 471
michael@0 472 switch (aWhence) {
michael@0 473 case NS_SEEK_SET:
michael@0 474 break;
michael@0 475 case NS_SEEK_CUR:
michael@0 476 pos += mLogicalCursor;
michael@0 477 break;
michael@0 478 case NS_SEEK_END:
michael@0 479 pos += mStorageStream->mLogicalLength;
michael@0 480 break;
michael@0 481 default:
michael@0 482 NS_NOTREACHED("unexpected whence value");
michael@0 483 return NS_ERROR_UNEXPECTED;
michael@0 484 }
michael@0 485 if (pos == int64_t(mLogicalCursor))
michael@0 486 return NS_OK;
michael@0 487
michael@0 488 return Seek(pos);
michael@0 489 }
michael@0 490
michael@0 491 NS_IMETHODIMP
michael@0 492 nsStorageInputStream::Tell(int64_t *aResult)
michael@0 493 {
michael@0 494 if (NS_FAILED(mStatus))
michael@0 495 return mStatus;
michael@0 496
michael@0 497 *aResult = mLogicalCursor;
michael@0 498 return NS_OK;
michael@0 499 }
michael@0 500
michael@0 501 NS_IMETHODIMP
michael@0 502 nsStorageInputStream::SetEOF()
michael@0 503 {
michael@0 504 NS_NOTREACHED("nsStorageInputStream::SetEOF");
michael@0 505 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 506 }
michael@0 507
michael@0 508 NS_METHOD
michael@0 509 nsStorageInputStream::Seek(uint32_t aPosition)
michael@0 510 {
michael@0 511 uint32_t length = mStorageStream->mLogicalLength;
michael@0 512 if (aPosition > length)
michael@0 513 return NS_ERROR_INVALID_ARG;
michael@0 514
michael@0 515 if (length == 0)
michael@0 516 return NS_OK;
michael@0 517
michael@0 518 mSegmentNum = SegNum(aPosition);
michael@0 519 mReadCursor = SegOffset(aPosition);
michael@0 520 uint32_t available = length - aPosition;
michael@0 521 mSegmentEnd = mReadCursor + XPCOM_MIN(mSegmentSize - mReadCursor, available);
michael@0 522 mLogicalCursor = aPosition;
michael@0 523 return NS_OK;
michael@0 524 }
michael@0 525
michael@0 526 nsresult
michael@0 527 NS_NewStorageStream(uint32_t segmentSize, uint32_t maxSize, nsIStorageStream **result)
michael@0 528 {
michael@0 529 nsStorageStream* storageStream = new nsStorageStream();
michael@0 530 if (!storageStream) return NS_ERROR_OUT_OF_MEMORY;
michael@0 531
michael@0 532 NS_ADDREF(storageStream);
michael@0 533 nsresult rv = storageStream->Init(segmentSize, maxSize);
michael@0 534 if (NS_FAILED(rv)) {
michael@0 535 NS_RELEASE(storageStream);
michael@0 536 return rv;
michael@0 537 }
michael@0 538 *result = storageStream;
michael@0 539 return NS_OK;
michael@0 540 }
michael@0 541
michael@0 542 // Undefine LOG, so that other .cpp files (or their includes) won't complain
michael@0 543 // about it already being defined, when we build in unified mode.
michael@0 544 #undef LOG

mercurial