blob: 4aef40dbb62d70a160782e919c192f1b562ebab1 [file] [log] [blame]
// Protocol Buffers - Google's data interchange format
// Copyright 2025 Google LLC. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include "upb/wire/test_util/wire_message.h"
#include <cstddef>
#include <cstdint>
#include <string>
#include <variant>
#include "absl/log/absl_check.h"
#include "upb/base/internal/endian.h"
#include "upb/wire/reader.h"
#include "upb/wire/types.h"
namespace upb {
namespace test {
using wire_types::Delimited;
using wire_types::Fixed32;
using wire_types::Fixed64;
using wire_types::Group;
using wire_types::Varint;
using wire_types::WireField;
using wire_types::WireMessage;
using wire_types::WireValue;
static void EncodeVarint(uint64_t val, int varint_length, std::string* str) {
uint64_t v = val;
std::string tmp;
for (int i = 0; v > 0 || i < varint_length; i++) {
if (i > 0) tmp.back() |= 0x80;
tmp.push_back(v & 0x7fU);
v >>= 7;
}
ABSL_CHECK_GE(tmp.size(), static_cast<size_t>(varint_length)) << val;
uint64_t parsed_val;
ABSL_CHECK_EQ(upb_WireReader_ReadVarint(tmp.data(), &parsed_val, nullptr),
tmp.data() + tmp.size())
<< "val=" << val << ", size=" << tmp.size()
<< ", varint_length=" << varint_length;
ABSL_CHECK_EQ(parsed_val, val);
str->append(tmp);
}
static void EncodeTag(const WireField& field, upb_WireType wire_type,
int min_tag_length, std::string* str) {
// Tags are 5 bytes max.
EncodeVarint(field.field_number << 3 | wire_type, min_tag_length, str);
}
static upb_WireType WireType(const WireValue& value) {
if (std::holds_alternative<Varint>(value)) {
return kUpb_WireType_Varint;
} else if (std::holds_alternative<Delimited>(value)) {
return kUpb_WireType_Delimited;
} else if (std::holds_alternative<Fixed64>(value)) {
return kUpb_WireType_64Bit;
} else if (std::holds_alternative<Fixed32>(value)) {
return kUpb_WireType_32Bit;
} else if (std::holds_alternative<Group>(value)) {
return kUpb_WireType_StartGroup;
}
ABSL_CHECK(false);
return kUpb_WireType_Varint;
}
std::string ToBinaryPayloadWithLongVarints(const WireValue& value,
int min_tag_length,
int min_val_varint_length) {
std::string ret;
if (const auto* val = std::get_if<Varint>(&value)) {
EncodeVarint(val->val, min_val_varint_length, &ret);
} else if (const auto* val = std::get_if<Delimited>(&value)) {
EncodeVarint(val->val.size(), min_val_varint_length, &ret);
ret.append(val->val);
} else if (const auto* val = std::get_if<Fixed64>(&value)) {
uint64_t swapped = upb_BigEndian64(val->val);
ret.append(reinterpret_cast<const char*>(&swapped), sizeof(swapped));
} else if (const auto* val = std::get_if<Fixed32>(&value)) {
uint32_t swapped = upb_BigEndian32(val->val);
ret.append(reinterpret_cast<const char*>(&swapped), sizeof(swapped));
} else if (const auto* val = std::get_if<Group>(&value)) {
ret.append(ToBinaryPayloadWithLongVarints(val->val, min_tag_length,
min_val_varint_length));
}
return ret;
}
std::string ToBinaryPayload(const WireValue& value) {
return ToBinaryPayloadWithLongVarints(value, 1, 1);
}
std::string ToBinaryPayloadWithLongVarints(const WireMessage& msg,
int min_tag_length,
int min_val_varint_length) {
std::string ret;
for (const auto& field : msg) {
EncodeTag(field, WireType(field.value), min_tag_length, &ret);
ret.append(ToBinaryPayloadWithLongVarints(field.value, min_tag_length,
min_val_varint_length));
if (std::holds_alternative<Group>(field.value)) {
EncodeTag(field, kUpb_WireType_EndGroup, min_tag_length, &ret);
}
}
return ret;
}
std::string ToBinaryPayload(const WireMessage& msg) {
return ToBinaryPayloadWithLongVarints(msg, 1, 1);
}
} // namespace test
} // namespace upb