| // 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. |
| // |
| |
| #include "jumbf/box_builder.h" |
| |
| #include <cstddef> |
| #include <cstdint> |
| #include <optional> |
| #include <string> |
| #include <utility> |
| |
| #include "absl/algorithm/container.h" |
| #include "absl/status/status.h" |
| #include "absl/status/status_macros.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/cord.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/str_join.h" |
| #include "absl/strings/string_view.h" |
| #include "absl/types/span.h" |
| #include "jumbf/constants.h" |
| #include "jumbf/encode_params.h" |
| #include "jumbf/internal/encode.h" |
| #include "uuid/uuid.h" |
| |
| namespace jumbf { |
| |
| SuperBoxBuilder::SuperBoxBuilder(credentio::Uuid type_uuid, |
| DescriptionBoxOptions description_options) |
| : type_uuid_(std::move(type_uuid)), |
| description_label_(description_options.label), |
| description_options_(std::move(description_options)) { |
| if (description_label_.has_value()) { |
| description_label_->Flatten(); |
| } |
| } |
| |
| absl::Status SuperBoxBuilder::AssertStateIn( |
| absl::Span<const SuperBoxBuildingState> want) const { |
| if (absl::c_linear_search(want, state_)) { |
| return absl::OkStatus(); |
| } |
| |
| std::string want_str; |
| if (want.size() == 1) { |
| want_str = absl::StrCat(want[0]); |
| } else { |
| want_str = absl::StrCat("one of [", absl::StrJoin(want, ", "), "]"); |
| } |
| return absl::FailedPreconditionError( |
| absl::StrCat("builder in invalid state for operation: got: ", state_, |
| ", want: ", want_str)); |
| } |
| |
| void SuperBoxBuilder::StateTransition( |
| absl::Span<const SuperBoxBuildingState> from, SuperBoxBuildingState to) { |
| if (absl::c_linear_search(from, state_)) { |
| state_ = to; |
| } |
| } |
| |
| absl::Status SuperBoxBuilder::AddPadding(size_t len) { |
| ABSL_RETURN_IF_ERROR(AssertStateIn({SuperBoxBuildingState::kEmpty})); |
| StateTransition({SuperBoxBuildingState::kEmpty}, |
| SuperBoxBuildingState::kContainsPadding); |
| return EncodePaddingBox(len, &writer_); |
| } |
| |
| absl::Status SuperBoxBuilder::AddChild(SuperBoxBuilder&& child) { |
| ABSL_RETURN_IF_ERROR(AssertStateIn( |
| {SuperBoxBuildingState::kEmpty, SuperBoxBuildingState::kContainsPadding, |
| SuperBoxBuildingState::kContainsContent})); |
| StateTransition( |
| {SuperBoxBuildingState::kEmpty, SuperBoxBuildingState::kContainsPadding}, |
| SuperBoxBuildingState::kContainsContent); |
| |
| ABSL_ASSIGN_OR_RETURN(auto serialized_child, std::move(child).Finalize()); |
| return AddChild(std::move(serialized_child)); |
| }; |
| |
| absl::Status SuperBoxBuilder::AddContent(uint32_t tbox, absl::Cord payload) { |
| ABSL_RETURN_IF_ERROR(AssertStateIn( |
| {SuperBoxBuildingState::kEmpty, SuperBoxBuildingState::kContainsPadding, |
| SuperBoxBuildingState::kContainsContent})); |
| StateTransition( |
| {SuperBoxBuildingState::kEmpty, SuperBoxBuildingState::kContainsPadding}, |
| SuperBoxBuildingState::kContainsContent); |
| return EncodeContentBox(tbox, std::move(payload), &writer_); |
| } |
| |
| absl::Status SuperBoxBuilder::AddChild(BuiltSuperBox&& serialized_superbox) { |
| StateTransition( |
| {SuperBoxBuildingState::kEmpty, SuperBoxBuildingState::kContainsPadding}, |
| SuperBoxBuildingState::kContainsContent); |
| return EncodeSerializedSuperBox(std::move(serialized_superbox).AsCord(), |
| &writer_); |
| } |
| |
| absl::Status SuperBoxBuilder::AddChild(absl::Cord serialized_superbox) { |
| StateTransition( |
| {SuperBoxBuildingState::kEmpty, SuperBoxBuildingState::kContainsPadding}, |
| SuperBoxBuildingState::kContainsContent); |
| return EncodeSerializedSuperBox(std::move(serialized_superbox), &writer_); |
| } |
| |
| absl::StatusOr<BuiltSuperBox> SuperBoxBuilder::Finalize() && { |
| // JUMBF requires at least one content box. |
| ABSL_RETURN_IF_ERROR( |
| AssertStateIn({SuperBoxBuildingState::kContainsContent})); |
| |
| ABSL_RETURN_IF_ERROR(EncodeDescriptionBox( |
| std::move(type_uuid_), std::move(description_options_), &writer_)); |
| ABSL_RETURN_IF_ERROR(EncodeBoxHeader(writer_.pos(), kSuperBoxType, &writer_)); |
| if (!writer_.Close()) { |
| return writer_.status(); |
| } |
| |
| std::optional<std::string> description_label; |
| if (description_label_.has_value()) { |
| description_label = std::string(*description_label_); |
| } |
| auto result = |
| BuiltSuperBox(std::move(writer_.dest()), std::move(description_label)); |
| |
| // Clear out everything to Builder in a reasonable state post-move: |
| state_ = SuperBoxBuildingState::kEmpty; |
| writer_.Reset(); |
| return result; |
| } |
| |
| } // namespace jumbf |