Apache Mesos
protobuf.hpp
Go to the documentation of this file.
1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 // http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License.
12 
13 #ifndef __STOUT_PROTOBUF_HPP__
14 #define __STOUT_PROTOBUF_HPP__
15 
16 #include <assert.h>
17 #include <errno.h>
18 #include <stdint.h>
19 #ifndef __WINDOWS__
20 #include <unistd.h>
21 #endif // __WINDOWS__
22 
23 #include <sys/types.h>
24 
25 #include <string>
26 #include <type_traits>
27 #include <vector>
28 
29 #include <google/protobuf/descriptor.h>
30 #include <google/protobuf/descriptor.pb.h>
31 #include <google/protobuf/message.h>
32 #include <google/protobuf/reflection.h>
33 #include <google/protobuf/repeated_field.h>
34 
35 #include <google/protobuf/io/zero_copy_stream_impl.h>
36 
37 #include <stout/abort.hpp>
38 #include <stout/base64.hpp>
39 #include <stout/error.hpp>
40 #include <stout/json.hpp>
41 #include <stout/jsonify.hpp>
42 #include <stout/none.hpp>
43 #include <stout/nothing.hpp>
44 #include <stout/representation.hpp>
45 #include <stout/result.hpp>
46 #include <stout/stringify.hpp>
47 #include <stout/try.hpp>
48 
49 #include <stout/os/close.hpp>
50 #include <stout/os/fsync.hpp>
51 #include <stout/os/int_fd.hpp>
52 #include <stout/os/lseek.hpp>
53 #include <stout/os/open.hpp>
54 #include <stout/os/read.hpp>
55 #include <stout/os/write.hpp>
56 
57 #ifdef __WINDOWS__
58 #include <stout/os/dup.hpp>
59 #endif // __WINDOWS__
60 
61 namespace protobuf {
62 
63 // TODO(bmahler): Re-use stout's 'recordio' facilities here. Note
64 // that these use a fixed size length header, whereas stout's
65 // currently uses a base-10 newline delimited header for language
66 // portability, which makes changing these a bit tricky.
67 
68 // Write out the given protobuf to the specified file descriptor by
69 // first writing out the length of the protobuf followed by the
70 // contents.
71 // NOTE: On error, this may have written partial data to the file.
72 inline Try<Nothing> write(int_fd fd, const google::protobuf::Message& message)
73 {
74  if (!message.IsInitialized()) {
75  return Error(message.InitializationErrorString() +
76  " is required but not initialized");
77  }
78 
79  // First write the size of the protobuf.
80  uint32_t size = message.ByteSize();
81  std::string bytes((char*) &size, sizeof(size));
82 
83  Try<Nothing> result = os::write(fd, bytes);
84  if (result.isError()) {
85  return Error("Failed to write size: " + result.error());
86  }
87 
88 #ifdef __WINDOWS__
89  // NOTE: On Windows, we need to explicitly allocate a CRT file
90  // descriptor because the Protobuf library requires it. Because
91  // users of `protobuf::write` are likely to call `os::close` on the
92  // `fd` we were given, we need to duplicate it before allocating the
93  // CRT fd. This is because once the CRT fd is allocated, it must be
94  // closed with `_close` instead of `os::close`. Since we need to
95  // call `_close` here, we duplicate the fd to prevent the users call
96  // of `os::close` from closing twice.
97  Try<int_fd> dup = os::dup(fd);
98  if (dup.isError()) {
99  return Error("Failed to duplicate handle: " + dup.error());
100  }
101 
102  int crt = dup->crt();
103 
104  if (!message.SerializeToFileDescriptor(crt)) {
105  ::_close(crt);
106  return Error("Failed to write/serialize message");
107  }
108  ::_close(crt);
109 #else
110  if (!message.SerializeToFileDescriptor(fd)) {
111  return Error("Failed to write/serialize message");
112  }
113 #endif
114 
115  return Nothing();
116 }
117 
118 // Write out the given sequence of protobuf messages to the
119 // specified file descriptor by repeatedly invoking write
120 // on each of the messages.
121 // NOTE: On error, this may have written partial data to the file.
122 template <typename T>
124  int_fd fd, const google::protobuf::RepeatedPtrField<T>& messages)
125 {
126  foreach (const T& message, messages) {
127  Try<Nothing> result = write(fd, message);
128  if (result.isError()) {
129  return Error(result.error());
130  }
131  }
132 
133  return Nothing();
134 }
135 
136 
137 // A wrapper function for the above `write()` with opening and closing the file.
138 // If `sync` is set to true, an `fsync()` will be called before `close()`.
139 template <typename T>
140 Try<Nothing> write(const std::string& path, const T& t, bool sync = false)
141 {
142  Try<int_fd> fd = os::open(
143  path,
144  O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
146 
147  if (fd.isError()) {
148  return Error("Failed to open file '" + path + "': " + fd.error());
149  }
150 
152 
153  if (sync && write.isSome()) {
154  // We call `fsync()` before closing the file instead of opening it with the
155  // `O_SYNC` flag for better performance. See:
156  // http://lkml.iu.edu/hypermail/linux/kernel/0105.3/0353.html
157  write = os::fsync(fd.get());
158  }
159 
160  Try<Nothing> close = os::close(fd.get());
161 
162  // We propagate `close` failures if `write` on the file was successful.
163  if (write.isSome() && close.isError()) {
164  return Error(
165  "Failed to close '" + stringify(fd.get()) + "':" + close.error());
166  }
167 
168  return write;
169 }
170 
171 
172 // A wrapper function to append a protobuf message with opening and closing the
173 // file. If `sync` is set to true, an `fsync()` will be called before `close()`.
175  const std::string& path,
176  const google::protobuf::Message& message,
177  bool sync = false)
178 {
179  Try<int_fd> fd = os::open(
180  path,
181  O_WRONLY | O_CREAT | O_APPEND | O_CLOEXEC,
183 
184  if (fd.isError()) {
185  return Error("Failed to open file '" + path + "': " + fd.error());
186  }
187 
188  Try<Nothing> write = protobuf::write(fd.get(), message);
189 
190  if (sync && write.isSome()) {
191  // We call `fsync()` before closing the file instead of opening it with the
192  // `O_SYNC` flag for better performance.
193  write = os::fsync(fd.get());
194  }
195 
196  Try<Nothing> close = os::close(fd.get());
197 
198  // We propagate `close` failures if `write` on the file was successful.
199  if (write.isSome() && close.isError()) {
200  return Error(
201  "Failed to close '" + stringify(fd.get()) + "':" + close.error());
202  }
203 
204  return write;
205 }
206 
207 
208 template <typename T>
209 Try<T> deserialize(const std::string& value)
210 {
211  T t;
212  (void) static_cast<google::protobuf::Message*>(&t);
213 
214  // Verify that the size of `value` fits into `ArrayInputStream`'s
215  // constructor. The maximum size of a proto2 message is 64 MB, so it is
216  // unlikely that we will hit this limit, but since an arbitrary string can be
217  // passed in, we include this check to be safe.
218  CHECK_LE(value.size(), static_cast<size_t>(std::numeric_limits<int>::max()));
219  google::protobuf::io::ArrayInputStream stream(
220  value.data(),
221  static_cast<int>(value.size()));
222  if (!t.ParseFromZeroCopyStream(&stream)) {
223  return Error("Failed to deserialize " + t.GetDescriptor()->full_name());
224  }
225  return t;
226 }
227 
228 
229 template <typename T>
231 {
232  (void) static_cast<const google::protobuf::Message*>(&t);
233 
234  std::string value;
235  if (!t.SerializeToString(&value)) {
236  return Error("Failed to serialize " + t.GetDescriptor()->full_name());
237  }
238  return value;
239 }
240 
241 
242 namespace internal {
243 
244 // Reads a single message of type T from the file by first reading the
245 // "size" followed by the contents (as written by 'write' above).
246 // NOTE: This struct is used by the public 'read' function.
247 // See comments there for the reason why we need this.
248 template <typename T>
249 struct Read
250 {
251  Result<T> operator()(int_fd fd, bool ignorePartial, bool undoFailed)
252  {
253  off_t offset = 0;
254 
255  if (undoFailed) {
256  // Save the offset so we can re-adjust if something goes wrong.
257  Try<off_t> lseek = os::lseek(fd, offset, SEEK_CUR);
258  if (lseek.isError()) {
259  return Error(lseek.error());
260  }
261 
262  offset = lseek.get();
263  }
264 
265  uint32_t size;
266  Result<std::string> result = os::read(fd, sizeof(size));
267 
268  if (result.isError()) {
269  if (undoFailed) {
270  os::lseek(fd, offset, SEEK_SET);
271  }
272  return Error("Failed to read size: " + result.error());
273  } else if (result.isNone()) {
274  return None(); // No more protobufs to read.
275  } else if (result->size() < sizeof(size)) {
276  // Hit EOF unexpectedly.
277  if (undoFailed) {
278  // Restore the offset to before the size read.
279  os::lseek(fd, offset, SEEK_SET);
280  }
281  if (ignorePartial) {
282  return None();
283  }
284  return Error(
285  "Failed to read size: hit EOF unexpectedly, possible corruption");
286  }
287 
288  // Parse the size from the bytes.
289  memcpy((void*)&size, (void*)result->data(), sizeof(size));
290 
291  // NOTE: Instead of specifically checking for corruption in 'size',
292  // we simply try to read 'size' bytes. If we hit EOF early, it is an
293  // indication of corruption.
294  result = os::read(fd, size);
295 
296  if (result.isError()) {
297  if (undoFailed) {
298  // Restore the offset to before the size read.
299  os::lseek(fd, offset, SEEK_SET);
300  }
301  return Error("Failed to read message: " + result.error());
302  } else if (result.isNone() || result->size() < size) {
303  // Hit EOF unexpectedly.
304  if (undoFailed) {
305  // Restore the offset to before the size read.
306  os::lseek(fd, offset, SEEK_SET);
307  }
308  if (ignorePartial) {
309  return None();
310  }
311  return Error("Failed to read message of size " + stringify(size) +
312  " bytes: hit EOF unexpectedly, possible corruption");
313  }
314 
315  // Parse the protobuf from the string.
316  // NOTE: We need to capture a const reference to the data because it
317  // must outlive the creation of ArrayInputStream.
318  const std::string& data = result.get();
319 
320  // Verify that the size of `data` fits into `ArrayInputStream`'s
321  // constructor. The maximum size of a proto2 message is 64 MB, so it is
322  // unlikely that we will hit this limit, but since an arbitrary string can
323  // be passed in, we include this check to be safe.
324  CHECK_LE(data.size(), static_cast<size_t>(std::numeric_limits<int>::max()));
325  T message;
326  google::protobuf::io::ArrayInputStream stream(
327  data.data(),
328  static_cast<int>(data.size()));
329 
330  if (!message.ParseFromZeroCopyStream(&stream)) {
331  if (undoFailed) {
332  // Restore the offset to before the size read.
333  os::lseek(fd, offset, SEEK_SET);
334  }
335  return Error("Failed to deserialize message");
336  }
337 
338  return message;
339  }
340 };
341 
342 
343 // Partial specialization for RepeatedPtrField<T> to read a sequence
344 // of protobuf messages from a given fd by repeatedly invoking
345 // Read<T> until None is reached, which we treat as EOF.
346 // NOTE: This struct is used by the public 'read' function.
347 // See comments there for the reason why we need this.
348 template <typename T>
349 struct Read<google::protobuf::RepeatedPtrField<T>>
350 {
352  int_fd fd, bool ignorePartial, bool undoFailed)
353  {
354  google::protobuf::RepeatedPtrField<T> result;
355  for (;;) {
356  Result<T> message = Read<T>()(fd, ignorePartial, undoFailed);
357  if (message.isError()) {
358  return Error(message.error());
359  } else if (message.isNone()) {
360  break;
361  } else {
362  result.Add()->CopyFrom(message.get());
363  }
364  }
365  return result;
366  }
367 };
368 
369 } // namespace internal {
370 
371 
372 // Reads the protobuf message(s) from a given fd based on the format
373 // written by write() above. We use partial specialization of
374 // - internal::Read<T> vs
375 // - internal::Read<google::protobuf::RepeatedPtrField<T>>
376 // in order to determine whether T is a single protobuf message or
377 // a sequence of messages.
378 // If 'ignorePartial' is true, None() is returned when we unexpectedly
379 // hit EOF while reading the protobuf (e.g., partial write).
380 // If 'undoFailed' is true, failed read attempts will restore the file
381 // read/write file offset towards the initial callup position.
382 template <typename T>
383 Result<T> read(int_fd fd, bool ignorePartial = false, bool undoFailed = false)
384 {
385  return internal::Read<T>()(fd, ignorePartial, undoFailed);
386 }
387 
388 
389 // A wrapper function that wraps the above read() with open and
390 // closing the file.
391 template <typename T>
392 Result<T> read(const std::string& path)
393 {
394  Try<int_fd> fd = os::open(
395  path,
396  O_RDONLY | O_CLOEXEC,
398 
399  if (fd.isError()) {
400  return Error("Failed to open file '" + path + "': " + fd.error());
401  }
402 
403  Result<T> result = read<T>(fd.get());
404 
405  // NOTE: We ignore the return value of close(). This is because
406  // users calling this function are interested in the return value of
407  // read(). Also an unsuccessful close() doesn't affect the read.
408  os::close(fd.get());
409 
410  return result;
411 }
412 
413 
414 namespace internal {
415 
416 // Forward declaration.
418  google::protobuf::Message* message,
419  const JSON::Object& object);
420 
421 
422 struct Parser : boost::static_visitor<Try<Nothing>>
423 {
424  Parser(google::protobuf::Message* _message,
425  const google::protobuf::FieldDescriptor* _field)
426  : message(_message),
427  reflection(message->GetReflection()),
428  field(_field) {}
429 
430  Try<Nothing> operator()(const JSON::Object& object) const
431  {
432  switch (field->type()) {
433  case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
434  if (field->is_map()) {
435  foreachpair (
436  const std::string& name,
437  const JSON::Value& value,
438  object.values) {
439  google::protobuf::Message* entry =
440  reflection->AddMessage(message, field);
441 
442  // A map is equivalent to:
443  //
444  // message MapFieldEntry {
445  // optional key_type key = 1;
446  // optional value_type value = 2;
447  // }
448  //
449  // repeated MapFieldEntry map_field = N;
450  //
451  // See the link below for details:
452  // https://developers.google.com/protocol-buffers/docs/proto#maps
453  const google::protobuf::FieldDescriptor* key_field =
454  entry->GetDescriptor()->FindFieldByNumber(1);
455 
456  JSON::Value key(name);
457 
458  Try<Nothing> apply =
459  boost::apply_visitor(Parser(entry, key_field), key);
460 
461  if (apply.isError()) {
462  return Error(apply.error());
463  }
464 
465  const google::protobuf::FieldDescriptor* value_field =
466  entry->GetDescriptor()->FindFieldByNumber(2);
467 
468  apply = boost::apply_visitor(Parser(entry, value_field), value);
469  if (apply.isError()) {
470  return Error(apply.error());
471  }
472  }
473  } else if (field->is_repeated()) {
474  // TODO(gilbert): We currently push up the nested error
475  // messages without wrapping the error message (due to
476  // the recursive nature of parse). We should pass along
477  // variable information in order to construct a helpful
478  // error message, e.g. "Failed to parse field 'a.b.c': ...".
479  return parse(reflection->AddMessage(message, field), object);
480  } else {
481  return parse(reflection->MutableMessage(message, field), object);
482  }
483  break;
484  default:
485  return Error("Not expecting a JSON object for field '" +
486  field->name() + "'");
487  }
488  return Nothing();
489  }
490 
491  Try<Nothing> operator()(const JSON::String& string) const
492  {
493  switch (field->type()) {
494  case google::protobuf::FieldDescriptor::TYPE_STRING:
495  if (field->is_repeated()) {
496  reflection->AddString(message, field, string.value);
497  } else {
498  reflection->SetString(message, field, string.value);
499  }
500  break;
501  case google::protobuf::FieldDescriptor::TYPE_BYTES: {
502  Try<std::string> decode = base64::decode(string.value);
503  if (decode.isError()) {
504  return Error("Failed to base64 decode bytes field"
505  " '" + field->name() + "': " + decode.error());
506  }
507 
508  if (field->is_repeated()) {
509  reflection->AddString(message, field, decode.get());
510  } else {
511  reflection->SetString(message, field, decode.get());
512  }
513  break;
514  }
515  case google::protobuf::FieldDescriptor::TYPE_ENUM: {
516  const google::protobuf::EnumValueDescriptor* descriptor =
517  field->enum_type()->FindValueByName(string.value);
518 
519  if (descriptor == nullptr) {
520  if (field->is_required()) {
521  return Error("Failed to find enum for '" + string.value + "'");
522  }
523 
524  // Unrecognized enum value will be discarded if this is not a
525  // required enum field, which makes the field's `has..` accessor
526  // return false and its getter return the first value listed in
527  // the enum definition, or the default value if one is specified.
528  //
529  // This is the deserialization behavior of proto2, see the link
530  // below for details:
531  // https://developers.google.com/protocol-buffers/docs/proto#updating
532  break;
533  }
534 
535  if (field->is_repeated()) {
536  reflection->AddEnum(message, field, descriptor);
537  } else {
538  reflection->SetEnum(message, field, descriptor);
539  }
540  break;
541  }
542  case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
543  case google::protobuf::FieldDescriptor::TYPE_FLOAT: {
544  if (string.value == "Infinity") {
545  return operator()(JSON::Number(
546  std::numeric_limits<double>::infinity()));
547  } else if (string.value == "-Infinity") {
548  return operator()(JSON::Number(
549  -std::numeric_limits<double>::infinity()));
550  } else {
551  Try<JSON::Number> number = JSON::parse<JSON::Number>(string.value);
552  if (number.isError()) {
553  return Error(
554  "Failed to parse '" + string.value + "' as a JSON number "
555  "for field '" + field->name() + "': " + number.error());
556  }
557 
558  return operator()(number.get());
559  }
560  }
561  case google::protobuf::FieldDescriptor::TYPE_INT64:
562  case google::protobuf::FieldDescriptor::TYPE_SINT64:
563  case google::protobuf::FieldDescriptor::TYPE_SFIXED64:
564  case google::protobuf::FieldDescriptor::TYPE_UINT64:
565  case google::protobuf::FieldDescriptor::TYPE_FIXED64:
566  case google::protobuf::FieldDescriptor::TYPE_INT32:
567  case google::protobuf::FieldDescriptor::TYPE_SINT32:
568  case google::protobuf::FieldDescriptor::TYPE_SFIXED32:
569  case google::protobuf::FieldDescriptor::TYPE_UINT32:
570  case google::protobuf::FieldDescriptor::TYPE_FIXED32: {
571  Try<JSON::Number> number = JSON::parse<JSON::Number>(string.value);
572  if (number.isError()) {
573  return Error(
574  "Failed to parse '" + string.value + "' as a JSON number "
575  "for field '" + field->name() + "': " + number.error());
576  }
577 
578  return operator()(number.get());
579  }
580  case google::protobuf::FieldDescriptor::TYPE_BOOL: {
581  Try<JSON::Boolean> boolean = JSON::parse<JSON::Boolean>(string.value);
582  if (boolean.isError()) {
583  return Error(
584  "Failed to parse '" + string.value + "' as a JSON boolean "
585  "for field '" + field->name() + "': " + boolean.error());
586  }
587 
588  return operator()(boolean.get());
589  }
590  default:
591  return Error("Not expecting a JSON string for field '" +
592  field->name() + "'");
593  }
594  return Nothing();
595  }
596 
597  Try<Nothing> operator()(const JSON::Number& number) const
598  {
599  switch (field->type()) {
600  case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
601  if (field->is_repeated()) {
602  reflection->AddDouble(message, field, number.as<double>());
603  } else {
604  reflection->SetDouble(message, field, number.as<double>());
605  }
606  break;
607  case google::protobuf::FieldDescriptor::TYPE_FLOAT:
608  if (field->is_repeated()) {
609  reflection->AddFloat(message, field, number.as<float>());
610  } else {
611  reflection->SetFloat(message, field, number.as<float>());
612  }
613  break;
614  case google::protobuf::FieldDescriptor::TYPE_INT64:
615  case google::protobuf::FieldDescriptor::TYPE_SINT64:
616  case google::protobuf::FieldDescriptor::TYPE_SFIXED64:
617  if (field->is_repeated()) {
618  reflection->AddInt64(message, field, number.as<int64_t>());
619  } else {
620  reflection->SetInt64(message, field, number.as<int64_t>());
621  }
622  break;
623  case google::protobuf::FieldDescriptor::TYPE_UINT64:
624  case google::protobuf::FieldDescriptor::TYPE_FIXED64:
625  if (field->is_repeated()) {
626  reflection->AddUInt64(message, field, number.as<uint64_t>());
627  } else {
628  reflection->SetUInt64(message, field, number.as<uint64_t>());
629  }
630  break;
631  case google::protobuf::FieldDescriptor::TYPE_INT32:
632  case google::protobuf::FieldDescriptor::TYPE_SINT32:
633  case google::protobuf::FieldDescriptor::TYPE_SFIXED32:
634  if (field->is_repeated()) {
635  reflection->AddInt32(message, field, number.as<int32_t>());
636  } else {
637  reflection->SetInt32(message, field, number.as<int32_t>());
638  }
639  break;
640  case google::protobuf::FieldDescriptor::TYPE_UINT32:
641  case google::protobuf::FieldDescriptor::TYPE_FIXED32:
642  if (field->is_repeated()) {
643  reflection->AddUInt32(message, field, number.as<uint32_t>());
644  } else {
645  reflection->SetUInt32(message, field, number.as<uint32_t>());
646  }
647  break;
648  default:
649  return Error("Not expecting a JSON number for field '" +
650  field->name() + "'");
651  }
652  return Nothing();
653  }
654 
655  Try<Nothing> operator()(const JSON::Array& array) const
656  {
657  if (!field->is_repeated()) {
658  return Error("Not expecting a JSON array for field '" +
659  field->name() + "'");
660  }
661 
662  foreach (const JSON::Value& value, array.values) {
663  Try<Nothing> apply =
664  boost::apply_visitor(Parser(message, field), value);
665 
666  if (apply.isError()) {
667  return Error(apply.error());
668  }
669  }
670 
671  return Nothing();
672  }
673 
674  Try<Nothing> operator()(const JSON::Boolean& boolean) const
675  {
676  switch (field->type()) {
677  case google::protobuf::FieldDescriptor::TYPE_BOOL:
678  if (field->is_repeated()) {
679  reflection->AddBool(message, field, boolean.value);
680  } else {
681  reflection->SetBool(message, field, boolean.value);
682  }
683  break;
684  default:
685  return Error("Not expecting a JSON boolean for field '" +
686  field->name() + "'");
687  }
688  return Nothing();
689  }
690 
692  {
693  // We treat 'null' as an unset field. Note that we allow
694  // unset required fields here since the top-level parse
695  // function is responsible for checking 'IsInitialized'.
696  return Nothing();
697  }
698 
699 private:
700  google::protobuf::Message* message;
701  const google::protobuf::Reflection* reflection;
702  const google::protobuf::FieldDescriptor* field;
703 };
704 
705 
707  google::protobuf::Message* message,
708  const JSON::Object& object)
709 {
710  foreachpair (
711  const std::string& name, const JSON::Value& value, object.values) {
712  // Look for a field by this name.
713  const google::protobuf::FieldDescriptor* field =
714  message->GetDescriptor()->FindFieldByName(name);
715 
716  if (field != nullptr) {
717  Try<Nothing> apply =
718  boost::apply_visitor(Parser(message, field), value);
719 
720  if (apply.isError()) {
721  return Error(apply.error());
722  }
723  }
724  }
725 
726  return Nothing();
727 }
728 
729 
730 // Parses a single protobuf message of type T from a JSON::Object.
731 // NOTE: This struct is used by the public parse<T>() function below. See
732 // comments there for the reason why we opted for this design.
733 template <typename T>
734 struct Parse
735 {
737  {
738  static_assert(std::is_convertible<T*, google::protobuf::Message*>::value,
739  "T must be a protobuf message");
740 
741  const JSON::Object* object = boost::get<JSON::Object>(&value);
742  if (object == nullptr) {
743  return Error("Expecting a JSON object");
744  }
745 
746  T message;
747 
748  Try<Nothing> parse = internal::parse(&message, *object);
749  if (parse.isError()) {
750  return Error(parse.error());
751  }
752 
753  if (!message.IsInitialized()) {
754  return Error("Missing required fields: " +
755  message.InitializationErrorString());
756  }
757 
758  return message;
759  }
760 };
761 
762 
763 // Partial specialization for RepeatedPtrField<T> to parse a sequence of
764 // protobuf messages from a JSON::Array by repeatedly invoking Parse<T> to
765 // facilitate conversions like JSON::Array -> Resources.
766 // NOTE: This struct is used by the public parse<T>() function below. See
767 // comments there for the reason why we opted for this design.
768 template <typename T>
769 struct Parse<google::protobuf::RepeatedPtrField<T>>
770 {
772  const JSON::Value& value)
773  {
774  static_assert(std::is_convertible<T*, google::protobuf::Message*>::value,
775  "T must be a protobuf message");
776 
777  const JSON::Array* array = boost::get<JSON::Array>(&value);
778  if (array == nullptr) {
779  return Error("Expecting a JSON array");
780  }
781 
782  google::protobuf::RepeatedPtrField<T> collection;
783  collection.Reserve(static_cast<int>(array->values.size()));
784 
785  // Parse messages one by one and propagate an error if it happens.
786  foreach (const JSON::Value& elem, array->values) {
787  Try<T> message = Parse<T>()(elem);
788  if (message.isError()) {
789  return Error(message.error());
790  }
791 
792  collection.Add()->CopyFrom(message.get());
793  }
794 
795  return collection;
796  }
797 };
798 
799 } // namespace internal {
800 
801 // A dispatch wrapper which parses protobuf messages(s) from a given JSON value.
802 // We use partial specialization of
803 // - internal::Parse<T> for JSON::Object
804 // - internal::Parse<google::protobuf::RepeatedPtrField<T>> for JSON::Array
805 // to determine whether T is a single message or a sequence of messages.
806 // We cannot partially specialize function templates and overloaded function
807 // approach combined with std::enable_if is not that clean, hence we leverage
808 // partial specialization of class templates.
809 template <typename T>
810 Try<T> parse(const JSON::Value& value)
811 {
812  return internal::Parse<T>()(value);
813 }
814 
815 } // namespace protobuf {
816 
817 namespace JSON {
818 
819 // The representation of generic protobuf => JSON,
820 // e.g., `jsonify(JSON::Protobuf(message))`.
821 struct Protobuf : Representation<google::protobuf::Message>
822 {
824 };
825 
826 
827 // `json` function for protobuf messages. Refer to `jsonify.hpp` for details.
828 // TODO(mpark): This currently uses the default value for optional fields
829 // that are not deprecated, but we may want to revisit this decision.
830 //
831 // TODO(mzhu): Use protobuf built-in JSON mapping utilities in favor of
832 // the reflection APIs. See MESOS-9896.
833 inline void json(ObjectWriter* writer, const Protobuf& protobuf)
834 {
835  using google::protobuf::FieldDescriptor;
836 
837  const google::protobuf::Message& message = protobuf;
838 
839  const google::protobuf::Descriptor* descriptor = message.GetDescriptor();
840  const google::protobuf::Reflection* reflection = message.GetReflection();
841 
842  // We first look through all the possible fields to determine both the set
843  // fields __and__ the optional fields with a default that are not set.
844  // `Reflection::ListFields()` alone will only include set fields and
845  // is therefore insufficient.
846  int fieldCount = descriptor->field_count();
847  std::vector<const FieldDescriptor*> fields;
848  fields.reserve(fieldCount);
849  for (int i = 0; i < fieldCount; ++i) {
850  const FieldDescriptor* field = descriptor->field(i);
851  if (field->is_repeated()) { // Repeated or Map.
852  if (reflection->FieldSize(message, field) > 0) {
853  // Has repeated field with members, output as JSON.
854  fields.push_back(field);
855  }
856  } else if (
857  reflection->HasField(message, field) ||
858  (field->has_default_value() && !field->options().deprecated())) {
859  // Field is set or has default, output as JSON.
860  fields.push_back(field);
861  }
862  }
863 
864  foreach (const FieldDescriptor* field, fields) {
865  if (field->is_repeated() && !field->is_map()) {
866  writer->field(
867  field->name(),
868  [&field, &reflection, &message](JSON::ArrayWriter* writer) {
869  int fieldSize = reflection->FieldSize(message, field);
870  for (int i = 0; i < fieldSize; ++i) {
871  switch (field->cpp_type()) {
872  case FieldDescriptor::CPPTYPE_BOOL:
873  writer->element(
874  reflection->GetRepeatedBool(message, field, i));
875  break;
876  case FieldDescriptor::CPPTYPE_INT32:
877  writer->element(
878  reflection->GetRepeatedInt32(message, field, i));
879  break;
880  case FieldDescriptor::CPPTYPE_INT64:
881  writer->element(
882  reflection->GetRepeatedInt64(message, field, i));
883  break;
884  case FieldDescriptor::CPPTYPE_UINT32:
885  writer->element(
886  reflection->GetRepeatedUInt32(message, field, i));
887  break;
888  case FieldDescriptor::CPPTYPE_UINT64:
889  writer->element(
890  reflection->GetRepeatedUInt64(message, field, i));
891  break;
892  case FieldDescriptor::CPPTYPE_FLOAT:
893  writer->element(
894  reflection->GetRepeatedFloat(message, field, i));
895  break;
896  case FieldDescriptor::CPPTYPE_DOUBLE:
897  writer->element(
898  reflection->GetRepeatedDouble(message, field, i));
899  break;
900  case FieldDescriptor::CPPTYPE_MESSAGE:
901  writer->element(Protobuf(
902  reflection->GetRepeatedMessage(message, field, i)));
903  break;
904  case FieldDescriptor::CPPTYPE_ENUM:
905  writer->element(
906  reflection->GetRepeatedEnum(message, field, i)->name());
907  break;
908  case FieldDescriptor::CPPTYPE_STRING:
909  const std::string& s = reflection->GetRepeatedStringReference(
910  message, field, i, nullptr);
911  if (field->type() == FieldDescriptor::TYPE_BYTES) {
912  writer->element(base64::encode(s));
913  } else {
914  writer->element(s);
915  }
916  break;
917  }
918  }
919  });
920  } else { // field->is_map() || !field->is_repeated()
921  auto writeField = [&writer](
922  const std::string& fieldName,
923  const google::protobuf::Reflection* reflection,
924  const google::protobuf::Message& message,
925  const FieldDescriptor* field) {
926  switch (field->cpp_type()) {
927  case FieldDescriptor::CPPTYPE_BOOL:
928  writer->field(fieldName, reflection->GetBool(message, field));
929  break;
930  case FieldDescriptor::CPPTYPE_INT32:
931  writer->field(fieldName, reflection->GetInt32(message, field));
932  break;
933  case FieldDescriptor::CPPTYPE_INT64:
934  writer->field(fieldName, reflection->GetInt64(message, field));
935  break;
936  case FieldDescriptor::CPPTYPE_UINT32:
937  writer->field(fieldName, reflection->GetUInt32(message, field));
938  break;
939  case FieldDescriptor::CPPTYPE_UINT64:
940  writer->field(fieldName, reflection->GetUInt64(message, field));
941  break;
942  case FieldDescriptor::CPPTYPE_FLOAT:
943  writer->field(fieldName, reflection->GetFloat(message, field));
944  break;
945  case FieldDescriptor::CPPTYPE_DOUBLE:
946  writer->field(fieldName, reflection->GetDouble(message, field));
947  break;
948  case FieldDescriptor::CPPTYPE_MESSAGE:
949  writer->field(
950  fieldName, Protobuf(reflection->GetMessage(message, field)));
951  break;
952  case FieldDescriptor::CPPTYPE_ENUM:
953  writer->field(
954  fieldName, reflection->GetEnum(message, field)->name());
955  break;
956  case FieldDescriptor::CPPTYPE_STRING:
957  const std::string& s =
958  reflection->GetStringReference(message, field, nullptr);
959  if (field->type() == FieldDescriptor::TYPE_BYTES) {
960  writer->field(fieldName, base64::encode(s));
961  } else {
962  writer->field(fieldName, s);
963  }
964  break;
965  }
966  };
967 
968  if (!field->is_repeated()) { // Singular field.
969  writeField(field->name(), reflection, message, field);
970  } else { // Map field.
971  CHECK(field->is_map());
972  writer->field(
973  field->name(),
974  [&field, &reflection, &message, &writeField](
975  JSON::ObjectWriter* writer) {
976  foreach (
977  const auto& mapEntry,
978  reflection->GetRepeatedFieldRef<google::protobuf::Message>(
979  message, field)) {
980  const google::protobuf::Descriptor* mapEntryDescriptor =
981  mapEntry.GetDescriptor();
982  const google::protobuf::Reflection* mapEntryReflection =
983  mapEntry.GetReflection();
984 
985  // Map entry must contain exactly two fields: `key` and `value`.
986  CHECK_EQ(mapEntryDescriptor->field_count(), 2);
987 
988  const FieldDescriptor* keyField = mapEntryDescriptor->field(0);
989  const FieldDescriptor* valueField =
990  mapEntryDescriptor->field(1);
991 
992  switch (keyField->cpp_type()) {
993  case FieldDescriptor::CPPTYPE_BOOL:
994  writeField(
995  jsonify(
996  mapEntryReflection->GetBool(mapEntry, keyField)),
997  mapEntryReflection,
998  mapEntry,
999  valueField);
1000  break;
1001  case FieldDescriptor::CPPTYPE_INT32:
1002  writeField(
1003  jsonify(
1004  mapEntryReflection->GetInt32(mapEntry, keyField)),
1005  mapEntryReflection,
1006  mapEntry,
1007  valueField);
1008  break;
1009  case FieldDescriptor::CPPTYPE_INT64:
1010  writeField(
1011  jsonify(
1012  mapEntryReflection->GetInt64(mapEntry, keyField)),
1013  mapEntryReflection,
1014  mapEntry,
1015  valueField);
1016  break;
1017  case FieldDescriptor::CPPTYPE_UINT32:
1018  writeField(
1019  jsonify(
1020  mapEntryReflection->GetUInt32(mapEntry, keyField)),
1021  mapEntryReflection,
1022  mapEntry,
1023  valueField);
1024  break;
1025  case FieldDescriptor::CPPTYPE_UINT64:
1026  writeField(
1027  jsonify(
1028  mapEntryReflection->GetUInt64(mapEntry, keyField)),
1029  mapEntryReflection,
1030  mapEntry,
1031  valueField);
1032  break;
1033  case FieldDescriptor::CPPTYPE_STRING:
1034  if (keyField->type() == FieldDescriptor::TYPE_BYTES) {
1035  LOG(FATAL)
1036  << "Unexpected key field type in protobuf Map: "
1037  << keyField->type_name();
1038  }
1039 
1040  writeField(
1041  mapEntryReflection->GetStringReference(
1042  mapEntry, keyField, nullptr),
1043  mapEntryReflection,
1044  mapEntry,
1045  valueField);
1046  break;
1047  case FieldDescriptor::CPPTYPE_FLOAT:
1048  case FieldDescriptor::CPPTYPE_DOUBLE:
1049  case FieldDescriptor::CPPTYPE_MESSAGE:
1050  case FieldDescriptor::CPPTYPE_ENUM:
1051  LOG(FATAL) << "Unexpected key field type in protobuf Map: "
1052  << keyField->cpp_type_name();
1053  }
1054  }
1055  });
1056  }
1057  }
1058  }
1059 }
1060 
1061 
1062 // TODO(bmahler): This currently uses the default value for optional fields
1063 // that are not deprecated, but we may want to revisit this decision.
1064 inline Object protobuf(const google::protobuf::Message& message)
1065 {
1066  Object object;
1067 
1068  const google::protobuf::Descriptor* descriptor = message.GetDescriptor();
1069  const google::protobuf::Reflection* reflection = message.GetReflection();
1070 
1071  auto value_for_field = [](
1072  const google::protobuf::Message& message,
1073  const google::protobuf::FieldDescriptor* field) -> JSON::Value {
1074  const google::protobuf::Reflection* reflection = message.GetReflection();
1075 
1076  switch (field->type()) {
1077  case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
1078  return JSON::Number(reflection->GetDouble(message, field));
1079  case google::protobuf::FieldDescriptor::TYPE_FLOAT:
1080  return JSON::Number(reflection->GetFloat(message, field));
1081  case google::protobuf::FieldDescriptor::TYPE_INT64:
1082  case google::protobuf::FieldDescriptor::TYPE_SINT64:
1083  case google::protobuf::FieldDescriptor::TYPE_SFIXED64:
1084  return JSON::Number(reflection->GetInt64(message, field));
1085  case google::protobuf::FieldDescriptor::TYPE_UINT64:
1086  case google::protobuf::FieldDescriptor::TYPE_FIXED64:
1087  return JSON::Number(reflection->GetUInt64(message, field));
1088  case google::protobuf::FieldDescriptor::TYPE_INT32:
1089  case google::protobuf::FieldDescriptor::TYPE_SINT32:
1090  case google::protobuf::FieldDescriptor::TYPE_SFIXED32:
1091  return JSON::Number(reflection->GetInt32(message, field));
1092  case google::protobuf::FieldDescriptor::TYPE_UINT32:
1093  case google::protobuf::FieldDescriptor::TYPE_FIXED32:
1094  return JSON::Number(reflection->GetUInt32(message, field));
1095  case google::protobuf::FieldDescriptor::TYPE_BOOL:
1096  if (reflection->GetBool(message, field)) {
1097  return JSON::Boolean(true);
1098  } else {
1099  return JSON::Boolean(false);
1100  }
1101  break;
1102  case google::protobuf::FieldDescriptor::TYPE_STRING:
1103  return JSON::String(reflection->GetString(message, field));
1104  case google::protobuf::FieldDescriptor::TYPE_BYTES:
1105  return JSON::String(
1106  base64::encode(reflection->GetString(message, field)));
1107  case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
1108  return protobuf(reflection->GetMessage(message, field));
1109  case google::protobuf::FieldDescriptor::TYPE_ENUM:
1110  return JSON::String(reflection->GetEnum(message, field)->name());
1111  case google::protobuf::FieldDescriptor::TYPE_GROUP:
1112  // Deprecated! We abort here instead of using a Try as return value,
1113  // because we expect this code path to never be taken.
1114  ABORT("Unhandled protobuf field type: " +
1115  stringify(field->type()));
1116  }
1117 
1118  UNREACHABLE();
1119  };
1120 
1121  // We first look through all the possible fields to determine both
1122  // the set fields _and_ the optional fields with a default that
1123  // are not set. Reflection::ListFields() alone will only include
1124  // set fields and is therefore insufficient.
1125  std::vector<const google::protobuf::FieldDescriptor*> fields;
1126  fields.reserve(descriptor->field_count());
1127  for (int i = 0; i < descriptor->field_count(); i++) {
1128  const google::protobuf::FieldDescriptor* field = descriptor->field(i);
1129  if (field->is_repeated()) {
1130  if (reflection->FieldSize(message, descriptor->field(i)) > 0) {
1131  // Has repeated field with members, output as JSON.
1132  fields.push_back(field);
1133  }
1134  } else if (
1135  reflection->HasField(message, field) ||
1136  (field->has_default_value() && !field->options().deprecated())) {
1137  // Field is set or has default, output as JSON.
1138  fields.push_back(field);
1139  }
1140  }
1141 
1142  foreach (const google::protobuf::FieldDescriptor* field, fields) {
1143  if (field->is_map()) {
1144  JSON::Object map;
1145 
1146  int fieldSize = reflection->FieldSize(message, field);
1147  for (int i = 0; i < fieldSize; ++i) {
1148  const google::protobuf::Message& entry =
1149  reflection->GetRepeatedMessage(message, field, i);
1150 
1151  // A map is equivalent to:
1152  //
1153  // message MapFieldEntry {
1154  // optional key_type key = 1;
1155  // optional value_type value = 2;
1156  // }
1157  //
1158  // repeated MapFieldEntry map_field = N;
1159  //
1160  // See the link below for details:
1161  // https://developers.google.com/protocol-buffers/docs/proto#maps
1162  const google::protobuf::FieldDescriptor* key_field =
1163  entry.GetDescriptor()->FindFieldByNumber(1);
1164 
1165  const google::protobuf::FieldDescriptor* value_field =
1166  entry.GetDescriptor()->FindFieldByNumber(2);
1167 
1168  JSON::Value key = value_for_field(entry, key_field);
1169 
1170  std::string name;
1171  if (key.is<JSON::String>()) {
1172  name = key.as<JSON::String>().value;
1173  } else {
1174  name = jsonify(key);
1175  }
1176 
1177  map.values[name] = value_for_field(entry, value_field);
1178  }
1179  object.values[field->name()] = map;
1180  } else if (field->is_repeated()) {
1181  JSON::Array array;
1182  int fieldSize = reflection->FieldSize(message, field);
1183  array.values.reserve(fieldSize);
1184  for (int i = 0; i < fieldSize; ++i) {
1185  switch (field->type()) {
1186  case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
1187  array.values.push_back(JSON::Number(
1188  reflection->GetRepeatedDouble(message, field, i)));
1189  break;
1190  case google::protobuf::FieldDescriptor::TYPE_FLOAT:
1191  array.values.push_back(JSON::Number(
1192  reflection->GetRepeatedFloat(message, field, i)));
1193  break;
1194  case google::protobuf::FieldDescriptor::TYPE_INT64:
1195  case google::protobuf::FieldDescriptor::TYPE_SINT64:
1196  case google::protobuf::FieldDescriptor::TYPE_SFIXED64:
1197  array.values.push_back(JSON::Number(
1198  reflection->GetRepeatedInt64(message, field, i)));
1199  break;
1200  case google::protobuf::FieldDescriptor::TYPE_UINT64:
1201  case google::protobuf::FieldDescriptor::TYPE_FIXED64:
1202  array.values.push_back(JSON::Number(
1203  reflection->GetRepeatedUInt64(message, field, i)));
1204  break;
1205  case google::protobuf::FieldDescriptor::TYPE_INT32:
1206  case google::protobuf::FieldDescriptor::TYPE_SINT32:
1207  case google::protobuf::FieldDescriptor::TYPE_SFIXED32:
1208  array.values.push_back(JSON::Number(
1209  reflection->GetRepeatedInt32(message, field, i)));
1210  break;
1211  case google::protobuf::FieldDescriptor::TYPE_UINT32:
1212  case google::protobuf::FieldDescriptor::TYPE_FIXED32:
1213  array.values.push_back(JSON::Number(
1214  reflection->GetRepeatedUInt32(message, field, i)));
1215  break;
1216  case google::protobuf::FieldDescriptor::TYPE_BOOL:
1217  if (reflection->GetRepeatedBool(message, field, i)) {
1218  array.values.push_back(JSON::Boolean(true));
1219  } else {
1220  array.values.push_back(JSON::Boolean(false));
1221  }
1222  break;
1223  case google::protobuf::FieldDescriptor::TYPE_STRING:
1224  array.values.push_back(JSON::String(
1225  reflection->GetRepeatedString(message, field, i)));
1226  break;
1227  case google::protobuf::FieldDescriptor::TYPE_BYTES:
1228  array.values.push_back(JSON::String(base64::encode(
1229  reflection->GetRepeatedString(message, field, i))));
1230  break;
1231  case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
1232  array.values.push_back(protobuf(
1233  reflection->GetRepeatedMessage(message, field, i)));
1234  break;
1235  case google::protobuf::FieldDescriptor::TYPE_ENUM:
1236  array.values.push_back(JSON::String(
1237  reflection->GetRepeatedEnum(message, field, i)->name()));
1238  break;
1239  case google::protobuf::FieldDescriptor::TYPE_GROUP:
1240  // Deprecated! We abort here instead of using a Try as return value,
1241  // because we expect this code path to never be taken.
1242  ABORT("Unhandled protobuf field type: " +
1243  stringify(field->type()));
1244  }
1245  }
1246  object.values[field->name()] = array;
1247  } else {
1248  object.values[field->name()] = value_for_field(message, field);
1249  }
1250  }
1251 
1252  return object;
1253 }
1254 
1255 
1256 template <typename T>
1257 Array protobuf(const google::protobuf::RepeatedPtrField<T>& repeated)
1258 {
1259  static_assert(std::is_convertible<T*, google::protobuf::Message*>::value,
1260  "T must be a protobuf message");
1261 
1262  JSON::Array array;
1263  array.values.reserve(repeated.size());
1264  foreach (const T& elem, repeated) {
1265  array.values.emplace_back(JSON::protobuf(elem));
1266  }
1267 
1268  return array;
1269 }
1270 
1271 } // namespace JSON {
1272 
1273 #endif // __STOUT_PROTOBUF_HPP__
T as() const
Definition: json.hpp:120
Try< std::string > decode(const std::string &s)
Decode a string that is Base64-encoded with the standard Base64 alphabet.
Definition: base64.hpp:183
Try< Nothing > operator()(const JSON::Object &object) const
Definition: protobuf.hpp:430
Definition: path.hpp:29
bool isNone() const
Definition: result.hpp:113
Definition: nothing.hpp:16
Definition: errorbase.hpp:36
#define ABORT(...)
Definition: abort.hpp:40
Try< Bytes > size(const std::string &path, const FollowSymlink follow=FollowSymlink::FOLLOW_SYMLINK)
Definition: stat.hpp:130
const mode_t S_IRGRP
Definition: windows.hpp:313
T & get()&
Definition: try.hpp:80
ssize_t write(const int_fd &fd, const void *data, size_t size)
Definition: write.hpp:72
Result< Classifier > decode(const Netlink< struct rtnl_cls > &cls)
Try< Nothing > operator()(const JSON::Number &number) const
Definition: protobuf.hpp:597
Definition: protobuf.hpp:734
Definition: json.hpp:231
Definition: check.hpp:33
static Result< T > error(const std::string &message)
Definition: result.hpp:54
Try< google::protobuf::RepeatedPtrField< T > > operator()(const JSON::Value &value)
Definition: protobuf.hpp:771
const mode_t S_IWUSR
Definition: windows.hpp:306
Result< google::protobuf::RepeatedPtrField< T > > operator()(int_fd fd, bool ignorePartial, bool undoFailed)
Definition: protobuf.hpp:351
Definition: json.hpp:207
Try< T > operator()(const JSON::Value &value)
Definition: protobuf.hpp:736
Try< int_fd > open(const std::string &path, int oflag, mode_t mode=0)
Definition: open.hpp:35
Definition: protobuf.hpp:422
std::string encode(const std::string &s)
Encode a string to Base64 with the standard Base64 alphabet.
Definition: base64.hpp:170
Try< Nothing > operator()(const JSON::String &string) const
Definition: protobuf.hpp:491
Try< Nothing > write(const std::string &path, const T &t, bool sync=false)
Definition: protobuf.hpp:140
Definition: json.hpp:198
const mode_t S_IRUSR
Definition: windows.hpp:305
Try< std::string > serialize(const T &t)
Definition: protobuf.hpp:230
std::map< std::string, Value > values
Definition: json.hpp:194
Definition: check.hpp:30
Definition: json.hpp:158
bool is() const
Definition: json.hpp:338
Try< Nothing > write(int_fd fd, const google::protobuf::Message &message)
Definition: protobuf.hpp:72
constexpr int O_CLOEXEC
Definition: open.hpp:41
Try< Nothing > operator()(const JSON::Array &array) const
Definition: protobuf.hpp:655
void field(const std::string &key, const T &value)
Definition: jsonify.hpp:347
Definition: protobuf.hpp:249
Try< Nothing > close(int fd)
Definition: close.hpp:24
Try< Nothing > append(const std::string &path, const google::protobuf::Message &message, bool sync=false)
Definition: protobuf.hpp:174
Try< off_t > lseek(int_fd fd, off_t offset, int whence)
Definition: lseek.hpp:25
Option< T > max(const Option< T > &left, const Option< T > &right)
Definition: option.hpp:214
Try< Nothing > operator()(const JSON::Boolean &boolean) const
Definition: protobuf.hpp:674
const T & as() const &
Definition: json.hpp:353
Definition: jsonify.hpp:326
Try< T > deserialize(const std::string &value)
Definition: protobuf.hpp:209
void json(ObjectWriter *writer, const Protobuf &protobuf)
Definition: protobuf.hpp:833
#define foreachpair(KEY, VALUE, ELEMS)
Definition: foreach.hpp:51
std::vector< Value > values
Definition: json.hpp:203
Result< T > read(int_fd fd, bool ignorePartial=false, bool undoFailed=false)
Definition: protobuf.hpp:383
Definition: protobuf.hpp:61
Result< T > operator()(int_fd fd, bool ignorePartial, bool undoFailed)
Definition: protobuf.hpp:251
static Try error(const E &e)
Definition: try.hpp:43
JSON::Proxy jsonify(const T &)
Definition: jsonify.hpp:702
#define UNREACHABLE()
Definition: unreachable.hpp:22
Try< Nothing > fsync(int fd)
Definition: fsync.hpp:29
Definition: json.hpp:247
Result< std::string > read(int_fd fd, size_t size)
Definition: read.hpp:55
Parser(google::protobuf::Message *_message, const google::protobuf::FieldDescriptor *_field)
Definition: protobuf.hpp:424
Try< Nothing > operator()(const JSON::Null &) const
Definition: protobuf.hpp:691
Iterable< V > map(F &&f, const Iterable< U, Us... > &input)
Definition: lambda.hpp:46
Definition: protobuf.hpp:821
Try< Nothing > parse(google::protobuf::Message *message, const JSON::Object &object)
Definition: protobuf.hpp:706
Definition: none.hpp:27
Definition: attributes.hpp:24
bool isError() const
Definition: try.hpp:78
T & get()&
Definition: result.hpp:116
Definition: json.hpp:79
Object protobuf(const google::protobuf::Message &message)
Definition: protobuf.hpp:1064
Definition: json.hpp:93
bool isError() const
Definition: result.hpp:114
Type utilities for the protobuf library that are not specific to particular protobuf classes...
Definition: type_utils.hpp:552
int int_fd
Definition: int_fd.hpp:35
std::string stringify(int flags)
Array protobuf(const google::protobuf::RepeatedPtrField< T > &repeated)
Definition: protobuf.hpp:1257
const mode_t S_IROTH
Definition: windows.hpp:321
constexpr const char * name
Definition: shell.hpp:41
Definition: json.hpp:51
Definition: representation.hpp:72
Definition: jsonify.hpp:296
Try< int > dup(int fd)
Definition: dup.hpp:23