diff options
Diffstat (limited to 'contrib/protoc-bnet/BnetFileGenerator.cpp')
-rw-r--r-- | contrib/protoc-bnet/BnetFileGenerator.cpp | 692 |
1 files changed, 692 insertions, 0 deletions
diff --git a/contrib/protoc-bnet/BnetFileGenerator.cpp b/contrib/protoc-bnet/BnetFileGenerator.cpp new file mode 100644 index 00000000000..92f836fad31 --- /dev/null +++ b/contrib/protoc-bnet/BnetFileGenerator.cpp @@ -0,0 +1,692 @@ +// 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 "BnetFileGenerator.h" +#include <memory> +#include <set> + +#include "google/protobuf/compiler/cpp/cpp_enum.h" +#include "BnetServiceGenerator.h" +#include "BnetCodeGenerator.h" +#include "google/protobuf/compiler/cpp/cpp_extension.h" +#include "google/protobuf/compiler/cpp/cpp_helpers.h" +#include "google/protobuf/compiler/cpp/cpp_message.h" +#include "google/protobuf/compiler/cpp/cpp_field.h" +#include <google/protobuf/io/printer.h> +#include <google/protobuf/descriptor.pb.h> +#include "google/protobuf/stubs/strutil.h" + + +// =================================================================== + +BnetFileGenerator::BnetFileGenerator(const pb::FileDescriptor* file, const pbcpp::Options& options) + : file_(file), + message_generators_( + new pb::scoped_ptr<pbcpp::MessageGenerator>[file->message_type_count()]), + enum_generators_( + new pb::scoped_ptr<pbcpp::EnumGenerator>[file->enum_type_count()]), + service_generators_( + new pb::scoped_ptr<BnetServiceGenerator>[file->service_count()]), + extension_generators_( + new pb::scoped_ptr<pbcpp::ExtensionGenerator>[file->extension_count()]), + options_(options) +{ + + for (int i = 0; i < file->message_type_count(); i++) + { + message_generators_[i].reset( + new pbcpp::MessageGenerator(file->message_type(i), options)); + } + + for (int i = 0; i < file->enum_type_count(); i++) + { + enum_generators_[i].reset( + new pbcpp::EnumGenerator(file->enum_type(i), options)); + } + + for (int i = 0; i < file->service_count(); i++) + { + service_generators_[i].reset( + new BnetServiceGenerator(file->service(i), options)); + } + + for (int i = 0; i < file->extension_count(); i++) + { + extension_generators_[i].reset( + new pbcpp::ExtensionGenerator(file->extension(i), options)); + } + + pb::SplitStringUsing(file_->package(), ".", &package_parts_); +} + +BnetFileGenerator::~BnetFileGenerator() { } + +void BnetFileGenerator::GenerateHeader(pb::io::Printer* printer) +{ + std::string filename_identifier = pbcpp::FilenameIdentifier(file_->name()); + + // Generate top of header. + printer->Print( + "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" + "// source: $filename$\n" + "\n" + "#ifndef PROTOBUF_$filename_identifier$__INCLUDED\n" + "#define PROTOBUF_$filename_identifier$__INCLUDED\n" + "\n" + "#include <string>\n" + "\n", + "filename", file_->name(), + "filename_identifier", filename_identifier); + + printer->Print("#include <google/protobuf/stubs/common.h>\n\n"); + + // Verify the protobuf library header version is compatible with the protoc + // version before going any further. + printer->Print( + "#if GOOGLE_PROTOBUF_VERSION < $min_header_version$\n" + "#error This file was generated by a newer version of protoc which is\n" + "#error incompatible with your Protocol Buffer headers. Please update\n" + "#error your headers.\n" + "#endif\n" + "#if $protoc_version$ < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION\n" + "#error This file was generated by an older version of protoc which is\n" + "#error incompatible with your Protocol Buffer headers. Please\n" + "#error regenerate this file with a newer version of protoc.\n" + "#endif\n" + "\n", + "min_header_version", + pb::SimpleItoa(pb::internal::kMinHeaderVersionForProtoc), + "protoc_version", pb::SimpleItoa(GOOGLE_PROTOBUF_VERSION)); + + // OK, it's now safe to #include other files. + printer->Print("#include <google/protobuf/generated_message_util.h>\n"); + if (file_->message_type_count() > 0) + { + if (pbcpp::HasDescriptorMethods(file_)) + printer->Print("#include <google/protobuf/message.h>\n"); + else + printer->Print("#include <google/protobuf/message_lite.h>\n"); + } + + printer->Print( + "#include <google/protobuf/repeated_field.h>\n" + "#include <google/protobuf/extension_set.h>\n"); + + if (pbcpp::HasDescriptorMethods(file_) && pbcpp::HasEnumDefinitions(file_)) + printer->Print("#include <google/protobuf/generated_enum_reflection.h>\n"); + + if (pbcpp::UseUnknownFieldSet(file_) && file_->message_type_count() > 0) + { + printer->Print("#include <google/protobuf/unknown_field_set.h>\n"); + } + + std::set<std::string> public_import_names; + for (int i = 0; i < file_->public_dependency_count(); i++) + public_import_names.insert(file_->public_dependency(i)->name()); + + for (int i = 0; i < file_->dependency_count(); i++) + { + const std::string& name = file_->dependency(i)->name(); + bool public_import = (public_import_names.count(name) != 0); + + printer->Print( + "#include \"$dependency$.pb.h\"$iwyu$\n", + "dependency", pbcpp::StripProto(name), + "iwyu", (public_import) ? " // IWYU pragma: export" : ""); + } + + if (file_->service_count() > 0) + { + printer->Print("#include \"ServiceBase.h\"\n"); + printer->Print("#include \"MessageBuffer.h\"\n"); + printer->Print("#include <functional>\n"); + printer->Print("#include <type_traits>\n"); + } + + printer->Print("// @@protoc_insertion_point(includes)\n"); + + // Open namespace. + GenerateNamespaceOpeners(printer); + + // Forward-declare the AddDescriptors, AssignDescriptors, and ShutdownFile + // functions, so that we can declare them to be friends of each class. + printer->Print( + "\n" + "// Internal implementation detail -- do not call these.\n" + "void $dllexport_decl$ $adddescriptorsname$();\n", + "adddescriptorsname", pbcpp::GlobalAddDescriptorsName(file_->name()), + "dllexport_decl", options_.dllexport_decl); + + printer->Print( + // Note that we don't put dllexport_decl on these because they are only + // called by the .pb.cc file in which they are defined. + "void $assigndescriptorsname$();\n" + "void $shutdownfilename$();\n" + "\n", + "assigndescriptorsname", pbcpp::GlobalAssignDescriptorsName(file_->name()), + "shutdownfilename", pbcpp::GlobalShutdownFileName(file_->name())); + + // Generate forward declarations of classes. + for (int i = 0; i < file_->message_type_count(); i++) + message_generators_[i]->GenerateForwardDeclaration(printer); + + printer->Print("\n"); + + // Generate enum definitions. + for (int i = 0; i < file_->message_type_count(); i++) + message_generators_[i]->GenerateEnumDefinitions(printer); + + for (int i = 0; i < file_->enum_type_count(); i++) + enum_generators_[i]->GenerateDefinition(printer); + + printer->Print(pbcpp::kThickSeparator); + printer->Print("\n"); + + // Generate class definitions. + for (int i = 0; i < file_->message_type_count(); i++) + { + if (i > 0) + { + printer->Print("\n"); + printer->Print(pbcpp::kThinSeparator); + printer->Print("\n"); + } + message_generators_[i]->GenerateClassDefinition(printer); + } + + printer->Print("\n"); + printer->Print(pbcpp::kThickSeparator); + printer->Print("\n"); + + // Generate service definitions. + for (int i = 0; i < file_->service_count(); i++) + { + if (i > 0) + { + printer->Print("\n"); + printer->Print(pbcpp::kThinSeparator); + printer->Print("\n"); + } + service_generators_[i]->GenerateDeclarations(printer); + } + + printer->Print("\n"); + printer->Print(pbcpp::kThickSeparator); + printer->Print("\n"); + + // Declare extension identifiers. + for (int i = 0; i < file_->extension_count(); i++) + { + extension_generators_[i]->GenerateDeclaration(printer); + } + + printer->Print("\n"); + printer->Print(pbcpp::kThickSeparator); + printer->Print("\n"); + + + // Generate class inline methods. + for (int i = 0; i < file_->message_type_count(); i++) + { + if (i > 0) + { + printer->Print(pbcpp::kThinSeparator); + printer->Print("\n"); + } + message_generators_[i]->GenerateInlineMethods(printer); + } + + printer->Print( + "\n" + "// @@protoc_insertion_point(namespace_scope)\n"); + + // Close up namespace. + GenerateNamespaceClosers(printer); + + // Emit GetEnumDescriptor specializations into google::protobuf namespace: + if (pbcpp::HasDescriptorMethods(file_)) + { + // The SWIG conditional is to avoid a null-pointer dereference + // (bug 1984964) in swig-1.3.21 resulting from the following syntax: + // namespace X { void Y<Z::W>(); } + // which appears in GetEnumDescriptor() specializations. + printer->Print( + "\n" + "#ifndef SWIG\n" + "namespace google {\nnamespace protobuf {\n" + "\n"); + for (int i = 0; i < file_->message_type_count(); i++) + { + message_generators_[i]->GenerateGetEnumDescriptorSpecializations(printer); + } + for (int i = 0; i < file_->enum_type_count(); i++) + { + enum_generators_[i]->GenerateGetEnumDescriptorSpecializations(printer); + } + printer->Print( + "\n" + "} // namespace google\n} // namespace protobuf\n" + "#endif // SWIG\n"); + } + + printer->Print( + "\n" + "// @@protoc_insertion_point(global_scope)\n" + "\n"); + + printer->Print( + "#endif // PROTOBUF_$filename_identifier$__INCLUDED\n", + "filename_identifier", filename_identifier); +} + +void BnetFileGenerator::GenerateSource(pb::io::Printer* printer) +{ + printer->Print( + "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" + "// source: $filename$\n" + "\n" + + // The generated code calls accessors that might be deprecated. We don't + // want the compiler to warn in generated code. + "#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION\n" + "#include \"$basename$.pb.h\"\n" + "\n" + "#include <algorithm>\n" // for swap() + "#include <utility>\n" // for move() + "\n" + "#include <google/protobuf/stubs/common.h>\n" + "#include <google/protobuf/stubs/once.h>\n" + "#include <google/protobuf/io/coded_stream.h>\n" + "#include <google/protobuf/wire_format_lite_inl.h>\n", + "filename", file_->name(), + "basename", pbcpp::StripProto(file_->name())); + + // Unknown fields implementation in lite mode uses StringOutputStream + if (!pbcpp::UseUnknownFieldSet(file_) && file_->message_type_count() > 0) + { + printer->Print( + "#include <google/protobuf/io/zero_copy_stream_impl_lite.h>\n"); + } + + if (pbcpp::HasDescriptorMethods(file_)) + { + printer->Print( + "#include <google/protobuf/descriptor.h>\n" + "#include <google/protobuf/generated_message_reflection.h>\n" + "#include <google/protobuf/reflection_ops.h>\n" + "#include <google/protobuf/wire_format.h>\n"); + } + + printer->Print("#include \"Log.h\"\n"); + + if (file_->service_count() > 0) + printer->Print("#include \"BattlenetRpcErrorCodes.h\"\n"); + + printer->Print( + "// @@protoc_insertion_point(includes)\n"); + + GenerateNamespaceOpeners(printer); + + if (pbcpp::HasDescriptorMethods(file_)) + { + printer->Print( + "\n" + "namespace {\n" + "\n"); + for (int i = 0; i < file_->message_type_count(); i++) + { + message_generators_[i]->GenerateDescriptorDeclarations(printer); + } + for (int i = 0; i < file_->enum_type_count(); i++) + { + printer->Print( + "const ::google::protobuf::EnumDescriptor* $name$_descriptor_ = NULL;\n", + "name", pbcpp::ClassName(file_->enum_type(i), false)); + } + + for (int i = 0; i < file_->service_count(); i++) + { + printer->Print( + "const ::google::protobuf::ServiceDescriptor* $name$_descriptor_ = NULL;\n", + "name", file_->service(i)->name()); + } + + printer->Print( + "\n" + "} // namespace\n" + "\n"); + } + + // Define our externally-visible BuildDescriptors() function. (For the lite + // library, all this does is initialize default instances.) + GenerateBuildDescriptors(printer); + + // Generate enums. + for (int i = 0; i < file_->enum_type_count(); i++) + { + enum_generators_[i]->GenerateMethods(printer); + } + + // Generate classes. + for (int i = 0; i < file_->message_type_count(); i++) + { + printer->Print("\n"); + printer->Print(pbcpp::kThickSeparator); + printer->Print("\n"); + message_generators_[i]->GenerateClassMethods(printer); + } + + // Generate services. + for (int i = 0; i < file_->service_count(); i++) + { + if (i == 0) + printer->Print("\n"); + printer->Print(pbcpp::kThickSeparator); + printer->Print("\n"); + service_generators_[i]->GenerateImplementation(printer); + } + + // Define extensions. + for (int i = 0; i < file_->extension_count(); i++) + { + extension_generators_[i]->GenerateDefinition(printer); + } + + printer->Print( + "\n" + "// @@protoc_insertion_point(namespace_scope)\n"); + + GenerateNamespaceClosers(printer); + + printer->Print( + "\n" + "// @@protoc_insertion_point(global_scope)\n"); +} + +void BnetFileGenerator::GenerateBuildDescriptors(pb::io::Printer* printer) +{ + // AddDescriptors() is a file-level procedure which adds the encoded + // FileDescriptorProto for this .proto file to the global DescriptorPool for + // generated files (DescriptorPool::generated_pool()). It either runs at + // static initialization time (by default) or when default_instance() is + // called for the first time (in LITE_RUNTIME mode with + // GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER flag enabled). This procedure also + // constructs default instances and registers extensions. + // + // Its sibling, AssignDescriptors(), actually pulls the compiled + // FileDescriptor from the DescriptorPool and uses it to populate all of + // the global variables which store pointers to the descriptor objects. + // It also constructs the reflection objects. It is called the first time + // anyone calls descriptor() or GetReflection() on one of the types defined + // in the file. + + // In optimize_for = LITE_RUNTIME mode, we don't generate AssignDescriptors() + // and we only use AddDescriptors() to allocate default instances. + if (pbcpp::HasDescriptorMethods(file_)) + { + printer->Print( + "\n" + "void $assigndescriptorsname$() {\n", + "assigndescriptorsname", pbcpp::GlobalAssignDescriptorsName(file_->name())); + printer->Indent(); + + // Make sure the file has found its way into the pool. If a descriptor + // is requested *during* static init then AddDescriptors() may not have + // been called yet, so we call it manually. Note that it's fine if + // AddDescriptors() is called multiple times. + printer->Print( + "$adddescriptorsname$();\n", + "adddescriptorsname", pbcpp::GlobalAddDescriptorsName(file_->name())); + + // Get the file's descriptor from the pool. + printer->Print( + "const ::google::protobuf::FileDescriptor* file =\n" + " ::google::protobuf::DescriptorPool::generated_pool()->FindFileByName(\n" + " \"$filename$\");\n" + // Note that this GOOGLE_CHECK is necessary to prevent a warning about "file" + // being unused when compiling an empty .proto file. + "GOOGLE_CHECK(file != NULL);\n", + "filename", file_->name()); + + // Go through all the stuff defined in this file and generated code to + // assign the global descriptor pointers based on the file descriptor. + for (int i = 0; i < file_->message_type_count(); i++) + { + message_generators_[i]->GenerateDescriptorInitializer(printer, i); + } + for (int i = 0; i < file_->enum_type_count(); i++) + { + enum_generators_[i]->GenerateDescriptorInitializer(printer, i); + } + for (int i = 0; i < file_->service_count(); i++) + { + service_generators_[i]->GenerateDescriptorInitializer(printer, i); + } + + printer->Outdent(); + printer->Print( + "}\n" + "\n"); + + // --------------------------------------------------------------- + + // protobuf_AssignDescriptorsOnce(): The first time it is called, calls + // AssignDescriptors(). All later times, waits for the first call to + // complete and then returns. + printer->Print( + "namespace {\n" + "\n" + "GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AssignDescriptors_once_);\n" + "inline void protobuf_AssignDescriptorsOnce() {\n" + " ::google::protobuf::GoogleOnceInit(&protobuf_AssignDescriptors_once_,\n" + " &$assigndescriptorsname$);\n" + "}\n" + "\n", + "assigndescriptorsname", pbcpp::GlobalAssignDescriptorsName(file_->name())); + + // protobuf_RegisterTypes(): Calls + // MessageFactory::InternalRegisterGeneratedType() for each message type. + printer->Print( + "void protobuf_RegisterTypes(const ::std::string&) {\n" + " protobuf_AssignDescriptorsOnce();\n"); + printer->Indent(); + + for (int i = 0; i < file_->message_type_count(); i++) + { + message_generators_[i]->GenerateTypeRegistrations(printer); + } + + printer->Outdent(); + printer->Print( + "}\n" + "\n" + "} // namespace\n"); + } + + // ----------------------------------------------------------------- + + // ShutdownFile(): Deletes descriptors, default instances, etc. on shutdown. + printer->Print( + "\n" + "void $shutdownfilename$() {\n", + "shutdownfilename", pbcpp::GlobalShutdownFileName(file_->name())); + printer->Indent(); + + for (int i = 0; i < file_->message_type_count(); i++) + { + message_generators_[i]->GenerateShutdownCode(printer); + } + + printer->Outdent(); + printer->Print( + "}\n\n"); + + // ----------------------------------------------------------------- + + // Now generate the AddDescriptors() function. + pbcpp::PrintHandlingOptionalStaticInitializers( + file_, printer, + // With static initializers. + // Note that we don't need any special synchronization in the following code + // because it is called at static init time before any threads exist. + "void $adddescriptorsname$() {\n" + " static bool already_here = false;\n" + " if (already_here) return;\n" + " already_here = true;\n" + " GOOGLE_PROTOBUF_VERIFY_VERSION;\n" + "\n", + // Without. + "void $adddescriptorsname$_impl() {\n" + " GOOGLE_PROTOBUF_VERIFY_VERSION;\n" + "\n", + // Vars. + "adddescriptorsname", pbcpp::GlobalAddDescriptorsName(file_->name())); + + printer->Indent(); + + // Call the AddDescriptors() methods for all of our dependencies, to make + // sure they get added first. + for (int i = 0; i < file_->dependency_count(); i++) + { + const pb::FileDescriptor* dependency = file_->dependency(i); + // Print the namespace prefix for the dependency. + std::string add_desc_name = pbcpp::QualifiedFileLevelSymbol( + dependency->package(), pbcpp::GlobalAddDescriptorsName(dependency->name())); + // Call its AddDescriptors function. + printer->Print( + "$name$();\n", + "name", add_desc_name); + } + + if (pbcpp::HasDescriptorMethods(file_)) + { + // Embed the descriptor. We simply serialize the entire FileDescriptorProto + // and embed it as a string literal, which is parsed and built into real + // descriptors at initialization time. + pb::FileDescriptorProto file_proto; + file_->CopyTo(&file_proto); + std::string file_data; + file_proto.SerializeToString(&file_data); + + printer->Print( + "::google::protobuf::DescriptorPool::InternalAddGeneratedFile("); + + // Only write 40 bytes per line. + static const int kBytesPerLine = 40; + for (int i = 0; i < file_data.size(); i += kBytesPerLine) + { + printer->Print("\n \"$data$\"", + "data", + pbcpp::EscapeTrigraphs( + pb::CEscape(file_data.substr(i, kBytesPerLine)))); + } + printer->Print( + ", $size$);\n", + "size", pb::SimpleItoa(file_data.size())); + + // Call MessageFactory::InternalRegisterGeneratedFile(). + printer->Print( + "::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(\n" + " \"$filename$\", &protobuf_RegisterTypes);\n", + "filename", file_->name()); + } + + // Allocate and initialize default instances. This can't be done lazily + // since default instances are returned by simple accessors and are used with + // extensions. Speaking of which, we also register extensions at this time. + for (int i = 0; i < file_->message_type_count(); i++) + { + message_generators_[i]->GenerateDefaultInstanceAllocator(printer); + } + for (int i = 0; i < file_->extension_count(); i++) + { + extension_generators_[i]->GenerateRegistration(printer); + } + for (int i = 0; i < file_->message_type_count(); i++) + { + message_generators_[i]->GenerateDefaultInstanceInitializer(printer); + } + + printer->Print( + "::google::protobuf::internal::OnShutdown(&$shutdownfilename$);\n", + "shutdownfilename", pbcpp::GlobalShutdownFileName(file_->name())); + + printer->Outdent(); + printer->Print( + "}\n" + "\n"); + + pbcpp::PrintHandlingOptionalStaticInitializers( + file_, printer, + // With static initializers. + "// Force AddDescriptors() to be called at static initialization time.\n" + "struct StaticDescriptorInitializer_$filename$ {\n" + " StaticDescriptorInitializer_$filename$() {\n" + " $adddescriptorsname$();\n" + " }\n" + "} static_descriptor_initializer_$filename$_;\n", + // Without. + "GOOGLE_PROTOBUF_DECLARE_ONCE($adddescriptorsname$_once_);\n" + "void $adddescriptorsname$() {\n" + " ::google::protobuf::GoogleOnceInit(&$adddescriptorsname$_once_,\n" + " &$adddescriptorsname$_impl);\n" + "}\n", + // Vars. + "adddescriptorsname", pbcpp::GlobalAddDescriptorsName(file_->name()), + "filename", pbcpp::FilenameIdentifier(file_->name())); +} + +void BnetFileGenerator::GenerateNamespaceOpeners(pb::io::Printer* printer) +{ + if (package_parts_.size() > 0) + printer->Print("\n"); + + for (int i = 0; i < package_parts_.size(); i++) + { + printer->Print("namespace $part$ {\n", + "part", package_parts_[i]); + } +} + +void BnetFileGenerator::GenerateNamespaceClosers(pb::io::Printer* printer) +{ + if (package_parts_.size() > 0) + printer->Print("\n"); + + for (int i = package_parts_.size() - 1; i >= 0; i--) + { + printer->Print("} // namespace $part$\n", + "part", package_parts_[i]); + } +} |