1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7
8 // Author: kenton@google.com (Kenton Varda)
9 // Based on original Protocol Buffers design by
10 // Sanjay Ghemawat, Jeff Dean, and others.
11
12 #include "google/protobuf/io/zero_copy_stream.h"
13
14 #include <cstring>
15 #include <utility>
16
17 #include "absl/log/absl_log.h"
18 #include "absl/strings/cord.h"
19 #include "absl/strings/cord_buffer.h"
20 #include "absl/strings/string_view.h"
21 #include "absl/types/span.h"
22
23 // Must be included last.
24 #include "google/protobuf/port_def.inc"
25
26 namespace google {
27 namespace protobuf {
28 namespace io {
29
ReadCord(absl::Cord * cord,int count)30 bool ZeroCopyInputStream::ReadCord(absl::Cord* cord, int count) {
31 if (count <= 0) return true;
32
33 absl::CordBuffer cord_buffer = cord->GetAppendBuffer(count);
34 absl::Span<char> out = cord_buffer.available_up_to(count);
35
36 auto FetchNextChunk = [&]() -> absl::Span<const char> {
37 const void* buffer;
38 int size;
39 if (!Next(&buffer, &size)) return {};
40
41 if (size > count) {
42 BackUp(size - count);
43 size = count;
44 }
45 return absl::MakeConstSpan(static_cast<const char*>(buffer), size);
46 };
47
48 auto AppendFullBuffer = [&]() -> absl::Span<char> {
49 cord->Append(std::move(cord_buffer));
50 cord_buffer = absl::CordBuffer::CreateWithDefaultLimit(count);
51 return cord_buffer.available_up_to(count);
52 };
53
54 auto CopyBytes = [&](absl::Span<char>& dst, absl::Span<const char>& src,
55 size_t bytes) {
56 memcpy(dst.data(), src.data(), bytes);
57 dst.remove_prefix(bytes);
58 src.remove_prefix(bytes);
59 count -= bytes;
60 cord_buffer.IncreaseLengthBy(bytes);
61 };
62
63 do {
64 absl::Span<const char> in = FetchNextChunk();
65 if (in.empty()) {
66 // Append whatever we have pending so far.
67 cord->Append(std::move(cord_buffer));
68 return false;
69 }
70
71 if (out.empty()) out = AppendFullBuffer();
72
73 while (in.size() > out.size()) {
74 CopyBytes(out, in, out.size());
75 out = AppendFullBuffer();
76 }
77
78 CopyBytes(out, in, in.size());
79 } while (count > 0);
80
81 cord->Append(std::move(cord_buffer));
82 return true;
83 }
84
WriteCord(const absl::Cord & cord)85 bool ZeroCopyOutputStream::WriteCord(const absl::Cord& cord) {
86 if (cord.empty()) return true;
87
88 void* buffer;
89 int buffer_size = 0;
90 if (!Next(&buffer, &buffer_size)) return false;
91
92 for (absl::string_view fragment : cord.Chunks()) {
93 while (fragment.size() > static_cast<size_t>(buffer_size)) {
94 std::memcpy(buffer, fragment.data(), buffer_size);
95
96 fragment.remove_prefix(buffer_size);
97
98 if (!Next(&buffer, &buffer_size)) return false;
99 }
100 std::memcpy(buffer, fragment.data(), fragment.size());
101
102 // Advance the buffer.
103 buffer = static_cast<char*>(buffer) + fragment.size();
104 buffer_size -= static_cast<int>(fragment.size());
105 }
106 BackUp(buffer_size);
107 return true;
108 }
109
110
WriteAliasedRaw(const void *,int)111 bool ZeroCopyOutputStream::WriteAliasedRaw(const void* /* data */,
112 int /* size */) {
113 ABSL_LOG(FATAL) << "This ZeroCopyOutputStream doesn't support aliasing. "
114 "Reaching here usually means a ZeroCopyOutputStream "
115 "implementation bug.";
116 return false;
117 }
118
119 } // namespace io
120 } // namespace protobuf
121 } // namespace google
122
123 #include "google/protobuf/port_undef.inc"
124