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