1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
|
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MEDIA_BASE_STATUS_H_
#define MEDIA_BASE_STATUS_H_
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/location.h"
#include "base/strings/string_piece.h"
#include "base/values.h"
#include "media/base/media_export.h"
#include "media/base/media_serializers_base.h"
#include "media/base/status_codes.h"
// Mojo namespaces for serialization friend declarations.
namespace mojo {
template <typename T, typename U>
struct StructTraits;
} // namespace mojo
namespace media {
namespace mojom {
class StatusDataView;
}
// Status is meant to be a relatively small (sizeof(void*) bytes) object
// that can be returned as a status value from functions or passed to callbacks
// that want a report of status. Status allows attaching of arbitrary named
// data, other Status' as causes, and stack frames, which can all be logged
// and reported throughout the media stack. The status code and message are
// immutable and can be used to give a stable numeric ID for any error
// generated by media code.
// There is also an OK state which can't hold any data and is only for
// successful returns.
class MEDIA_EXPORT Status {
public:
// Default constructor can be used for OkStatus();
Status();
// Constructor to create a new Status from a numeric code & message.
// These are immutable; if you'd like to change them, then you likely should
// create a new Status. {} or OkStatus() should be used to create a
// success status.
// NOTE: This should never be given a location parameter when called - It is
// defaulted in order to grab the caller location.
Status(StatusCode code,
base::StringPiece message = "",
const base::Location& location = base::Location::Current());
// Copy Constructor & assignment. (Mojo uses both of these)
Status(const Status&);
Status& operator=(const Status&);
// Allows move.
Status(Status&&);
Status& operator=(Status&&);
// Needs an out of line destructor...
~Status();
bool is_ok() const { return !data_; }
// Getters for internal fields
const std::string& message() const {
DCHECK(data_);
return data_->message;
}
StatusCode code() const { return data_ ? data_->code : StatusCode::kOk; }
// Adds the current location to Status as it’s passed upwards.
// This does not need to be called at every location that touches it, but
// should be called for those locations where the path is ambiguous or
// critical. This can be especially helpful across IPC boundaries. This will
// fail on an OK status.
// NOTE: This should never be given a parameter when called - It is defaulted
// in order to grab the caller location.
Status&& AddHere(
const base::Location& location = base::Location::Current()) &&;
// Add |cause| as the error that triggered this one. For example,
// DecoderStream might return kDecoderSelectionFailed with one or more causes
// that are the specific errors from the decoders that it tried.
Status&& AddCause(Status&& cause) &&;
void AddCause(Status&& cause) &;
// Allows us to append any datatype which can be converted to
// an int/bool/string/base::Value. Any existing data associated with |key|
// will be overwritten by |value|. This will fail on an OK status.
template <typename T>
Status&& WithData(const char* key, const T& value) && {
DCHECK(data_);
data_->data.SetKey(key, MediaSerialize(value));
return std::move(*this);
}
template <typename T>
void WithData(const char* key, const T& value) & {
DCHECK(data_);
data_->data.SetKey(key, MediaSerialize(value));
}
private:
// Private helper to add the current stack frame to the error trace.
void AddFrame(const base::Location& location);
// Keep the internal data in a unique ptr to minimize size of OK errors.
struct MEDIA_EXPORT StatusInternal {
StatusInternal(StatusCode code, std::string message);
~StatusInternal();
// The current error code
StatusCode code = StatusCode::kOk;
// The current error message (Can be used for
// https://developer.mozilla.org/en-US/docs/Web/API/Status)
std::string message;
// Stack frames
std::vector<base::Value> frames;
// Causes
std::vector<Status> causes;
// Data attached to the error
base::Value data;
};
// Allow self-serialization
friend struct internal::MediaSerializer<Status>;
// Allow mojo-serialization
friend struct mojo::StructTraits<media::mojom::StatusDataView, Status>;
// A null internals is an implicit OK.
std::unique_ptr<StatusInternal> data_;
};
// Convenience function to return |kOk|.
// OK won't have a message, trace, or data associated with them, and DCHECK
// if they are added.
MEDIA_EXPORT Status OkStatus();
// Helper class to allow returning a |T| or a Status. Typical usage:
//
// ErrorOr<std::unique_ptr<MyObject>> FactoryFn() {
// if (success)
// return std::make_unique<MyObject>();
// return Status(StatusCodes::kSomethingBadHappened);
// }
//
// auto result = FactoryFn();
// if (result.has_error()) return std::move(result.error());
// my_object_ = std::move(result.value());
//
// Also useful if one would like to get an enum class return value, unless an
// error occurs:
//
// enum class ResultType { kNeedMoreInput, kOutputIsReady, kFormatChanged };
//
// ErrorOr<ResultType> Foo() { ... }
//
// auto result = Foo();
// if (result.has_error()) return std::move(result.error());
// switch (result.value()) {
// case ResultType::kNeedMoreInput:
// ...
// }
template <typename T>
class ErrorOr {
public:
// All of these may be implicit, so that one may just return Status or
// the value in question.
ErrorOr(Status&& error) : error_(std::move(error)) {}
ErrorOr(const Status& error) : error_(error) {}
ErrorOr(T&& value) : value_(std::move(value)) {}
ErrorOr(const T& value) : value_(value) {}
~ErrorOr() = default;
// Move- and copy- construction and assignment are okay.
ErrorOr(const ErrorOr&) = default;
ErrorOr(ErrorOr&&) = default;
ErrorOr& operator=(ErrorOr&) = default;
ErrorOr& operator=(ErrorOr&&) = default;
// Do we have a value?
bool has_value() const { return value_.has_value(); }
// Since we often test for errors, provide this too.
bool has_error() const { return !has_value(); }
// Return the error, if we have one. Up to the caller to make sure that we
// have one via |!has_value()|.
Status& error() { return *error_; }
// Return a ref to the value. It's up to the caller to verify that we have a
// value before calling this.
T& value() { return *value_; }
private:
base::Optional<Status> error_;
base::Optional<T> value_;
};
} // namespace media
#endif // MEDIA_BASE_STATUS_H_
|