| // Copyright 2026 Google LLC | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // https://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| // | |
| // SuperBoxEncoder: Stateful JUMBF encoder. | |
| // | |
| // This class tracks box lengths to correctly write headers. See | |
| // credentio/jumbf/box_builder_test.cc for usage | |
| // examples. | |
| #ifndef THIRD_PARTY_CREDENTIO_JUMBF_BOX_BUILDER_H_ | |
| #define THIRD_PARTY_CREDENTIO_JUMBF_BOX_BUILDER_H_ | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <optional> | |
| #include <string> | |
| #include <utility> | |
| #include "absl/log/log.h" | |
| #include "absl/status/status.h" | |
| #include "absl/status/statusor.h" | |
| #include "absl/strings/cord.h" | |
| #include "absl/strings/str_cat.h" | |
| #include "absl/strings/string_view.h" | |
| #include "absl/types/span.h" | |
| #include "jumbf/encode_params.h" | |
| #include "riegeli/bytes/cord_backward_writer.h" | |
| #include "uuid/uuid.h" | |
| namespace jumbf { | |
| enum class SuperBoxBuildingState { | |
| kEmpty, | |
| kContainsPadding, | |
| kContainsContent, | |
| }; | |
| class BuiltSuperBox; | |
| class SuperBoxBuilder { | |
| public: | |
| SuperBoxBuilder(credentio::Uuid type_uuid, | |
| DescriptionBoxOptions description_options); | |
| // Adds a padding box to the superbox. May only be called on an empty encoder. | |
| absl::Status AddPadding(size_t len); | |
| // Adds a child superbox as a content box of this superbox. New boxes are | |
| // added to the front of this box's content. | |
| absl::Status AddChild(SuperBoxBuilder&& child); | |
| // Add a child superbox as a content box of this superbox. New boxes are | |
| // added to the front of this box's content. | |
| absl::Status AddChild(BuiltSuperBox&& serialized_superbox); | |
| // Add a child superbox as a content box of this superbox. New boxes are | |
| // added to the front of this box's content. | |
| absl::Status AddChild(absl::Cord serialized_superbox); | |
| // Adds a content box. New boxes are added to the front of this box's | |
| // content. | |
| absl::Status AddContent(uint32_t tbox, absl::Cord payload); | |
| // Finish writing the box. This consumes the builder, use | |
| // `std::move(builder).Finalize()`; | |
| absl::StatusOr<BuiltSuperBox> Finalize() &&; | |
| private: | |
| absl::Status AssertStateIn( | |
| absl::Span<const SuperBoxBuildingState> want) const; | |
| void StateTransition(absl::Span<const SuperBoxBuildingState> from, | |
| SuperBoxBuildingState to); | |
| credentio::Uuid type_uuid_; | |
| // Need to cache the underlying `absl::Cord` for the label, because the | |
| // `DescriptionBoxOptions` gets consumed by `Finalize()`. | |
| std::optional<absl::Cord> description_label_; | |
| DescriptionBoxOptions description_options_; | |
| SuperBoxBuildingState state_ = SuperBoxBuildingState::kEmpty; | |
| riegeli::CordBackwardWriter<absl::Cord> writer_; | |
| }; | |
| class BuiltSuperBox { | |
| public: | |
| BuiltSuperBox() = default; | |
| std::optional<absl::string_view> description_label() const { | |
| return description_label_; | |
| } | |
| // Peeks at the underlying cord. Less efficient than `AsCord()` if the value | |
| // is to be stored elsewhere. | |
| const absl::Cord& Peek() const { return rep_; } | |
| // Moves the underlying cord out of this object. Use | |
| // `std::move(box).AsCord()`. | |
| absl::Cord AsCord() && { | |
| description_label_ = std::nullopt; | |
| return std::move(rep_); | |
| } | |
| private: | |
| friend absl::StatusOr<BuiltSuperBox> SuperBoxBuilder::Finalize() &&; | |
| explicit BuiltSuperBox(absl::Cord&& rep, | |
| std::optional<std::string> description_label) | |
| : rep_(std::move(rep)), | |
| description_label_(std::move(description_label)) {} | |
| absl::Cord rep_; | |
| std::optional<std::string> description_label_; | |
| }; | |
| template <typename Sink> | |
| void AbslStringify(Sink& sink, SuperBoxBuildingState state) { | |
| switch (state) { | |
| case SuperBoxBuildingState::kEmpty: | |
| sink.Append("EMPTY"); | |
| return; | |
| case jumbf::SuperBoxBuildingState::kContainsPadding: | |
| sink.Append("CONTAINS_PADDING"); | |
| return; | |
| case jumbf::SuperBoxBuildingState::kContainsContent: | |
| sink.Append("CONTAINS_CONTENT"); | |
| return; | |
| }; | |
| LOG(DFATAL) << "Fell through end of exhaustive switch statement."; | |
| sink.Append(absl::StrCat("UNKNOWN_STATE_", static_cast<int>(state))); | |
| } | |
| } // namespace jumbf | |
| #endif // THIRD_PARTY_CREDENTIO_JUMBF_BOX_BUILDER_H_ |