blob: 003f1d490db14120b1a5af0fa2dad57d74a11ce4 [file] [edit]
// 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_