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  case google::protobuf::FieldDescriptor::TYPE_INT64:
545  case google::protobuf::FieldDescriptor::TYPE_SINT64:
546  case google::protobuf::FieldDescriptor::TYPE_SFIXED64:
547  case google::protobuf::FieldDescriptor::TYPE_UINT64:
548  case google::protobuf::FieldDescriptor::TYPE_FIXED64:
549  case google::protobuf::FieldDescriptor::TYPE_INT32:
550  case google::protobuf::FieldDescriptor::TYPE_SINT32:
551  case google::protobuf::FieldDescriptor::TYPE_SFIXED32:
552  case google::protobuf::FieldDescriptor::TYPE_UINT32:
553  case google::protobuf::FieldDescriptor::TYPE_FIXED32: {
554  Try<JSON::Number> number = JSON::parse<JSON::Number>(string.value);
555  if (number.isError()) {
556  return Error(
557  "Failed to parse '" + string.value + "' as a JSON number "
558  "for field '" + field->name() + "': " + number.error());
559  }
560 
561  return operator()(number.get());
562  }
563  case google::protobuf::FieldDescriptor::TYPE_BOOL: {
564  Try<JSON::Boolean> boolean = JSON::parse<JSON::Boolean>(string.value);
565  if (boolean.isError()) {
566  return Error(
567  "Failed to parse '" + string.value + "' as a JSON boolean "
568  "for field '" + field->name() + "': " + boolean.error());
569  }
570 
571  return operator()(boolean.get());
572  }
573  default:
574  return Error("Not expecting a JSON string for field '" +
575  field->name() + "'");
576  }
577  return Nothing();
578  }
579 
580  Try<Nothing> operator()(const JSON::Number& number) const
581  {
582  switch (field->type()) {
583  case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
584  if (field->is_repeated()) {
585  reflection->AddDouble(message, field, number.as<double>());
586  } else {
587  reflection->SetDouble(message, field, number.as<double>());
588  }
589  break;
590  case google::protobuf::FieldDescriptor::TYPE_FLOAT:
591  if (field->is_repeated()) {
592  reflection->AddFloat(message, field, number.as<float>());
593  } else {
594  reflection->SetFloat(message, field, number.as<float>());
595  }
596  break;
597  case google::protobuf::FieldDescriptor::TYPE_INT64:
598  case google::protobuf::FieldDescriptor::TYPE_SINT64:
599  case google::protobuf::FieldDescriptor::TYPE_SFIXED64:
600  if (field->is_repeated()) {
601  reflection->AddInt64(message, field, number.as<int64_t>());
602  } else {
603  reflection->SetInt64(message, field, number.as<int64_t>());
604  }
605  break;
606  case google::protobuf::FieldDescriptor::TYPE_UINT64:
607  case google::protobuf::FieldDescriptor::TYPE_FIXED64:
608  if (field->is_repeated()) {
609  reflection->AddUInt64(message, field, number.as<uint64_t>());
610  } else {
611  reflection->SetUInt64(message, field, number.as<uint64_t>());
612  }
613  break;
614  case google::protobuf::FieldDescriptor::TYPE_INT32:
615  case google::protobuf::FieldDescriptor::TYPE_SINT32:
616  case google::protobuf::FieldDescriptor::TYPE_SFIXED32:
617  if (field->is_repeated()) {
618  reflection->AddInt32(message, field, number.as<int32_t>());
619  } else {
620  reflection->SetInt32(message, field, number.as<int32_t>());
621  }
622  break;
623  case google::protobuf::FieldDescriptor::TYPE_UINT32:
624  case google::protobuf::FieldDescriptor::TYPE_FIXED32:
625  if (field->is_repeated()) {
626  reflection->AddUInt32(message, field, number.as<uint32_t>());
627  } else {
628  reflection->SetUInt32(message, field, number.as<uint32_t>());
629  }
630  break;
631  default:
632  return Error("Not expecting a JSON number for field '" +
633  field->name() + "'");
634  }
635  return Nothing();
636  }
637 
638  Try<Nothing> operator()(const JSON::Array& array) const
639  {
640  if (!field->is_repeated()) {
641  return Error("Not expecting a JSON array for field '" +
642  field->name() + "'");
643  }
644 
645  foreach (const JSON::Value& value, array.values) {
646  Try<Nothing> apply =
647  boost::apply_visitor(Parser(message, field), value);
648 
649  if (apply.isError()) {
650  return Error(apply.error());
651  }
652  }
653 
654  return Nothing();
655  }
656 
657  Try<Nothing> operator()(const JSON::Boolean& boolean) const
658  {
659  switch (field->type()) {
660  case google::protobuf::FieldDescriptor::TYPE_BOOL:
661  if (field->is_repeated()) {
662  reflection->AddBool(message, field, boolean.value);
663  } else {
664  reflection->SetBool(message, field, boolean.value);
665  }
666  break;
667  default:
668  return Error("Not expecting a JSON boolean for field '" +
669  field->name() + "'");
670  }
671  return Nothing();
672  }
673 
675  {
676  // We treat 'null' as an unset field. Note that we allow
677  // unset required fields here since the top-level parse
678  // function is responsible for checking 'IsInitialized'.
679  return Nothing();
680  }
681 
682 private:
683  google::protobuf::Message* message;
684  const google::protobuf::Reflection* reflection;
685  const google::protobuf::FieldDescriptor* field;
686 };
687 
688 
690  google::protobuf::Message* message,
691  const JSON::Object& object)
692 {
693  foreachpair (
694  const std::string& name, const JSON::Value& value, object.values) {
695  // Look for a field by this name.
696  const google::protobuf::FieldDescriptor* field =
697  message->GetDescriptor()->FindFieldByName(name);
698 
699  if (field != nullptr) {
700  Try<Nothing> apply =
701  boost::apply_visitor(Parser(message, field), value);
702 
703  if (apply.isError()) {
704  return Error(apply.error());
705  }
706  }
707  }
708 
709  return Nothing();
710 }
711 
712 
713 // Parses a single protobuf message of type T from a JSON::Object.
714 // NOTE: This struct is used by the public parse<T>() function below. See
715 // comments there for the reason why we opted for this design.
716 template <typename T>
717 struct Parse
718 {
720  {
721  static_assert(std::is_convertible<T*, google::protobuf::Message*>::value,
722  "T must be a protobuf message");
723 
724  const JSON::Object* object = boost::get<JSON::Object>(&value);
725  if (object == nullptr) {
726  return Error("Expecting a JSON object");
727  }
728 
729  T message;
730 
731  Try<Nothing> parse = internal::parse(&message, *object);
732  if (parse.isError()) {
733  return Error(parse.error());
734  }
735 
736  if (!message.IsInitialized()) {
737  return Error("Missing required fields: " +
738  message.InitializationErrorString());
739  }
740 
741  return message;
742  }
743 };
744 
745 
746 // Partial specialization for RepeatedPtrField<T> to parse a sequence of
747 // protobuf messages from a JSON::Array by repeatedly invoking Parse<T> to
748 // facilitate conversions like JSON::Array -> Resources.
749 // NOTE: This struct is used by the public parse<T>() function below. See
750 // comments there for the reason why we opted for this design.
751 template <typename T>
752 struct Parse<google::protobuf::RepeatedPtrField<T>>
753 {
755  const JSON::Value& value)
756  {
757  static_assert(std::is_convertible<T*, google::protobuf::Message*>::value,
758  "T must be a protobuf message");
759 
760  const JSON::Array* array = boost::get<JSON::Array>(&value);
761  if (array == nullptr) {
762  return Error("Expecting a JSON array");
763  }
764 
765  google::protobuf::RepeatedPtrField<T> collection;
766  collection.Reserve(static_cast<int>(array->values.size()));
767 
768  // Parse messages one by one and propagate an error if it happens.
769  foreach (const JSON::Value& elem, array->values) {
770  Try<T> message = Parse<T>()(elem);
771  if (message.isError()) {
772  return Error(message.error());
773  }
774 
775  collection.Add()->CopyFrom(message.get());
776  }
777 
778  return collection;
779  }
780 };
781 
782 } // namespace internal {
783 
784 // A dispatch wrapper which parses protobuf messages(s) from a given JSON value.
785 // We use partial specialization of
786 // - internal::Parse<T> for JSON::Object
787 // - internal::Parse<google::protobuf::RepeatedPtrField<T>> for JSON::Array
788 // to determine whether T is a single message or a sequence of messages.
789 // We cannot partially specialize function templates and overloaded function
790 // approach combined with std::enable_if is not that clean, hence we leverage
791 // partial specialization of class templates.
792 template <typename T>
793 Try<T> parse(const JSON::Value& value)
794 {
795  return internal::Parse<T>()(value);
796 }
797 
798 } // namespace protobuf {
799 
800 namespace JSON {
801 
802 // The representation of generic protobuf => JSON,
803 // e.g., `jsonify(JSON::Protobuf(message))`.
804 struct Protobuf : Representation<google::protobuf::Message>
805 {
807 };
808 
809 
810 // `json` function for protobuf messages. Refer to `jsonify.hpp` for details.
811 // TODO(mpark): This currently uses the default value for optional fields
812 // that are not deprecated, but we may want to revisit this decision.
813 //
814 // TODO(mzhu): Use protobuf built-in JSON mapping utilities in favor of
815 // the reflection APIs. See MESOS-9896.
816 inline void json(ObjectWriter* writer, const Protobuf& protobuf)
817 {
818  using google::protobuf::FieldDescriptor;
819 
820  const google::protobuf::Message& message = protobuf;
821 
822  const google::protobuf::Descriptor* descriptor = message.GetDescriptor();
823  const google::protobuf::Reflection* reflection = message.GetReflection();
824 
825  // We first look through all the possible fields to determine both the set
826  // fields __and__ the optional fields with a default that are not set.
827  // `Reflection::ListFields()` alone will only include set fields and
828  // is therefore insufficient.
829  int fieldCount = descriptor->field_count();
830  std::vector<const FieldDescriptor*> fields;
831  fields.reserve(fieldCount);
832  for (int i = 0; i < fieldCount; ++i) {
833  const FieldDescriptor* field = descriptor->field(i);
834  if (field->is_repeated()) { // Repeated or Map.
835  if (reflection->FieldSize(message, field) > 0) {
836  // Has repeated field with members, output as JSON.
837  fields.push_back(field);
838  }
839  } else if (
840  reflection->HasField(message, field) ||
841  (field->has_default_value() && !field->options().deprecated())) {
842  // Field is set or has default, output as JSON.
843  fields.push_back(field);
844  }
845  }
846 
847  foreach (const FieldDescriptor* field, fields) {
848  if (field->is_repeated() && !field->is_map()) {
849  writer->field(
850  field->name(),
851  [&field, &reflection, &message](JSON::ArrayWriter* writer) {
852  int fieldSize = reflection->FieldSize(message, field);
853  for (int i = 0; i < fieldSize; ++i) {
854  switch (field->cpp_type()) {
855  case FieldDescriptor::CPPTYPE_BOOL:
856  writer->element(
857  reflection->GetRepeatedBool(message, field, i));
858  break;
859  case FieldDescriptor::CPPTYPE_INT32:
860  writer->element(
861  reflection->GetRepeatedInt32(message, field, i));
862  break;
863  case FieldDescriptor::CPPTYPE_INT64:
864  writer->element(
865  reflection->GetRepeatedInt64(message, field, i));
866  break;
867  case FieldDescriptor::CPPTYPE_UINT32:
868  writer->element(
869  reflection->GetRepeatedUInt32(message, field, i));
870  break;
871  case FieldDescriptor::CPPTYPE_UINT64:
872  writer->element(
873  reflection->GetRepeatedUInt64(message, field, i));
874  break;
875  case FieldDescriptor::CPPTYPE_FLOAT:
876  writer->element(
877  reflection->GetRepeatedFloat(message, field, i));
878  break;
879  case FieldDescriptor::CPPTYPE_DOUBLE:
880  writer->element(
881  reflection->GetRepeatedDouble(message, field, i));
882  break;
883  case FieldDescriptor::CPPTYPE_MESSAGE:
884  writer->element(Protobuf(
885  reflection->GetRepeatedMessage(message, field, i)));
886  break;
887  case FieldDescriptor::CPPTYPE_ENUM:
888  writer->element(
889  reflection->GetRepeatedEnum(message, field, i)->name());
890  break;
891  case FieldDescriptor::CPPTYPE_STRING:
892  const std::string& s = reflection->GetRepeatedStringReference(
893  message, field, i, nullptr);
894  if (field->type() == FieldDescriptor::TYPE_BYTES) {
895  writer->element(base64::encode(s));
896  } else {
897  writer->element(s);
898  }
899  break;
900  }
901  }
902  });
903  } else { // field->is_map() || !field->is_repeated()
904  auto writeField = [&writer](
905  const std::string& fieldName,
906  const google::protobuf::Reflection* reflection,
907  const google::protobuf::Message& message,
908  const FieldDescriptor* field) {
909  switch (field->cpp_type()) {
910  case FieldDescriptor::CPPTYPE_BOOL:
911  writer->field(fieldName, reflection->GetBool(message, field));
912  break;
913  case FieldDescriptor::CPPTYPE_INT32:
914  writer->field(fieldName, reflection->GetInt32(message, field));
915  break;
916  case FieldDescriptor::CPPTYPE_INT64:
917  writer->field(fieldName, reflection->GetInt64(message, field));
918  break;
919  case FieldDescriptor::CPPTYPE_UINT32:
920  writer->field(fieldName, reflection->GetUInt32(message, field));
921  break;
922  case FieldDescriptor::CPPTYPE_UINT64:
923  writer->field(fieldName, reflection->GetUInt64(message, field));
924  break;
925  case FieldDescriptor::CPPTYPE_FLOAT:
926  writer->field(fieldName, reflection->GetFloat(message, field));
927  break;
928  case FieldDescriptor::CPPTYPE_DOUBLE:
929  writer->field(fieldName, reflection->GetDouble(message, field));
930  break;
931  case FieldDescriptor::CPPTYPE_MESSAGE:
932  writer->field(
933  fieldName, Protobuf(reflection->GetMessage(message, field)));
934  break;
935  case FieldDescriptor::CPPTYPE_ENUM:
936  writer->field(
937  fieldName, reflection->GetEnum(message, field)->name());
938  break;
939  case FieldDescriptor::CPPTYPE_STRING:
940  const std::string& s =
941  reflection->GetStringReference(message, field, nullptr);
942  if (field->type() == FieldDescriptor::TYPE_BYTES) {
943  writer->field(fieldName, base64::encode(s));
944  } else {
945  writer->field(fieldName, s);
946  }
947  break;
948  }
949  };
950 
951  if (!field->is_repeated()) { // Singular field.
952  writeField(field->name(), reflection, message, field);
953  } else { // Map field.
954  CHECK(field->is_map());
955  writer->field(
956  field->name(),
957  [&field, &reflection, &message, &writeField](
958  JSON::ObjectWriter* writer) {
959  foreach (
960  const auto& mapEntry,
961  reflection->GetRepeatedFieldRef<google::protobuf::Message>(
962  message, field)) {
963  const google::protobuf::Descriptor* mapEntryDescriptor =
964  mapEntry.GetDescriptor();
965  const google::protobuf::Reflection* mapEntryReflection =
966  mapEntry.GetReflection();
967 
968  // Map entry must contain exactly two fields: `key` and `value`.
969  CHECK_EQ(mapEntryDescriptor->field_count(), 2);
970 
971  const FieldDescriptor* keyField = mapEntryDescriptor->field(0);
972  const FieldDescriptor* valueField =
973  mapEntryDescriptor->field(1);
974 
975  switch (keyField->cpp_type()) {
976  case FieldDescriptor::CPPTYPE_BOOL:
977  writeField(
978  jsonify(
979  mapEntryReflection->GetBool(mapEntry, keyField)),
980  mapEntryReflection,
981  mapEntry,
982  valueField);
983  break;
984  case FieldDescriptor::CPPTYPE_INT32:
985  writeField(
986  jsonify(
987  mapEntryReflection->GetInt32(mapEntry, keyField)),
988  mapEntryReflection,
989  mapEntry,
990  valueField);
991  break;
992  case FieldDescriptor::CPPTYPE_INT64:
993  writeField(
994  jsonify(
995  mapEntryReflection->GetInt64(mapEntry, keyField)),
996  mapEntryReflection,
997  mapEntry,
998  valueField);
999  break;
1000  case FieldDescriptor::CPPTYPE_UINT32:
1001  writeField(
1002  jsonify(
1003  mapEntryReflection->GetUInt32(mapEntry, keyField)),
1004  mapEntryReflection,
1005  mapEntry,
1006  valueField);
1007  break;
1008  case FieldDescriptor::CPPTYPE_UINT64:
1009  writeField(
1010  jsonify(
1011  mapEntryReflection->GetUInt64(mapEntry, keyField)),
1012  mapEntryReflection,
1013  mapEntry,
1014  valueField);
1015  break;
1016  case FieldDescriptor::CPPTYPE_STRING:
1017  if (keyField->type() == FieldDescriptor::TYPE_BYTES) {
1018  LOG(FATAL)
1019  << "Unexpected key field type in protobuf Map: "
1020  << keyField->type_name();
1021  }
1022 
1023  writeField(
1024  mapEntryReflection->GetStringReference(
1025  mapEntry, keyField, nullptr),
1026  mapEntryReflection,
1027  mapEntry,
1028  valueField);
1029  break;
1030  case FieldDescriptor::CPPTYPE_FLOAT:
1031  case FieldDescriptor::CPPTYPE_DOUBLE:
1032  case FieldDescriptor::CPPTYPE_MESSAGE:
1033  case FieldDescriptor::CPPTYPE_ENUM:
1034  LOG(FATAL) << "Unexpected key field type in protobuf Map: "
1035  << keyField->cpp_type_name();
1036  }
1037  }
1038  });
1039  }
1040  }
1041  }
1042 }
1043 
1044 
1045 // TODO(bmahler): This currently uses the default value for optional fields
1046 // that are not deprecated, but we may want to revisit this decision.
1047 inline Object protobuf(const google::protobuf::Message& message)
1048 {
1049  Object object;
1050 
1051  const google::protobuf::Descriptor* descriptor = message.GetDescriptor();
1052  const google::protobuf::Reflection* reflection = message.GetReflection();
1053 
1054  auto value_for_field = [](
1055  const google::protobuf::Message& message,
1056  const google::protobuf::FieldDescriptor* field) -> JSON::Value {
1057  const google::protobuf::Reflection* reflection = message.GetReflection();
1058 
1059  switch (field->type()) {
1060  case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
1061  return JSON::Number(reflection->GetDouble(message, field));
1062  case google::protobuf::FieldDescriptor::TYPE_FLOAT:
1063  return JSON::Number(reflection->GetFloat(message, field));
1064  case google::protobuf::FieldDescriptor::TYPE_INT64:
1065  case google::protobuf::FieldDescriptor::TYPE_SINT64:
1066  case google::protobuf::FieldDescriptor::TYPE_SFIXED64:
1067  return JSON::Number(reflection->GetInt64(message, field));
1068  case google::protobuf::FieldDescriptor::TYPE_UINT64:
1069  case google::protobuf::FieldDescriptor::TYPE_FIXED64:
1070  return JSON::Number(reflection->GetUInt64(message, field));
1071  case google::protobuf::FieldDescriptor::TYPE_INT32:
1072  case google::protobuf::FieldDescriptor::TYPE_SINT32:
1073  case google::protobuf::FieldDescriptor::TYPE_SFIXED32:
1074  return JSON::Number(reflection->GetInt32(message, field));
1075  case google::protobuf::FieldDescriptor::TYPE_UINT32:
1076  case google::protobuf::FieldDescriptor::TYPE_FIXED32:
1077  return JSON::Number(reflection->GetUInt32(message, field));
1078  case google::protobuf::FieldDescriptor::TYPE_BOOL:
1079  if (reflection->GetBool(message, field)) {
1080  return JSON::Boolean(true);
1081  } else {
1082  return JSON::Boolean(false);
1083  }
1084  break;
1085  case google::protobuf::FieldDescriptor::TYPE_STRING:
1086  return JSON::String(reflection->GetString(message, field));
1087  case google::protobuf::FieldDescriptor::TYPE_BYTES:
1088  return JSON::String(
1089  base64::encode(reflection->GetString(message, field)));
1090  case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
1091  return protobuf(reflection->GetMessage(message, field));
1092  case google::protobuf::FieldDescriptor::TYPE_ENUM:
1093  return JSON::String(reflection->GetEnum(message, field)->name());
1094  case google::protobuf::FieldDescriptor::TYPE_GROUP:
1095  // Deprecated! We abort here instead of using a Try as return value,
1096  // because we expect this code path to never be taken.
1097  ABORT("Unhandled protobuf field type: " +
1098  stringify(field->type()));
1099  }
1100 
1101  UNREACHABLE();
1102  };
1103 
1104  // We first look through all the possible fields to determine both
1105  // the set fields _and_ the optional fields with a default that
1106  // are not set. Reflection::ListFields() alone will only include
1107  // set fields and is therefore insufficient.
1108  std::vector<const google::protobuf::FieldDescriptor*> fields;
1109  fields.reserve(descriptor->field_count());
1110  for (int i = 0; i < descriptor->field_count(); i++) {
1111  const google::protobuf::FieldDescriptor* field = descriptor->field(i);
1112  if (field->is_repeated()) {
1113  if (reflection->FieldSize(message, descriptor->field(i)) > 0) {
1114  // Has repeated field with members, output as JSON.
1115  fields.push_back(field);
1116  }
1117  } else if (
1118  reflection->HasField(message, field) ||
1119  (field->has_default_value() && !field->options().deprecated())) {
1120  // Field is set or has default, output as JSON.
1121  fields.push_back(field);
1122  }
1123  }
1124 
1125  foreach (const google::protobuf::FieldDescriptor* field, fields) {
1126  if (field->is_map()) {
1127  JSON::Object map;
1128 
1129  int fieldSize = reflection->FieldSize(message, field);
1130  for (int i = 0; i < fieldSize; ++i) {
1131  const google::protobuf::Message& entry =
1132  reflection->GetRepeatedMessage(message, field, i);
1133 
1134  // A map is equivalent to:
1135  //
1136  // message MapFieldEntry {
1137  // optional key_type key = 1;
1138  // optional value_type value = 2;
1139  // }
1140  //
1141  // repeated MapFieldEntry map_field = N;
1142  //
1143  // See the link below for details:
1144  // https://developers.google.com/protocol-buffers/docs/proto#maps
1145  const google::protobuf::FieldDescriptor* key_field =
1146  entry.GetDescriptor()->FindFieldByNumber(1);
1147 
1148  const google::protobuf::FieldDescriptor* value_field =
1149  entry.GetDescriptor()->FindFieldByNumber(2);
1150 
1151  JSON::Value key = value_for_field(entry, key_field);
1152 
1153  std::string name;
1154  if (key.is<JSON::String>()) {
1155  name = key.as<JSON::String>().value;
1156  } else {
1157  name = jsonify(key);
1158  }
1159 
1160  map.values[name] = value_for_field(entry, value_field);
1161  }
1162  object.values[field->name()] = map;
1163  } else if (field->is_repeated()) {
1164  JSON::Array array;
1165  int fieldSize = reflection->FieldSize(message, field);
1166  array.values.reserve(fieldSize);
1167  for (int i = 0; i < fieldSize; ++i) {
1168  switch (field->type()) {
1169  case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
1170  array.values.push_back(JSON::Number(
1171  reflection->GetRepeatedDouble(message, field, i)));
1172  break;
1173  case google::protobuf::FieldDescriptor::TYPE_FLOAT:
1174  array.values.push_back(JSON::Number(
1175  reflection->GetRepeatedFloat(message, field, i)));
1176  break;
1177  case google::protobuf::FieldDescriptor::TYPE_INT64:
1178  case google::protobuf::FieldDescriptor::TYPE_SINT64:
1179  case google::protobuf::FieldDescriptor::TYPE_SFIXED64:
1180  array.values.push_back(JSON::Number(
1181  reflection->GetRepeatedInt64(message, field, i)));
1182  break;
1183  case google::protobuf::FieldDescriptor::TYPE_UINT64:
1184  case google::protobuf::FieldDescriptor::TYPE_FIXED64:
1185  array.values.push_back(JSON::Number(
1186  reflection->GetRepeatedUInt64(message, field, i)));
1187  break;
1188  case google::protobuf::FieldDescriptor::TYPE_INT32:
1189  case google::protobuf::FieldDescriptor::TYPE_SINT32:
1190  case google::protobuf::FieldDescriptor::TYPE_SFIXED32:
1191  array.values.push_back(JSON::Number(
1192  reflection->GetRepeatedInt32(message, field, i)));
1193  break;
1194  case google::protobuf::FieldDescriptor::TYPE_UINT32:
1195  case google::protobuf::FieldDescriptor::TYPE_FIXED32:
1196  array.values.push_back(JSON::Number(
1197  reflection->GetRepeatedUInt32(message, field, i)));
1198  break;
1199  case google::protobuf::FieldDescriptor::TYPE_BOOL:
1200  if (reflection->GetRepeatedBool(message, field, i)) {
1201  array.values.push_back(JSON::Boolean(true));
1202  } else {
1203  array.values.push_back(JSON::Boolean(false));
1204  }
1205  break;
1206  case google::protobuf::FieldDescriptor::TYPE_STRING:
1207  array.values.push_back(JSON::String(
1208  reflection->GetRepeatedString(message, field, i)));
1209  break;
1210  case google::protobuf::FieldDescriptor::TYPE_BYTES:
1211  array.values.push_back(JSON::String(base64::encode(
1212  reflection->GetRepeatedString(message, field, i))));
1213  break;
1214  case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
1215  array.values.push_back(protobuf(
1216  reflection->GetRepeatedMessage(message, field, i)));
1217  break;
1218  case google::protobuf::FieldDescriptor::TYPE_ENUM:
1219  array.values.push_back(JSON::String(
1220  reflection->GetRepeatedEnum(message, field, i)->name()));
1221  break;
1222  case google::protobuf::FieldDescriptor::TYPE_GROUP:
1223  // Deprecated! We abort here instead of using a Try as return value,
1224  // because we expect this code path to never be taken.
1225  ABORT("Unhandled protobuf field type: " +
1226  stringify(field->type()));
1227  }
1228  }
1229  object.values[field->name()] = array;
1230  } else {
1231  object.values[field->name()] = value_for_field(message, field);
1232  }
1233  }
1234 
1235  return object;
1236 }
1237 
1238 
1239 template <typename T>
1240 Array protobuf(const google::protobuf::RepeatedPtrField<T>& repeated)
1241 {
1242  static_assert(std::is_convertible<T*, google::protobuf::Message*>::value,
1243  "T must be a protobuf message");
1244 
1245  JSON::Array array;
1246  array.values.reserve(repeated.size());
1247  foreach (const T& elem, repeated) {
1248  array.values.emplace_back(JSON::protobuf(elem));
1249  }
1250 
1251  return array;
1252 }
1253 
1254 } // namespace JSON {
1255 
1256 #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:580
Definition: protobuf.hpp:717
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:754
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:719
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:638
void field(const std::string &key, const T &value)
Definition: jsonify.hpp:346
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:657
const T & as() const &
Definition: json.hpp:353
Definition: jsonify.hpp:325
Try< T > deserialize(const std::string &value)
Definition: protobuf.hpp:209
void json(ObjectWriter *writer, const Protobuf &protobuf)
Definition: protobuf.hpp:816
#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:701
#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:674
Iterable< V > map(F &&f, const Iterable< U, Us... > &input)
Definition: lambda.hpp:46
Definition: protobuf.hpp:804
Try< Nothing > parse(google::protobuf::Message *message, const JSON::Object &object)
Definition: protobuf.hpp:689
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:1047
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:531
int int_fd
Definition: int_fd.hpp:35
std::string stringify(int flags)
Array protobuf(const google::protobuf::RepeatedPtrField< T > &repeated)
Definition: protobuf.hpp:1240
const mode_t S_IROTH
Definition: windows.hpp:321
constexpr const char * name
Definition: shell.hpp:43
Definition: json.hpp:51
Definition: representation.hpp:72
Definition: jsonify.hpp:295
Try< int > dup(int fd)
Definition: dup.hpp:23