blob: 01d2854b53206996270fb1a6ca30480edab073ea [file]
// 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