diff options
author | erikkay@google.com <erikkay@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-11-03 17:18:14 +0000 |
---|---|---|
committer | erikkay@google.com <erikkay@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-11-03 17:18:14 +0000 |
commit | 21da6eb1f8a9740de03cb1435bf935f5a3609a37 (patch) | |
tree | 47e6bdd8db72ee4b66f526bbe79cbca74ef85560 /net/base | |
parent | 0a173a23af355f6b4eceeb18f28b453063e4287c (diff) | |
download | chromium_src-21da6eb1f8a9740de03cb1435bf935f5a3609a37.zip chromium_src-21da6eb1f8a9740de03cb1435bf935f5a3609a37.tar.gz chromium_src-21da6eb1f8a9740de03cb1435bf935f5a3609a37.tar.bz2 |
* Add write and read/write support to FileStream (renamed from FileInputStream).
* Moved net/disk_cache/os_file to base/platform_file.
Review URL: http://codereview.chromium.org/8843
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@4454 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/base')
-rw-r--r-- | net/base/file_input_stream_unittest.cc | 205 | ||||
-rw-r--r-- | net/base/file_stream.h (renamed from net/base/file_input_stream.h) | 63 | ||||
-rw-r--r-- | net/base/file_stream_posix.cc (renamed from net/base/file_input_stream_posix.cc) | 78 | ||||
-rw-r--r-- | net/base/file_stream_unittest.cc | 369 | ||||
-rw-r--r-- | net/base/file_stream_win.cc (renamed from net/base/file_input_stream_win.cc) | 110 | ||||
-rw-r--r-- | net/base/upload_data_stream.cc | 4 | ||||
-rw-r--r-- | net/base/upload_data_stream.h | 4 |
7 files changed, 537 insertions, 296 deletions
diff --git a/net/base/file_input_stream_unittest.cc b/net/base/file_input_stream_unittest.cc deleted file mode 100644 index b259de8..0000000 --- a/net/base/file_input_stream_unittest.cc +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright (c) 2008 The Chromium Authors. All rights reserved. Use of this -// source code is governed by a BSD-style license that can be found in the -// LICENSE file. - -#include "base/file_util.h" -#include "base/path_service.h" -#include "base/platform_test.h" -#include "net/base/file_input_stream.h" -#include "net/base/net_errors.h" -#include "net/base/test_completion_callback.h" -#include "testing/gtest/include/gtest/gtest.h" - -static const char kTestData[] = "0123456789"; - -class FileInputStreamTest : public PlatformTest { - public: - virtual void SetUp() { - PlatformTest::SetUp(); - - file_util::CreateTemporaryFileName(&temp_file_path_); - file_util::WriteFile(temp_file_path_, kTestData, arraysize(kTestData)-1); - } - virtual void TearDown() { - file_util::Delete(temp_file_path_, false); - - PlatformTest::TearDown(); - } - const std::wstring temp_file_path() const { return temp_file_path_; } - private: - std::wstring temp_file_path_; -}; - -TEST_F(FileInputStreamTest, BasicOpenClose) { - net::FileInputStream stream; - int rv = stream.Open(temp_file_path(), false); - EXPECT_EQ(net::OK, rv); -} - -TEST_F(FileInputStreamTest, UseClosedStream) { - net::FileInputStream stream; - - EXPECT_FALSE(stream.IsOpen()); - - // Try seeking... - int64 new_offset = stream.Seek(net::FROM_BEGIN, 5); - EXPECT_EQ(net::ERR_UNEXPECTED, new_offset); - - // Try available... - int64 avail = stream.Available(); - EXPECT_EQ(net::ERR_UNEXPECTED, avail); - - // Try reading... - char buf[10]; - int rv = stream.Read(buf, sizeof(buf), NULL); - EXPECT_EQ(net::ERR_UNEXPECTED, rv); -} - -TEST_F(FileInputStreamTest, BasicRead) { - int64 file_size; - bool ok = file_util::GetFileSize(temp_file_path(), &file_size); - EXPECT_TRUE(ok); - - net::FileInputStream stream; - int rv = stream.Open(temp_file_path(), false); - EXPECT_EQ(net::OK, rv); - - int64 total_bytes_avail = stream.Available(); - EXPECT_EQ(file_size, total_bytes_avail); - - int64 total_bytes_read = 0; - - std::string data_read; - for (;;) { - char buf[4]; - rv = stream.Read(buf, sizeof(buf), NULL); - EXPECT_LE(0, rv); - if (rv <= 0) - break; - total_bytes_read += rv; - data_read.append(buf, rv); - } - EXPECT_EQ(file_size, total_bytes_read); - EXPECT_TRUE(data_read == kTestData); -} - -TEST_F(FileInputStreamTest, AsyncRead) { - int64 file_size; - bool ok = file_util::GetFileSize(temp_file_path(), &file_size); - EXPECT_TRUE(ok); - - net::FileInputStream stream; - int rv = stream.Open(temp_file_path(), true); - EXPECT_EQ(net::OK, rv); - - int64 total_bytes_avail = stream.Available(); - EXPECT_EQ(file_size, total_bytes_avail); - - TestCompletionCallback callback; - - int64 total_bytes_read = 0; - - std::string data_read; - for (;;) { - char buf[4]; - rv = stream.Read(buf, sizeof(buf), &callback); - if (rv == net::ERR_IO_PENDING) - rv = callback.WaitForResult(); - EXPECT_LE(0, rv); - if (rv <= 0) - break; - total_bytes_read += rv; - data_read.append(buf, rv); - } - EXPECT_EQ(file_size, total_bytes_read); - EXPECT_TRUE(data_read == kTestData); -} - -TEST_F(FileInputStreamTest, BasicRead_FromOffset) { - int64 file_size; - bool ok = file_util::GetFileSize(temp_file_path(), &file_size); - EXPECT_TRUE(ok); - - net::FileInputStream stream; - int rv = stream.Open(temp_file_path(), false); - EXPECT_EQ(net::OK, rv); - - const int64 kOffset = 3; - int64 new_offset = stream.Seek(net::FROM_BEGIN, kOffset); - EXPECT_EQ(kOffset, new_offset); - - int64 total_bytes_avail = stream.Available(); - EXPECT_EQ(file_size - kOffset, total_bytes_avail); - - int64 total_bytes_read = 0; - - std::string data_read; - for (;;) { - char buf[4]; - rv = stream.Read(buf, sizeof(buf), NULL); - EXPECT_LE(0, rv); - if (rv <= 0) - break; - total_bytes_read += rv; - data_read.append(buf, rv); - } - EXPECT_EQ(file_size - kOffset, total_bytes_read); - EXPECT_TRUE(data_read == kTestData + kOffset); -} - -TEST_F(FileInputStreamTest, AsyncRead_FromOffset) { - int64 file_size; - bool ok = file_util::GetFileSize(temp_file_path(), &file_size); - EXPECT_TRUE(ok); - - net::FileInputStream stream; - int rv = stream.Open(temp_file_path(), true); - EXPECT_EQ(net::OK, rv); - - const int64 kOffset = 3; - int64 new_offset = stream.Seek(net::FROM_BEGIN, kOffset); - EXPECT_EQ(kOffset, new_offset); - - int64 total_bytes_avail = stream.Available(); - EXPECT_EQ(file_size - kOffset, total_bytes_avail); - - TestCompletionCallback callback; - - int64 total_bytes_read = 0; - - std::string data_read; - for (;;) { - char buf[4]; - rv = stream.Read(buf, sizeof(buf), &callback); - if (rv == net::ERR_IO_PENDING) - rv = callback.WaitForResult(); - EXPECT_LE(0, rv); - if (rv <= 0) - break; - total_bytes_read += rv; - data_read.append(buf, rv); - } - EXPECT_EQ(file_size - kOffset, total_bytes_read); - EXPECT_TRUE(data_read == kTestData + kOffset); -} - -TEST_F(FileInputStreamTest, SeekAround) { - net::FileInputStream stream; - int rv = stream.Open(temp_file_path(), true); - EXPECT_EQ(net::OK, rv); - - const int64 kOffset = 3; - int64 new_offset = stream.Seek(net::FROM_BEGIN, kOffset); - EXPECT_EQ(kOffset, new_offset); - - new_offset = stream.Seek(net::FROM_CURRENT, kOffset); - EXPECT_EQ(2 * kOffset, new_offset); - - new_offset = stream.Seek(net::FROM_CURRENT, -kOffset); - EXPECT_EQ(kOffset, new_offset); - - const int kTestDataLen = arraysize(kTestData) - 1; - - new_offset = stream.Seek(net::FROM_END, -kTestDataLen); - EXPECT_EQ(0, new_offset); -} diff --git a/net/base/file_input_stream.h b/net/base/file_stream.h index eb4b2ae..b98cb15 100644 --- a/net/base/file_input_stream.h +++ b/net/base/file_stream.h @@ -2,18 +2,17 @@ // source code is governed by a BSD-style license that can be found in the // LICENSE file. -// This file defines FileInputStream, a basic interface for reading files +// This file defines FileStream, a basic interface for reading and writing files // synchronously or asynchronously with support for seeking to an offset. +// Note that even when used asynchronously, only one operation is supported at +// a time. -#ifndef NET_BASE_FILE_INPUT_STREAM_H_ -#define NET_BASE_FILE_INPUT_STREAM_H_ +#ifndef NET_BASE_FILE_STREAM_H_ +#define NET_BASE_FILE_STREAM_H_ +#include "base/platform_file.h" #include "net/base/completion_callback.h" -#if defined(OS_WIN) -typedef void* HANDLE; -#endif - namespace net { // TODO(darin): Move this to a more generic location. @@ -24,20 +23,21 @@ enum Whence { FROM_END = 2 }; -class FileInputStream { +class FileStream { public: - FileInputStream(); - ~FileInputStream(); + FileStream(); + ~FileStream(); - // Call this method to close the FileInputStream. It is OK to call Close + // Call this method to close the FileStream. It is OK to call Close // multiple times. Redundant calls are ignored. + // Note that if there are any pending async operations, they'll be aborted. void Close(); - // Call this method to open the FileInputStream. The remaining methods + // Call this method to open the FileStream. The remaining methods // cannot be used unless this method returns OK. If the file cannot be // opened then an error code is returned. - // NOTE: The underlying file is opened with non-exclusive access. - int Open(const std::wstring& path, bool asynchronous_mode); + // open_flags is a bitfield of base::PlatformFileFlags + int Open(const std::wstring& path, int open_flags); // Returns true if Open succeeded and Close has not been called. bool IsOpen() const; @@ -57,7 +57,7 @@ class FileInputStream { // allowed.) Returns the number of bytes copied, 0 if at end-of-file, or an // error code if the operation could not be performed. // - // If opened with |asynchronous_mode| set to true, then a non-null callback + // If opened with PLATFORM_FILE_ASYNC, then a non-null callback // must be passed to this method. In asynchronous mode, if the read could // not complete synchronously, then ERR_IO_PENDING is returned, and the // callback will be notified on the current thread (via the MessageLoop) when @@ -68,25 +68,42 @@ class FileInputStream { // destroy or close the file stream while there is an asynchronous read in // progress. That will cancel the read and allow the buffer to be freed. // + // This method should not be called if the stream was opened WRITE_ONLY. int Read(char* buf, int buf_len, CompletionCallback* callback); + // Call this method to write data at the current stream position. Up to + // buf_len bytes will be written from buf. (In other words, partial writes are + // allowed.) Returns the number of bytes written, or an error code if the + // operation could not be performed. + // + // If opened with PLATFORM_FILE_ASYNC, then a non-null callback + // must be passed to this method. In asynchronous mode, if the write could + // not complete synchronously, then ERR_IO_PENDING is returned, and the + // callback will be notified on the current thread (via the MessageLoop) when + // the write has completed. + // + // In the case of an asychronous write, the memory pointed to by |buf| must + // remain valid until the callback is notified. However, it is valid to + // destroy or close the file stream while there is an asynchronous write in + // progress. That will cancel the write and allow the buffer to be freed. + // + // This method should not be called if the stream was opened READ_ONLY. + int Write(const char* buf, int buf_len, CompletionCallback* callback); + private: class AsyncContext; friend class AsyncContext; // This member is used to support asynchronous reads. It is non-null when - // the FileInputStream was opened with asynchronous_mode set to true. + // the FileStream was opened with PLATFORM_FILE_ASYNC. scoped_ptr<AsyncContext> async_context_; -#if defined(OS_WIN) - HANDLE handle_; -#elif defined(OS_POSIX) - int fd_; -#endif + base::PlatformFile file_; + int open_flags_; - DISALLOW_COPY_AND_ASSIGN(FileInputStream); + DISALLOW_COPY_AND_ASSIGN(FileStream); }; } // namespace net -#endif // NET_BASE_FILE_INPUT_STREAM_H_ +#endif // NET_BASE_FILE_STREAM_H_ diff --git a/net/base/file_input_stream_posix.cc b/net/base/file_stream_posix.cc index 4f2eb937..53589166 100644 --- a/net/base/file_input_stream_posix.cc +++ b/net/base/file_stream_posix.cc @@ -5,7 +5,7 @@ // For 64-bit file access (off_t = off64_t, lseek64, etc). #define _FILE_OFFSET_BITS 64 -#include "net/base/file_input_stream.h" +#include "net/base/file_stream.h" #include <sys/types.h> #include <sys/stat.h> @@ -29,10 +29,10 @@ COMPILE_ASSERT(net::FROM_BEGIN == SEEK_SET && namespace net { -// FileInputStream::AsyncContext ---------------------------------------------- +// FileStream::AsyncContext ---------------------------------------------- // TODO(deanm): Figure out how to best do async IO. -class FileInputStream::AsyncContext { +class FileStream::AsyncContext { public: CompletionCallback* callback() const { return NULL; } @@ -41,22 +41,22 @@ class FileInputStream::AsyncContext { DISALLOW_COPY_AND_ASSIGN(AsyncContext); }; -// FileInputStream ------------------------------------------------------------ +// FileStream ------------------------------------------------------------ -FileInputStream::FileInputStream() : fd_(-1) { +FileStream::FileStream() : file_(base::kInvalidPlatformFileValue) { DCHECK(!IsOpen()); } -FileInputStream::~FileInputStream() { +FileStream::~FileStream() { Close(); } -void FileInputStream::Close() { - if (fd_ != -1) { - if (close(fd_) != 0) { +void FileStream::Close() { + if (file_ != base::kInvalidPlatformFileValue) { + if (close(file_) != 0) { NOTREACHED(); } - fd_ = -1; + file_ = base::kInvalidPlatformFileValue; } async_context_.reset(); } @@ -74,34 +74,42 @@ static int64 MapErrorCode(int err) { } } -int FileInputStream::Open(const std::wstring& path, bool asynchronous_mode) { - // We don't need O_LARGEFILE here since we set the 64-bit off_t feature. - fd_ = open(WideToUTF8(path).c_str(), 0, O_RDONLY); - if (fd_ == -1) +int FileStream::Open(const std::wstring& path, int open_flags) { + if (IsOpen()) { + DLOG(FATAL) << "File is already open!"; + return ERR_UNEXPECTED; + } + + open_flags_ = open_flags; + file_ = base::CreatePlatformFile(path, open_flags_, NULL); + if (file_ == base::kInvalidPlatformFileValue) { + LOG(WARNING) << "Failed to open file: " << errno; return MapErrorCode(errno); + } return OK; } -bool FileInputStream::IsOpen() const { - return fd_ != -1; +bool FileStream::IsOpen() const { + return file_ != base::kInvalidPlatformFileValue; } -int64 FileInputStream::Seek(Whence whence, int64 offset) { +int64 FileStream::Seek(Whence whence, int64 offset) { if (!IsOpen()) return ERR_UNEXPECTED; // If we're in async, make sure we don't have a request in flight. DCHECK(!async_context_.get() || !async_context_->callback()); - off_t res = lseek(fd_, static_cast<off_t>(offset), static_cast<int>(whence)); + off_t res = lseek(file_, static_cast<off_t>(offset), + static_cast<int>(whence)); if (res == static_cast<off_t>(-1)) return MapErrorCode(errno); return res; } -int64 FileInputStream::Available() { +int64 FileStream::Available() { if (!IsOpen()) return ERR_UNEXPECTED; @@ -110,7 +118,7 @@ int64 FileInputStream::Available() { return cur_pos; struct stat info; - if (fstat(fd_, &info) != 0) + if (fstat(file_, &info) != 0) return MapErrorCode(errno); int64 size = static_cast<int64>(info.st_size); @@ -120,7 +128,7 @@ int64 FileInputStream::Available() { } // TODO(deanm): async. -int FileInputStream::Read( +int FileStream::Read( char* buf, int buf_len, CompletionCallback* callback) { // read(..., 0) will return 0, which indicates end-of-file. DCHECK(buf_len > 0 && buf_len <= SSIZE_MAX); @@ -130,7 +138,7 @@ int FileInputStream::Read( // Loop in the case of getting interrupted by a signal. for (;;) { - ssize_t res = read(fd_, buf, static_cast<size_t>(buf_len)); + ssize_t res = read(file_, buf, static_cast<size_t>(buf_len)); if (res == static_cast<ssize_t>(-1)) { if (errno == EINTR) continue; @@ -140,4 +148,30 @@ int FileInputStream::Read( } } +// TODO(deanm): async. +int FileStream::Write( + const char* buf, int buf_len, CompletionCallback* callback) { + + // read(..., 0) will return 0, which indicates end-of-file. + DCHECK(buf_len > 0 && buf_len <= SSIZE_MAX); + + if (!IsOpen()) + return ERR_UNEXPECTED; + + int total_bytes_written = 0; + size_t len = static_cast<size_t>(buf_len); + while (total_bytes_written < buf_len) { + ssize_t res = write(file_, buf, len); + if (res == static_cast<ssize_t>(-1)) { + if (errno == EINTR) + continue; + return MapErrorCode(errno); + } + total_bytes_written += res; + buf += res; + len -= res; + } + return total_bytes_written; +} + } // namespace net diff --git a/net/base/file_stream_unittest.cc b/net/base/file_stream_unittest.cc new file mode 100644 index 0000000..f3f208b --- /dev/null +++ b/net/base/file_stream_unittest.cc @@ -0,0 +1,369 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. Use of this +// source code is governed by a BSD-style license that can be found in the +// LICENSE file. + +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/platform_test.h" +#include "net/base/file_stream.h" +#include "net/base/net_errors.h" +#include "net/base/test_completion_callback.h" +#include "testing/gtest/include/gtest/gtest.h" + +static const char kTestData[] = "0123456789"; +static const int kTestDataSize = arraysize(kTestData) - 1; + +class FileStreamTest : public PlatformTest { + public: + virtual void SetUp() { + PlatformTest::SetUp(); + + file_util::CreateTemporaryFileName(&temp_file_path_); + file_util::WriteFile(temp_file_path_, kTestData, kTestDataSize); + } + virtual void TearDown() { + file_util::Delete(temp_file_path_, false); + + PlatformTest::TearDown(); + } + const std::wstring temp_file_path() const { return temp_file_path_; } + private: + std::wstring temp_file_path_; +}; + +TEST_F(FileStreamTest, BasicOpenClose) { + net::FileStream stream; + int rv = stream.Open(temp_file_path(), + base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ); + EXPECT_EQ(net::OK, rv); +} + +TEST_F(FileStreamTest, UseClosedStream) { + net::FileStream stream; + + EXPECT_FALSE(stream.IsOpen()); + + // Try seeking... + int64 new_offset = stream.Seek(net::FROM_BEGIN, 5); + EXPECT_EQ(net::ERR_UNEXPECTED, new_offset); + + // Try available... + int64 avail = stream.Available(); + EXPECT_EQ(net::ERR_UNEXPECTED, avail); + + // Try reading... + char buf[10]; + int rv = stream.Read(buf, sizeof(buf), NULL); + EXPECT_EQ(net::ERR_UNEXPECTED, rv); +} + +TEST_F(FileStreamTest, BasicRead) { + int64 file_size; + bool ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + + net::FileStream stream; + int flags = base::PLATFORM_FILE_OPEN | + base::PLATFORM_FILE_READ; + int rv = stream.Open(temp_file_path(), flags); + EXPECT_EQ(net::OK, rv); + + int64 total_bytes_avail = stream.Available(); + EXPECT_EQ(file_size, total_bytes_avail); + + int64 total_bytes_read = 0; + + std::string data_read; + for (;;) { + char buf[4]; + rv = stream.Read(buf, sizeof(buf), NULL); + EXPECT_LE(0, rv); + if (rv <= 0) + break; + total_bytes_read += rv; + data_read.append(buf, rv); + } + EXPECT_EQ(file_size, total_bytes_read); + EXPECT_TRUE(data_read == kTestData); +} + +TEST_F(FileStreamTest, AsyncRead) { + int64 file_size; + bool ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + + net::FileStream stream; + int flags = base::PLATFORM_FILE_OPEN | + base::PLATFORM_FILE_READ | + base::PLATFORM_FILE_ASYNC; + int rv = stream.Open(temp_file_path(), flags); + EXPECT_EQ(net::OK, rv); + + int64 total_bytes_avail = stream.Available(); + EXPECT_EQ(file_size, total_bytes_avail); + + TestCompletionCallback callback; + + int64 total_bytes_read = 0; + + std::string data_read; + for (;;) { + char buf[4]; + rv = stream.Read(buf, sizeof(buf), &callback); + if (rv == net::ERR_IO_PENDING) + rv = callback.WaitForResult(); + EXPECT_LE(0, rv); + if (rv <= 0) + break; + total_bytes_read += rv; + data_read.append(buf, rv); + } + EXPECT_EQ(file_size, total_bytes_read); + EXPECT_TRUE(data_read == kTestData); +} + +TEST_F(FileStreamTest, BasicRead_FromOffset) { + int64 file_size; + bool ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + + net::FileStream stream; + int flags = base::PLATFORM_FILE_OPEN | + base::PLATFORM_FILE_READ; + int rv = stream.Open(temp_file_path(), flags); + EXPECT_EQ(net::OK, rv); + + const int64 kOffset = 3; + int64 new_offset = stream.Seek(net::FROM_BEGIN, kOffset); + EXPECT_EQ(kOffset, new_offset); + + int64 total_bytes_avail = stream.Available(); + EXPECT_EQ(file_size - kOffset, total_bytes_avail); + + int64 total_bytes_read = 0; + + std::string data_read; + for (;;) { + char buf[4]; + rv = stream.Read(buf, sizeof(buf), NULL); + EXPECT_LE(0, rv); + if (rv <= 0) + break; + total_bytes_read += rv; + data_read.append(buf, rv); + } + EXPECT_EQ(file_size - kOffset, total_bytes_read); + EXPECT_TRUE(data_read == kTestData + kOffset); +} + +TEST_F(FileStreamTest, AsyncRead_FromOffset) { + int64 file_size; + bool ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + + net::FileStream stream; + int flags = base::PLATFORM_FILE_OPEN | + base::PLATFORM_FILE_READ | + base::PLATFORM_FILE_ASYNC; + int rv = stream.Open(temp_file_path(), flags); + EXPECT_EQ(net::OK, rv); + + const int64 kOffset = 3; + int64 new_offset = stream.Seek(net::FROM_BEGIN, kOffset); + EXPECT_EQ(kOffset, new_offset); + + int64 total_bytes_avail = stream.Available(); + EXPECT_EQ(file_size - kOffset, total_bytes_avail); + + TestCompletionCallback callback; + + int64 total_bytes_read = 0; + + std::string data_read; + for (;;) { + char buf[4]; + rv = stream.Read(buf, sizeof(buf), &callback); + if (rv == net::ERR_IO_PENDING) + rv = callback.WaitForResult(); + EXPECT_LE(0, rv); + if (rv <= 0) + break; + total_bytes_read += rv; + data_read.append(buf, rv); + } + EXPECT_EQ(file_size - kOffset, total_bytes_read); + EXPECT_TRUE(data_read == kTestData + kOffset); +} + +TEST_F(FileStreamTest, SeekAround) { + net::FileStream stream; + int flags = base::PLATFORM_FILE_OPEN | + base::PLATFORM_FILE_READ; + int rv = stream.Open(temp_file_path(), flags); + EXPECT_EQ(net::OK, rv); + + const int64 kOffset = 3; + int64 new_offset = stream.Seek(net::FROM_BEGIN, kOffset); + EXPECT_EQ(kOffset, new_offset); + + new_offset = stream.Seek(net::FROM_CURRENT, kOffset); + EXPECT_EQ(2 * kOffset, new_offset); + + new_offset = stream.Seek(net::FROM_CURRENT, -kOffset); + EXPECT_EQ(kOffset, new_offset); + + const int kTestDataLen = arraysize(kTestData) - 1; + + new_offset = stream.Seek(net::FROM_END, -kTestDataLen); + EXPECT_EQ(0, new_offset); +} + +TEST_F(FileStreamTest, BasicWrite) { + net::FileStream stream; + int flags = base::PLATFORM_FILE_CREATE_ALWAYS | + base::PLATFORM_FILE_WRITE; + int rv = stream.Open(temp_file_path(), flags); + EXPECT_EQ(net::OK, rv); + + int64 file_size; + bool ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + EXPECT_EQ(0, file_size); + + rv = stream.Write(kTestData, kTestDataSize, NULL); + EXPECT_EQ(kTestDataSize, rv); + stream.Close(); + + ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + EXPECT_EQ(kTestDataSize, file_size); +} + +TEST_F(FileStreamTest, AsyncWrite) { + net::FileStream stream; + int flags = base::PLATFORM_FILE_CREATE_ALWAYS | + base::PLATFORM_FILE_WRITE | + base::PLATFORM_FILE_ASYNC; + int rv = stream.Open(temp_file_path(), flags); + EXPECT_EQ(net::OK, rv); + + int64 file_size; + bool ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + EXPECT_EQ(0, file_size); + + TestCompletionCallback callback; + int64 total_bytes_written = 0; + + while (total_bytes_written != kTestDataSize) { + rv = stream.Write(kTestData, kTestDataSize, &callback); + if (rv == net::ERR_IO_PENDING) + rv = callback.WaitForResult(); + EXPECT_LT(0, rv); + if (rv <= 0) + break; + total_bytes_written += rv; + } + ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + EXPECT_EQ(file_size, total_bytes_written); +} + +TEST_F(FileStreamTest, BasicWrite_FromOffset) { + net::FileStream stream; + int flags = base::PLATFORM_FILE_OPEN | + base::PLATFORM_FILE_WRITE; + int rv = stream.Open(temp_file_path(), flags); + EXPECT_EQ(net::OK, rv); + + int64 file_size; + bool ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + EXPECT_EQ(kTestDataSize, file_size); + + const int64 kOffset = 0; + int64 new_offset = stream.Seek(net::FROM_END, kOffset); + EXPECT_EQ(kTestDataSize, new_offset); + + rv = stream.Write(kTestData, kTestDataSize, NULL); + EXPECT_EQ(kTestDataSize, rv); + stream.Close(); + + ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + EXPECT_EQ(kTestDataSize * 2, file_size); +} + +TEST_F(FileStreamTest, AsyncWrite_FromOffset) { + int64 file_size; + bool ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + + net::FileStream stream; + int flags = base::PLATFORM_FILE_OPEN | + base::PLATFORM_FILE_WRITE | + base::PLATFORM_FILE_ASYNC; + int rv = stream.Open(temp_file_path(), flags); + EXPECT_EQ(net::OK, rv); + + const int64 kOffset = 0; + int64 new_offset = stream.Seek(net::FROM_END, kOffset); + EXPECT_EQ(kTestDataSize, new_offset); + + TestCompletionCallback callback; + int64 total_bytes_written = 0; + + while (total_bytes_written != kTestDataSize) { + rv = stream.Write(kTestData, kTestDataSize, &callback); + if (rv == net::ERR_IO_PENDING) + rv = callback.WaitForResult(); + EXPECT_LT(0, rv); + if (rv <= 0) + break; + total_bytes_written += rv; + } + ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + EXPECT_EQ(file_size, kTestDataSize * 2); +} + +TEST_F(FileStreamTest, BasicReadWrite) { + int64 file_size; + bool ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + + net::FileStream stream; + int flags = base::PLATFORM_FILE_OPEN | + base::PLATFORM_FILE_READ | + base::PLATFORM_FILE_WRITE; + int rv = stream.Open(temp_file_path(), flags); + EXPECT_EQ(net::OK, rv); + + int64 total_bytes_avail = stream.Available(); + EXPECT_EQ(file_size, total_bytes_avail); + + int64 total_bytes_read = 0; + + std::string data_read; + for (;;) { + char buf[4]; + rv = stream.Read(buf, sizeof(buf), NULL); + EXPECT_LE(0, rv); + if (rv <= 0) + break; + total_bytes_read += rv; + data_read.append(buf, rv); + } + EXPECT_EQ(file_size, total_bytes_read); + EXPECT_TRUE(data_read == kTestData); + + rv = stream.Write(kTestData, kTestDataSize, NULL); + EXPECT_EQ(kTestDataSize, rv); + stream.Close(); + + ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + EXPECT_EQ(kTestDataSize * 2, file_size); +} + +// TODO(erikkay): more READ_WRITE tests? diff --git a/net/base/file_input_stream_win.cc b/net/base/file_stream_win.cc index f964362..e59ef0d 100644 --- a/net/base/file_input_stream_win.cc +++ b/net/base/file_stream_win.cc @@ -2,7 +2,7 @@ // source code is governed by a BSD-style license that can be found in the // LICENSE file. -#include "net/base/file_input_stream.h" +#include "net/base/file_stream.h" #include <windows.h> @@ -45,11 +45,11 @@ static int MapErrorCode(DWORD err) { } } -// FileInputStream::AsyncContext ---------------------------------------------- +// FileStream::AsyncContext ---------------------------------------------- -class FileInputStream::AsyncContext : public MessageLoopForIO::IOHandler { +class FileStream::AsyncContext : public MessageLoopForIO::IOHandler { public: - AsyncContext(FileInputStream* owner) + AsyncContext(FileStream* owner) : owner_(owner), overlapped_(), callback_(NULL) { overlapped_.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); } @@ -67,15 +67,15 @@ class FileInputStream::AsyncContext : public MessageLoopForIO::IOHandler { private: // MessageLoopForIO::IOHandler implementation: - virtual void OnIOCompleted(OVERLAPPED* context, DWORD bytes_read, + virtual void OnIOCompleted(OVERLAPPED* context, DWORD num_bytes, DWORD error); - FileInputStream* owner_; + FileStream* owner_; OVERLAPPED overlapped_; CompletionCallback* callback_; }; -void FileInputStream::AsyncContext::IOCompletionIsPending( +void FileStream::AsyncContext::IOCompletionIsPending( CompletionCallback* callback) { DCHECK(!callback_); callback_ = callback; @@ -83,82 +83,73 @@ void FileInputStream::AsyncContext::IOCompletionIsPending( MessageLoopForIO::current()->RegisterIOContext(&overlapped_, this); } -void FileInputStream::AsyncContext::OnIOCompleted(OVERLAPPED* context, - DWORD bytes_read, +void FileStream::AsyncContext::OnIOCompleted(OVERLAPPED* context, + DWORD num_bytes, DWORD error) { DCHECK(&overlapped_ == context); DCHECK(callback_); MessageLoopForIO::current()->RegisterIOContext(&overlapped_, NULL); - HANDLE handle = owner_->handle_; + HANDLE handle = owner_->file_; - int result = static_cast<int>(bytes_read); + int result = static_cast<int>(num_bytes); if (error && error != ERROR_HANDLE_EOF) result = MapErrorCode(error); - if (bytes_read) - IncrementOffset(&overlapped_, bytes_read); + if (num_bytes) + IncrementOffset(&overlapped_, num_bytes); CompletionCallback* temp = NULL; std::swap(temp, callback_); temp->Run(result); } -// FileInputStream ------------------------------------------------------------ +// FileStream ------------------------------------------------------------ -FileInputStream::FileInputStream() : handle_(INVALID_HANDLE_VALUE) { +FileStream::FileStream() : file_(INVALID_HANDLE_VALUE) { } -FileInputStream::~FileInputStream() { +FileStream::~FileStream() { Close(); } -void FileInputStream::Close() { - if (handle_ != INVALID_HANDLE_VALUE) { - CloseHandle(handle_); - handle_ = INVALID_HANDLE_VALUE; +void FileStream::Close() { + if (file_ != INVALID_HANDLE_VALUE) { + CloseHandle(file_); + file_ = INVALID_HANDLE_VALUE; } async_context_.reset(); } -int FileInputStream::Open(const std::wstring& path, bool asynchronous_mode) { +int FileStream::Open(const std::wstring& path, int open_flags) { if (IsOpen()) { DLOG(FATAL) << "File is already open!"; return ERR_UNEXPECTED; } - // Optimize for streaming, not seeking. If someone does a lot of random - // access operations, then we should consider revising this. - DWORD create_file_flags = FILE_FLAG_SEQUENTIAL_SCAN; - - if (asynchronous_mode) - create_file_flags |= FILE_FLAG_OVERLAPPED; - - handle_ = - CreateFile(path.c_str(), GENERIC_READ | SYNCHRONIZE, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, OPEN_EXISTING, create_file_flags, NULL); - if (handle_ == INVALID_HANDLE_VALUE) { + open_flags_ = open_flags; + file_ = base::CreatePlatformFile(path, open_flags_, NULL); + if (file_ == INVALID_HANDLE_VALUE) { DWORD error = GetLastError(); LOG(WARNING) << "Failed to open file: " << error; return MapErrorCode(error); } - if (asynchronous_mode) { + if (open_flags_ & base::PLATFORM_FILE_ASYNC) { async_context_.reset(new AsyncContext(this)); - MessageLoopForIO::current()->RegisterIOHandler(handle_, + MessageLoopForIO::current()->RegisterIOHandler(file_, async_context_.get()); } return OK; } -bool FileInputStream::IsOpen() const { - return handle_ != INVALID_HANDLE_VALUE; +bool FileStream::IsOpen() const { + return file_ != INVALID_HANDLE_VALUE; } -int64 FileInputStream::Seek(Whence whence, int64 offset) { +int64 FileStream::Seek(Whence whence, int64 offset) { if (!IsOpen()) return ERR_UNEXPECTED; DCHECK(!async_context_.get() || !async_context_->callback()); @@ -166,7 +157,7 @@ int64 FileInputStream::Seek(Whence whence, int64 offset) { LARGE_INTEGER distance, result; distance.QuadPart = offset; DWORD move_method = static_cast<DWORD>(whence); - if (!SetFilePointerEx(handle_, distance, &result, move_method)) { + if (!SetFilePointerEx(file_, distance, &result, move_method)) { DWORD error = GetLastError(); LOG(WARNING) << "SetFilePointerEx failed: " << error; return MapErrorCode(error); @@ -176,7 +167,7 @@ int64 FileInputStream::Seek(Whence whence, int64 offset) { return result.QuadPart; } -int64 FileInputStream::Available() { +int64 FileStream::Available() { if (!IsOpen()) return ERR_UNEXPECTED; @@ -185,7 +176,7 @@ int64 FileInputStream::Available() { return cur_pos; LARGE_INTEGER file_size; - if (!GetFileSizeEx(handle_, &file_size)) { + if (!GetFileSizeEx(file_, &file_size)) { DWORD error = GetLastError(); LOG(WARNING) << "GetFileSizeEx failed: " << error; return MapErrorCode(error); @@ -194,10 +185,11 @@ int64 FileInputStream::Available() { return file_size.QuadPart - cur_pos; } -int FileInputStream::Read( +int FileStream::Read( char* buf, int buf_len, CompletionCallback* callback) { if (!IsOpen()) return ERR_UNEXPECTED; + DCHECK(open_flags_ & base::PLATFORM_FILE_READ); OVERLAPPED* overlapped = NULL; if (async_context_.get()) { @@ -208,7 +200,7 @@ int FileInputStream::Read( int rv; DWORD bytes_read; - if (!ReadFile(handle_, buf, buf_len, &bytes_read, overlapped)) { + if (!ReadFile(file_, buf, buf_len, &bytes_read, overlapped)) { DWORD error = GetLastError(); if (async_context_.get() && error == ERROR_IO_PENDING) { async_context_->IOCompletionIsPending(callback); @@ -227,4 +219,36 @@ int FileInputStream::Read( return rv; } +int FileStream::Write( + const char* buf, int buf_len, CompletionCallback* callback) { + if (!IsOpen()) + return ERR_UNEXPECTED; + DCHECK(open_flags_ & base::PLATFORM_FILE_READ); + + OVERLAPPED* overlapped = NULL; + if (async_context_.get()) { + DCHECK(!async_context_->callback()); + overlapped = async_context_->overlapped(); + } + + int rv; + DWORD bytes_written; + if (!WriteFile(file_, buf, buf_len, &bytes_written, overlapped)) { + DWORD error = GetLastError(); + if (async_context_.get() && error == ERROR_IO_PENDING) { + async_context_->IOCompletionIsPending(callback); + rv = ERR_IO_PENDING; + } else { + LOG(WARNING) << "WriteFile failed: " << error; + rv = MapErrorCode(error); + } + } else { + if (overlapped) + IncrementOffset(overlapped, bytes_written); + rv = static_cast<int>(bytes_written); + } + return rv; +} + } // namespace net + diff --git a/net/base/upload_data_stream.cc b/net/base/upload_data_stream.cc index df5ef0f..e874439 100644 --- a/net/base/upload_data_stream.cc +++ b/net/base/upload_data_stream.cc @@ -68,7 +68,9 @@ void UploadDataStream::FillBuf() { DCHECK(element.type() == UploadData::TYPE_FILE); if (!next_element_stream_.IsOpen()) { - int rv = next_element_stream_.Open(element.file_path(), false); + int flags = base::PLATFORM_FILE_OPEN | + base::PLATFORM_FILE_READ; + int rv = next_element_stream_.Open(element.file_path(), flags); // If the file does not exist, that's technically okay.. we'll just // upload an empty file. This is for consistency with Mozilla. DLOG_IF(WARNING, rv != OK) << "Failed to open \"" << diff --git a/net/base/upload_data_stream.h b/net/base/upload_data_stream.h index 9d22d2e..7c2bb6d 100644 --- a/net/base/upload_data_stream.h +++ b/net/base/upload_data_stream.h @@ -5,7 +5,7 @@ #ifndef NET_BASE_UPLOAD_DATA_STREAM_H_ #define NET_BASE_UPLOAD_DATA_STREAM_H_ -#include "net/base/file_input_stream.h" +#include "net/base/file_stream.h" #include "net/base/upload_data.h" namespace net { @@ -53,7 +53,7 @@ class UploadDataStream { // A stream to the currently open file, for next_element_ if the next element // is a TYPE_FILE element. - FileInputStream next_element_stream_; + FileStream next_element_stream_; // The number of bytes remaining to be read from the currently open file // if the next element is of TYPE_FILE. |