1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "mojo/common/data_pipe_utils.h"
6
7#include <stddef.h>
8#include <stdint.h>
9#include <stdio.h>
10#include <utility>
11
12#include "base/files/file_path.h"
13#include "base/files/file_util.h"
14#include "base/files/scoped_file.h"
15#include "base/message_loop/message_loop.h"
16#include "base/task_runner_util.h"
17
18namespace mojo {
19namespace common {
20namespace {
21
22bool BlockingCopyHelper(ScopedDataPipeConsumerHandle source,
23    const base::Callback<size_t(const void*, uint32_t)>& write_bytes) {
24  for (;;) {
25    const void* buffer;
26    uint32_t num_bytes;
27    MojoResult result = BeginReadDataRaw(
28        source.get(), &buffer, &num_bytes, MOJO_READ_DATA_FLAG_NONE);
29    if (result == MOJO_RESULT_OK) {
30      size_t bytes_written = write_bytes.Run(buffer, num_bytes);
31      result = EndReadDataRaw(source.get(), num_bytes);
32      if (bytes_written < num_bytes || result != MOJO_RESULT_OK)
33        return false;
34    } else if (result == MOJO_RESULT_SHOULD_WAIT) {
35      result = Wait(source.get(),
36                    MOJO_HANDLE_SIGNAL_READABLE,
37                    MOJO_DEADLINE_INDEFINITE,
38                    nullptr);
39      if (result != MOJO_RESULT_OK) {
40        // If the producer handle was closed, then treat as EOF.
41        return result == MOJO_RESULT_FAILED_PRECONDITION;
42      }
43    } else if (result == MOJO_RESULT_FAILED_PRECONDITION) {
44      // If the producer handle was closed, then treat as EOF.
45      return true;
46    } else {
47      // Some other error occurred.
48      break;
49    }
50  }
51
52  return false;
53}
54
55size_t CopyToStringHelper(
56    std::string* result, const void* buffer, uint32_t num_bytes) {
57  result->append(static_cast<const char*>(buffer), num_bytes);
58  return num_bytes;
59}
60
61size_t CopyToFileHelper(FILE* fp, const void* buffer, uint32_t num_bytes) {
62  return fwrite(buffer, 1, num_bytes, fp);
63}
64
65} // namespace
66
67
68// TODO(hansmuller): Add a max_size parameter.
69bool BlockingCopyToString(ScopedDataPipeConsumerHandle source,
70                          std::string* result) {
71  CHECK(result);
72  result->clear();
73  return BlockingCopyHelper(std::move(source),
74                            base::Bind(&CopyToStringHelper, result));
75}
76
77bool MOJO_COMMON_EXPORT BlockingCopyFromString(
78    const std::string& source,
79    const ScopedDataPipeProducerHandle& destination) {
80  auto it = source.begin();
81  for (;;) {
82    void* buffer = nullptr;
83    uint32_t buffer_num_bytes = 0;
84    MojoResult result =
85        BeginWriteDataRaw(destination.get(), &buffer, &buffer_num_bytes,
86                          MOJO_WRITE_DATA_FLAG_NONE);
87    if (result == MOJO_RESULT_OK) {
88      char* char_buffer = static_cast<char*>(buffer);
89      uint32_t byte_index = 0;
90      while (it != source.end() && byte_index < buffer_num_bytes) {
91        char_buffer[byte_index++] = *it++;
92      }
93      EndWriteDataRaw(destination.get(), byte_index);
94      if (it == source.end())
95        return true;
96    } else if (result == MOJO_RESULT_SHOULD_WAIT) {
97      result = Wait(destination.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
98                    MOJO_DEADLINE_INDEFINITE, nullptr);
99      if (result != MOJO_RESULT_OK) {
100        // If the consumer handle was closed, then treat as EOF.
101        return result == MOJO_RESULT_FAILED_PRECONDITION;
102      }
103    } else {
104      // If the consumer handle was closed, then treat as EOF.
105      return result == MOJO_RESULT_FAILED_PRECONDITION;
106    }
107  }
108}
109
110bool BlockingCopyToFile(ScopedDataPipeConsumerHandle source,
111                        const base::FilePath& destination) {
112  base::ScopedFILE fp(base::OpenFile(destination, "wb"));
113  if (!fp)
114    return false;
115  return BlockingCopyHelper(std::move(source),
116                            base::Bind(&CopyToFileHelper, fp.get()));
117}
118
119void CopyToFile(ScopedDataPipeConsumerHandle source,
120                const base::FilePath& destination,
121                base::TaskRunner* task_runner,
122                const base::Callback<void(bool)>& callback) {
123  base::PostTaskAndReplyWithResult(
124      task_runner,
125      FROM_HERE,
126      base::Bind(&BlockingCopyToFile, base::Passed(&source), destination),
127      callback);
128}
129
130}  // namespace common
131}  // namespace mojo
132