| // 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/internal/consume_box.h" |
| |
| #include <cstddef> |
| #include <cstdint> |
| |
| #include "absl/log/check.h" |
| #include "absl/status/status.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/string_view.h" |
| #include "jumbf/internal/intermediate.h" |
| #include "jumbf/internal/serialization_utils.h" |
| |
| namespace jumbf_internal { |
| |
| absl::StatusOr<IntermediateBox> ConsumeBox(absl::string_view* input, |
| bool allow_implicit_length) { |
| absl::string_view temp_input = *input; |
| |
| auto lbox = ConsumeInteger<uint32_t>(&temp_input); |
| if (!lbox.ok()) { |
| return absl::Status( |
| lbox.status().code(), |
| absl::StrCat("cannot read LBox value: ", lbox.status().message())); |
| } |
| |
| auto tbox = ConsumeInteger<uint32_t>(&temp_input); |
| if (!tbox.ok()) { |
| return absl::Status( |
| tbox.status().code(), |
| absl::StrCat("cannot read TBox value: ", tbox.status().message())); |
| } |
| uint64_t box_length; |
| |
| if (*lbox == 0) { |
| if (allow_implicit_length) { |
| box_length = input->length(); |
| } else { |
| return absl::InvalidArgumentError( |
| R"(LBox is 0, indicating box extends to end of input, but context does not allow for implicit box lengths; implicit boxes are only allowed if they are the last box in the file, and if they are contained within a super box, that super box must also have an implicit length)"); |
| } |
| |
| } else if (*lbox == 1) { |
| auto xlbox = ConsumeInteger<uint64_t>(&temp_input); |
| if (!xlbox.ok()) { |
| return absl::Status( |
| xlbox.status().code(), |
| absl::StrCat("cannot read XLBox value: ", xlbox.status().message())); |
| } |
| if (*xlbox < 16) { |
| return absl::InvalidArgumentError(absl::StrCat( |
| "XLBox is ", *xlbox, |
| ", which is less than the number of bytes already consumed by " |
| "this box's header")); |
| } |
| box_length = *xlbox; |
| } else if (*lbox <= 7) { |
| return absl::InvalidArgumentError(absl::StrCat( |
| "LBox is ", *lbox, "; LBox values 2-7 are reserved by the standard")); |
| } else { |
| box_length = *lbox; |
| } |
| |
| if (box_length > input->length()) { |
| return absl::InvalidArgumentError(absl::StrCat( |
| "not enough input bytes for declared box size; have ", input->length(), |
| " input bytes, box declares its size as ", box_length, " bytes")); |
| } |
| |
| size_t header_bytes = temp_input.data() - input->data(); |
| absl::string_view serialized = input->substr(0, box_length); |
| input->remove_prefix(box_length); |
| |
| return IntermediateBox{ |
| .payload = temp_input.substr(0, box_length - header_bytes), |
| .serialized = serialized, |
| .type = *tbox, |
| .implicit_length = *lbox == 0, |
| }; |
| } |
| |
| } // namespace jumbf_internal |