1 // Copyright 2019 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 /////////////////////////////////////////////////////////////////////////////// 16 17 #ifndef TINK_OUTPUT_STREAM_WITH_RESULT_H_ 18 #define TINK_OUTPUT_STREAM_WITH_RESULT_H_ 19 20 #include <type_traits> 21 22 #include "tink/output_stream.h" 23 #include "tink/util/status.h" 24 #include "tink/util/statusor.h" 25 26 namespace crypto { 27 namespace tink { 28 29 // TODO(tholenst): Forward declare ExtractStatus function, 30 // as discussed in cl/269918867. 31 namespace internal { 32 // Closes the OutputStream and returns the status, 33 // for when ResultType is StatusOr<T> 34 template <class ResultType> 35 auto ExtractStatus(const ResultType& result) 36 -> decltype(std::declval<ResultType>().status()) { 37 return result.status(); 38 } 39 40 // Closes the OutputStream and returns the status, 41 // for when ResultType is Status 42 template <class ResultType> 43 auto ExtractStatus(ResultType result) -> 44 typename std::enable_if<std::is_same<ResultType, util::Status>::value, 45 util::Status>::type { 46 return result; 47 } 48 } // namespace internal 49 50 // An abstract OutputStream subclass that acts as a sink for data and returns a 51 // result after the stream has been closed. 52 // The result is only available if the stream was able to close with an ok 53 // status. Otherwise, the status of the GetResult call will be the same status 54 // as the Close call had. If Close is successful, GetResult is guaranteed to 55 // return a valid result. 56 // The return type of GetResult will be StatusOr<T>, except for T == Status, in 57 // which case the return type is Status. 58 template <class T> 59 class OutputStreamWithResult : public OutputStream { 60 public: OutputStreamWithResult()61 OutputStreamWithResult() : closed_(false) {} 62 ~OutputStreamWithResult() override = default; 63 64 // The return type is StatusOr<T> if T != Status, and Status otherwise. 65 using ResultType = 66 typename std::conditional<std::is_same<T, util::Status>::value, 67 util::Status, util::StatusOr<T>>::type; 68 69 // Get the result associated with this OutputStream. Can only be called on 70 // closed streams, and will otherwise fail with FAILED_PRECONDITION as error 71 // code. 72 // If Close() returned an ok status, this method is guaranteed to contain a 73 // valid result. 74 // The return type is StatusOr<T> if T != Status, and Status otherwise. GetResult()75 ResultType GetResult() { 76 if (!closed_) { 77 return util::Status(absl::StatusCode::kFailedPrecondition, 78 "Stream is not closed"); 79 } 80 return result_; 81 } 82 83 // Close the stream and return the computed result. Equivalent to calling 84 // Close() and GetResult() if Close() returned an OK status. 85 // The return type is StatusOr<T> if T != Status, and Status otherwise. CloseAndGetResult()86 ResultType CloseAndGetResult() { 87 util::Status closing_status = Close(); 88 if (!closing_status.ok()) { 89 return closing_status; 90 } 91 return GetResult(); 92 } 93 94 // Closes the OutputStream. Close()95 util::Status Close() final { 96 if (closed_) { 97 return util::Status(absl::StatusCode::kFailedPrecondition, 98 "Stream closed"); 99 } 100 result_ = CloseStreamAndComputeResult(); 101 closed_ = true; 102 103 return internal::ExtractStatus(result_); 104 } 105 106 // Getting the next OutputStream buffer. See OutputStream for detailed 107 // description. Next(void ** data)108 crypto::tink::util::StatusOr<int> Next(void** data) final { 109 if (closed_) { 110 return util::Status(absl::StatusCode::kFailedPrecondition, 111 "Write on closed Stream"); 112 } 113 return NextBuffer(data); 114 } 115 116 protected: 117 // Compute the result for this OutputStream. Safe for thread safety problems, 118 // this method will only be called once. 119 // The return type is StatusOr<T> if T != Status, and Status otherwise. 120 virtual ResultType CloseStreamAndComputeResult() = 0; 121 // Getting the next OutputStream buffer. See OutputStream for detailed 122 // description. 123 virtual util::StatusOr<int> NextBuffer(void** data) = 0; 124 125 private: 126 bool closed_; 127 ResultType result_; 128 }; 129 130 } // namespace tink 131 } // namespace crypto 132 133 #endif // TINK_OUTPUT_STREAM_WITH_RESULT_H_ 134