// Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. // https://developers.google.com/protocol-buffers/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Author: kenton@google.com (Kenton Varda) // Based on original Protocol Buffers design by // Sanjay Ghemawat, Jeff Dean, and others. #include "google/protobuf/compiler/java/helpers.h" #include <algorithm> #include <cstdint> #include <limits> #include <vector> #include "y_absl/container/flat_hash_set.h" #include "y_absl/log/absl_check.h" #include "y_absl/log/absl_log.h" #include "y_absl/strings/ascii.h" #include "y_absl/strings/escaping.h" #include "y_absl/strings/str_cat.h" #include "y_absl/strings/str_format.h" #include "y_absl/strings/str_replace.h" #include "y_absl/strings/str_split.h" #include "y_absl/strings/string_view.h" #include "y_absl/strings/substitute.h" #include "google/protobuf/compiler/java/name_resolver.h" #include "google/protobuf/descriptor.pb.h" #include "google/protobuf/io/strtod.h" #include "google/protobuf/wire_format.h" // Must be last. #include "google/protobuf/port_def.inc" namespace google { namespace protobuf { namespace compiler { namespace java { using internal::WireFormat; using internal::WireFormatLite; const char kThickSeparator[] = "// ===================================================================\n"; const char kThinSeparator[] = "// -------------------------------------------------------------------\n"; void PrintGeneratedAnnotation(io::Printer* printer, char delimiter, y_absl::string_view annotation_file, Options options) { if (annotation_file.empty()) { return; } TProtoStringType ptemplate = "@javax.annotation.Generated(value=\"protoc\", comments=\"annotations:"; ptemplate.push_back(delimiter); ptemplate.append("annotation_file"); ptemplate.push_back(delimiter); ptemplate.append("\")\n"); printer->Print(ptemplate.c_str(), "annotation_file", annotation_file); } void PrintEnumVerifierLogic( io::Printer* printer, const FieldDescriptor* descriptor, const y_absl::flat_hash_map<y_absl::string_view, TProtoStringType>& variables, y_absl::string_view var_name, y_absl::string_view terminating_string, bool enforce_lite) { TProtoStringType enum_verifier_string = enforce_lite ? y_absl::StrCat(var_name, ".internalGetVerifier()") : y_absl::StrCat( "new com.google.protobuf.Internal.EnumVerifier() {\n" " @java.lang.Override\n" " public boolean isInRange(int number) {\n" " return ", var_name, ".forNumber(number) != null;\n" " }\n" " }"); printer->Print(variables, y_absl::StrCat(enum_verifier_string, terminating_string)); } TProtoStringType UnderscoresToCamelCase(y_absl::string_view input, bool cap_next_letter) { Y_ABSL_CHECK(!input.empty()); TProtoStringType result; // Note: I distrust ctype.h due to locales. for (int i = 0; i < input.size(); i++) { if ('a' <= input[i] && input[i] <= 'z') { if (cap_next_letter) { result += input[i] + ('A' - 'a'); } else { result += input[i]; } cap_next_letter = false; } else if ('A' <= input[i] && input[i] <= 'Z') { if (i == 0 && !cap_next_letter) { // Force first letter to lower-case unless explicitly told to // capitalize it. result += input[i] + ('a' - 'A'); } else { // Capital letters after the first are left as-is. result += input[i]; } cap_next_letter = false; } else if ('0' <= input[i] && input[i] <= '9') { result += input[i]; cap_next_letter = true; } else { cap_next_letter = true; } } // Add a trailing "_" if the name should be altered. if (input[input.size() - 1] == '#') { result += '_'; } return result; } TProtoStringType ToCamelCase(y_absl::string_view input, bool lower_first) { bool capitalize_next = !lower_first; TProtoStringType result; result.reserve(input.size()); for (char i : input) { if (i == '_') { capitalize_next = true; } else if (capitalize_next) { result.push_back(y_absl::ascii_toupper(i)); capitalize_next = false; } else { result.push_back(i); } } // Lower-case the first letter. if (lower_first && !result.empty()) { result[0] = y_absl::ascii_tolower(result[0]); } return result; } // Names that should be avoided as field names in Kotlin. // All Kotlin hard keywords are in this list. bool IsForbiddenKotlin(y_absl::string_view field_name) { static const auto& kKotlinForbiddenNames = *new y_absl::flat_hash_set<y_absl::string_view>({ "as", "as?", "break", "class", "continue", "do", "else", "false", "for", "fun", "if", "in", "!in", "interface", "is", "!is", "null", "object", "package", "return", "super", "this", "throw", "true", "try", "typealias", "typeof", "val", "var", "when", "while", }); return kKotlinForbiddenNames.contains(field_name); } TProtoStringType EscapeKotlinKeywords(TProtoStringType name) { std::vector<TProtoStringType> escaped_packages; std::vector<TProtoStringType> packages = y_absl::StrSplit(name, "."); // NOLINT for (y_absl::string_view package : packages) { if (IsForbiddenKotlin(package)) { escaped_packages.push_back(y_absl::StrCat("`", package, "`")); } else { escaped_packages.emplace_back(package); } } return y_absl::StrJoin(escaped_packages, "."); } TProtoStringType UniqueFileScopeIdentifier(const Descriptor* descriptor) { return y_absl::StrCat( "static_", y_absl::StrReplaceAll(descriptor->full_name(), {{".", "_"}})); } TProtoStringType CamelCaseFieldName(const FieldDescriptor* field) { TProtoStringType fieldName = UnderscoresToCamelCase(field); if ('0' <= fieldName[0] && fieldName[0] <= '9') { return y_absl::StrCat("_", fieldName); } return fieldName; } TProtoStringType FileClassName(const FileDescriptor* file, bool immutable) { return ClassNameResolver().GetFileClassName(file, immutable); } TProtoStringType JavaPackageToDir(TProtoStringType package_name) { TProtoStringType package_dir = y_absl::StrReplaceAll(package_name, {{".", "/"}}); if (!package_dir.empty()) y_absl::StrAppend(&package_dir, "/"); return package_dir; } TProtoStringType ExtraMessageInterfaces(const Descriptor* descriptor) { return y_absl::StrCat("// @@protoc_insertion_point(message_implements:", descriptor->full_name(), ")"); } TProtoStringType ExtraBuilderInterfaces(const Descriptor* descriptor) { return y_absl::StrCat("// @@protoc_insertion_point(builder_implements:", descriptor->full_name(), ")"); } TProtoStringType ExtraMessageOrBuilderInterfaces(const Descriptor* descriptor) { return y_absl::StrCat("// @@protoc_insertion_point(interface_extends:", descriptor->full_name(), ")"); } TProtoStringType FieldConstantName(const FieldDescriptor* field) { TProtoStringType name = y_absl::StrCat(field->name(), "_FIELD_NUMBER"); y_absl::AsciiStrToUpper(&name); return name; } FieldDescriptor::Type GetType(const FieldDescriptor* field) { return field->type(); } JavaType GetJavaType(const FieldDescriptor* field) { switch (GetType(field)) { case FieldDescriptor::TYPE_INT32: case FieldDescriptor::TYPE_UINT32: case FieldDescriptor::TYPE_SINT32: case FieldDescriptor::TYPE_FIXED32: case FieldDescriptor::TYPE_SFIXED32: return JAVATYPE_INT; case FieldDescriptor::TYPE_INT64: case FieldDescriptor::TYPE_UINT64: case FieldDescriptor::TYPE_SINT64: case FieldDescriptor::TYPE_FIXED64: case FieldDescriptor::TYPE_SFIXED64: return JAVATYPE_LONG; case FieldDescriptor::TYPE_FLOAT: return JAVATYPE_FLOAT; case FieldDescriptor::TYPE_DOUBLE: return JAVATYPE_DOUBLE; case FieldDescriptor::TYPE_BOOL: return JAVATYPE_BOOLEAN; case FieldDescriptor::TYPE_STRING: return JAVATYPE_STRING; case FieldDescriptor::TYPE_BYTES: return JAVATYPE_BYTES; case FieldDescriptor::TYPE_ENUM: return JAVATYPE_ENUM; case FieldDescriptor::TYPE_GROUP: case FieldDescriptor::TYPE_MESSAGE: return JAVATYPE_MESSAGE; // No default because we want the compiler to complain if any new // types are added. } Y_ABSL_LOG(FATAL) << "Can't get here."; return JAVATYPE_INT; } y_absl::string_view PrimitiveTypeName(JavaType type) { switch (type) { case JAVATYPE_INT: return "int"; case JAVATYPE_LONG: return "long"; case JAVATYPE_FLOAT: return "float"; case JAVATYPE_DOUBLE: return "double"; case JAVATYPE_BOOLEAN: return "boolean"; case JAVATYPE_STRING: return "java.lang.String"; case JAVATYPE_BYTES: return "com.google.protobuf.ByteString"; case JAVATYPE_ENUM: return {}; case JAVATYPE_MESSAGE: return {}; // No default because we want the compiler to complain if any new // JavaTypes are added. } Y_ABSL_LOG(FATAL) << "Can't get here."; return {}; } y_absl::string_view PrimitiveTypeName(const FieldDescriptor* descriptor) { return PrimitiveTypeName(GetJavaType(descriptor)); } y_absl::string_view BoxedPrimitiveTypeName(JavaType type) { switch (type) { case JAVATYPE_INT: return "java.lang.Integer"; case JAVATYPE_LONG: return "java.lang.Long"; case JAVATYPE_FLOAT: return "java.lang.Float"; case JAVATYPE_DOUBLE: return "java.lang.Double"; case JAVATYPE_BOOLEAN: return "java.lang.Boolean"; case JAVATYPE_STRING: return "java.lang.String"; case JAVATYPE_BYTES: return "com.google.protobuf.ByteString"; case JAVATYPE_ENUM: return {}; case JAVATYPE_MESSAGE: return {}; // No default because we want the compiler to complain if any new // JavaTypes are added. } Y_ABSL_LOG(FATAL) << "Can't get here."; return {}; } y_absl::string_view BoxedPrimitiveTypeName(const FieldDescriptor* descriptor) { return BoxedPrimitiveTypeName(GetJavaType(descriptor)); } y_absl::string_view KotlinTypeName(JavaType type) { switch (type) { case JAVATYPE_INT: return "kotlin.Int"; case JAVATYPE_LONG: return "kotlin.Long"; case JAVATYPE_FLOAT: return "kotlin.Float"; case JAVATYPE_DOUBLE: return "kotlin.Double"; case JAVATYPE_BOOLEAN: return "kotlin.Boolean"; case JAVATYPE_STRING: return "kotlin.String"; case JAVATYPE_BYTES: return "com.google.protobuf.ByteString"; case JAVATYPE_ENUM: return {}; case JAVATYPE_MESSAGE: return {}; // No default because we want the compiler to complain if any new // JavaTypes are added. } Y_ABSL_LOG(FATAL) << "Can't get here."; return {}; } TProtoStringType GetOneofStoredType(const FieldDescriptor* field) { const JavaType javaType = GetJavaType(field); switch (javaType) { case JAVATYPE_ENUM: return "java.lang.Integer"; case JAVATYPE_MESSAGE: return ClassNameResolver().GetClassName(field->message_type(), true); default: return TProtoStringType(BoxedPrimitiveTypeName(javaType)); } } y_absl::string_view FieldTypeName(FieldDescriptor::Type field_type) { switch (field_type) { case FieldDescriptor::TYPE_INT32: return "INT32"; case FieldDescriptor::TYPE_UINT32: return "UINT32"; case FieldDescriptor::TYPE_SINT32: return "SINT32"; case FieldDescriptor::TYPE_FIXED32: return "FIXED32"; case FieldDescriptor::TYPE_SFIXED32: return "SFIXED32"; case FieldDescriptor::TYPE_INT64: return "INT64"; case FieldDescriptor::TYPE_UINT64: return "UINT64"; case FieldDescriptor::TYPE_SINT64: return "SINT64"; case FieldDescriptor::TYPE_FIXED64: return "FIXED64"; case FieldDescriptor::TYPE_SFIXED64: return "SFIXED64"; case FieldDescriptor::TYPE_FLOAT: return "FLOAT"; case FieldDescriptor::TYPE_DOUBLE: return "DOUBLE"; case FieldDescriptor::TYPE_BOOL: return "BOOL"; case FieldDescriptor::TYPE_STRING: return "STRING"; case FieldDescriptor::TYPE_BYTES: return "BYTES"; case FieldDescriptor::TYPE_ENUM: return "ENUM"; case FieldDescriptor::TYPE_GROUP: return "GROUP"; case FieldDescriptor::TYPE_MESSAGE: return "MESSAGE"; // No default because we want the compiler to complain if any new // types are added. } Y_ABSL_LOG(FATAL) << "Can't get here."; return {}; } bool AllAscii(y_absl::string_view text) { for (int i = 0; i < text.size(); i++) { if ((text[i] & 0x80) != 0) { return false; } } return true; } TProtoStringType DefaultValue(const FieldDescriptor* field, bool immutable, ClassNameResolver* name_resolver, Options options) { // Switch on CppType since we need to know which default_value_* method // of FieldDescriptor to call. switch (field->cpp_type()) { case FieldDescriptor::CPPTYPE_INT32: return y_absl::StrCat(field->default_value_int32()); case FieldDescriptor::CPPTYPE_UINT32: // Need to print as a signed int since Java has no unsigned. return y_absl::StrCat(static_cast<arc_i32>(field->default_value_uint32())); case FieldDescriptor::CPPTYPE_INT64: return y_absl::StrCat(field->default_value_int64(), "L"); case FieldDescriptor::CPPTYPE_UINT64: return y_absl::StrCat(static_cast<arc_i64>(field->default_value_uint64())) + "L"; case FieldDescriptor::CPPTYPE_DOUBLE: { double value = field->default_value_double(); if (value == std::numeric_limits<double>::infinity()) { return "Double.POSITIVE_INFINITY"; } else if (value == -std::numeric_limits<double>::infinity()) { return "Double.NEGATIVE_INFINITY"; } else if (value != value) { return "Double.NaN"; } else { return y_absl::StrCat(io::SimpleDtoa(value), "D"); } } case FieldDescriptor::CPPTYPE_FLOAT: { float value = field->default_value_float(); if (value == std::numeric_limits<float>::infinity()) { return "Float.POSITIVE_INFINITY"; } else if (value == -std::numeric_limits<float>::infinity()) { return "Float.NEGATIVE_INFINITY"; } else if (value != value) { return "Float.NaN"; } else { return y_absl::StrCat(io::SimpleFtoa(value), "F"); } } case FieldDescriptor::CPPTYPE_BOOL: return field->default_value_bool() ? "true" : "false"; case FieldDescriptor::CPPTYPE_STRING: if (GetType(field) == FieldDescriptor::TYPE_BYTES) { if (field->has_default_value()) { // See comments in Internal.java for gory details. return y_absl::Substitute( "com.google.protobuf.Internal.bytesDefaultValue(\"$0\")", y_absl::CEscape(field->default_value_string())); } else { return "com.google.protobuf.ByteString.EMPTY"; } } else { if (AllAscii(field->default_value_string())) { // All chars are ASCII. In this case CEscape() works fine. return y_absl::StrCat( "\"", y_absl::CEscape(field->default_value_string()), "\""); } else { // See comments in Internal.java for gory details. return y_absl::Substitute( "com.google.protobuf.Internal.stringDefaultValue(\"$0\")", y_absl::CEscape(field->default_value_string())); } } case FieldDescriptor::CPPTYPE_ENUM: return y_absl::StrCat( name_resolver->GetClassName(field->enum_type(), immutable), ".", field->default_value_enum()->name()); case FieldDescriptor::CPPTYPE_MESSAGE: return y_absl::StrCat( name_resolver->GetClassName(field->message_type(), immutable), ".getDefaultInstance()"); // No default because we want the compiler to complain if any new // types are added. } Y_ABSL_LOG(FATAL) << "Can't get here."; return ""; } bool IsDefaultValueJavaDefault(const FieldDescriptor* field) { // Switch on CppType since we need to know which default_value_* method // of FieldDescriptor to call. switch (field->cpp_type()) { case FieldDescriptor::CPPTYPE_INT32: return field->default_value_int32() == 0; case FieldDescriptor::CPPTYPE_UINT32: return field->default_value_uint32() == 0; case FieldDescriptor::CPPTYPE_INT64: return field->default_value_int64() == 0L; case FieldDescriptor::CPPTYPE_UINT64: return field->default_value_uint64() == 0L; case FieldDescriptor::CPPTYPE_DOUBLE: return field->default_value_double() == 0.0; case FieldDescriptor::CPPTYPE_FLOAT: return field->default_value_float() == 0.0; case FieldDescriptor::CPPTYPE_BOOL: return field->default_value_bool() == false; case FieldDescriptor::CPPTYPE_ENUM: return field->default_value_enum()->number() == 0; case FieldDescriptor::CPPTYPE_STRING: case FieldDescriptor::CPPTYPE_MESSAGE: return false; // No default because we want the compiler to complain if any new // types are added. } Y_ABSL_LOG(FATAL) << "Can't get here."; return false; } bool IsByteStringWithCustomDefaultValue(const FieldDescriptor* field) { return GetJavaType(field) == JAVATYPE_BYTES && field->default_value_string() != ""; } constexpr y_absl::string_view bit_masks[] = { "0x00000001", "0x00000002", "0x00000004", "0x00000008", "0x00000010", "0x00000020", "0x00000040", "0x00000080", "0x00000100", "0x00000200", "0x00000400", "0x00000800", "0x00001000", "0x00002000", "0x00004000", "0x00008000", "0x00010000", "0x00020000", "0x00040000", "0x00080000", "0x00100000", "0x00200000", "0x00400000", "0x00800000", "0x01000000", "0x02000000", "0x04000000", "0x08000000", "0x10000000", "0x20000000", "0x40000000", "0x80000000", }; TProtoStringType GetBitFieldName(int index) { return y_absl::StrCat("bitField", index, "_"); } TProtoStringType GetBitFieldNameForBit(int bitIndex) { return GetBitFieldName(bitIndex / 32); } namespace { TProtoStringType GenerateGetBitInternal(y_absl::string_view prefix, int bitIndex) { TProtoStringType varName = y_absl::StrCat(prefix, GetBitFieldNameForBit(bitIndex)); int bitInVarIndex = bitIndex % 32; return y_absl::StrCat("((", varName, " & ", bit_masks[bitInVarIndex], ") != 0)"); } TProtoStringType GenerateSetBitInternal(y_absl::string_view prefix, int bitIndex) { TProtoStringType varName = y_absl::StrCat(prefix, GetBitFieldNameForBit(bitIndex)); int bitInVarIndex = bitIndex % 32; return y_absl::StrCat(varName, " |= ", bit_masks[bitInVarIndex]); } } // namespace TProtoStringType GenerateGetBit(int bitIndex) { return GenerateGetBitInternal("", bitIndex); } TProtoStringType GenerateSetBit(int bitIndex) { return GenerateSetBitInternal("", bitIndex); } TProtoStringType GenerateClearBit(int bitIndex) { TProtoStringType varName = GetBitFieldNameForBit(bitIndex); int bitInVarIndex = bitIndex % 32; return y_absl::StrCat(varName, " = (", varName, " & ~", bit_masks[bitInVarIndex], ")"); } TProtoStringType GenerateGetBitFromLocal(int bitIndex) { return GenerateGetBitInternal("from_", bitIndex); } TProtoStringType GenerateSetBitToLocal(int bitIndex) { return GenerateSetBitInternal("to_", bitIndex); } TProtoStringType GenerateGetBitMutableLocal(int bitIndex) { return GenerateGetBitInternal("mutable_", bitIndex); } TProtoStringType GenerateSetBitMutableLocal(int bitIndex) { return GenerateSetBitInternal("mutable_", bitIndex); } bool IsReferenceType(JavaType type) { switch (type) { case JAVATYPE_INT: return false; case JAVATYPE_LONG: return false; case JAVATYPE_FLOAT: return false; case JAVATYPE_DOUBLE: return false; case JAVATYPE_BOOLEAN: return false; case JAVATYPE_STRING: return true; case JAVATYPE_BYTES: return true; case JAVATYPE_ENUM: return true; case JAVATYPE_MESSAGE: return true; // No default because we want the compiler to complain if any new // JavaTypes are added. } Y_ABSL_LOG(FATAL) << "Can't get here."; return false; } y_absl::string_view GetCapitalizedType(const FieldDescriptor* field, bool immutable, Options options) { switch (GetType(field)) { case FieldDescriptor::TYPE_INT32: return "Int32"; case FieldDescriptor::TYPE_UINT32: return "UInt32"; case FieldDescriptor::TYPE_SINT32: return "SInt32"; case FieldDescriptor::TYPE_FIXED32: return "Fixed32"; case FieldDescriptor::TYPE_SFIXED32: return "SFixed32"; case FieldDescriptor::TYPE_INT64: return "Int64"; case FieldDescriptor::TYPE_UINT64: return "UInt64"; case FieldDescriptor::TYPE_SINT64: return "SInt64"; case FieldDescriptor::TYPE_FIXED64: return "Fixed64"; case FieldDescriptor::TYPE_SFIXED64: return "SFixed64"; case FieldDescriptor::TYPE_FLOAT: return "Float"; case FieldDescriptor::TYPE_DOUBLE: return "Double"; case FieldDescriptor::TYPE_BOOL: return "Bool"; case FieldDescriptor::TYPE_STRING: return "String"; case FieldDescriptor::TYPE_BYTES: { return "Bytes"; } case FieldDescriptor::TYPE_ENUM: return "Enum"; case FieldDescriptor::TYPE_GROUP: return "Group"; case FieldDescriptor::TYPE_MESSAGE: return "Message"; // No default because we want the compiler to complain if any new // types are added. } Y_ABSL_LOG(FATAL) << "Can't get here."; return {}; } // For encodings with fixed sizes, returns that size in bytes. Otherwise // returns -1. int FixedSize(FieldDescriptor::Type type) { switch (type) { case FieldDescriptor::TYPE_INT32: return -1; case FieldDescriptor::TYPE_INT64: return -1; case FieldDescriptor::TYPE_UINT32: return -1; case FieldDescriptor::TYPE_UINT64: return -1; case FieldDescriptor::TYPE_SINT32: return -1; case FieldDescriptor::TYPE_SINT64: return -1; case FieldDescriptor::TYPE_FIXED32: return WireFormatLite::kFixed32Size; case FieldDescriptor::TYPE_FIXED64: return WireFormatLite::kFixed64Size; case FieldDescriptor::TYPE_SFIXED32: return WireFormatLite::kSFixed32Size; case FieldDescriptor::TYPE_SFIXED64: return WireFormatLite::kSFixed64Size; case FieldDescriptor::TYPE_FLOAT: return WireFormatLite::kFloatSize; case FieldDescriptor::TYPE_DOUBLE: return WireFormatLite::kDoubleSize; case FieldDescriptor::TYPE_BOOL: return WireFormatLite::kBoolSize; case FieldDescriptor::TYPE_ENUM: return -1; case FieldDescriptor::TYPE_STRING: return -1; case FieldDescriptor::TYPE_BYTES: return -1; case FieldDescriptor::TYPE_GROUP: return -1; case FieldDescriptor::TYPE_MESSAGE: return -1; // No default because we want the compiler to complain if any new // types are added. } Y_ABSL_LOG(FATAL) << "Can't get here."; return -1; } // Sort the fields of the given Descriptor by number into a new[]'d array // and return it. The caller should delete the returned array. const FieldDescriptor** SortFieldsByNumber(const Descriptor* descriptor) { const FieldDescriptor** fields = new const FieldDescriptor*[descriptor->field_count()]; for (int i = 0; i < descriptor->field_count(); i++) { fields[i] = descriptor->field(i); } std::sort(fields, fields + descriptor->field_count(), FieldOrderingByNumber()); return fields; } // Returns true if the message type has any required fields. If it doesn't, // we can optimize out calls to its isInitialized() method. // // already_seen is used to avoid checking the same type multiple times // (and also to protect against recursion). bool HasRequiredFields(const Descriptor* type, y_absl::flat_hash_set<const Descriptor*>* already_seen) { if (already_seen->count(type) > 0) { // The type is already in cache. This means that either: // a. The type has no required fields. // b. We are in the midst of checking if the type has required fields, // somewhere up the stack. In this case, we know that if the type // has any required fields, they'll be found when we return to it, // and the whole call to HasRequiredFields() will return true. // Therefore, we don't have to check if this type has required fields // here. return false; } already_seen->insert(type); // If the type has extensions, an extension with message type could contain // required fields, so we have to be conservative and assume such an // extension exists. if (type->extension_range_count() > 0) return true; for (int i = 0; i < type->field_count(); i++) { const FieldDescriptor* field = type->field(i); if (field->is_required()) { return true; } if (GetJavaType(field) == JAVATYPE_MESSAGE) { if (HasRequiredFields(field->message_type(), already_seen)) { return true; } } } return false; } bool HasRequiredFields(const Descriptor* type) { y_absl::flat_hash_set<const Descriptor*> already_seen; return HasRequiredFields(type, &already_seen); } bool HasRepeatedFields(const Descriptor* descriptor) { for (int i = 0; i < descriptor->field_count(); ++i) { const FieldDescriptor* field = descriptor->field(i); if (field->is_repeated()) { return true; } } return false; } // Encode an unsigned 32-bit value into a sequence of UTF-16 characters. // // If the value is in [0x0000, 0xD7FF], we encode it with a single character // with the same numeric value. // // If the value is larger than 0xD7FF, we encode its lowest 13 bits into a // character in the range [0xE000, 0xFFFF] by combining these 13 bits with // 0xE000 using logic-or. Then we shift the value to the right by 13 bits, and // encode the remaining value by repeating this same process until we get to // a value in [0x0000, 0xD7FF] where we will encode it using a character with // the same numeric value. // // Note that we only use code points in [0x0000, 0xD7FF] and [0xE000, 0xFFFF]. // There will be no surrogate pairs in the encoded character sequence. void WriteUInt32ToUtf16CharSequence(arc_ui32 number, std::vector<uint16_t>* output) { // For values in [0x0000, 0xD7FF], only use one char to encode it. if (number < 0xD800) { output->push_back(static_cast<uint16_t>(number)); return; } // Encode into multiple chars. All except the last char will be in the range // [0xE000, 0xFFFF], and the last char will be in the range [0x0000, 0xD7FF]. // Note that we don't use any value in range [0xD800, 0xDFFF] because they // have to come in pairs and the encoding is just more space-efficient w/o // them. while (number >= 0xD800) { // [0xE000, 0xFFFF] can represent 13 bits of info. output->push_back(static_cast<uint16_t>(0xE000 | (number & 0x1FFF))); number >>= 13; } output->push_back(static_cast<uint16_t>(number)); } int GetExperimentalJavaFieldTypeForSingular(const FieldDescriptor* field) { // j/c/g/protobuf/FieldType.java lists field types in a slightly different // order from FieldDescriptor::Type so we can't do a simple cast. // // TODO(xiaofeng): Make j/c/g/protobuf/FieldType.java follow the same order. int result = field->type(); if (result == FieldDescriptor::TYPE_GROUP) { return 17; } else if (result < FieldDescriptor::TYPE_GROUP) { return result - 1; } else { return result - 2; } } int GetExperimentalJavaFieldTypeForRepeated(const FieldDescriptor* field) { if (field->type() == FieldDescriptor::TYPE_GROUP) { return 49; } else { return GetExperimentalJavaFieldTypeForSingular(field) + 18; } } int GetExperimentalJavaFieldTypeForPacked(const FieldDescriptor* field) { int result = field->type(); if (result < FieldDescriptor::TYPE_STRING) { return result + 34; } else if (result > FieldDescriptor::TYPE_BYTES) { return result + 30; } else { Y_ABSL_LOG(FATAL) << field->full_name() << " can't be packed."; return 0; } } int GetExperimentalJavaFieldType(const FieldDescriptor* field) { static const int kMapFieldType = 50; static const int kOneofFieldTypeOffset = 51; static const int kRequiredBit = 0x100; static const int kUtf8CheckBit = 0x200; static const int kCheckInitialized = 0x400; static const int kMapWithProto2EnumValue = 0x800; static const int kHasHasBit = 0x1000; int extra_bits = field->is_required() ? kRequiredBit : 0; if (field->type() == FieldDescriptor::TYPE_STRING && CheckUtf8(field)) { extra_bits |= kUtf8CheckBit; } if (field->is_required() || (GetJavaType(field) == JAVATYPE_MESSAGE && HasRequiredFields(field->message_type()))) { extra_bits |= kCheckInitialized; } if (HasHasbit(field)) { extra_bits |= kHasHasBit; } if (field->is_map()) { if (!SupportUnknownEnumValue(field)) { const FieldDescriptor* value = field->message_type()->map_value(); if (GetJavaType(value) == JAVATYPE_ENUM) { extra_bits |= kMapWithProto2EnumValue; } } return kMapFieldType | extra_bits; } else if (field->is_packed()) { return GetExperimentalJavaFieldTypeForPacked(field); } else if (field->is_repeated()) { return GetExperimentalJavaFieldTypeForRepeated(field) | extra_bits; } else if (IsRealOneof(field)) { return (GetExperimentalJavaFieldTypeForSingular(field) + kOneofFieldTypeOffset) | extra_bits; } else { return GetExperimentalJavaFieldTypeForSingular(field) | extra_bits; } } // Escape a UTF-16 character to be embedded in a Java string. void EscapeUtf16ToString(uint16_t code, TProtoStringType* output) { if (code == '\t') { output->append("\\t"); } else if (code == '\b') { output->append("\\b"); } else if (code == '\n') { output->append("\\n"); } else if (code == '\r') { output->append("\\r"); } else if (code == '\f') { output->append("\\f"); } else if (code == '\'') { output->append("\\'"); } else if (code == '\"') { output->append("\\\""); } else if (code == '\\') { output->append("\\\\"); } else if (code >= 0x20 && code <= 0x7f) { output->push_back(static_cast<char>(code)); } else { output->append(y_absl::StrFormat("\\u%04x", code)); } } } // namespace java } // namespace compiler } // namespace protobuf } // namespace google #include "google/protobuf/port_undef.inc"