blob: ea9040176df1cb0203689d9bf8461859359baec5 [file]
// 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