| // 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 |
| // c2pa/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_ |