| // 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/serialization_utils.h" |
| |
| #include <climits> |
| #include <cstddef> |
| #include <cstdint> |
| #include <utility> |
| |
| #include "absl/status/status.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/cord.h" |
| #include "absl/strings/match.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/string_view.h" |
| #include "riegeli/base/byte_fill.h" |
| #include "riegeli/bytes/backward_writer.h" |
| #include "riegeli/endian/endian_writing.h" |
| |
| #define WRITER_CALL_OR_RETURN(writer, call) \ |
| do { \ |
| if (!writer->call) { \ |
| return writer->status(); \ |
| } \ |
| } while (false) |
| |
| namespace jumbf_internal { |
| |
| // This library stores non-text data in strings and string_views, and reads |
| // values from byte offsets within them. No effort has been made to take into |
| // account alternate char widths. |
| static_assert(CHAR_BIT == 8); |
| |
| absl::StatusOr<absl::string_view> ConsumeNullTerminated(absl::string_view* sv) { |
| size_t len = sv->find('\0'); |
| if (len == absl::string_view::npos) { |
| return absl::OutOfRangeError( |
| "ran out of input bytes before reading null terminator"); |
| } |
| |
| // `len` does not include null terminator, which would have been contained |
| // within the bounds of `sv`. |
| absl::string_view output = sv->substr(0, len); |
| sv->remove_prefix(len + 1); |
| return output; |
| } |
| |
| // Consumes the first `n` bytes of `sv`. |
| absl::StatusOr<absl::string_view> ConsumeBytes(absl::string_view* sv, |
| size_t n) { |
| if (sv->length() < n) { |
| return absl::OutOfRangeError(absl::StrCat("ran out of input bytes; ", n, |
| " bytes requested, ", |
| sv->length(), " remaining")); |
| } |
| |
| absl::string_view output = sv->substr(0, n); |
| sv->remove_prefix(n); |
| return output; |
| } |
| |
| absl::Status WriteRaw(absl::Cord content, riegeli::BackwardWriter* writer) { |
| WRITER_CALL_OR_RETURN(writer, Write(std::move(content))); |
| return absl::OkStatus(); |
| } |
| |
| absl::Status WriteNulTerminated(absl::Cord content, |
| riegeli::BackwardWriter* writer) { |
| for (absl::string_view chunk : content.Chunks()) { |
| if (absl::StrContains(chunk, '\0')) { |
| return absl::InvalidArgumentError( |
| "cannot create NUL-terminated string from content: content contains " |
| "NUL"); |
| } |
| } |
| WRITER_CALL_OR_RETURN(writer, WriteByte('\0')); |
| WRITER_CALL_OR_RETURN(writer, Write(std::move(content))); |
| return absl::OkStatus(); |
| } |
| |
| template <> |
| absl::Status WriteInteger(uint8_t n, riegeli::BackwardWriter* writer) { |
| WRITER_CALL_OR_RETURN(writer, WriteByte(n)); |
| return absl::OkStatus(); |
| } |
| template <> |
| absl::Status WriteInteger(uint32_t n, riegeli::BackwardWriter* writer) { |
| char buf[4]; |
| riegeli::WriteBigEndian<uint32_t>(n, buf); |
| WRITER_CALL_OR_RETURN(writer, Write(absl::string_view(buf, 4))); |
| return absl::OkStatus(); |
| } |
| template <> |
| absl::Status WriteInteger(uint64_t n, riegeli::BackwardWriter* writer) { |
| char buf[8]; |
| riegeli::WriteBigEndian<uint64_t>(n, buf); |
| WRITER_CALL_OR_RETURN(writer, Write(absl::string_view(buf, 8))); |
| return absl::OkStatus(); |
| } |
| |
| absl::Status WritePadding(uint64_t length, riegeli::BackwardWriter* writer) { |
| WRITER_CALL_OR_RETURN(writer, Write(riegeli::ByteFill(length))); |
| return absl::OkStatus(); |
| } |
| |
| } // namespace jumbf_internal |