| // 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 "formats/bmff/box_matcher.h" |
| |
| #include <cstdint> |
| #include <string> |
| #include <vector> |
| |
| #include "formats/bmff/box_header.h" |
| #include "formats/bmff/xpath.h" |
| #include "proto/bmff_based_hash_assertion.pb.h" |
| |
| namespace credentio { |
| namespace { |
| |
| uint32_t NetworkUInt24(std::string value) { |
| uint32_t result = 0; |
| for (int i = 0; i < 3; ++i) { |
| uint8_t byte = (i < value.size()) ? static_cast<uint8_t>(value[i]) : 0; |
| result = (result << 8) | byte; |
| } |
| return result; |
| } |
| |
| bool MatchLength(const BmffRange& bmff_range, const BmffBoxHeader& header) { |
| if (!bmff_range.has_length()) { |
| return true; |
| } |
| return header.box_size == bmff_range.length(); |
| } |
| |
| bool MatchVersion(const BmffRange& bmff_range, const BmffBoxHeader& header) { |
| if (!bmff_range.has_version()) { |
| return true; |
| } |
| return header.version == bmff_range.version(); |
| } |
| |
| bool MatchFlags(const BmffRange& bmff_range, const BmffBoxHeader& header) { |
| if (!bmff_range.has_flags()) { |
| return true; |
| } |
| if (!header.IsFullBox()) { |
| return false; |
| } |
| if (!bmff_range.has_exact() || bmff_range.exact()) { |
| return header.flags == bmff_range.flags(); |
| } |
| auto desired_flags = NetworkUInt24(std::string(bmff_range.flags())); |
| auto box_flags = NetworkUInt24(header.flags); |
| return (desired_flags & box_flags) == desired_flags; |
| } |
| |
| } // namespace |
| |
| bool BMFFBoxMatcher::Matches(BmffBoxHeader header) const { |
| if (!(XPathMatcher(bmff_range_.xpath()).Matches(header.xpath)) || |
| !MatchLength(bmff_range_, header) || !MatchVersion(bmff_range_, header) || |
| !MatchFlags(bmff_range_, header)) { |
| return false; |
| } |
| for (const auto& data : bmff_range_.data()) { |
| uint64_t offset = header.start + data.offset(); |
| input_.Seek(offset); |
| if (input_.pos() != offset) { |
| return false; |
| } |
| std::string value; |
| if (!input_.Read(data.value().size(), value)) { |
| return false; |
| } |
| if (value != data.value()) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| std::vector<BMFFBoxMatcher::Range> BMFFBoxMatcher::MatchedRanges( |
| BmffBoxHeader header) const { |
| std::vector<Range> ranges; |
| if (bmff_range_.subsets_size() == 0) { |
| ranges.push_back({.offset = header.start, |
| .length = static_cast<int64_t>(header.box_size)}); |
| return ranges; |
| } |
| for (const auto& subset : bmff_range_.subsets()) { |
| int64_t offset = header.start + subset.offset(); |
| int64_t end = header.start + subset.offset() + subset.length(); |
| if (offset > end) { |
| // Invalid subset. |
| continue; |
| } |
| if (subset.length() == 0 || end > header.start + header.box_size) { |
| end = header.start + header.box_size; |
| } |
| ranges.push_back( |
| {.offset = header.start + subset.offset(), .length = end - offset}); |
| } |
| return ranges; |
| } |
| } // namespace credentio |