| // 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/test_utils.h" | |
| #include <cstdint> | |
| #include <cstring> | |
| #include <optional> | |
| #include <string> | |
| #include "absl/log/check.h" | |
| #include "absl/strings/cord.h" | |
| #include "absl/strings/match.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 "riegeli/endian/endian_writing.h" | |
| #include "uuid/uuid.h" | |
| namespace jumbf { | |
| std::string WrapBox(absl::string_view content, uint32_t tbox) { | |
| const uint32_t lbox = content.length() + /*lbox*/ 4 + /*tbox*/ 4; | |
| std::string serialized(lbox, '\0'); | |
| char* cursor = serialized.data(); | |
| riegeli::WriteBigEndian<uint32_t>(lbox, cursor); | |
| cursor += sizeof(uint32_t); | |
| riegeli::WriteBigEndian<uint32_t>(tbox, cursor); | |
| cursor += sizeof(uint32_t); | |
| // This is safe because the length of `serialized` has been calculated above | |
| // to always have enough room for `content`. | |
| memcpy(cursor, content.data(), content.length()); | |
| return serialized; | |
| } | |
| std::string WrapUndeterminedLengthBox(absl::string_view content, | |
| uint32_t tbox) { | |
| const uint32_t box_length = content.length() + /*lbox*/ 4 + /*tbox*/ 4; | |
| std::string serialized(box_length, '\0'); | |
| char* cursor = serialized.data(); | |
| // LBox value | |
| riegeli::WriteBigEndian<uint32_t>(0, cursor); | |
| cursor += sizeof(uint32_t); | |
| riegeli::WriteBigEndian<uint32_t>(tbox, cursor); | |
| cursor += sizeof(uint32_t); | |
| // This is safe because the length of `serialized` has been calculated above | |
| // to always have enough room for `content`. | |
| memcpy(cursor, content.data(), content.length()); | |
| return serialized; | |
| } | |
| void SerializeUuid(char** dest, credentio::Uuid uuid) { | |
| const std::string bytes = uuid.ToProtoBytes(); | |
| if (bytes.size() == 16) { | |
| memcpy(*dest, bytes.data(), 16); | |
| } else { | |
| memset(*dest, 0, 16); | |
| } | |
| *dest += 16; | |
| } | |
| std::string EncodeDescriptionBox(credentio::Uuid uuid, | |
| std::optional<absl::string_view> label, | |
| bool requestable) { | |
| if (label.has_value()) { | |
| CHECK(!absl::StrContains(*label, '\0')); | |
| const size_t description_content_length = /*uuid*/ 16 + /*toggles*/ 1 + | |
| label->size() + | |
| /*label NUL terminator*/ 1; | |
| std::string description_content(description_content_length, '\0'); | |
| char* content_cursor = description_content.data(); | |
| jumbf::SerializeUuid(&content_cursor, uuid); | |
| uint8_t toggles = kDescriptionToggleLabelPresent; | |
| if (requestable) { | |
| toggles |= kDescriptionToggleRequestable; | |
| } | |
| riegeli::WriteBigEndian<uint8_t>(toggles, content_cursor); | |
| content_cursor += sizeof(uint8_t); | |
| memcpy(content_cursor, label->data(), label->size()); | |
| content_cursor += label->size(); | |
| *content_cursor++ = '\0'; | |
| return WrapBox(description_content, kDescriptionBoxType); | |
| } | |
| const size_t description_content_length = /*uuid*/ 16 + /*toggles*/ 1 + | |
| /*label NUL terminator*/ 1; | |
| std::string description_content(description_content_length, '\0'); | |
| char* content_cursor = description_content.data(); | |
| jumbf::SerializeUuid(&content_cursor, uuid); | |
| uint8_t toggles = kDescriptionToggleEmpty; | |
| riegeli::WriteBigEndian<uint8_t>(toggles, content_cursor); | |
| content_cursor += sizeof(uint8_t); | |
| *content_cursor++ = '\0'; | |
| return WrapBox(description_content, kDescriptionBoxType); | |
| } | |
| std::string EncodeCborBox(absl::string_view contents) { | |
| return WrapBox(contents, kCborBoxType); | |
| } | |
| std::string EncodeSuperBox(credentio::Uuid uuid, | |
| std::optional<absl::string_view> label, | |
| absl::Span<const absl::string_view> contents, | |
| bool requestable) { | |
| return jumbf::WrapBox( | |
| absl::StrCat(EncodeDescriptionBox(uuid, label, requestable), | |
| absl::StrJoin(contents, "")), | |
| jumbf::kSuperBoxType); | |
| } | |
| absl::Cord CordFromConstexpr(absl::string_view data) { | |
| return absl::MakeCordFromExternal(data, [](absl::string_view data) {}); | |
| } | |
| } // namespace jumbf |