remove shaded protobuf-java v2 jar (pekko-protobuf) (#489)
* remove protobuf v2 support * Update OSGi.scala * ci issue * remove refs to pekko-protobuf classes * fix comment * update comments * Update Serialization.scala
This commit is contained in:
parent
c18a81a243
commit
964dcf53eb
58 changed files with 6 additions and 49069 deletions
2
.github/workflows/scala3-build.yml
vendored
2
.github/workflows/scala3-build.yml
vendored
|
|
@ -30,7 +30,7 @@ jobs:
|
|||
- serialization-jackson/test
|
||||
- stream/test stream-testkit/test stream-tests/test stream-typed/test
|
||||
- stream-tests-tck/test
|
||||
- remote/test remote-tests/test protobuf/test protobuf-v3/test
|
||||
- remote/test remote-tests/test protobuf-v3/test
|
||||
fail-fast: true
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
|
|
|||
2
.github/workflows/scala3-compile.yml
vendored
2
.github/workflows/scala3-compile.yml
vendored
|
|
@ -28,7 +28,7 @@ jobs:
|
|||
- serialization-jackson/Test/compile
|
||||
- stream/Test/compile stream-testkit/Test/compile stream-tests/Test/compile stream-typed/Test/compile
|
||||
- stream-tests-tck/Test/compile
|
||||
- remote/Test/compile remote-tests/Test/compile protobuf/Test/compile protobuf-v3/Test/compile
|
||||
- remote/Test/compile remote-tests/Test/compile protobuf-v3/Test/compile
|
||||
fail-fast: true
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
|
|
|||
6
LICENSE
6
LICENSE
|
|
@ -353,12 +353,6 @@ Copyright 2014 - 2016 Real Logic Ltd.
|
|||
|
||||
---------------
|
||||
|
||||
pekko-protobuf contains the sources of Google protobuf 2.5.0 runtime support,
|
||||
moved into the source package `org.apache.pekko.protobuf` so as to avoid version conflicts.
|
||||
For license information see COPYING.protobuf
|
||||
|
||||
---------------
|
||||
|
||||
pekko-persistence-typed contains AuctionEntity.java in its test source.
|
||||
This code is derived from a class in Lagom <https://github.com/lagom>,
|
||||
licensed under the Apache 2.0 license.
|
||||
|
|
|
|||
|
|
@ -800,7 +800,6 @@ pekko {
|
|||
"com.google.protobuf.GeneratedMessage",
|
||||
"com.google.protobuf.GeneratedMessageV3",
|
||||
"scalapb.GeneratedMessageCompanion",
|
||||
"org.apache.pekko.protobuf.GeneratedMessage",
|
||||
"org.apache.pekko.protobufv3.internal.GeneratedMessageV3"
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -447,7 +447,7 @@ class Serialization(val system: ExtendedActorSystem) extends Extension {
|
|||
private[pekko] val bindings: immutable.Seq[ClassSerializer] = {
|
||||
val fromConfig = for {
|
||||
(className: String, alias: String) <- settings.SerializationBindings
|
||||
if alias != "none" && checkGoogleProtobuf(className) && checkPekkoProtobuf(className)
|
||||
if alias != "none" && checkGoogleProtobuf(className)
|
||||
} yield (system.dynamicAccess.getClassFor[Any](className).get, serializers(alias))
|
||||
|
||||
val fromSettings = serializerDetails.flatMap { detail =>
|
||||
|
|
@ -484,10 +484,6 @@ class Serialization(val system: ExtendedActorSystem) extends Extension {
|
|||
// include "com.google.protobuf.GeneratedMessage" = proto in configured serialization-bindings.
|
||||
private def checkGoogleProtobuf(className: String): Boolean = checkClass("com.google.protobuf", className)
|
||||
|
||||
// pekko-protobuf is now not a dependency of remote so only load if user has explicitly added it
|
||||
// remove in v1.1
|
||||
private def checkPekkoProtobuf(className: String): Boolean = checkClass("org.apache.pekko.protobuf", className)
|
||||
|
||||
private def checkClass(prefix: String, className: String): Boolean =
|
||||
!className.startsWith(prefix) || system.dynamicAccess.getClassFor[Any](className).isSuccess
|
||||
|
||||
|
|
|
|||
11
build.sbt
11
build.sbt
|
|
@ -84,7 +84,6 @@ lazy val userProjects: Seq[ProjectReference] = List[ProjectReference](
|
|||
persistenceQuery,
|
||||
persistenceTyped,
|
||||
persistenceTestkit,
|
||||
protobuf,
|
||||
protobufV3,
|
||||
pki,
|
||||
remote,
|
||||
|
|
@ -116,7 +115,6 @@ lazy val root = Project(id = "pekko", base = file("."))
|
|||
UnidocRoot.autoImport.unidocRootIgnoreProjects := Seq(
|
||||
remoteTests,
|
||||
benchJmh,
|
||||
protobuf,
|
||||
protobufV3,
|
||||
pekkoScalaNightly,
|
||||
docs,
|
||||
|
|
@ -362,14 +360,6 @@ lazy val persistenceTypedTests = pekkoModule("persistence-typed-tests")
|
|||
.disablePlugins(MimaPlugin)
|
||||
.enablePlugins(NoPublish)
|
||||
|
||||
lazy val protobuf = pekkoModule("protobuf")
|
||||
.settings(OSGi.protobuf)
|
||||
.settings(AutomaticModuleName.settings("pekko.protobuf"))
|
||||
.settings(AddMetaInfLicenseFiles.protobufSettings)
|
||||
.enablePlugins(ScaladocNoVerificationOfDiagrams)
|
||||
.disablePlugins(MimaPlugin)
|
||||
.settings(autoScalaLibrary := false) // Pure java project
|
||||
|
||||
lazy val protobufV3 = pekkoModule("protobuf-v3")
|
||||
.settings(OSGi.protobufV3)
|
||||
.settings(AutomaticModuleName.settings("pekko.protobuf.v3"))
|
||||
|
|
@ -413,7 +403,6 @@ lazy val remote =
|
|||
actor,
|
||||
stream,
|
||||
pki,
|
||||
protobuf % "test",
|
||||
actorTests % "test->test",
|
||||
testkit % "test->test",
|
||||
streamTestkit % "test",
|
||||
|
|
|
|||
|
|
@ -1,207 +0,0 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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
|
||||
|
||||
http://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.
|
||||
|
||||
---------------
|
||||
|
||||
pekko-protobuf contains the sources of Google protobuf 2.5.0 runtime support,
|
||||
moved into the source package `org.apache.pekko.protobuf` so as to avoid version conflicts.
|
||||
For license information see COPYING.protobuf
|
||||
|
|
@ -64,19 +64,6 @@ object AddMetaInfLicenseFiles extends AutoPlugin {
|
|||
apacheSonatypeLicenseFile := baseDir.value / "legal" / "pekko-remote-jar-license.txt",
|
||||
apacheSonatypeNoticeFile := baseDir.value / "legal" / "pekko-remote-jar-notice.txt")
|
||||
|
||||
/**
|
||||
* Settings specific for Pekko protobuf subproject which requires a different license file
|
||||
* as well as an additional "COPYING.protobuf" file.
|
||||
*/
|
||||
lazy val protobufSettings = Seq(
|
||||
apacheSonatypeLicenseFile := baseDir.value / "legal" / "pekko-protobuf-jar-license.txt") ++ inConfig(Compile)(Seq(
|
||||
resourceGenerators += {
|
||||
Def.task {
|
||||
List(
|
||||
ApacheSonatypePlugin.addFileToMetaInf(resourceManaged.value, baseDir.value / "COPYING.protobuf"))
|
||||
}
|
||||
}))
|
||||
|
||||
/**
|
||||
* Settings specific for Pekko protobuf-v3 subproject which requires a different license file
|
||||
* as well as an additional "COPYING.protobuf" file.
|
||||
|
|
|
|||
|
|
@ -70,8 +70,6 @@ object OSGi {
|
|||
|
||||
val osgi = exports(Seq("org.apache.pekko.osgi.*"))
|
||||
|
||||
val protobuf = exports(Seq("org.apache.pekko.protobuf.*"))
|
||||
|
||||
val protobufV3 = osgiSettings ++ Seq(
|
||||
OsgiKeys.importPackage := Seq(
|
||||
"!sun.misc",
|
||||
|
|
|
|||
|
|
@ -32,8 +32,6 @@ object PekkoDisciplinePlugin extends AutoPlugin {
|
|||
"pekko-actor-typed-tests",
|
||||
// references to deprecated PARSER fields in generated message formats?
|
||||
"pekko-cluster-typed",
|
||||
// use of deprecated org.apache.pekko.protobuf.GeneratedMessage
|
||||
"pekko-protobuf",
|
||||
"pekko-protobuf-v3",
|
||||
// references to deprecated PARSER fields in generated message formats?
|
||||
"pekko-remote",
|
||||
|
|
|
|||
|
|
@ -1,943 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
import org.apache.pekko.protobuf.Descriptors.Descriptor;
|
||||
import org.apache.pekko.protobuf.Descriptors.FieldDescriptor;
|
||||
import org.apache.pekko.protobuf.GeneratedMessage.ExtendableBuilder;
|
||||
import org.apache.pekko.protobuf.Internal.EnumLite;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A partial implementation of the {@link Message} interface which implements
|
||||
* as many methods of that interface as possible in terms of other methods.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public abstract class AbstractMessage extends AbstractMessageLite
|
||||
implements Message {
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean isInitialized() {
|
||||
// Check that all required fields are present.
|
||||
for (final FieldDescriptor field : getDescriptorForType().getFields()) {
|
||||
if (field.isRequired()) {
|
||||
if (!hasField(field)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that embedded messages are initialized.
|
||||
for (final Map.Entry<FieldDescriptor, Object> entry :
|
||||
getAllFields().entrySet()) {
|
||||
final FieldDescriptor field = entry.getKey();
|
||||
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
if (field.isRepeated()) {
|
||||
for (final Message element : (List<Message>) entry.getValue()) {
|
||||
if (!element.isInitialized()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!((Message) entry.getValue()).isInitialized()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<String> findInitializationErrors() {
|
||||
return Builder.findMissingFields(this);
|
||||
}
|
||||
|
||||
public String getInitializationErrorString() {
|
||||
return delimitWithCommas(findInitializationErrors());
|
||||
}
|
||||
|
||||
private static String delimitWithCommas(List<String> parts) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (String part : parts) {
|
||||
if (result.length() > 0) {
|
||||
result.append(", ");
|
||||
}
|
||||
result.append(part);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
return TextFormat.printToString(this);
|
||||
}
|
||||
|
||||
public void writeTo(final CodedOutputStream output) throws IOException {
|
||||
final boolean isMessageSet =
|
||||
getDescriptorForType().getOptions().getMessageSetWireFormat();
|
||||
|
||||
for (final Map.Entry<FieldDescriptor, Object> entry :
|
||||
getAllFields().entrySet()) {
|
||||
final FieldDescriptor field = entry.getKey();
|
||||
final Object value = entry.getValue();
|
||||
if (isMessageSet && field.isExtension() &&
|
||||
field.getType() == FieldDescriptor.Type.MESSAGE &&
|
||||
!field.isRepeated()) {
|
||||
output.writeMessageSetExtension(field.getNumber(), (Message) value);
|
||||
} else {
|
||||
FieldSet.writeField(field, value, output);
|
||||
}
|
||||
}
|
||||
|
||||
final UnknownFieldSet unknownFields = getUnknownFields();
|
||||
if (isMessageSet) {
|
||||
unknownFields.writeAsMessageSetTo(output);
|
||||
} else {
|
||||
unknownFields.writeTo(output);
|
||||
}
|
||||
}
|
||||
|
||||
private int memoizedSize = -1;
|
||||
|
||||
public int getSerializedSize() {
|
||||
int size = memoizedSize;
|
||||
if (size != -1) {
|
||||
return size;
|
||||
}
|
||||
|
||||
size = 0;
|
||||
final boolean isMessageSet =
|
||||
getDescriptorForType().getOptions().getMessageSetWireFormat();
|
||||
|
||||
for (final Map.Entry<FieldDescriptor, Object> entry :
|
||||
getAllFields().entrySet()) {
|
||||
final FieldDescriptor field = entry.getKey();
|
||||
final Object value = entry.getValue();
|
||||
if (isMessageSet && field.isExtension() &&
|
||||
field.getType() == FieldDescriptor.Type.MESSAGE &&
|
||||
!field.isRepeated()) {
|
||||
size += CodedOutputStream.computeMessageSetExtensionSize(
|
||||
field.getNumber(), (Message) value);
|
||||
} else {
|
||||
size += FieldSet.computeFieldSize(field, value);
|
||||
}
|
||||
}
|
||||
|
||||
final UnknownFieldSet unknownFields = getUnknownFields();
|
||||
if (isMessageSet) {
|
||||
size += unknownFields.getSerializedSizeAsMessageSet();
|
||||
} else {
|
||||
size += unknownFields.getSerializedSize();
|
||||
}
|
||||
|
||||
memoizedSize = size;
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (other == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof Message)) {
|
||||
return false;
|
||||
}
|
||||
final Message otherMessage = (Message) other;
|
||||
if (getDescriptorForType() != otherMessage.getDescriptorForType()) {
|
||||
return false;
|
||||
}
|
||||
return getAllFields().equals(otherMessage.getAllFields()) &&
|
||||
getUnknownFields().equals(otherMessage.getUnknownFields());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 41;
|
||||
hash = (19 * hash) + getDescriptorForType().hashCode();
|
||||
hash = hashFields(hash, getAllFields());
|
||||
hash = (29 * hash) + getUnknownFields().hashCode();
|
||||
return hash;
|
||||
}
|
||||
|
||||
/** Get a hash code for given fields and values, using the given seed. */
|
||||
@SuppressWarnings("unchecked")
|
||||
protected int hashFields(int hash, Map<FieldDescriptor, Object> map) {
|
||||
for (Map.Entry<FieldDescriptor, Object> entry : map.entrySet()) {
|
||||
FieldDescriptor field = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
hash = (37 * hash) + field.getNumber();
|
||||
if (field.getType() != FieldDescriptor.Type.ENUM){
|
||||
hash = (53 * hash) + value.hashCode();
|
||||
} else if (field.isRepeated()) {
|
||||
List<? extends EnumLite> list = (List<? extends EnumLite>) value;
|
||||
hash = (53 * hash) + hashEnumList(list);
|
||||
} else {
|
||||
hash = (53 * hash) + hashEnum((EnumLite) value);
|
||||
}
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for implementing {@link Message#hashCode()}.
|
||||
* @see Boolean#hashCode()
|
||||
*/
|
||||
protected static int hashLong(long n) {
|
||||
return (int) (n ^ (n >>> 32));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for implementing {@link Message#hashCode()}.
|
||||
* @see Boolean#hashCode()
|
||||
*/
|
||||
protected static int hashBoolean(boolean b) {
|
||||
return b ? 1231 : 1237;
|
||||
}
|
||||
|
||||
/**
|
||||
* Package private helper method for AbstractParser to create
|
||||
* UninitializedMessageException with missing field information.
|
||||
*/
|
||||
@Override
|
||||
UninitializedMessageException newUninitializedMessageException() {
|
||||
return Builder.newUninitializedMessageException(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for implementing {@link Message#hashCode()}.
|
||||
* <p>
|
||||
* This is needed because {@link java.lang.Enum#hashCode()} is final, but we
|
||||
* need to use the field number as the hash code to ensure compatibility
|
||||
* between statically and dynamically generated enum objects.
|
||||
*/
|
||||
protected static int hashEnum(EnumLite e) {
|
||||
return e.getNumber();
|
||||
}
|
||||
|
||||
/** Helper method for implementing {@link Message#hashCode()}. */
|
||||
protected static int hashEnumList(List<? extends EnumLite> list) {
|
||||
int hash = 1;
|
||||
for (EnumLite e : list) {
|
||||
hash = 31 * hash + hashEnum(e);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
|
||||
/**
|
||||
* A partial implementation of the {@link Message.Builder} interface which
|
||||
* implements as many methods of that interface as possible in terms of
|
||||
* other methods.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static abstract class Builder<BuilderType extends Builder>
|
||||
extends AbstractMessageLite.Builder<BuilderType>
|
||||
implements Message.Builder {
|
||||
// The compiler produces an error if this is not declared explicitly.
|
||||
@Override
|
||||
public abstract BuilderType clone();
|
||||
|
||||
public BuilderType clear() {
|
||||
for (final Map.Entry<FieldDescriptor, Object> entry :
|
||||
getAllFields().entrySet()) {
|
||||
clearField(entry.getKey());
|
||||
}
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
public List<String> findInitializationErrors() {
|
||||
return findMissingFields(this);
|
||||
}
|
||||
|
||||
public String getInitializationErrorString() {
|
||||
return delimitWithCommas(findInitializationErrors());
|
||||
}
|
||||
|
||||
public BuilderType mergeFrom(final Message other) {
|
||||
if (other.getDescriptorForType() != getDescriptorForType()) {
|
||||
throw new IllegalArgumentException(
|
||||
"mergeFrom(Message) can only merge messages of the same type.");
|
||||
}
|
||||
|
||||
// Note: We don't attempt to verify that other's fields have valid
|
||||
// types. Doing so would be a losing battle. We'd have to verify
|
||||
// all sub-messages as well, and we'd have to make copies of all of
|
||||
// them to insure that they don't change after verification (since
|
||||
// the Message interface itself cannot enforce immutability of
|
||||
// implementations).
|
||||
// TODO(kenton): Provide a function somewhere called makeDeepCopy()
|
||||
// which allows people to make secure deep copies of messages.
|
||||
|
||||
for (final Map.Entry<FieldDescriptor, Object> entry :
|
||||
other.getAllFields().entrySet()) {
|
||||
final FieldDescriptor field = entry.getKey();
|
||||
if (field.isRepeated()) {
|
||||
for (final Object element : (List)entry.getValue()) {
|
||||
addRepeatedField(field, element);
|
||||
}
|
||||
} else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
final Message existingValue = (Message)getField(field);
|
||||
if (existingValue == existingValue.getDefaultInstanceForType()) {
|
||||
setField(field, entry.getValue());
|
||||
} else {
|
||||
setField(field,
|
||||
existingValue.newBuilderForType()
|
||||
.mergeFrom(existingValue)
|
||||
.mergeFrom((Message)entry.getValue())
|
||||
.build());
|
||||
}
|
||||
} else {
|
||||
setField(field, entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
mergeUnknownFields(other.getUnknownFields());
|
||||
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(final CodedInputStream input)
|
||||
throws IOException {
|
||||
return mergeFrom(input, ExtensionRegistry.getEmptyRegistry());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(
|
||||
final CodedInputStream input,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException {
|
||||
final UnknownFieldSet.Builder unknownFields =
|
||||
UnknownFieldSet.newBuilder(getUnknownFields());
|
||||
while (true) {
|
||||
final int tag = input.readTag();
|
||||
if (tag == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!mergeFieldFrom(input, unknownFields, extensionRegistry,
|
||||
getDescriptorForType(), this, null, tag)) {
|
||||
// end group tag
|
||||
break;
|
||||
}
|
||||
}
|
||||
setUnknownFields(unknownFields.build());
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
/** helper method to handle {@code builder} and {@code extensions}. */
|
||||
private static void addRepeatedField(
|
||||
Message.Builder builder,
|
||||
FieldSet<FieldDescriptor> extensions,
|
||||
FieldDescriptor field,
|
||||
Object value) {
|
||||
if (builder != null) {
|
||||
builder.addRepeatedField(field, value);
|
||||
} else {
|
||||
extensions.addRepeatedField(field, value);
|
||||
}
|
||||
}
|
||||
|
||||
/** helper method to handle {@code builder} and {@code extensions}. */
|
||||
private static void setField(
|
||||
Message.Builder builder,
|
||||
FieldSet<FieldDescriptor> extensions,
|
||||
FieldDescriptor field,
|
||||
Object value) {
|
||||
if (builder != null) {
|
||||
builder.setField(field, value);
|
||||
} else {
|
||||
extensions.setField(field, value);
|
||||
}
|
||||
}
|
||||
|
||||
/** helper method to handle {@code builder} and {@code extensions}. */
|
||||
private static boolean hasOriginalMessage(
|
||||
Message.Builder builder,
|
||||
FieldSet<FieldDescriptor> extensions,
|
||||
FieldDescriptor field) {
|
||||
if (builder != null) {
|
||||
return builder.hasField(field);
|
||||
} else {
|
||||
return extensions.hasField(field);
|
||||
}
|
||||
}
|
||||
|
||||
/** helper method to handle {@code builder} and {@code extensions}. */
|
||||
private static Message getOriginalMessage(
|
||||
Message.Builder builder,
|
||||
FieldSet<FieldDescriptor> extensions,
|
||||
FieldDescriptor field) {
|
||||
if (builder != null) {
|
||||
return (Message) builder.getField(field);
|
||||
} else {
|
||||
return (Message) extensions.getField(field);
|
||||
}
|
||||
}
|
||||
|
||||
/** helper method to handle {@code builder} and {@code extensions}. */
|
||||
private static void mergeOriginalMessage(
|
||||
Message.Builder builder,
|
||||
FieldSet<FieldDescriptor> extensions,
|
||||
FieldDescriptor field,
|
||||
Message.Builder subBuilder) {
|
||||
Message originalMessage = getOriginalMessage(builder, extensions, field);
|
||||
if (originalMessage != null) {
|
||||
subBuilder.mergeFrom(originalMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #mergeFrom(CodedInputStream, ExtensionRegistryLite)}, but
|
||||
* parses a single field.
|
||||
*
|
||||
* When {@code builder} is not null, the method will parse and merge the
|
||||
* field into {@code builder}. Otherwise, it will try to parse the field
|
||||
* into {@code extensions}, when it's called by the parsing constructor in
|
||||
* generated classes.
|
||||
*
|
||||
* Package-private because it is used by GeneratedMessage.ExtendableMessage.
|
||||
* @param tag The tag, which should have already been read.
|
||||
* @return {@code true} unless the tag is an end-group tag.
|
||||
*/
|
||||
static boolean mergeFieldFrom(
|
||||
CodedInputStream input,
|
||||
UnknownFieldSet.Builder unknownFields,
|
||||
ExtensionRegistryLite extensionRegistry,
|
||||
Descriptor type,
|
||||
Message.Builder builder,
|
||||
FieldSet<FieldDescriptor> extensions,
|
||||
int tag) throws IOException {
|
||||
if (type.getOptions().getMessageSetWireFormat() &&
|
||||
tag == WireFormat.MESSAGE_SET_ITEM_TAG) {
|
||||
mergeMessageSetExtensionFromCodedStream(
|
||||
input, unknownFields, extensionRegistry, type, builder, extensions);
|
||||
return true;
|
||||
}
|
||||
|
||||
final int wireType = WireFormat.getTagWireType(tag);
|
||||
final int fieldNumber = WireFormat.getTagFieldNumber(tag);
|
||||
|
||||
final FieldDescriptor field;
|
||||
Message defaultInstance = null;
|
||||
|
||||
if (type.isExtensionNumber(fieldNumber)) {
|
||||
// extensionRegistry may be either ExtensionRegistry or
|
||||
// ExtensionRegistryLite. Since the type we are parsing is a full
|
||||
// message, only a full ExtensionRegistry could possibly contain
|
||||
// extensions of it. Otherwise we will treat the registry as if it
|
||||
// were empty.
|
||||
if (extensionRegistry instanceof ExtensionRegistry) {
|
||||
final ExtensionRegistry.ExtensionInfo extension =
|
||||
((ExtensionRegistry) extensionRegistry)
|
||||
.findExtensionByNumber(type, fieldNumber);
|
||||
if (extension == null) {
|
||||
field = null;
|
||||
} else {
|
||||
field = extension.descriptor;
|
||||
defaultInstance = extension.defaultInstance;
|
||||
if (defaultInstance == null &&
|
||||
field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
throw new IllegalStateException(
|
||||
"Message-typed extension lacked default instance: " +
|
||||
field.getFullName());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
field = null;
|
||||
}
|
||||
} else if (builder != null) {
|
||||
field = type.findFieldByNumber(fieldNumber);
|
||||
} else {
|
||||
field = null;
|
||||
}
|
||||
|
||||
boolean unknown = false;
|
||||
boolean packed = false;
|
||||
if (field == null) {
|
||||
unknown = true; // Unknown field.
|
||||
} else if (wireType == FieldSet.getWireFormatForFieldType(
|
||||
field.getLiteType(),
|
||||
false /* isPacked */)) {
|
||||
packed = false;
|
||||
} else if (field.isPackable() &&
|
||||
wireType == FieldSet.getWireFormatForFieldType(
|
||||
field.getLiteType(),
|
||||
true /* isPacked */)) {
|
||||
packed = true;
|
||||
} else {
|
||||
unknown = true; // Unknown wire type.
|
||||
}
|
||||
|
||||
if (unknown) { // Unknown field or wrong wire type. Skip.
|
||||
return unknownFields.mergeFieldFrom(tag, input);
|
||||
}
|
||||
|
||||
if (packed) {
|
||||
final int length = input.readRawVarint32();
|
||||
final int limit = input.pushLimit(length);
|
||||
if (field.getLiteType() == WireFormat.FieldType.ENUM) {
|
||||
while (input.getBytesUntilLimit() > 0) {
|
||||
final int rawValue = input.readEnum();
|
||||
final Object value = field.getEnumType().findValueByNumber(rawValue);
|
||||
if (value == null) {
|
||||
// If the number isn't recognized as a valid value for this
|
||||
// enum, drop it (don't even add it to unknownFields).
|
||||
return true;
|
||||
}
|
||||
addRepeatedField(builder, extensions, field, value);
|
||||
}
|
||||
} else {
|
||||
while (input.getBytesUntilLimit() > 0) {
|
||||
final Object value =
|
||||
FieldSet.readPrimitiveField(input, field.getLiteType());
|
||||
addRepeatedField(builder, extensions, field, value);
|
||||
}
|
||||
}
|
||||
input.popLimit(limit);
|
||||
} else {
|
||||
final Object value;
|
||||
switch (field.getType()) {
|
||||
case GROUP: {
|
||||
final Message.Builder subBuilder;
|
||||
if (defaultInstance != null) {
|
||||
subBuilder = defaultInstance.newBuilderForType();
|
||||
} else {
|
||||
subBuilder = builder.newBuilderForField(field);
|
||||
}
|
||||
if (!field.isRepeated()) {
|
||||
mergeOriginalMessage(builder, extensions, field, subBuilder);
|
||||
}
|
||||
input.readGroup(field.getNumber(), subBuilder, extensionRegistry);
|
||||
value = subBuilder.buildPartial();
|
||||
break;
|
||||
}
|
||||
case MESSAGE: {
|
||||
final Message.Builder subBuilder;
|
||||
if (defaultInstance != null) {
|
||||
subBuilder = defaultInstance.newBuilderForType();
|
||||
} else {
|
||||
subBuilder = builder.newBuilderForField(field);
|
||||
}
|
||||
if (!field.isRepeated()) {
|
||||
mergeOriginalMessage(builder, extensions, field, subBuilder);
|
||||
}
|
||||
input.readMessage(subBuilder, extensionRegistry);
|
||||
value = subBuilder.buildPartial();
|
||||
break;
|
||||
}
|
||||
case ENUM:
|
||||
final int rawValue = input.readEnum();
|
||||
value = field.getEnumType().findValueByNumber(rawValue);
|
||||
// If the number isn't recognized as a valid value for this enum,
|
||||
// drop it.
|
||||
if (value == null) {
|
||||
unknownFields.mergeVarintField(fieldNumber, rawValue);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
value = FieldSet.readPrimitiveField(input, field.getLiteType());
|
||||
break;
|
||||
}
|
||||
|
||||
if (field.isRepeated()) {
|
||||
addRepeatedField(builder, extensions, field, value);
|
||||
} else {
|
||||
setField(builder, extensions, field, value);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by {@code #mergeFieldFrom()} to parse a MessageSet extension.
|
||||
* If {@code builder} is not null, this method will merge MessageSet into
|
||||
* the builder. Otherwise, it will merge the MessageSet into {@code
|
||||
* extensions}.
|
||||
*/
|
||||
private static void mergeMessageSetExtensionFromCodedStream(
|
||||
CodedInputStream input,
|
||||
UnknownFieldSet.Builder unknownFields,
|
||||
ExtensionRegistryLite extensionRegistry,
|
||||
Descriptor type,
|
||||
Message.Builder builder,
|
||||
FieldSet<FieldDescriptor> extensions) throws IOException {
|
||||
|
||||
// The wire format for MessageSet is:
|
||||
// message MessageSet {
|
||||
// repeated group Item = 1 {
|
||||
// required int32 typeId = 2;
|
||||
// required bytes message = 3;
|
||||
// }
|
||||
// }
|
||||
// "typeId" is the extension's field number. The extension can only be
|
||||
// a message type, where "message" contains the encoded bytes of that
|
||||
// message.
|
||||
//
|
||||
// In practice, we will probably never see a MessageSet item in which
|
||||
// the message appears before the type ID, or where either field does not
|
||||
// appear exactly once. However, in theory such cases are valid, so we
|
||||
// should be prepared to accept them.
|
||||
|
||||
int typeId = 0;
|
||||
ByteString rawBytes = null; // If we encounter "message" before "typeId"
|
||||
ExtensionRegistry.ExtensionInfo extension = null;
|
||||
|
||||
// Read bytes from input, if we get it's type first then parse it eagerly,
|
||||
// otherwise we store the raw bytes in a local variable.
|
||||
while (true) {
|
||||
final int tag = input.readTag();
|
||||
if (tag == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (tag == WireFormat.MESSAGE_SET_TYPE_ID_TAG) {
|
||||
typeId = input.readUInt32();
|
||||
if (typeId != 0) {
|
||||
// extensionRegistry may be either ExtensionRegistry or
|
||||
// ExtensionRegistryLite. Since the type we are parsing is a full
|
||||
// message, only a full ExtensionRegistry could possibly contain
|
||||
// extensions of it. Otherwise we will treat the registry as if it
|
||||
// were empty.
|
||||
if (extensionRegistry instanceof ExtensionRegistry) {
|
||||
extension = ((ExtensionRegistry) extensionRegistry)
|
||||
.findExtensionByNumber(type, typeId);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (tag == WireFormat.MESSAGE_SET_MESSAGE_TAG) {
|
||||
if (typeId != 0) {
|
||||
if (extension != null && ExtensionRegistryLite.isEagerlyParseMessageSets()) {
|
||||
// We already know the type, so we can parse directly from the
|
||||
// input with no copying. Hooray!
|
||||
eagerlyMergeMessageSetExtension(
|
||||
input, extension, extensionRegistry, builder, extensions);
|
||||
rawBytes = null;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// We haven't seen a type ID yet or we want parse message lazily.
|
||||
rawBytes = input.readBytes();
|
||||
|
||||
} else { // Unknown tag. Skip it.
|
||||
if (!input.skipField(tag)) {
|
||||
break; // End of group
|
||||
}
|
||||
}
|
||||
}
|
||||
input.checkLastTagWas(WireFormat.MESSAGE_SET_ITEM_END_TAG);
|
||||
|
||||
// Process the raw bytes.
|
||||
if (rawBytes != null && typeId != 0) { // Zero is not a valid type ID.
|
||||
if (extension != null) { // We known the type
|
||||
mergeMessageSetExtensionFromBytes(
|
||||
rawBytes, extension, extensionRegistry, builder, extensions);
|
||||
} else { // We don't know how to parse this. Ignore it.
|
||||
if (rawBytes != null) {
|
||||
unknownFields.mergeField(typeId, UnknownFieldSet.Field.newBuilder()
|
||||
.addLengthDelimited(rawBytes).build());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void eagerlyMergeMessageSetExtension(
|
||||
CodedInputStream input,
|
||||
ExtensionRegistry.ExtensionInfo extension,
|
||||
ExtensionRegistryLite extensionRegistry,
|
||||
Message.Builder builder,
|
||||
FieldSet<FieldDescriptor> extensions) throws IOException {
|
||||
|
||||
FieldDescriptor field = extension.descriptor;
|
||||
Message value = null;
|
||||
if (hasOriginalMessage(builder, extensions, field)) {
|
||||
Message originalMessage =
|
||||
getOriginalMessage(builder, extensions, field);
|
||||
Message.Builder subBuilder = originalMessage.toBuilder();
|
||||
input.readMessage(subBuilder, extensionRegistry);
|
||||
value = subBuilder.buildPartial();
|
||||
} else {
|
||||
value = input.readMessage(extension.defaultInstance.getParserForType(),
|
||||
extensionRegistry);
|
||||
}
|
||||
|
||||
if (builder != null) {
|
||||
builder.setField(field, value);
|
||||
} else {
|
||||
extensions.setField(field, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static void mergeMessageSetExtensionFromBytes(
|
||||
ByteString rawBytes,
|
||||
ExtensionRegistry.ExtensionInfo extension,
|
||||
ExtensionRegistryLite extensionRegistry,
|
||||
Message.Builder builder,
|
||||
FieldSet<FieldDescriptor> extensions) throws IOException {
|
||||
|
||||
FieldDescriptor field = extension.descriptor;
|
||||
boolean hasOriginalValue = hasOriginalMessage(builder, extensions, field);
|
||||
|
||||
if (hasOriginalValue || ExtensionRegistryLite.isEagerlyParseMessageSets()) {
|
||||
// If the field already exists, we just parse the field.
|
||||
Message value = null;
|
||||
if (hasOriginalValue) {
|
||||
Message originalMessage =
|
||||
getOriginalMessage(builder, extensions, field);
|
||||
Message.Builder subBuilder= originalMessage.toBuilder();
|
||||
subBuilder.mergeFrom(rawBytes, extensionRegistry);
|
||||
value = subBuilder.buildPartial();
|
||||
} else {
|
||||
value = extension.defaultInstance.getParserForType()
|
||||
.parsePartialFrom(rawBytes, extensionRegistry);
|
||||
}
|
||||
setField(builder, extensions, field, value);
|
||||
} else {
|
||||
// Use LazyField to load MessageSet lazily.
|
||||
LazyField lazyField = new LazyField(
|
||||
extension.defaultInstance, extensionRegistry, rawBytes);
|
||||
if (builder != null) {
|
||||
// TODO(xiangl): it looks like this method can only be invoked by
|
||||
// ExtendableBuilder, but I'm not sure. So I double check the type of
|
||||
// builder here. It may be useless and need more investigation.
|
||||
if (builder instanceof ExtendableBuilder) {
|
||||
builder.setField(field, lazyField);
|
||||
} else {
|
||||
builder.setField(field, lazyField.getValue());
|
||||
}
|
||||
} else {
|
||||
extensions.setField(field, lazyField);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BuilderType mergeUnknownFields(final UnknownFieldSet unknownFields) {
|
||||
setUnknownFields(
|
||||
UnknownFieldSet.newBuilder(getUnknownFields())
|
||||
.mergeFrom(unknownFields)
|
||||
.build());
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
public Message.Builder getFieldBuilder(final FieldDescriptor field) {
|
||||
throw new UnsupportedOperationException(
|
||||
"getFieldBuilder() called on an unsupported message type.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an UninitializedMessageException reporting missing fields in
|
||||
* the given message.
|
||||
*/
|
||||
protected static UninitializedMessageException
|
||||
newUninitializedMessageException(Message message) {
|
||||
return new UninitializedMessageException(findMissingFields(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates {@code this.missingFields} with the full "path" of each
|
||||
* missing required field in the given message.
|
||||
*/
|
||||
private static List<String> findMissingFields(
|
||||
final MessageOrBuilder message) {
|
||||
final List<String> results = new ArrayList<String>();
|
||||
findMissingFields(message, "", results);
|
||||
return results;
|
||||
}
|
||||
|
||||
/** Recursive helper implementing {@link #findMissingFields(Message)}. */
|
||||
private static void findMissingFields(final MessageOrBuilder message,
|
||||
final String prefix,
|
||||
final List<String> results) {
|
||||
for (final FieldDescriptor field :
|
||||
message.getDescriptorForType().getFields()) {
|
||||
if (field.isRequired() && !message.hasField(field)) {
|
||||
results.add(prefix + field.getName());
|
||||
}
|
||||
}
|
||||
|
||||
for (final Map.Entry<FieldDescriptor, Object> entry :
|
||||
message.getAllFields().entrySet()) {
|
||||
final FieldDescriptor field = entry.getKey();
|
||||
final Object value = entry.getValue();
|
||||
|
||||
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
if (field.isRepeated()) {
|
||||
int i = 0;
|
||||
for (final Object element : (List) value) {
|
||||
findMissingFields((MessageOrBuilder) element,
|
||||
subMessagePrefix(prefix, field, i++),
|
||||
results);
|
||||
}
|
||||
} else {
|
||||
if (message.hasField(field)) {
|
||||
findMissingFields((MessageOrBuilder) value,
|
||||
subMessagePrefix(prefix, field, -1),
|
||||
results);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String subMessagePrefix(final String prefix,
|
||||
final FieldDescriptor field,
|
||||
final int index) {
|
||||
final StringBuilder result = new StringBuilder(prefix);
|
||||
if (field.isExtension()) {
|
||||
result.append('(')
|
||||
.append(field.getFullName())
|
||||
.append(')');
|
||||
} else {
|
||||
result.append(field.getName());
|
||||
}
|
||||
if (index != -1) {
|
||||
result.append('[')
|
||||
.append(index)
|
||||
.append(']');
|
||||
}
|
||||
result.append('.');
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
// The following definitions seem to be required in order to make javac
|
||||
// not produce weird errors like:
|
||||
//
|
||||
// java/com/google/protobuf/DynamicMessage.java:203: types
|
||||
// org.apache.pekko.protobuf.AbstractMessage.Builder<
|
||||
// org.apache.pekko.protobuf.DynamicMessage.Builder> and
|
||||
// org.apache.pekko.protobuf.AbstractMessage.Builder<
|
||||
// org.apache.pekko.protobuf.DynamicMessage.Builder> are incompatible; both
|
||||
// define mergeFrom(org.apache.pekko.protobuf.ByteString), but with unrelated
|
||||
// return types.
|
||||
//
|
||||
// Strangely, these lines are only needed if javac is invoked separately
|
||||
// on AbstractMessage.java and AbstractMessageLite.java. If javac is
|
||||
// invoked on both simultaneously, it works. (Or maybe the important
|
||||
// point is whether or not DynamicMessage.java is compiled together with
|
||||
// AbstractMessageLite.java -- not sure.) I suspect this is a compiler
|
||||
// bug.
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(final ByteString data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return super.mergeFrom(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(
|
||||
final ByteString data,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return super.mergeFrom(data, extensionRegistry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(final byte[] data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return super.mergeFrom(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(
|
||||
final byte[] data, final int off, final int len)
|
||||
throws InvalidProtocolBufferException {
|
||||
return super.mergeFrom(data, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(
|
||||
final byte[] data,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return super.mergeFrom(data, extensionRegistry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(
|
||||
final byte[] data, final int off, final int len,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return super.mergeFrom(data, off, len, extensionRegistry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(final InputStream input)
|
||||
throws IOException {
|
||||
return super.mergeFrom(input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(
|
||||
final InputStream input,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException {
|
||||
return super.mergeFrom(input, extensionRegistry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mergeDelimitedFrom(final InputStream input)
|
||||
throws IOException {
|
||||
return super.mergeDelimitedFrom(input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mergeDelimitedFrom(
|
||||
final InputStream input,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException {
|
||||
return super.mergeDelimitedFrom(input, extensionRegistry);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,356 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* A partial implementation of the {@link MessageLite} interface which
|
||||
* implements as many methods of that interface as possible in terms of other
|
||||
* methods.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public abstract class AbstractMessageLite implements MessageLite {
|
||||
public ByteString toByteString() {
|
||||
try {
|
||||
final ByteString.CodedBuilder out =
|
||||
ByteString.newCodedBuilder(getSerializedSize());
|
||||
writeTo(out.getCodedOutput());
|
||||
return out.build();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Serializing to a ByteString threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] toByteArray() {
|
||||
try {
|
||||
final byte[] result = new byte[getSerializedSize()];
|
||||
final CodedOutputStream output = CodedOutputStream.newInstance(result);
|
||||
writeTo(output);
|
||||
output.checkNoSpaceLeft();
|
||||
return result;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Serializing to a byte array threw an IOException " +
|
||||
"(should never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeTo(final OutputStream output) throws IOException {
|
||||
final int bufferSize =
|
||||
CodedOutputStream.computePreferredBufferSize(getSerializedSize());
|
||||
final CodedOutputStream codedOutput =
|
||||
CodedOutputStream.newInstance(output, bufferSize);
|
||||
writeTo(codedOutput);
|
||||
codedOutput.flush();
|
||||
}
|
||||
|
||||
public void writeDelimitedTo(final OutputStream output) throws IOException {
|
||||
final int serialized = getSerializedSize();
|
||||
final int bufferSize = CodedOutputStream.computePreferredBufferSize(
|
||||
CodedOutputStream.computeRawVarint32Size(serialized) + serialized);
|
||||
final CodedOutputStream codedOutput =
|
||||
CodedOutputStream.newInstance(output, bufferSize);
|
||||
codedOutput.writeRawVarint32(serialized);
|
||||
writeTo(codedOutput);
|
||||
codedOutput.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Package private helper method for AbstractParser to create
|
||||
* UninitializedMessageException.
|
||||
*/
|
||||
UninitializedMessageException newUninitializedMessageException() {
|
||||
return new UninitializedMessageException(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* A partial implementation of the {@link Message.Builder} interface which
|
||||
* implements as many methods of that interface as possible in terms of
|
||||
* other methods.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static abstract class Builder<BuilderType extends Builder>
|
||||
implements MessageLite.Builder {
|
||||
// The compiler produces an error if this is not declared explicitly.
|
||||
@Override
|
||||
public abstract BuilderType clone();
|
||||
|
||||
public BuilderType mergeFrom(final CodedInputStream input)
|
||||
throws IOException {
|
||||
return mergeFrom(input, ExtensionRegistryLite.getEmptyRegistry());
|
||||
}
|
||||
|
||||
// Re-defined here for return type covariance.
|
||||
public abstract BuilderType mergeFrom(
|
||||
final CodedInputStream input,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException;
|
||||
|
||||
public BuilderType mergeFrom(final ByteString data)
|
||||
throws InvalidProtocolBufferException {
|
||||
try {
|
||||
final CodedInputStream input = data.newCodedInput();
|
||||
mergeFrom(input);
|
||||
input.checkLastTagWas(0);
|
||||
return (BuilderType) this;
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Reading from a ByteString threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
public BuilderType mergeFrom(
|
||||
final ByteString data,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
try {
|
||||
final CodedInputStream input = data.newCodedInput();
|
||||
mergeFrom(input, extensionRegistry);
|
||||
input.checkLastTagWas(0);
|
||||
return (BuilderType) this;
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Reading from a ByteString threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
public BuilderType mergeFrom(final byte[] data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return mergeFrom(data, 0, data.length);
|
||||
}
|
||||
|
||||
public BuilderType mergeFrom(final byte[] data, final int off,
|
||||
final int len)
|
||||
throws InvalidProtocolBufferException {
|
||||
try {
|
||||
final CodedInputStream input =
|
||||
CodedInputStream.newInstance(data, off, len);
|
||||
mergeFrom(input);
|
||||
input.checkLastTagWas(0);
|
||||
return (BuilderType) this;
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Reading from a byte array threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
public BuilderType mergeFrom(
|
||||
final byte[] data,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return mergeFrom(data, 0, data.length, extensionRegistry);
|
||||
}
|
||||
|
||||
public BuilderType mergeFrom(
|
||||
final byte[] data, final int off, final int len,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
try {
|
||||
final CodedInputStream input =
|
||||
CodedInputStream.newInstance(data, off, len);
|
||||
mergeFrom(input, extensionRegistry);
|
||||
input.checkLastTagWas(0);
|
||||
return (BuilderType) this;
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Reading from a byte array threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
public BuilderType mergeFrom(final InputStream input) throws IOException {
|
||||
final CodedInputStream codedInput = CodedInputStream.newInstance(input);
|
||||
mergeFrom(codedInput);
|
||||
codedInput.checkLastTagWas(0);
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
public BuilderType mergeFrom(
|
||||
final InputStream input,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException {
|
||||
final CodedInputStream codedInput = CodedInputStream.newInstance(input);
|
||||
mergeFrom(codedInput, extensionRegistry);
|
||||
codedInput.checkLastTagWas(0);
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* An InputStream implementations which reads from some other InputStream
|
||||
* but is limited to a particular number of bytes. Used by
|
||||
* mergeDelimitedFrom(). This is intentionally package-private so that
|
||||
* UnknownFieldSet can share it.
|
||||
*/
|
||||
static final class LimitedInputStream extends FilterInputStream {
|
||||
private int limit;
|
||||
|
||||
LimitedInputStream(InputStream in, int limit) {
|
||||
super(in);
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
return Math.min(super.available(), limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (limit <= 0) {
|
||||
return -1;
|
||||
}
|
||||
final int result = super.read();
|
||||
if (result >= 0) {
|
||||
--limit;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(final byte[] b, final int off, int len)
|
||||
throws IOException {
|
||||
if (limit <= 0) {
|
||||
return -1;
|
||||
}
|
||||
len = Math.min(len, limit);
|
||||
final int result = super.read(b, off, len);
|
||||
if (result >= 0) {
|
||||
limit -= result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(final long n) throws IOException {
|
||||
final long result = super.skip(Math.min(n, limit));
|
||||
if (result >= 0) {
|
||||
limit -= result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean mergeDelimitedFrom(
|
||||
final InputStream input,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException {
|
||||
final int firstByte = input.read();
|
||||
if (firstByte == -1) {
|
||||
return false;
|
||||
}
|
||||
final int size = CodedInputStream.readRawVarint32(firstByte, input);
|
||||
final InputStream limitedInput = new LimitedInputStream(input, size);
|
||||
mergeFrom(limitedInput, extensionRegistry);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean mergeDelimitedFrom(final InputStream input)
|
||||
throws IOException {
|
||||
return mergeDelimitedFrom(input,
|
||||
ExtensionRegistryLite.getEmptyRegistry());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an UninitializedMessageException reporting missing fields in
|
||||
* the given message.
|
||||
*/
|
||||
protected static UninitializedMessageException
|
||||
newUninitializedMessageException(MessageLite message) {
|
||||
return new UninitializedMessageException(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the {@code values} to the {@code list}. This is a helper method
|
||||
* used by generated code. Users should ignore it.
|
||||
*
|
||||
* @throws NullPointerException if any of the elements of {@code values} is
|
||||
* null.
|
||||
*/
|
||||
protected static <T> void addAll(final Iterable<T> values,
|
||||
final Collection<? super T> list) {
|
||||
if (values instanceof LazyStringList) {
|
||||
// For StringOrByteStringLists, check the underlying elements to avoid
|
||||
// forcing conversions of ByteStrings to Strings.
|
||||
checkForNullValues(((LazyStringList) values).getUnderlyingElements());
|
||||
} else {
|
||||
checkForNullValues(values);
|
||||
}
|
||||
if (values instanceof Collection) {
|
||||
final Collection<T> collection = (Collection<T>) values;
|
||||
list.addAll(collection);
|
||||
} else {
|
||||
for (final T value : values) {
|
||||
list.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkForNullValues(final Iterable<?> values) {
|
||||
for (final Object value : values) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,266 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
import org.apache.pekko.protobuf.AbstractMessageLite.Builder.LimitedInputStream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* A partial implementation of the {@link Parser} interface which implements
|
||||
* as many methods of that interface as possible in terms of other methods.
|
||||
*
|
||||
* Note: This class implements all the convenience methods in the
|
||||
* {@link Parser} interface. See {@link Parser} for related javadocs.
|
||||
* Subclasses need to implement
|
||||
* {@link Parser#parsePartialFrom(CodedInputStream, ExtensionRegistryLite)}
|
||||
*
|
||||
* @author liujisi@google.com (Pherl Liu)
|
||||
*/
|
||||
public abstract class AbstractParser<MessageType extends MessageLite>
|
||||
implements Parser<MessageType> {
|
||||
/**
|
||||
* Creates an UninitializedMessageException for MessageType.
|
||||
*/
|
||||
private UninitializedMessageException
|
||||
newUninitializedMessageException(MessageType message) {
|
||||
if (message instanceof AbstractMessageLite) {
|
||||
return ((AbstractMessageLite) message).newUninitializedMessageException();
|
||||
}
|
||||
return new UninitializedMessageException(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to check if message is initialized.
|
||||
*
|
||||
* @throws InvalidProtocolBufferException if it is not initialized.
|
||||
* @return The message to check.
|
||||
*/
|
||||
private MessageType checkMessageInitialized(MessageType message)
|
||||
throws InvalidProtocolBufferException {
|
||||
if (message != null && !message.isInitialized()) {
|
||||
throw newUninitializedMessageException(message)
|
||||
.asInvalidProtocolBufferException()
|
||||
.setUnfinishedMessage(message);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
private static final ExtensionRegistryLite EMPTY_REGISTRY
|
||||
= ExtensionRegistryLite.getEmptyRegistry();
|
||||
|
||||
public MessageType parsePartialFrom(CodedInputStream input)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parsePartialFrom(input, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parseFrom(CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return checkMessageInitialized(
|
||||
parsePartialFrom(input, extensionRegistry));
|
||||
}
|
||||
|
||||
public MessageType parseFrom(CodedInputStream input)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parseFrom(input, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parsePartialFrom(ByteString data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
MessageType message;
|
||||
try {
|
||||
CodedInputStream input = data.newCodedInput();
|
||||
message = parsePartialFrom(input, extensionRegistry);
|
||||
try {
|
||||
input.checkLastTagWas(0);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e.setUnfinishedMessage(message);
|
||||
}
|
||||
return message;
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public MessageType parsePartialFrom(ByteString data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parsePartialFrom(data, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parseFrom(ByteString data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return checkMessageInitialized(parsePartialFrom(data, extensionRegistry));
|
||||
}
|
||||
|
||||
public MessageType parseFrom(ByteString data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parseFrom(data, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parsePartialFrom(byte[] data, int off, int len,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
try {
|
||||
CodedInputStream input = CodedInputStream.newInstance(data, off, len);
|
||||
MessageType message = parsePartialFrom(input, extensionRegistry);
|
||||
try {
|
||||
input.checkLastTagWas(0);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e.setUnfinishedMessage(message);
|
||||
}
|
||||
return message;
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public MessageType parsePartialFrom(byte[] data, int off, int len)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parsePartialFrom(data, off, len, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parsePartialFrom(byte[] data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parsePartialFrom(data, 0, data.length, extensionRegistry);
|
||||
}
|
||||
|
||||
public MessageType parsePartialFrom(byte[] data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parsePartialFrom(data, 0, data.length, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parseFrom(byte[] data, int off, int len,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return checkMessageInitialized(
|
||||
parsePartialFrom(data, off, len, extensionRegistry));
|
||||
}
|
||||
|
||||
public MessageType parseFrom(byte[] data, int off, int len)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parseFrom(data, off, len, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parseFrom(byte[] data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parseFrom(data, 0, data.length, extensionRegistry);
|
||||
}
|
||||
|
||||
public MessageType parseFrom(byte[] data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parseFrom(data, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parsePartialFrom(InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
CodedInputStream codedInput = CodedInputStream.newInstance(input);
|
||||
MessageType message = parsePartialFrom(codedInput, extensionRegistry);
|
||||
try {
|
||||
codedInput.checkLastTagWas(0);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e.setUnfinishedMessage(message);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
public MessageType parsePartialFrom(InputStream input)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parsePartialFrom(input, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parseFrom(InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return checkMessageInitialized(
|
||||
parsePartialFrom(input, extensionRegistry));
|
||||
}
|
||||
|
||||
public MessageType parseFrom(InputStream input)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parseFrom(input, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parsePartialDelimitedFrom(
|
||||
InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
int size;
|
||||
try {
|
||||
int firstByte = input.read();
|
||||
if (firstByte == -1) {
|
||||
return null;
|
||||
}
|
||||
size = CodedInputStream.readRawVarint32(firstByte, input);
|
||||
} catch (IOException e) {
|
||||
throw new InvalidProtocolBufferException(e.getMessage());
|
||||
}
|
||||
InputStream limitedInput = new LimitedInputStream(input, size);
|
||||
return parsePartialFrom(limitedInput, extensionRegistry);
|
||||
}
|
||||
|
||||
public MessageType parsePartialDelimitedFrom(InputStream input)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parsePartialDelimitedFrom(input, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parseDelimitedFrom(
|
||||
InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return checkMessageInitialized(
|
||||
parsePartialDelimitedFrom(input, extensionRegistry));
|
||||
}
|
||||
|
||||
public MessageType parseDelimitedFrom(InputStream input)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parseDelimitedFrom(input, EMPTY_REGISTRY);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
/**
|
||||
* <p>Abstract interface for a blocking RPC channel. {@code BlockingRpcChannel}
|
||||
* is the blocking equivalent to {@link RpcChannel}.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
* @author cpovirk@google.com Chris Povirk
|
||||
*/
|
||||
public interface BlockingRpcChannel {
|
||||
/**
|
||||
* Call the given method of the remote service and blocks until it returns.
|
||||
* {@code callBlockingMethod()} is the blocking equivalent to
|
||||
* {@link RpcChannel#callMethod}.
|
||||
*/
|
||||
Message callBlockingMethod(
|
||||
Descriptors.MethodDescriptor method,
|
||||
RpcController controller,
|
||||
Message request,
|
||||
Message responsePrototype) throws ServiceException;
|
||||
}
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
/**
|
||||
* Blocking equivalent to {@link Service}.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
* @author cpovirk@google.com Chris Povirk
|
||||
*/
|
||||
public interface BlockingService {
|
||||
/**
|
||||
* Equivalent to {@link Service#getDescriptorForType}.
|
||||
*/
|
||||
Descriptors.ServiceDescriptor getDescriptorForType();
|
||||
|
||||
/**
|
||||
* Equivalent to {@link Service#callMethod}, except that
|
||||
* {@code callBlockingMethod()} returns the result of the RPC or throws a
|
||||
* {@link ServiceException} if there is a failure, rather than passing the
|
||||
* information to a callback.
|
||||
*/
|
||||
Message callBlockingMethod(Descriptors.MethodDescriptor method,
|
||||
RpcController controller,
|
||||
Message request) throws ServiceException;
|
||||
|
||||
/**
|
||||
* Equivalent to {@link Service#getRequestPrototype}.
|
||||
*/
|
||||
Message getRequestPrototype(Descriptors.MethodDescriptor method);
|
||||
|
||||
/**
|
||||
* Equivalent to {@link Service#getResponsePrototype}.
|
||||
*/
|
||||
Message getResponsePrototype(Descriptors.MethodDescriptor method);
|
||||
}
|
||||
|
|
@ -1,176 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* This class is used to represent the substring of a {@link ByteString} over a
|
||||
* single byte array. In terms of the public API of {@link ByteString}, you end
|
||||
* up here by calling {@link ByteString#copyFrom(byte[])} followed by {@link
|
||||
* ByteString#substring(int, int)}.
|
||||
*
|
||||
* <p>This class contains most of the overhead involved in creating a substring
|
||||
* from a {@link LiteralByteString}. The overhead involves some range-checking
|
||||
* and two extra fields.
|
||||
*
|
||||
* @author carlanton@google.com (Carl Haverl)
|
||||
*/
|
||||
class BoundedByteString extends LiteralByteString {
|
||||
|
||||
private final int bytesOffset;
|
||||
private final int bytesLength;
|
||||
|
||||
/**
|
||||
* Creates a {@code BoundedByteString} backed by the sub-range of given array,
|
||||
* without copying.
|
||||
*
|
||||
* @param bytes array to wrap
|
||||
* @param offset index to first byte to use in bytes
|
||||
* @param length number of bytes to use from bytes
|
||||
* @throws IllegalArgumentException if {@code offset < 0}, {@code length < 0},
|
||||
* or if {@code offset + length >
|
||||
* bytes.length}.
|
||||
*/
|
||||
BoundedByteString(byte[] bytes, int offset, int length) {
|
||||
super(bytes);
|
||||
if (offset < 0) {
|
||||
throw new IllegalArgumentException("Offset too small: " + offset);
|
||||
}
|
||||
if (length < 0) {
|
||||
throw new IllegalArgumentException("Length too small: " + offset);
|
||||
}
|
||||
if ((long) offset + length > bytes.length) {
|
||||
throw new IllegalArgumentException(
|
||||
"Offset+Length too large: " + offset + "+" + length);
|
||||
}
|
||||
|
||||
this.bytesOffset = offset;
|
||||
this.bytesLength = length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the byte at the given index.
|
||||
* Throws {@link ArrayIndexOutOfBoundsException}
|
||||
* for backwards-compatibility reasons although it would more properly be
|
||||
* {@link IndexOutOfBoundsException}.
|
||||
*
|
||||
* @param index index of byte
|
||||
* @return the value
|
||||
* @throws ArrayIndexOutOfBoundsException {@code index} is < 0 or >= size
|
||||
*/
|
||||
@Override
|
||||
public byte byteAt(int index) {
|
||||
// We must check the index ourselves as we cannot rely on Java array index
|
||||
// checking for substrings.
|
||||
if (index < 0) {
|
||||
throw new ArrayIndexOutOfBoundsException("Index too small: " + index);
|
||||
}
|
||||
if (index >= size()) {
|
||||
throw new ArrayIndexOutOfBoundsException(
|
||||
"Index too large: " + index + ", " + size());
|
||||
}
|
||||
|
||||
return bytes[bytesOffset + index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return bytesLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getOffsetIntoBytes() {
|
||||
return bytesOffset;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ByteString -> byte[]
|
||||
|
||||
@Override
|
||||
protected void copyToInternal(byte[] target, int sourceOffset,
|
||||
int targetOffset, int numberToCopy) {
|
||||
System.arraycopy(bytes, getOffsetIntoBytes() + sourceOffset, target,
|
||||
targetOffset, numberToCopy);
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ByteIterator
|
||||
|
||||
@Override
|
||||
public ByteIterator iterator() {
|
||||
return new BoundedByteIterator();
|
||||
}
|
||||
|
||||
private class BoundedByteIterator implements ByteIterator {
|
||||
|
||||
private int position;
|
||||
private final int limit;
|
||||
|
||||
private BoundedByteIterator() {
|
||||
position = getOffsetIntoBytes();
|
||||
limit = position + size();
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return (position < limit);
|
||||
}
|
||||
|
||||
public Byte next() {
|
||||
// Boxing calls Byte.valueOf(byte), which does not instantiate.
|
||||
return nextByte();
|
||||
}
|
||||
|
||||
public byte nextByte() {
|
||||
if (position >= limit) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
return bytes[position++];
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,979 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* Immutable sequence of bytes. Substring is supported by sharing the reference
|
||||
* to the immutable underlying bytes, as with {@link String}. Concatenation is
|
||||
* likewise supported without copying (long strings) by building a tree of
|
||||
* pieces in {@link RopeByteString}.
|
||||
* <p>
|
||||
* Like {@link String}, the contents of a {@link ByteString} can never be
|
||||
* observed to change, not even in the presence of a data race or incorrect
|
||||
* API usage in the client code.
|
||||
*
|
||||
* @author crazybob@google.com Bob Lee
|
||||
* @author kenton@google.com Kenton Varda
|
||||
* @author carlanton@google.com Carl Haverl
|
||||
* @author martinrb@google.com Martin Buchholz
|
||||
*/
|
||||
public abstract class ByteString implements Iterable<Byte> {
|
||||
|
||||
/**
|
||||
* When two strings to be concatenated have a combined length shorter than
|
||||
* this, we just copy their bytes on {@link #concat(ByteString)}.
|
||||
* The trade-off is copy size versus the overhead of creating tree nodes
|
||||
* in {@link RopeByteString}.
|
||||
*/
|
||||
static final int CONCATENATE_BY_COPY_SIZE = 128;
|
||||
|
||||
/**
|
||||
* When copying an InputStream into a ByteString with .readFrom(),
|
||||
* the chunks in the underlying rope start at 256 bytes, but double
|
||||
* each iteration up to 8192 bytes.
|
||||
*/
|
||||
static final int MIN_READ_FROM_CHUNK_SIZE = 0x100; // 256b
|
||||
static final int MAX_READ_FROM_CHUNK_SIZE = 0x2000; // 8k
|
||||
|
||||
/**
|
||||
* Empty {@code ByteString}.
|
||||
*/
|
||||
public static final ByteString EMPTY = new LiteralByteString(new byte[0]);
|
||||
|
||||
// This constructor is here to prevent subclassing outside of this package,
|
||||
ByteString() {}
|
||||
|
||||
/**
|
||||
* Gets the byte at the given index. This method should be used only for
|
||||
* random access to individual bytes. To access bytes sequentially, use the
|
||||
* {@link ByteIterator} returned by {@link #iterator()}, and call {@link
|
||||
* #substring(int, int)} first if necessary.
|
||||
*
|
||||
* @param index index of byte
|
||||
* @return the value
|
||||
* @throws ArrayIndexOutOfBoundsException {@code index} is < 0 or ≥ size
|
||||
*/
|
||||
public abstract byte byteAt(int index);
|
||||
|
||||
/**
|
||||
* Return a {@link ByteString.ByteIterator} over the bytes in the ByteString.
|
||||
* To avoid auto-boxing, you may get the iterator manually and call
|
||||
* {@link ByteIterator#nextByte()}.
|
||||
*
|
||||
* @return the iterator
|
||||
*/
|
||||
public abstract ByteIterator iterator();
|
||||
|
||||
/**
|
||||
* This interface extends {@code Iterator<Byte>}, so that we can return an
|
||||
* unboxed {@code byte}.
|
||||
*/
|
||||
public interface ByteIterator extends Iterator<Byte> {
|
||||
/**
|
||||
* An alternative to {@link Iterator#next()} that returns an
|
||||
* unboxed primitive {@code byte}.
|
||||
*
|
||||
* @return the next {@code byte} in the iteration
|
||||
* @throws NoSuchElementException if the iteration has no more elements
|
||||
*/
|
||||
byte nextByte();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of bytes.
|
||||
*
|
||||
* @return size in bytes
|
||||
*/
|
||||
public abstract int size();
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the size is {@code 0}, {@code false} otherwise.
|
||||
*
|
||||
* @return true if this is zero bytes long
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ByteString -> substring
|
||||
|
||||
/**
|
||||
* Return the substring from {@code beginIndex}, inclusive, to the end of the
|
||||
* string.
|
||||
*
|
||||
* @param beginIndex start at this index
|
||||
* @return substring sharing underlying data
|
||||
* @throws IndexOutOfBoundsException if {@code beginIndex < 0} or
|
||||
* {@code beginIndex > size()}.
|
||||
*/
|
||||
public ByteString substring(int beginIndex) {
|
||||
return substring(beginIndex, size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the substring from {@code beginIndex}, inclusive, to {@code
|
||||
* endIndex}, exclusive.
|
||||
*
|
||||
* @param beginIndex start at this index
|
||||
* @param endIndex the last character is the one before this index
|
||||
* @return substring sharing underlying data
|
||||
* @throws IndexOutOfBoundsException if {@code beginIndex < 0},
|
||||
* {@code endIndex > size()}, or {@code beginIndex > endIndex}.
|
||||
*/
|
||||
public abstract ByteString substring(int beginIndex, int endIndex);
|
||||
|
||||
/**
|
||||
* Tests if this bytestring starts with the specified prefix.
|
||||
* Similar to {@link String#startsWith(String)}
|
||||
*
|
||||
* @param prefix the prefix.
|
||||
* @return <code>true</code> if the byte sequence represented by the
|
||||
* argument is a prefix of the byte sequence represented by
|
||||
* this string; <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean startsWith(ByteString prefix) {
|
||||
return size() >= prefix.size() &&
|
||||
substring(0, prefix.size()).equals(prefix);
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// byte[] -> ByteString
|
||||
|
||||
/**
|
||||
* Copies the given bytes into a {@code ByteString}.
|
||||
*
|
||||
* @param bytes source array
|
||||
* @param offset offset in source array
|
||||
* @param size number of bytes to copy
|
||||
* @return new {@code ByteString}
|
||||
*/
|
||||
public static ByteString copyFrom(byte[] bytes, int offset, int size) {
|
||||
byte[] copy = new byte[size];
|
||||
System.arraycopy(bytes, offset, copy, 0, size);
|
||||
return new LiteralByteString(copy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the given bytes into a {@code ByteString}.
|
||||
*
|
||||
* @param bytes to copy
|
||||
* @return new {@code ByteString}
|
||||
*/
|
||||
public static ByteString copyFrom(byte[] bytes) {
|
||||
return copyFrom(bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the next {@code size} bytes from a {@code java.nio.ByteBuffer} into
|
||||
* a {@code ByteString}.
|
||||
*
|
||||
* @param bytes source buffer
|
||||
* @param size number of bytes to copy
|
||||
* @return new {@code ByteString}
|
||||
*/
|
||||
public static ByteString copyFrom(ByteBuffer bytes, int size) {
|
||||
byte[] copy = new byte[size];
|
||||
bytes.get(copy);
|
||||
return new LiteralByteString(copy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the remaining bytes from a {@code java.nio.ByteBuffer} into
|
||||
* a {@code ByteString}.
|
||||
*
|
||||
* @param bytes sourceBuffer
|
||||
* @return new {@code ByteString}
|
||||
*/
|
||||
public static ByteString copyFrom(ByteBuffer bytes) {
|
||||
return copyFrom(bytes, bytes.remaining());
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes {@code text} into a sequence of bytes using the named charset
|
||||
* and returns the result as a {@code ByteString}.
|
||||
*
|
||||
* @param text source string
|
||||
* @param charsetName encoding to use
|
||||
* @return new {@code ByteString}
|
||||
* @throws UnsupportedEncodingException if the encoding isn't found
|
||||
*/
|
||||
public static ByteString copyFrom(String text, String charsetName)
|
||||
throws UnsupportedEncodingException {
|
||||
return new LiteralByteString(text.getBytes(charsetName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes {@code text} into a sequence of UTF-8 bytes and returns the
|
||||
* result as a {@code ByteString}.
|
||||
*
|
||||
* @param text source string
|
||||
* @return new {@code ByteString}
|
||||
*/
|
||||
public static ByteString copyFromUtf8(String text) {
|
||||
return new LiteralByteString(text.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// InputStream -> ByteString
|
||||
|
||||
/**
|
||||
* Completely reads the given stream's bytes into a
|
||||
* {@code ByteString}, blocking if necessary until all bytes are
|
||||
* read through to the end of the stream.
|
||||
*
|
||||
* <b>Performance notes:</b> The returned {@code ByteString} is an
|
||||
* immutable tree of byte arrays ("chunks") of the stream data. The
|
||||
* first chunk is small, with subsequent chunks each being double
|
||||
* the size, up to 8K. If the caller knows the precise length of
|
||||
* the stream and wishes to avoid all unnecessary copies and
|
||||
* allocations, consider using the two-argument version of this
|
||||
* method, below.
|
||||
*
|
||||
* @param streamToDrain The source stream, which is read completely
|
||||
* but not closed.
|
||||
* @return A new {@code ByteString} which is made up of chunks of
|
||||
* various sizes, depending on the behavior of the underlying
|
||||
* stream.
|
||||
* @throws IOException IOException is thrown if there is a problem
|
||||
* reading the underlying stream.
|
||||
*/
|
||||
public static ByteString readFrom(InputStream streamToDrain)
|
||||
throws IOException {
|
||||
return readFrom(
|
||||
streamToDrain, MIN_READ_FROM_CHUNK_SIZE, MAX_READ_FROM_CHUNK_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Completely reads the given stream's bytes into a
|
||||
* {@code ByteString}, blocking if necessary until all bytes are
|
||||
* read through to the end of the stream.
|
||||
*
|
||||
* <b>Performance notes:</b> The returned {@code ByteString} is an
|
||||
* immutable tree of byte arrays ("chunks") of the stream data. The
|
||||
* chunkSize parameter sets the size of these byte arrays. In
|
||||
* particular, if the chunkSize is precisely the same as the length
|
||||
* of the stream, unnecessary allocations and copies will be
|
||||
* avoided. Otherwise, the chunks will be of the given size, except
|
||||
* for the last chunk, which will be resized (via a reallocation and
|
||||
* copy) to contain the remainder of the stream.
|
||||
*
|
||||
* @param streamToDrain The source stream, which is read completely
|
||||
* but not closed.
|
||||
* @param chunkSize The size of the chunks in which to read the
|
||||
* stream.
|
||||
* @return A new {@code ByteString} which is made up of chunks of
|
||||
* the given size.
|
||||
* @throws IOException IOException is thrown if there is a problem
|
||||
* reading the underlying stream.
|
||||
*/
|
||||
public static ByteString readFrom(InputStream streamToDrain, int chunkSize)
|
||||
throws IOException {
|
||||
return readFrom(streamToDrain, chunkSize, chunkSize);
|
||||
}
|
||||
|
||||
// Helper method that takes the chunk size range as a parameter.
|
||||
public static ByteString readFrom(InputStream streamToDrain, int minChunkSize,
|
||||
int maxChunkSize) throws IOException {
|
||||
Collection<ByteString> results = new ArrayList<>();
|
||||
|
||||
// copy the inbound bytes into a list of chunks; the chunk size
|
||||
// grows exponentially to support both short and long streams.
|
||||
int chunkSize = minChunkSize;
|
||||
while (true) {
|
||||
ByteString chunk = readChunk(streamToDrain, chunkSize);
|
||||
if (chunk == null) {
|
||||
break;
|
||||
}
|
||||
results.add(chunk);
|
||||
chunkSize = Math.min(chunkSize * 2, maxChunkSize);
|
||||
}
|
||||
|
||||
return ByteString.copyFrom(results);
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocks until a chunk of the given size can be made from the
|
||||
* stream, or EOF is reached. Calls read() repeatedly in case the
|
||||
* given stream implementation doesn't completely fill the given
|
||||
* buffer in one read() call.
|
||||
*
|
||||
* @return A chunk of the desired size, or else a chunk as large as
|
||||
* was available when end of stream was reached. Returns null if the
|
||||
* given stream had no more data in it.
|
||||
*/
|
||||
private static ByteString readChunk(InputStream in, final int chunkSize)
|
||||
throws IOException {
|
||||
final byte[] buf = new byte[chunkSize];
|
||||
int bytesRead = 0;
|
||||
while (bytesRead < chunkSize) {
|
||||
final int count = in.read(buf, bytesRead, chunkSize - bytesRead);
|
||||
if (count == -1) {
|
||||
break;
|
||||
}
|
||||
bytesRead += count;
|
||||
}
|
||||
|
||||
if (bytesRead == 0) {
|
||||
return null;
|
||||
} else {
|
||||
return ByteString.copyFrom(buf, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Multiple ByteStrings -> One ByteString
|
||||
|
||||
/**
|
||||
* Concatenate the given {@code ByteString} to this one. Short concatenations,
|
||||
* of total size smaller than {@link ByteString#CONCATENATE_BY_COPY_SIZE}, are
|
||||
* produced by copying the underlying bytes (as per Rope.java, <a
|
||||
* href="https://www.cs.ubc.ca/local/reading/proceedings/spe91-95/spe/vol25/issue12/spe986.pdf">
|
||||
* BAP95 </a>. In general, the concatenate involves no copying.
|
||||
*
|
||||
* @param other string to concatenate
|
||||
* @return a new {@code ByteString} instance
|
||||
*/
|
||||
public ByteString concat(ByteString other) {
|
||||
int thisSize = size();
|
||||
int otherSize = other.size();
|
||||
if ((long) thisSize + otherSize >= Integer.MAX_VALUE) {
|
||||
throw new IllegalArgumentException("ByteString would be too long: " +
|
||||
thisSize + "+" + otherSize);
|
||||
}
|
||||
|
||||
return RopeByteString.concatenate(this, other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates all byte strings in the iterable and returns the result.
|
||||
* This is designed to run in O(list size), not O(total bytes).
|
||||
*
|
||||
* <p>The returned {@code ByteString} is not necessarily a unique object.
|
||||
* If the list is empty, the returned object is the singleton empty
|
||||
* {@code ByteString}. If the list has only one element, that
|
||||
* {@code ByteString} will be returned without copying.
|
||||
*
|
||||
* @param byteStrings strings to be concatenated
|
||||
* @return new {@code ByteString}
|
||||
*/
|
||||
public static ByteString copyFrom(Iterable<ByteString> byteStrings) {
|
||||
Collection<ByteString> collection;
|
||||
if (!(byteStrings instanceof Collection)) {
|
||||
collection = new ArrayList<>();
|
||||
for (ByteString byteString : byteStrings) {
|
||||
collection.add(byteString);
|
||||
}
|
||||
} else {
|
||||
collection = (Collection<ByteString>) byteStrings;
|
||||
}
|
||||
ByteString result;
|
||||
if (collection.isEmpty()) {
|
||||
result = EMPTY;
|
||||
} else {
|
||||
result = balancedConcat(collection.iterator(), collection.size());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Internal function used by copyFrom(Iterable<ByteString>).
|
||||
// Create a balanced concatenation of the next "length" elements from the
|
||||
// iterable.
|
||||
private static ByteString balancedConcat(Iterator<ByteString> iterator,
|
||||
int length) {
|
||||
assert length >= 1;
|
||||
ByteString result;
|
||||
if (length == 1) {
|
||||
result = iterator.next();
|
||||
} else {
|
||||
int halfLength = length >>> 1;
|
||||
ByteString left = balancedConcat(iterator, halfLength);
|
||||
ByteString right = balancedConcat(iterator, length - halfLength);
|
||||
result = left.concat(right);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ByteString -> byte[]
|
||||
|
||||
/**
|
||||
* Copies bytes into a buffer at the given offset.
|
||||
*
|
||||
* @param target buffer to copy into
|
||||
* @param offset in the target buffer
|
||||
* @throws IndexOutOfBoundsException if the offset is negative or too large
|
||||
*/
|
||||
public void copyTo(byte[] target, int offset) {
|
||||
copyTo(target, 0, offset, size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies bytes into a buffer.
|
||||
*
|
||||
* @param target buffer to copy into
|
||||
* @param sourceOffset offset within these bytes
|
||||
* @param targetOffset offset within the target buffer
|
||||
* @param numberToCopy number of bytes to copy
|
||||
* @throws IndexOutOfBoundsException if an offset or size is negative or too
|
||||
* large
|
||||
*/
|
||||
public void copyTo(byte[] target, int sourceOffset, int targetOffset,
|
||||
int numberToCopy) {
|
||||
if (sourceOffset < 0) {
|
||||
throw new IndexOutOfBoundsException("Source offset < 0: " + sourceOffset);
|
||||
}
|
||||
if (targetOffset < 0) {
|
||||
throw new IndexOutOfBoundsException("Target offset < 0: " + targetOffset);
|
||||
}
|
||||
if (numberToCopy < 0) {
|
||||
throw new IndexOutOfBoundsException("Length < 0: " + numberToCopy);
|
||||
}
|
||||
if (sourceOffset + numberToCopy > size()) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"Source end offset < 0: " + (sourceOffset + numberToCopy));
|
||||
}
|
||||
if (targetOffset + numberToCopy > target.length) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"Target end offset < 0: " + (targetOffset + numberToCopy));
|
||||
}
|
||||
if (numberToCopy > 0) {
|
||||
copyToInternal(target, sourceOffset, targetOffset, numberToCopy);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal (package private) implementation of <code>copyTo</code>.
|
||||
* It assumes that all error checking has already been performed and that
|
||||
* <code>numberToCopy > 0</code>.
|
||||
*/
|
||||
protected abstract void copyToInternal(byte[] target, int sourceOffset,
|
||||
int targetOffset, int numberToCopy);
|
||||
|
||||
/**
|
||||
* Copies bytes into a ByteBuffer.
|
||||
*
|
||||
* @param target ByteBuffer to copy into.
|
||||
* @throws java.nio.ReadOnlyBufferException if the {@code target} is read-only
|
||||
* @throws java.nio.BufferOverflowException if the {@code target}'s
|
||||
* remaining() space is not large enough to hold the data.
|
||||
*/
|
||||
public abstract void copyTo(ByteBuffer target);
|
||||
|
||||
/**
|
||||
* Copies bytes to a {@code byte[]}.
|
||||
*
|
||||
* @return copied bytes
|
||||
*/
|
||||
public byte[] toByteArray() {
|
||||
int size = size();
|
||||
byte[] result = new byte[size];
|
||||
copyToInternal(result, 0, 0, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the complete contents of this byte string to
|
||||
* the specified output stream argument.
|
||||
*
|
||||
* @param out the output stream to which to write the data.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public abstract void writeTo(OutputStream out) throws IOException;
|
||||
|
||||
/**
|
||||
* Constructs a read-only {@code java.nio.ByteBuffer} whose content
|
||||
* is equal to the contents of this byte string.
|
||||
* The result uses the same backing array as the byte string, if possible.
|
||||
*
|
||||
* @return wrapped bytes
|
||||
*/
|
||||
public abstract ByteBuffer asReadOnlyByteBuffer();
|
||||
|
||||
/**
|
||||
* Constructs a list of read-only {@code java.nio.ByteBuffer} objects
|
||||
* such that the concatenation of their contents is equal to the contents
|
||||
* of this byte string. The result uses the same backing arrays as the
|
||||
* byte string.
|
||||
* <p>
|
||||
* By returning a list, implementations of this method may be able to avoid
|
||||
* copying even when there are multiple backing arrays.
|
||||
*
|
||||
* @return a list of wrapped bytes
|
||||
*/
|
||||
public abstract List<ByteBuffer> asReadOnlyByteBufferList();
|
||||
|
||||
/**
|
||||
* Constructs a new {@code String} by decoding the bytes using the
|
||||
* specified charset.
|
||||
*
|
||||
* @param charsetName encode using this charset
|
||||
* @return new string
|
||||
* @throws UnsupportedEncodingException if charset isn't recognized
|
||||
*/
|
||||
public abstract String toString(String charsetName)
|
||||
throws UnsupportedEncodingException;
|
||||
|
||||
// =================================================================
|
||||
// UTF-8 decoding
|
||||
|
||||
/**
|
||||
* Constructs a new {@code String} by decoding the bytes as UTF-8.
|
||||
*
|
||||
* @return new string using UTF-8 encoding
|
||||
*/
|
||||
public String toStringUtf8() {
|
||||
try {
|
||||
return toString("UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException("UTF-8 not supported?", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether this {@code ByteString} represents a well-formed UTF-8
|
||||
* byte sequence, such that the original bytes can be converted to a
|
||||
* String object and then round tripped back to bytes without loss.
|
||||
*
|
||||
* <p>More precisely, returns {@code true} whenever: <pre> {@code
|
||||
* Arrays.equals(byteString.toByteArray(),
|
||||
* new String(byteString.toByteArray(), "UTF-8").getBytes("UTF-8"))
|
||||
* }</pre>
|
||||
*
|
||||
* <p>This method returns {@code false} for "overlong" byte sequences,
|
||||
* as well as for 3-byte sequences that would map to a surrogate
|
||||
* character, in accordance with the restricted definition of UTF-8
|
||||
* introduced in Unicode 3.1. Note that the UTF-8 decoder included in
|
||||
* Oracle's JDK has been modified to also reject "overlong" byte
|
||||
* sequences, but (as of 2011) still accepts 3-byte surrogate
|
||||
* character byte sequences.
|
||||
*
|
||||
* <p>See the Unicode Standard,<br>
|
||||
* Table 3-6. <em>UTF-8 Bit Distribution</em>,<br>
|
||||
* Table 3-7. <em>Well Formed UTF-8 Byte Sequences</em>.
|
||||
*
|
||||
* @return whether the bytes in this {@code ByteString} are a
|
||||
* well-formed UTF-8 byte sequence
|
||||
*/
|
||||
public abstract boolean isValidUtf8();
|
||||
|
||||
/**
|
||||
* Tells whether the given byte sequence is a well-formed, malformed, or
|
||||
* incomplete UTF-8 byte sequence. This method accepts and returns a partial
|
||||
* state result, allowing the bytes for a complete UTF-8 byte sequence to be
|
||||
* composed from multiple {@code ByteString} segments.
|
||||
*
|
||||
* @param state either {@code 0} (if this is the initial decoding operation)
|
||||
* or the value returned from a call to a partial decoding method for the
|
||||
* previous bytes
|
||||
* @param offset offset of the first byte to check
|
||||
* @param length number of bytes to check
|
||||
*
|
||||
* @return {@code -1} if the partial byte sequence is definitely malformed,
|
||||
* {@code 0} if it is well-formed (no additional input needed), or, if the
|
||||
* byte sequence is "incomplete", i.e. apparently terminated in the middle of
|
||||
* a character, an opaque integer "state" value containing enough information
|
||||
* to decode the character when passed to a subsequent invocation of a
|
||||
* partial decoding method.
|
||||
*/
|
||||
protected abstract int partialIsValidUtf8(int state, int offset, int length);
|
||||
|
||||
// =================================================================
|
||||
// equals() and hashCode()
|
||||
|
||||
@Override
|
||||
public abstract boolean equals(Object o);
|
||||
|
||||
/**
|
||||
* Return a non-zero hashCode depending only on the sequence of bytes
|
||||
* in this ByteString.
|
||||
*
|
||||
* @return hashCode value for this object
|
||||
*/
|
||||
@Override
|
||||
public abstract int hashCode();
|
||||
|
||||
// =================================================================
|
||||
// Input stream
|
||||
|
||||
/**
|
||||
* Creates an {@code InputStream} which can be used to read the bytes.
|
||||
* <p>
|
||||
* The {@link InputStream} returned by this method is guaranteed to be
|
||||
* completely non-blocking. The method {@link InputStream#available()}
|
||||
* returns the number of bytes remaining in the stream. The methods
|
||||
* {@link InputStream#read(byte[])}, {@link InputStream#read(byte[],int,int)}
|
||||
* and {@link InputStream#skip(long)} will read/skip as many bytes as are
|
||||
* available.
|
||||
* <p>
|
||||
* The methods in the returned {@link InputStream} might <b>not</b> be
|
||||
* thread safe.
|
||||
*
|
||||
* @return an input stream that returns the bytes of this byte string.
|
||||
*/
|
||||
public abstract InputStream newInput();
|
||||
|
||||
/**
|
||||
* Creates a {@link CodedInputStream} which can be used to read the bytes.
|
||||
* Using this is often more efficient than creating a {@link CodedInputStream}
|
||||
* that wraps the result of {@link #newInput()}.
|
||||
*
|
||||
* @return stream based on wrapped data
|
||||
*/
|
||||
public abstract CodedInputStream newCodedInput();
|
||||
|
||||
// =================================================================
|
||||
// Output stream
|
||||
|
||||
/**
|
||||
* Creates a new {@link Output} with the given initial capacity. Call {@link
|
||||
* Output#toByteString()} to create the {@code ByteString} instance.
|
||||
* <p>
|
||||
* A {@link ByteString.Output} offers the same functionality as a
|
||||
* {@link ByteArrayOutputStream}, except that it returns a {@link ByteString}
|
||||
* rather than a {@code byte} array.
|
||||
*
|
||||
* @param initialCapacity estimate of number of bytes to be written
|
||||
* @return {@code OutputStream} for building a {@code ByteString}
|
||||
*/
|
||||
public static Output newOutput(int initialCapacity) {
|
||||
return new Output(initialCapacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Output}. Call {@link Output#toByteString()} to create
|
||||
* the {@code ByteString} instance.
|
||||
* <p>
|
||||
* A {@link ByteString.Output} offers the same functionality as a
|
||||
* {@link ByteArrayOutputStream}, except that it returns a {@link ByteString}
|
||||
* rather than a {@code byte array}.
|
||||
*
|
||||
* @return {@code OutputStream} for building a {@code ByteString}
|
||||
*/
|
||||
public static Output newOutput() {
|
||||
return new Output(CONCATENATE_BY_COPY_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs to a {@code ByteString} instance. Call {@link #toByteString()} to
|
||||
* create the {@code ByteString} instance.
|
||||
*/
|
||||
public static final class Output extends OutputStream {
|
||||
// Implementation note.
|
||||
// The public methods of this class must be synchronized. ByteStrings
|
||||
// are guaranteed to be immutable. Without some sort of locking, it could
|
||||
// be possible for one thread to call toByteSring(), while another thread
|
||||
// is still modifying the underlying byte array.
|
||||
|
||||
private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
|
||||
// argument passed by user, indicating initial capacity.
|
||||
private final int initialCapacity;
|
||||
// ByteStrings to be concatenated to create the result
|
||||
private final ArrayList<ByteString> flushedBuffers;
|
||||
// Total number of bytes in the ByteStrings of flushedBuffers
|
||||
private int flushedBuffersTotalBytes;
|
||||
// Current buffer to which we are writing
|
||||
private byte[] buffer;
|
||||
// Location in buffer[] to which we write the next byte.
|
||||
private int bufferPos;
|
||||
|
||||
/**
|
||||
* Creates a new ByteString output stream with the specified
|
||||
* initial capacity.
|
||||
*
|
||||
* @param initialCapacity the initial capacity of the output stream.
|
||||
*/
|
||||
Output(int initialCapacity) {
|
||||
if (initialCapacity < 0) {
|
||||
throw new IllegalArgumentException("Buffer size < 0");
|
||||
}
|
||||
this.initialCapacity = initialCapacity;
|
||||
this.flushedBuffers = new ArrayList<>();
|
||||
this.buffer = new byte[initialCapacity];
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(int b) {
|
||||
if (bufferPos == buffer.length) {
|
||||
flushFullBuffer(1);
|
||||
}
|
||||
buffer[bufferPos++] = (byte)b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(byte[] b, int offset, int length) {
|
||||
if (length <= buffer.length - bufferPos) {
|
||||
// The bytes can fit into the current buffer.
|
||||
System.arraycopy(b, offset, buffer, bufferPos, length);
|
||||
bufferPos += length;
|
||||
} else {
|
||||
// Use up the current buffer
|
||||
int copySize = buffer.length - bufferPos;
|
||||
System.arraycopy(b, offset, buffer, bufferPos, copySize);
|
||||
offset += copySize;
|
||||
length -= copySize;
|
||||
// Flush the buffer, and get a new buffer at least big enough to cover
|
||||
// what we still need to output
|
||||
flushFullBuffer(length);
|
||||
System.arraycopy(b, offset, buffer, 0 /* count */, length);
|
||||
bufferPos = length;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a byte string. Its size is the current size of this output
|
||||
* stream and its output has been copied to it.
|
||||
*
|
||||
* @return the current contents of this output stream, as a byte string.
|
||||
*/
|
||||
public synchronized ByteString toByteString() {
|
||||
flushLastBuffer();
|
||||
return ByteString.copyFrom(flushedBuffers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement java.util.Arrays.copyOf() for jdk 1.5.
|
||||
*/
|
||||
private byte[] copyArray(byte[] buffer, int length) {
|
||||
byte[] result = new byte[length];
|
||||
System.arraycopy(buffer, 0, result, 0, Math.min(buffer.length, length));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the complete contents of this byte array output stream to
|
||||
* the specified output stream argument.
|
||||
*
|
||||
* @param out the output stream to which to write the data.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public void writeTo(OutputStream out) throws IOException {
|
||||
ByteString[] cachedFlushBuffers;
|
||||
byte[] cachedBuffer;
|
||||
int cachedBufferPos;
|
||||
synchronized (this) {
|
||||
// Copy the information we need into local variables so as to hold
|
||||
// the lock for as short a time as possible.
|
||||
cachedFlushBuffers =
|
||||
flushedBuffers.toArray(new ByteString[flushedBuffers.size()]);
|
||||
cachedBuffer = buffer;
|
||||
cachedBufferPos = bufferPos;
|
||||
}
|
||||
for (ByteString byteString : cachedFlushBuffers) {
|
||||
byteString.writeTo(out);
|
||||
}
|
||||
|
||||
out.write(copyArray(cachedBuffer, cachedBufferPos));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current size of the output stream.
|
||||
*
|
||||
* @return the current size of the output stream
|
||||
*/
|
||||
public synchronized int size() {
|
||||
return flushedBuffersTotalBytes + bufferPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets this stream, so that all currently accumulated output in the
|
||||
* output stream is discarded. The output stream can be used again,
|
||||
* reusing the already allocated buffer space.
|
||||
*/
|
||||
public synchronized void reset() {
|
||||
flushedBuffers.clear();
|
||||
flushedBuffersTotalBytes = 0;
|
||||
bufferPos = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("<ByteString.Output@%s size=%d>",
|
||||
Integer.toHexString(System.identityHashCode(this)), size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function used by writers. The current buffer is full, and the
|
||||
* writer needs a new buffer whose size is at least the specified minimum
|
||||
* size.
|
||||
*/
|
||||
private void flushFullBuffer(int minSize) {
|
||||
flushedBuffers.add(new LiteralByteString(buffer));
|
||||
flushedBuffersTotalBytes += buffer.length;
|
||||
// We want to increase our total capacity by 50%, but as a minimum,
|
||||
// the new buffer should also at least be >= minSize and
|
||||
// >= initial Capacity.
|
||||
int newSize = Math.max(initialCapacity,
|
||||
Math.max(minSize, flushedBuffersTotalBytes >>> 1));
|
||||
buffer = new byte[newSize];
|
||||
bufferPos = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function used by {@link #toByteString()}. The current buffer may
|
||||
* or may not be full, but it needs to be flushed.
|
||||
*/
|
||||
private void flushLastBuffer() {
|
||||
if (bufferPos < buffer.length) {
|
||||
if (bufferPos > 0) {
|
||||
byte[] bufferCopy = copyArray(buffer, bufferPos);
|
||||
flushedBuffers.add(new LiteralByteString(bufferCopy));
|
||||
}
|
||||
// We reuse this buffer for further writes.
|
||||
} else {
|
||||
// Buffer is completely full. Huzzah.
|
||||
flushedBuffers.add(new LiteralByteString(buffer));
|
||||
// 99% of the time, we're not going to use this OutputStream again.
|
||||
// We set buffer to an empty byte stream so that we're handling this
|
||||
// case without wasting space. In the rare case that more writes
|
||||
// *do* occur, this empty buffer will be flushed and an appropriately
|
||||
// sized new buffer will be created.
|
||||
buffer = EMPTY_BYTE_ARRAY;
|
||||
}
|
||||
flushedBuffersTotalBytes += bufferPos;
|
||||
bufferPos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code ByteString} builder, which allows you to
|
||||
* efficiently construct a {@code ByteString} by writing to a {@link
|
||||
* CodedOutputStream}. Using this is much more efficient than calling {@code
|
||||
* newOutput()} and wrapping that in a {@code CodedOutputStream}.
|
||||
*
|
||||
* <p>This is package-private because it's a somewhat confusing interface.
|
||||
* Users can call {@link Message#toByteString()} instead of calling this
|
||||
* directly.
|
||||
*
|
||||
* @param size The target byte size of the {@code ByteString}. You must write
|
||||
* exactly this many bytes before building the result.
|
||||
* @return the builder
|
||||
*/
|
||||
static CodedBuilder newCodedBuilder(int size) {
|
||||
return new CodedBuilder(size);
|
||||
}
|
||||
|
||||
/** See {@link ByteString#newCodedBuilder(int)}. */
|
||||
static final class CodedBuilder {
|
||||
private final CodedOutputStream output;
|
||||
private final byte[] buffer;
|
||||
|
||||
private CodedBuilder(int size) {
|
||||
buffer = new byte[size];
|
||||
output = CodedOutputStream.newInstance(buffer);
|
||||
}
|
||||
|
||||
public ByteString build() {
|
||||
output.checkNoSpaceLeft();
|
||||
|
||||
// We can be confident that the CodedOutputStream will not modify the
|
||||
// underlying bytes anymore because it already wrote all of them. So,
|
||||
// no need to make a copy.
|
||||
return new LiteralByteString(buffer);
|
||||
}
|
||||
|
||||
public CodedOutputStream getCodedOutput() {
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Methods {@link RopeByteString} needs on instances, which aren't part of the
|
||||
// public API.
|
||||
|
||||
/**
|
||||
* Return the depth of the tree representing this {@code ByteString}, if any,
|
||||
* whose root is this node. If this is a leaf node, return 0.
|
||||
*
|
||||
* @return tree depth or zero
|
||||
*/
|
||||
protected abstract int getTreeDepth();
|
||||
|
||||
/**
|
||||
* Return {@code true} if this ByteString is literal (a leaf node) or a
|
||||
* flat-enough tree in the sense of {@link RopeByteString}.
|
||||
*
|
||||
* @return true if the tree is flat enough
|
||||
*/
|
||||
protected abstract boolean isBalanced();
|
||||
|
||||
/**
|
||||
* Return the cached hash code if available.
|
||||
*
|
||||
* @return value of cached hash code or 0 if not computed yet
|
||||
*/
|
||||
protected abstract int peekCachedHashCode();
|
||||
|
||||
/**
|
||||
* Compute the hash across the value bytes starting with the given hash, and
|
||||
* return the result. This is used to compute the hash across strings
|
||||
* represented as a set of pieces by allowing the hash computation to be
|
||||
* continued from piece to piece.
|
||||
*
|
||||
* @param h starting hash value
|
||||
* @param offset offset into this value to start looking at data values
|
||||
* @param length number of data values to include in the hash computation
|
||||
* @return ending hash value
|
||||
*/
|
||||
protected abstract int partialHash(int h, int offset, int length);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("<ByteString@%s size=%d>",
|
||||
Integer.toHexString(System.identityHashCode(this)), size());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,933 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Reads and decodes protocol message fields.
|
||||
*
|
||||
* This class contains two kinds of methods: methods that read specific
|
||||
* protocol message constructs and field types (e.g. {@link #readTag()} and
|
||||
* {@link #readInt32()}) and methods that read low-level values (e.g.
|
||||
* {@link #readRawVarint32()} and {@link #readRawBytes}). If you are reading
|
||||
* encoded protocol messages, you should use the former methods, but if you are
|
||||
* reading some other format of your own design, use the latter.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public final class CodedInputStream {
|
||||
/**
|
||||
* Create a new CodedInputStream wrapping the given InputStream.
|
||||
*/
|
||||
public static CodedInputStream newInstance(final InputStream input) {
|
||||
return new CodedInputStream(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new CodedInputStream wrapping the given byte array.
|
||||
*/
|
||||
public static CodedInputStream newInstance(final byte[] buf) {
|
||||
return newInstance(buf, 0, buf.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new CodedInputStream wrapping the given byte array slice.
|
||||
*/
|
||||
public static CodedInputStream newInstance(final byte[] buf, final int off,
|
||||
final int len) {
|
||||
CodedInputStream result = new CodedInputStream(buf, off, len);
|
||||
try {
|
||||
// Some uses of CodedInputStream can be more efficient if they know
|
||||
// exactly how many bytes are available. By pushing the end point of the
|
||||
// buffer as a limit, we allow them to get this information via
|
||||
// getBytesUntilLimit(). Pushing a limit that we know is at the end of
|
||||
// the stream can never hurt, since we can never past that point anyway.
|
||||
result.pushLimit(len);
|
||||
} catch (InvalidProtocolBufferException ex) {
|
||||
// The only reason pushLimit() might throw an exception here is if len
|
||||
// is negative. Normally pushLimit()'s parameter comes directly off the
|
||||
// wire, so it's important to catch exceptions in case of corrupt or
|
||||
// malicious data. However, in this case, we expect that len is not a
|
||||
// user-supplied value, so we can assume that it being negative indicates
|
||||
// a programming error. Therefore, throwing an unchecked exception is
|
||||
// appropriate.
|
||||
throw new IllegalArgumentException(ex);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Attempt to read a field tag, returning zero if we have reached EOF.
|
||||
* Protocol message parsers use this to read tags, since a protocol message
|
||||
* may legally end wherever a tag occurs, and zero is not a valid tag number.
|
||||
*/
|
||||
public int readTag() throws IOException {
|
||||
if (isAtEnd()) {
|
||||
lastTag = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
lastTag = readRawVarint32();
|
||||
if (WireFormat.getTagFieldNumber(lastTag) == 0) {
|
||||
// If we actually read zero (or any tag number corresponding to field
|
||||
// number zero), that's not a valid tag.
|
||||
throw InvalidProtocolBufferException.invalidTag();
|
||||
}
|
||||
return lastTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the last call to readTag() returned the given tag value.
|
||||
* This is used to verify that a nested group ended with the correct
|
||||
* end tag.
|
||||
*
|
||||
* @throws InvalidProtocolBufferException {@code value} does not match the
|
||||
* last tag.
|
||||
*/
|
||||
public void checkLastTagWas(final int value)
|
||||
throws InvalidProtocolBufferException {
|
||||
if (lastTag != value) {
|
||||
throw InvalidProtocolBufferException.invalidEndTag();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and discards a single field, given its tag value.
|
||||
*
|
||||
* @return {@code false} if the tag is an endgroup tag, in which case
|
||||
* nothing is skipped. Otherwise, returns {@code true}.
|
||||
*/
|
||||
public boolean skipField(final int tag) throws IOException {
|
||||
switch (WireFormat.getTagWireType(tag)) {
|
||||
case WireFormat.WIRETYPE_VARINT:
|
||||
readInt32();
|
||||
return true;
|
||||
case WireFormat.WIRETYPE_FIXED64:
|
||||
readRawLittleEndian64();
|
||||
return true;
|
||||
case WireFormat.WIRETYPE_LENGTH_DELIMITED:
|
||||
skipRawBytes(readRawVarint32());
|
||||
return true;
|
||||
case WireFormat.WIRETYPE_START_GROUP:
|
||||
skipMessage();
|
||||
checkLastTagWas(
|
||||
WireFormat.makeTag(WireFormat.getTagFieldNumber(tag),
|
||||
WireFormat.WIRETYPE_END_GROUP));
|
||||
return true;
|
||||
case WireFormat.WIRETYPE_END_GROUP:
|
||||
return false;
|
||||
case WireFormat.WIRETYPE_FIXED32:
|
||||
readRawLittleEndian32();
|
||||
return true;
|
||||
default:
|
||||
throw InvalidProtocolBufferException.invalidWireType();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and discards an entire message. This will read either until EOF
|
||||
* or until an endgroup tag, whichever comes first.
|
||||
*/
|
||||
public void skipMessage() throws IOException {
|
||||
while (true) {
|
||||
final int tag = readTag();
|
||||
if (tag == 0 || !skipField(tag)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
/** Read a {@code double} field value from the stream. */
|
||||
public double readDouble() throws IOException {
|
||||
return Double.longBitsToDouble(readRawLittleEndian64());
|
||||
}
|
||||
|
||||
/** Read a {@code float} field value from the stream. */
|
||||
public float readFloat() throws IOException {
|
||||
return Float.intBitsToFloat(readRawLittleEndian32());
|
||||
}
|
||||
|
||||
/** Read a {@code uint64} field value from the stream. */
|
||||
public long readUInt64() throws IOException {
|
||||
return readRawVarint64();
|
||||
}
|
||||
|
||||
/** Read an {@code int64} field value from the stream. */
|
||||
public long readInt64() throws IOException {
|
||||
return readRawVarint64();
|
||||
}
|
||||
|
||||
/** Read an {@code int32} field value from the stream. */
|
||||
public int readInt32() throws IOException {
|
||||
return readRawVarint32();
|
||||
}
|
||||
|
||||
/** Read a {@code fixed64} field value from the stream. */
|
||||
public long readFixed64() throws IOException {
|
||||
return readRawLittleEndian64();
|
||||
}
|
||||
|
||||
/** Read a {@code fixed32} field value from the stream. */
|
||||
public int readFixed32() throws IOException {
|
||||
return readRawLittleEndian32();
|
||||
}
|
||||
|
||||
/** Read a {@code bool} field value from the stream. */
|
||||
public boolean readBool() throws IOException {
|
||||
return readRawVarint32() != 0;
|
||||
}
|
||||
|
||||
/** Read a {@code string} field value from the stream. */
|
||||
public String readString() throws IOException {
|
||||
final int size = readRawVarint32();
|
||||
if (size <= (bufferSize - bufferPos) && size > 0) {
|
||||
// Fast path: We already have the bytes in a contiguous buffer, so
|
||||
// just copy directly from it.
|
||||
final String result = new String(buffer, bufferPos, size, "UTF-8");
|
||||
bufferPos += size;
|
||||
return result;
|
||||
} else {
|
||||
// Slow path: Build a byte array first then copy it.
|
||||
return new String(readRawBytes(size), "UTF-8");
|
||||
}
|
||||
}
|
||||
|
||||
/** Read a {@code group} field value from the stream. */
|
||||
public void readGroup(final int fieldNumber,
|
||||
final MessageLite.Builder builder,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException {
|
||||
if (recursionDepth >= recursionLimit) {
|
||||
throw InvalidProtocolBufferException.recursionLimitExceeded();
|
||||
}
|
||||
++recursionDepth;
|
||||
builder.mergeFrom(this, extensionRegistry);
|
||||
checkLastTagWas(
|
||||
WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP));
|
||||
--recursionDepth;
|
||||
}
|
||||
|
||||
/** Read a {@code group} field value from the stream. */
|
||||
public <T extends MessageLite> T readGroup(
|
||||
final int fieldNumber,
|
||||
final Parser<T> parser,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException {
|
||||
if (recursionDepth >= recursionLimit) {
|
||||
throw InvalidProtocolBufferException.recursionLimitExceeded();
|
||||
}
|
||||
++recursionDepth;
|
||||
T result = parser.parsePartialFrom(this, extensionRegistry);
|
||||
checkLastTagWas(
|
||||
WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP));
|
||||
--recursionDepth;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a {@code group} field value from the stream and merges it into the
|
||||
* given {@link UnknownFieldSet}.
|
||||
*
|
||||
* @deprecated UnknownFieldSet.Builder now implements MessageLite.Builder, so
|
||||
* you can just call {@link #readGroup}.
|
||||
*/
|
||||
@Deprecated
|
||||
public void readUnknownGroup(final int fieldNumber,
|
||||
final MessageLite.Builder builder)
|
||||
throws IOException {
|
||||
// We know that UnknownFieldSet will ignore any ExtensionRegistry so it
|
||||
// is safe to pass null here. (We can't call
|
||||
// ExtensionRegistry.getEmptyRegistry() because that would make this
|
||||
// class depend on ExtensionRegistry, which is not part of the lite
|
||||
// library.)
|
||||
readGroup(fieldNumber, builder, null);
|
||||
}
|
||||
|
||||
/** Read an embedded message field value from the stream. */
|
||||
public void readMessage(final MessageLite.Builder builder,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException {
|
||||
final int length = readRawVarint32();
|
||||
if (recursionDepth >= recursionLimit) {
|
||||
throw InvalidProtocolBufferException.recursionLimitExceeded();
|
||||
}
|
||||
final int oldLimit = pushLimit(length);
|
||||
++recursionDepth;
|
||||
builder.mergeFrom(this, extensionRegistry);
|
||||
checkLastTagWas(0);
|
||||
--recursionDepth;
|
||||
popLimit(oldLimit);
|
||||
}
|
||||
|
||||
/** Read an embedded message field value from the stream. */
|
||||
public <T extends MessageLite> T readMessage(
|
||||
final Parser<T> parser,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException {
|
||||
int length = readRawVarint32();
|
||||
if (recursionDepth >= recursionLimit) {
|
||||
throw InvalidProtocolBufferException.recursionLimitExceeded();
|
||||
}
|
||||
final int oldLimit = pushLimit(length);
|
||||
++recursionDepth;
|
||||
T result = parser.parsePartialFrom(this, extensionRegistry);
|
||||
checkLastTagWas(0);
|
||||
--recursionDepth;
|
||||
popLimit(oldLimit);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Read a {@code bytes} field value from the stream. */
|
||||
public ByteString readBytes() throws IOException {
|
||||
final int size = readRawVarint32();
|
||||
if (size == 0) {
|
||||
return ByteString.EMPTY;
|
||||
} else if (size <= (bufferSize - bufferPos) && size > 0) {
|
||||
// Fast path: We already have the bytes in a contiguous buffer, so
|
||||
// just copy directly from it.
|
||||
final ByteString result = ByteString.copyFrom(buffer, bufferPos, size);
|
||||
bufferPos += size;
|
||||
return result;
|
||||
} else {
|
||||
// Slow path: Build a byte array first then copy it.
|
||||
return ByteString.copyFrom(readRawBytes(size));
|
||||
}
|
||||
}
|
||||
|
||||
/** Read a {@code uint32} field value from the stream. */
|
||||
public int readUInt32() throws IOException {
|
||||
return readRawVarint32();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an enum field value from the stream. Caller is responsible
|
||||
* for converting the numeric value to an actual enum.
|
||||
*/
|
||||
public int readEnum() throws IOException {
|
||||
return readRawVarint32();
|
||||
}
|
||||
|
||||
/** Read an {@code sfixed32} field value from the stream. */
|
||||
public int readSFixed32() throws IOException {
|
||||
return readRawLittleEndian32();
|
||||
}
|
||||
|
||||
/** Read an {@code sfixed64} field value from the stream. */
|
||||
public long readSFixed64() throws IOException {
|
||||
return readRawLittleEndian64();
|
||||
}
|
||||
|
||||
/** Read an {@code sint32} field value from the stream. */
|
||||
public int readSInt32() throws IOException {
|
||||
return decodeZigZag32(readRawVarint32());
|
||||
}
|
||||
|
||||
/** Read an {@code sint64} field value from the stream. */
|
||||
public long readSInt64() throws IOException {
|
||||
return decodeZigZag64(readRawVarint64());
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
|
||||
/**
|
||||
* Read a raw Varint from the stream. If larger than 32 bits, discard the
|
||||
* upper bits.
|
||||
*/
|
||||
public int readRawVarint32() throws IOException {
|
||||
byte tmp = readRawByte();
|
||||
if (tmp >= 0) {
|
||||
return tmp;
|
||||
}
|
||||
int result = tmp & 0x7f;
|
||||
if ((tmp = readRawByte()) >= 0) {
|
||||
result |= tmp << 7;
|
||||
} else {
|
||||
result |= (tmp & 0x7f) << 7;
|
||||
if ((tmp = readRawByte()) >= 0) {
|
||||
result |= tmp << 14;
|
||||
} else {
|
||||
result |= (tmp & 0x7f) << 14;
|
||||
if ((tmp = readRawByte()) >= 0) {
|
||||
result |= tmp << 21;
|
||||
} else {
|
||||
result |= (tmp & 0x7f) << 21;
|
||||
result |= (tmp = readRawByte()) << 28;
|
||||
if (tmp < 0) {
|
||||
// Discard upper 32 bits.
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (readRawByte() >= 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
throw InvalidProtocolBufferException.malformedVarint();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a varint from the input one byte at a time, so that it does not
|
||||
* read any bytes after the end of the varint. If you simply wrapped the
|
||||
* stream in a CodedInputStream and used {@link #readRawVarint32(InputStream)}
|
||||
* then you would probably end up reading past the end of the varint since
|
||||
* CodedInputStream buffers its input.
|
||||
*/
|
||||
static int readRawVarint32(final InputStream input) throws IOException {
|
||||
final int firstByte = input.read();
|
||||
if (firstByte == -1) {
|
||||
throw InvalidProtocolBufferException.truncatedMessage();
|
||||
}
|
||||
return readRawVarint32(firstByte, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #readRawVarint32(InputStream)}, but expects that the caller
|
||||
* has already read one byte. This allows the caller to determine if EOF
|
||||
* has been reached before attempting to read.
|
||||
*/
|
||||
public static int readRawVarint32(
|
||||
final int firstByte, final InputStream input) throws IOException {
|
||||
if ((firstByte & 0x80) == 0) {
|
||||
return firstByte;
|
||||
}
|
||||
|
||||
int result = firstByte & 0x7f;
|
||||
int offset = 7;
|
||||
for (; offset < 32; offset += 7) {
|
||||
final int b = input.read();
|
||||
if (b == -1) {
|
||||
throw InvalidProtocolBufferException.truncatedMessage();
|
||||
}
|
||||
result |= (b & 0x7f) << offset;
|
||||
if ((b & 0x80) == 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
// Keep reading up to 64 bits.
|
||||
for (; offset < 64; offset += 7) {
|
||||
final int b = input.read();
|
||||
if (b == -1) {
|
||||
throw InvalidProtocolBufferException.truncatedMessage();
|
||||
}
|
||||
if ((b & 0x80) == 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
throw InvalidProtocolBufferException.malformedVarint();
|
||||
}
|
||||
|
||||
/** Read a raw Varint from the stream. */
|
||||
public long readRawVarint64() throws IOException {
|
||||
int shift = 0;
|
||||
long result = 0;
|
||||
while (shift < 64) {
|
||||
final byte b = readRawByte();
|
||||
result |= (long)(b & 0x7F) << shift;
|
||||
if ((b & 0x80) == 0) {
|
||||
return result;
|
||||
}
|
||||
shift += 7;
|
||||
}
|
||||
throw InvalidProtocolBufferException.malformedVarint();
|
||||
}
|
||||
|
||||
/** Read a 32-bit little-endian integer from the stream. */
|
||||
public int readRawLittleEndian32() throws IOException {
|
||||
final byte b1 = readRawByte();
|
||||
final byte b2 = readRawByte();
|
||||
final byte b3 = readRawByte();
|
||||
final byte b4 = readRawByte();
|
||||
return (((int)b1 & 0xff) ) |
|
||||
(((int)b2 & 0xff) << 8) |
|
||||
(((int)b3 & 0xff) << 16) |
|
||||
(((int)b4 & 0xff) << 24);
|
||||
}
|
||||
|
||||
/** Read a 64-bit little-endian integer from the stream. */
|
||||
public long readRawLittleEndian64() throws IOException {
|
||||
final byte b1 = readRawByte();
|
||||
final byte b2 = readRawByte();
|
||||
final byte b3 = readRawByte();
|
||||
final byte b4 = readRawByte();
|
||||
final byte b5 = readRawByte();
|
||||
final byte b6 = readRawByte();
|
||||
final byte b7 = readRawByte();
|
||||
final byte b8 = readRawByte();
|
||||
return (((long)b1 & 0xff) ) |
|
||||
(((long)b2 & 0xff) << 8) |
|
||||
(((long)b3 & 0xff) << 16) |
|
||||
(((long)b4 & 0xff) << 24) |
|
||||
(((long)b5 & 0xff) << 32) |
|
||||
(((long)b6 & 0xff) << 40) |
|
||||
(((long)b7 & 0xff) << 48) |
|
||||
(((long)b8 & 0xff) << 56);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a ZigZag-encoded 32-bit value. ZigZag encodes signed integers
|
||||
* into values that can be efficiently encoded with varint. (Otherwise,
|
||||
* negative values must be sign-extended to 64 bits to be varint encoded,
|
||||
* thus always taking 10 bytes on the wire.)
|
||||
*
|
||||
* @param n An unsigned 32-bit integer, stored in a signed int because
|
||||
* Java has no explicit unsigned support.
|
||||
* @return A signed 32-bit integer.
|
||||
*/
|
||||
public static int decodeZigZag32(final int n) {
|
||||
return (n >>> 1) ^ -(n & 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a ZigZag-encoded 64-bit value. ZigZag encodes signed integers
|
||||
* into values that can be efficiently encoded with varint. (Otherwise,
|
||||
* negative values must be sign-extended to 64 bits to be varint encoded,
|
||||
* thus always taking 10 bytes on the wire.)
|
||||
*
|
||||
* @param n An unsigned 64-bit integer, stored in a signed int because
|
||||
* Java has no explicit unsigned support.
|
||||
* @return A signed 64-bit integer.
|
||||
*/
|
||||
public static long decodeZigZag64(final long n) {
|
||||
return (n >>> 1) ^ -(n & 1);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
private final byte[] buffer;
|
||||
private int bufferSize;
|
||||
private int bufferSizeAfterLimit;
|
||||
private int bufferPos;
|
||||
private final InputStream input;
|
||||
private int lastTag;
|
||||
|
||||
/**
|
||||
* The total number of bytes read before the current buffer. The total
|
||||
* bytes read up to the current position can be computed as
|
||||
* {@code totalBytesRetired + bufferPos}. This value may be negative if
|
||||
* reading started in the middle of the current buffer (e.g. if the
|
||||
* constructor that takes a byte array and an offset was used).
|
||||
*/
|
||||
private int totalBytesRetired;
|
||||
|
||||
/** The absolute position of the end of the current message. */
|
||||
private int currentLimit = Integer.MAX_VALUE;
|
||||
|
||||
/** See setRecursionLimit() */
|
||||
private int recursionDepth;
|
||||
private int recursionLimit = DEFAULT_RECURSION_LIMIT;
|
||||
|
||||
/** See setSizeLimit() */
|
||||
private int sizeLimit = DEFAULT_SIZE_LIMIT;
|
||||
|
||||
private static final int DEFAULT_RECURSION_LIMIT = 64;
|
||||
private static final int DEFAULT_SIZE_LIMIT = 64 << 20; // 64MB
|
||||
private static final int BUFFER_SIZE = 4096;
|
||||
|
||||
private CodedInputStream(final byte[] buffer, final int off, final int len) {
|
||||
this.buffer = buffer;
|
||||
bufferSize = off + len;
|
||||
bufferPos = off;
|
||||
totalBytesRetired = -off;
|
||||
input = null;
|
||||
}
|
||||
|
||||
private CodedInputStream(final InputStream input) {
|
||||
buffer = new byte[BUFFER_SIZE];
|
||||
bufferSize = 0;
|
||||
bufferPos = 0;
|
||||
totalBytesRetired = 0;
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum message recursion depth. In order to prevent malicious
|
||||
* messages from causing stack overflows, {@code CodedInputStream} limits
|
||||
* how deeply messages may be nested. The default limit is 64.
|
||||
*
|
||||
* @return the old limit.
|
||||
*/
|
||||
public int setRecursionLimit(final int limit) {
|
||||
if (limit < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Recursion limit cannot be negative: " + limit);
|
||||
}
|
||||
final int oldLimit = recursionLimit;
|
||||
recursionLimit = limit;
|
||||
return oldLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum message size. In order to prevent malicious
|
||||
* messages from exhausting memory or causing integer overflows,
|
||||
* {@code CodedInputStream} limits how large a message may be.
|
||||
* The default limit is 64MB. You should set this limit as small
|
||||
* as you can without harming your app's functionality. Note that
|
||||
* size limits only apply when reading from an {@code InputStream}, not
|
||||
* when constructed around a raw byte array (nor with
|
||||
* {@link ByteString#newCodedInput}).
|
||||
* <p>
|
||||
* If you want to read several messages from a single CodedInputStream, you
|
||||
* could call {@link #resetSizeCounter()} after each one to avoid hitting the
|
||||
* size limit.
|
||||
*
|
||||
* @return the old limit.
|
||||
*/
|
||||
public int setSizeLimit(final int limit) {
|
||||
if (limit < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Size limit cannot be negative: " + limit);
|
||||
}
|
||||
final int oldLimit = sizeLimit;
|
||||
sizeLimit = limit;
|
||||
return oldLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the current size counter to zero (see {@link #setSizeLimit(int)}).
|
||||
*/
|
||||
public void resetSizeCounter() {
|
||||
totalBytesRetired = -bufferPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@code currentLimit} to (current position) + {@code byteLimit}. This
|
||||
* is called when descending into a length-delimited embedded message.
|
||||
*
|
||||
* <p>Note that {@code pushLimit()} does NOT affect how many bytes the
|
||||
* {@code CodedInputStream} reads from an underlying {@code InputStream} when
|
||||
* refreshing its buffer. If you need to prevent reading past a certain
|
||||
* point in the underlying {@code InputStream} (e.g. because you expect it to
|
||||
* contain more data after the end of the message which you need to handle
|
||||
* differently) then you must place a wrapper around your {@code InputStream}
|
||||
* which limits the amount of data that can be read from it.
|
||||
*
|
||||
* @return the old limit.
|
||||
*/
|
||||
public int pushLimit(int byteLimit) throws InvalidProtocolBufferException {
|
||||
if (byteLimit < 0) {
|
||||
throw InvalidProtocolBufferException.negativeSize();
|
||||
}
|
||||
byteLimit += totalBytesRetired + bufferPos;
|
||||
final int oldLimit = currentLimit;
|
||||
if (byteLimit > oldLimit) {
|
||||
throw InvalidProtocolBufferException.truncatedMessage();
|
||||
}
|
||||
currentLimit = byteLimit;
|
||||
|
||||
recomputeBufferSizeAfterLimit();
|
||||
|
||||
return oldLimit;
|
||||
}
|
||||
|
||||
private void recomputeBufferSizeAfterLimit() {
|
||||
bufferSize += bufferSizeAfterLimit;
|
||||
final int bufferEnd = totalBytesRetired + bufferSize;
|
||||
if (bufferEnd > currentLimit) {
|
||||
// Limit is in current buffer.
|
||||
bufferSizeAfterLimit = bufferEnd - currentLimit;
|
||||
bufferSize -= bufferSizeAfterLimit;
|
||||
} else {
|
||||
bufferSizeAfterLimit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Discards the current limit, returning to the previous limit.
|
||||
*
|
||||
* @param oldLimit The old limit, as returned by {@code pushLimit}.
|
||||
*/
|
||||
public void popLimit(final int oldLimit) {
|
||||
currentLimit = oldLimit;
|
||||
recomputeBufferSizeAfterLimit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bytes to be read before the current limit.
|
||||
* If no limit is set, returns -1.
|
||||
*/
|
||||
public int getBytesUntilLimit() {
|
||||
if (currentLimit == Integer.MAX_VALUE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
final int currentAbsolutePosition = totalBytesRetired + bufferPos;
|
||||
return currentLimit - currentAbsolutePosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the stream has reached the end of the input. This is the
|
||||
* case if either the end of the underlying input source has been reached or
|
||||
* if the stream has reached a limit created using {@link #pushLimit(int)}.
|
||||
*/
|
||||
public boolean isAtEnd() throws IOException {
|
||||
return bufferPos == bufferSize && !refillBuffer(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* The total bytes read up to the current position. Calling
|
||||
* {@link #resetSizeCounter()} resets this value to zero.
|
||||
*/
|
||||
public int getTotalBytesRead() {
|
||||
return totalBytesRetired + bufferPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called with {@code this.buffer} is empty to read more bytes from the
|
||||
* input. If {@code mustSucceed} is true, refillBuffer() guarantees that
|
||||
* either there will be at least one byte in the buffer when it returns
|
||||
* or it will throw an exception. If {@code mustSucceed} is false,
|
||||
* refillBuffer() returns false if no more bytes were available.
|
||||
*/
|
||||
private boolean refillBuffer(final boolean mustSucceed) throws IOException {
|
||||
if (bufferPos < bufferSize) {
|
||||
throw new IllegalStateException(
|
||||
"refillBuffer() called when buffer wasn't empty.");
|
||||
}
|
||||
|
||||
if (totalBytesRetired + bufferSize == currentLimit) {
|
||||
// Oops, we hit a limit.
|
||||
if (mustSucceed) {
|
||||
throw InvalidProtocolBufferException.truncatedMessage();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
totalBytesRetired += bufferSize;
|
||||
|
||||
bufferPos = 0;
|
||||
bufferSize = (input == null) ? -1 : input.read(buffer);
|
||||
if (bufferSize == 0 || bufferSize < -1) {
|
||||
throw new IllegalStateException(
|
||||
"InputStream#read(byte[]) returned invalid result: " + bufferSize +
|
||||
"\nThe InputStream implementation is buggy.");
|
||||
}
|
||||
if (bufferSize == -1) {
|
||||
bufferSize = 0;
|
||||
if (mustSucceed) {
|
||||
throw InvalidProtocolBufferException.truncatedMessage();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
recomputeBufferSizeAfterLimit();
|
||||
final int totalBytesRead =
|
||||
totalBytesRetired + bufferSize + bufferSizeAfterLimit;
|
||||
if (totalBytesRead > sizeLimit || totalBytesRead < 0) {
|
||||
throw InvalidProtocolBufferException.sizeLimitExceeded();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read one byte from the input.
|
||||
*
|
||||
* @throws InvalidProtocolBufferException The end of the stream or the current
|
||||
* limit was reached.
|
||||
*/
|
||||
public byte readRawByte() throws IOException {
|
||||
if (bufferPos == bufferSize) {
|
||||
refillBuffer(true);
|
||||
}
|
||||
return buffer[bufferPos++];
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a fixed size of bytes from the input.
|
||||
*
|
||||
* @throws InvalidProtocolBufferException The end of the stream or the current
|
||||
* limit was reached.
|
||||
*/
|
||||
public byte[] readRawBytes(final int size) throws IOException {
|
||||
if (size < 0) {
|
||||
throw InvalidProtocolBufferException.negativeSize();
|
||||
}
|
||||
|
||||
if (totalBytesRetired + bufferPos + size > currentLimit) {
|
||||
// Read to the end of the stream anyway.
|
||||
skipRawBytes(currentLimit - totalBytesRetired - bufferPos);
|
||||
// Then fail.
|
||||
throw InvalidProtocolBufferException.truncatedMessage();
|
||||
}
|
||||
|
||||
if (size <= bufferSize - bufferPos) {
|
||||
// We have all the bytes we need already.
|
||||
final byte[] bytes = new byte[size];
|
||||
System.arraycopy(buffer, bufferPos, bytes, 0, size);
|
||||
bufferPos += size;
|
||||
return bytes;
|
||||
} else if (size < BUFFER_SIZE) {
|
||||
// Reading more bytes than are in the buffer, but not an excessive number
|
||||
// of bytes. We can safely allocate the resulting array ahead of time.
|
||||
|
||||
// First copy what we have.
|
||||
final byte[] bytes = new byte[size];
|
||||
int pos = bufferSize - bufferPos;
|
||||
System.arraycopy(buffer, bufferPos, bytes, 0, pos);
|
||||
bufferPos = bufferSize;
|
||||
|
||||
// We want to use refillBuffer() and then copy from the buffer into our
|
||||
// byte array rather than reading directly into our byte array because
|
||||
// the input may be unbuffered.
|
||||
refillBuffer(true);
|
||||
|
||||
while (size - pos > bufferSize) {
|
||||
System.arraycopy(buffer, 0, bytes, pos, bufferSize);
|
||||
pos += bufferSize;
|
||||
bufferPos = bufferSize;
|
||||
refillBuffer(true);
|
||||
}
|
||||
|
||||
System.arraycopy(buffer, 0, bytes, pos, size - pos);
|
||||
bufferPos = size - pos;
|
||||
|
||||
return bytes;
|
||||
} else {
|
||||
// The size is very large. For security reasons, we can't allocate the
|
||||
// entire byte array yet. The size comes directly from the input, so a
|
||||
// maliciously-crafted message could provide a bogus very large size in
|
||||
// order to trick the app into allocating a lot of memory. We avoid this
|
||||
// by allocating and reading only a small chunk at a time, so that the
|
||||
// malicious message must actually *be* extremely large to cause
|
||||
// problems. Meanwhile, we limit the allowed size of a message elsewhere.
|
||||
|
||||
// Remember the buffer markers since we'll have to copy the bytes out of
|
||||
// it later.
|
||||
final int originalBufferPos = bufferPos;
|
||||
final int originalBufferSize = bufferSize;
|
||||
|
||||
// Mark the current buffer consumed.
|
||||
totalBytesRetired += bufferSize;
|
||||
bufferPos = 0;
|
||||
bufferSize = 0;
|
||||
|
||||
// Read all the rest of the bytes we need.
|
||||
int sizeLeft = size - (originalBufferSize - originalBufferPos);
|
||||
final List<byte[]> chunks = new ArrayList<byte[]>();
|
||||
|
||||
while (sizeLeft > 0) {
|
||||
final byte[] chunk = new byte[Math.min(sizeLeft, BUFFER_SIZE)];
|
||||
int pos = 0;
|
||||
while (pos < chunk.length) {
|
||||
final int n = (input == null) ? -1 :
|
||||
input.read(chunk, pos, chunk.length - pos);
|
||||
if (n == -1) {
|
||||
throw InvalidProtocolBufferException.truncatedMessage();
|
||||
}
|
||||
totalBytesRetired += n;
|
||||
pos += n;
|
||||
}
|
||||
sizeLeft -= chunk.length;
|
||||
chunks.add(chunk);
|
||||
}
|
||||
|
||||
// OK, got everything. Now concatenate it all into one buffer.
|
||||
final byte[] bytes = new byte[size];
|
||||
|
||||
// Start by copying the leftover bytes from this.buffer.
|
||||
int pos = originalBufferSize - originalBufferPos;
|
||||
System.arraycopy(buffer, originalBufferPos, bytes, 0, pos);
|
||||
|
||||
// And now all the chunks.
|
||||
for (final byte[] chunk : chunks) {
|
||||
System.arraycopy(chunk, 0, bytes, pos, chunk.length);
|
||||
pos += chunk.length;
|
||||
}
|
||||
|
||||
// Done.
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and discards {@code size} bytes.
|
||||
*
|
||||
* @throws InvalidProtocolBufferException The end of the stream or the current
|
||||
* limit was reached.
|
||||
*/
|
||||
public void skipRawBytes(final int size) throws IOException {
|
||||
if (size < 0) {
|
||||
throw InvalidProtocolBufferException.negativeSize();
|
||||
}
|
||||
|
||||
if (totalBytesRetired + bufferPos + size > currentLimit) {
|
||||
// Read to the end of the stream anyway.
|
||||
skipRawBytes(currentLimit - totalBytesRetired - bufferPos);
|
||||
// Then fail.
|
||||
throw InvalidProtocolBufferException.truncatedMessage();
|
||||
}
|
||||
|
||||
if (size <= bufferSize - bufferPos) {
|
||||
// We have all the bytes we need already.
|
||||
bufferPos += size;
|
||||
} else {
|
||||
// Skipping more bytes than are in the buffer. First skip what we have.
|
||||
int pos = bufferSize - bufferPos;
|
||||
bufferPos = bufferSize;
|
||||
|
||||
// Keep refilling the buffer until we get to the point we wanted to skip
|
||||
// to. This has the side effect of ensuring the limits are updated
|
||||
// correctly.
|
||||
refillBuffer(true);
|
||||
while (size - pos > bufferSize) {
|
||||
pos += bufferSize;
|
||||
bufferPos = bufferSize;
|
||||
refillBuffer(true);
|
||||
}
|
||||
|
||||
bufferPos = size - pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,495 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
import org.apache.pekko.protobuf.Descriptors.Descriptor;
|
||||
import org.apache.pekko.protobuf.Descriptors.FieldDescriptor;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* An implementation of {@link Message} that can represent arbitrary types,
|
||||
* given a {@link Descriptors.Descriptor}.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public final class DynamicMessage extends AbstractMessage {
|
||||
private final Descriptor type;
|
||||
private final FieldSet<FieldDescriptor> fields;
|
||||
private final UnknownFieldSet unknownFields;
|
||||
private int memoizedSize = -1;
|
||||
|
||||
/**
|
||||
* Construct a {@code DynamicMessage} using the given {@code FieldSet}.
|
||||
*/
|
||||
private DynamicMessage(Descriptor type, FieldSet<FieldDescriptor> fields,
|
||||
UnknownFieldSet unknownFields) {
|
||||
this.type = type;
|
||||
this.fields = fields;
|
||||
this.unknownFields = unknownFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a {@code DynamicMessage} representing the default instance of the
|
||||
* given type.
|
||||
*/
|
||||
public static DynamicMessage getDefaultInstance(Descriptor type) {
|
||||
return new DynamicMessage(type, FieldSet.<FieldDescriptor>emptySet(),
|
||||
UnknownFieldSet.getDefaultInstance());
|
||||
}
|
||||
|
||||
/** Parse a message of the given type from the given input stream. */
|
||||
public static DynamicMessage parseFrom(Descriptor type,
|
||||
CodedInputStream input)
|
||||
throws IOException {
|
||||
return newBuilder(type).mergeFrom(input).buildParsed();
|
||||
}
|
||||
|
||||
/** Parse a message of the given type from the given input stream. */
|
||||
public static DynamicMessage parseFrom(
|
||||
Descriptor type,
|
||||
CodedInputStream input,
|
||||
ExtensionRegistry extensionRegistry)
|
||||
throws IOException {
|
||||
return newBuilder(type).mergeFrom(input, extensionRegistry).buildParsed();
|
||||
}
|
||||
|
||||
/** Parse {@code data} as a message of the given type and return it. */
|
||||
public static DynamicMessage parseFrom(Descriptor type, ByteString data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return newBuilder(type).mergeFrom(data).buildParsed();
|
||||
}
|
||||
|
||||
/** Parse {@code data} as a message of the given type and return it. */
|
||||
public static DynamicMessage parseFrom(Descriptor type, ByteString data,
|
||||
ExtensionRegistry extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return newBuilder(type).mergeFrom(data, extensionRegistry).buildParsed();
|
||||
}
|
||||
|
||||
/** Parse {@code data} as a message of the given type and return it. */
|
||||
public static DynamicMessage parseFrom(Descriptor type, byte[] data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return newBuilder(type).mergeFrom(data).buildParsed();
|
||||
}
|
||||
|
||||
/** Parse {@code data} as a message of the given type and return it. */
|
||||
public static DynamicMessage parseFrom(Descriptor type, byte[] data,
|
||||
ExtensionRegistry extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return newBuilder(type).mergeFrom(data, extensionRegistry).buildParsed();
|
||||
}
|
||||
|
||||
/** Parse a message of the given type from {@code input} and return it. */
|
||||
public static DynamicMessage parseFrom(Descriptor type, InputStream input)
|
||||
throws IOException {
|
||||
return newBuilder(type).mergeFrom(input).buildParsed();
|
||||
}
|
||||
|
||||
/** Parse a message of the given type from {@code input} and return it. */
|
||||
public static DynamicMessage parseFrom(Descriptor type, InputStream input,
|
||||
ExtensionRegistry extensionRegistry)
|
||||
throws IOException {
|
||||
return newBuilder(type).mergeFrom(input, extensionRegistry).buildParsed();
|
||||
}
|
||||
|
||||
/** Construct a {@link Message.Builder} for the given type. */
|
||||
public static Builder newBuilder(Descriptor type) {
|
||||
return new Builder(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a {@link Message.Builder} for a message of the same type as
|
||||
* {@code prototype}, and initialize it with {@code prototype}'s contents.
|
||||
*/
|
||||
public static Builder newBuilder(Message prototype) {
|
||||
return new Builder(prototype.getDescriptorForType()).mergeFrom(prototype);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Implementation of Message interface.
|
||||
|
||||
public Descriptor getDescriptorForType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public DynamicMessage getDefaultInstanceForType() {
|
||||
return getDefaultInstance(type);
|
||||
}
|
||||
|
||||
public Map<FieldDescriptor, Object> getAllFields() {
|
||||
return fields.getAllFields();
|
||||
}
|
||||
|
||||
public boolean hasField(FieldDescriptor field) {
|
||||
verifyContainingType(field);
|
||||
return fields.hasField(field);
|
||||
}
|
||||
|
||||
public Object getField(FieldDescriptor field) {
|
||||
verifyContainingType(field);
|
||||
Object result = fields.getField(field);
|
||||
if (result == null) {
|
||||
if (field.isRepeated()) {
|
||||
result = Collections.emptyList();
|
||||
} else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
result = getDefaultInstance(field.getMessageType());
|
||||
} else {
|
||||
result = field.getDefaultValue();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public int getRepeatedFieldCount(FieldDescriptor field) {
|
||||
verifyContainingType(field);
|
||||
return fields.getRepeatedFieldCount(field);
|
||||
}
|
||||
|
||||
public Object getRepeatedField(FieldDescriptor field, int index) {
|
||||
verifyContainingType(field);
|
||||
return fields.getRepeatedField(field, index);
|
||||
}
|
||||
|
||||
public UnknownFieldSet getUnknownFields() {
|
||||
return unknownFields;
|
||||
}
|
||||
|
||||
private static boolean isInitialized(Descriptor type,
|
||||
FieldSet<FieldDescriptor> fields) {
|
||||
// Check that all required fields are present.
|
||||
for (final FieldDescriptor field : type.getFields()) {
|
||||
if (field.isRequired()) {
|
||||
if (!fields.hasField(field)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that embedded messages are initialized.
|
||||
return fields.isInitialized();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInitialized() {
|
||||
return isInitialized(type, fields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(CodedOutputStream output) throws IOException {
|
||||
if (type.getOptions().getMessageSetWireFormat()) {
|
||||
fields.writeMessageSetTo(output);
|
||||
unknownFields.writeAsMessageSetTo(output);
|
||||
} else {
|
||||
fields.writeTo(output);
|
||||
unknownFields.writeTo(output);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSerializedSize() {
|
||||
int size = memoizedSize;
|
||||
if (size != -1) return size;
|
||||
|
||||
if (type.getOptions().getMessageSetWireFormat()) {
|
||||
size = fields.getMessageSetSerializedSize();
|
||||
size += unknownFields.getSerializedSizeAsMessageSet();
|
||||
} else {
|
||||
size = fields.getSerializedSize();
|
||||
size += unknownFields.getSerializedSize();
|
||||
}
|
||||
|
||||
memoizedSize = size;
|
||||
return size;
|
||||
}
|
||||
|
||||
public Builder newBuilderForType() {
|
||||
return new Builder(type);
|
||||
}
|
||||
|
||||
public Builder toBuilder() {
|
||||
return newBuilderForType().mergeFrom(this);
|
||||
}
|
||||
|
||||
public Parser<DynamicMessage> getParserForType() {
|
||||
return new AbstractParser<DynamicMessage>() {
|
||||
public DynamicMessage parsePartialFrom(
|
||||
CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
Builder builder = newBuilder(type);
|
||||
try {
|
||||
builder.mergeFrom(input, extensionRegistry);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e.setUnfinishedMessage(builder.buildPartial());
|
||||
} catch (IOException e) {
|
||||
throw new InvalidProtocolBufferException(e.getMessage())
|
||||
.setUnfinishedMessage(builder.buildPartial());
|
||||
}
|
||||
return builder.buildPartial();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** Verifies that the field is a field of this message. */
|
||||
private void verifyContainingType(FieldDescriptor field) {
|
||||
if (field.getContainingType() != type) {
|
||||
throw new IllegalArgumentException(
|
||||
"FieldDescriptor does not match message type.");
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
|
||||
/**
|
||||
* Builder for {@link DynamicMessage}s.
|
||||
*/
|
||||
public static final class Builder extends AbstractMessage.Builder<Builder> {
|
||||
private final Descriptor type;
|
||||
private FieldSet<FieldDescriptor> fields;
|
||||
private UnknownFieldSet unknownFields;
|
||||
|
||||
/** Construct a {@code Builder} for the given type. */
|
||||
private Builder(Descriptor type) {
|
||||
this.type = type;
|
||||
this.fields = FieldSet.newFieldSet();
|
||||
this.unknownFields = UnknownFieldSet.getDefaultInstance();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Implementation of Message.Builder interface.
|
||||
|
||||
@Override
|
||||
public Builder clear() {
|
||||
if (fields.isImmutable()) {
|
||||
fields = FieldSet.newFieldSet();
|
||||
} else {
|
||||
fields.clear();
|
||||
}
|
||||
unknownFields = UnknownFieldSet.getDefaultInstance();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder mergeFrom(Message other) {
|
||||
if (other instanceof DynamicMessage) {
|
||||
// This should be somewhat faster than calling super.mergeFrom().
|
||||
DynamicMessage otherDynamicMessage = (DynamicMessage) other;
|
||||
if (otherDynamicMessage.type != type) {
|
||||
throw new IllegalArgumentException(
|
||||
"mergeFrom(Message) can only merge messages of the same type.");
|
||||
}
|
||||
ensureIsMutable();
|
||||
fields.mergeFrom(otherDynamicMessage.fields);
|
||||
mergeUnknownFields(otherDynamicMessage.unknownFields);
|
||||
return this;
|
||||
} else {
|
||||
return super.mergeFrom(other);
|
||||
}
|
||||
}
|
||||
|
||||
public DynamicMessage build() {
|
||||
if (!isInitialized()) {
|
||||
throw newUninitializedMessageException(
|
||||
new DynamicMessage(type, fields, unknownFields));
|
||||
}
|
||||
return buildPartial();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for DynamicMessage.parseFrom() methods to call. Throws
|
||||
* {@link InvalidProtocolBufferException} instead of
|
||||
* {@link UninitializedMessageException}.
|
||||
*/
|
||||
private DynamicMessage buildParsed() throws InvalidProtocolBufferException {
|
||||
if (!isInitialized()) {
|
||||
throw newUninitializedMessageException(
|
||||
new DynamicMessage(type, fields, unknownFields))
|
||||
.asInvalidProtocolBufferException();
|
||||
}
|
||||
return buildPartial();
|
||||
}
|
||||
|
||||
public DynamicMessage buildPartial() {
|
||||
fields.makeImmutable();
|
||||
DynamicMessage result =
|
||||
new DynamicMessage(type, fields, unknownFields);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder clone() {
|
||||
Builder result = new Builder(type);
|
||||
result.fields.mergeFrom(fields);
|
||||
result.mergeUnknownFields(unknownFields);
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isInitialized() {
|
||||
return DynamicMessage.isInitialized(type, fields);
|
||||
}
|
||||
|
||||
public Descriptor getDescriptorForType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public DynamicMessage getDefaultInstanceForType() {
|
||||
return getDefaultInstance(type);
|
||||
}
|
||||
|
||||
public Map<FieldDescriptor, Object> getAllFields() {
|
||||
return fields.getAllFields();
|
||||
}
|
||||
|
||||
public Builder newBuilderForField(FieldDescriptor field) {
|
||||
verifyContainingType(field);
|
||||
|
||||
if (field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) {
|
||||
throw new IllegalArgumentException(
|
||||
"newBuilderForField is only valid for fields with message type.");
|
||||
}
|
||||
|
||||
return new Builder(field.getMessageType());
|
||||
}
|
||||
|
||||
public boolean hasField(FieldDescriptor field) {
|
||||
verifyContainingType(field);
|
||||
return fields.hasField(field);
|
||||
}
|
||||
|
||||
public Object getField(FieldDescriptor field) {
|
||||
verifyContainingType(field);
|
||||
Object result = fields.getField(field);
|
||||
if (result == null) {
|
||||
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
result = getDefaultInstance(field.getMessageType());
|
||||
} else {
|
||||
result = field.getDefaultValue();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Builder setField(FieldDescriptor field, Object value) {
|
||||
verifyContainingType(field);
|
||||
ensureIsMutable();
|
||||
fields.setField(field, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder clearField(FieldDescriptor field) {
|
||||
verifyContainingType(field);
|
||||
ensureIsMutable();
|
||||
fields.clearField(field);
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getRepeatedFieldCount(FieldDescriptor field) {
|
||||
verifyContainingType(field);
|
||||
return fields.getRepeatedFieldCount(field);
|
||||
}
|
||||
|
||||
public Object getRepeatedField(FieldDescriptor field, int index) {
|
||||
verifyContainingType(field);
|
||||
return fields.getRepeatedField(field, index);
|
||||
}
|
||||
|
||||
public Builder setRepeatedField(FieldDescriptor field,
|
||||
int index, Object value) {
|
||||
verifyContainingType(field);
|
||||
ensureIsMutable();
|
||||
fields.setRepeatedField(field, index, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addRepeatedField(FieldDescriptor field, Object value) {
|
||||
verifyContainingType(field);
|
||||
ensureIsMutable();
|
||||
fields.addRepeatedField(field, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public UnknownFieldSet getUnknownFields() {
|
||||
return unknownFields;
|
||||
}
|
||||
|
||||
public Builder setUnknownFields(UnknownFieldSet unknownFields) {
|
||||
this.unknownFields = unknownFields;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder mergeUnknownFields(UnknownFieldSet unknownFields) {
|
||||
this.unknownFields =
|
||||
UnknownFieldSet.newBuilder(this.unknownFields)
|
||||
.mergeFrom(unknownFields)
|
||||
.build();
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Verifies that the field is a field of this message. */
|
||||
private void verifyContainingType(FieldDescriptor field) {
|
||||
if (field.getContainingType() != type) {
|
||||
throw new IllegalArgumentException(
|
||||
"FieldDescriptor does not match message type.");
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureIsMutable() {
|
||||
if (fields.isImmutable()) {
|
||||
fields = fields.clone();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.apache.pekko.protobuf.Message.Builder getFieldBuilder(FieldDescriptor field) {
|
||||
// TODO(xiangl): need implementation for dynamic message
|
||||
throw new UnsupportedOperationException(
|
||||
"getFieldBuilder() called on a dynamic message type.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,279 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
import org.apache.pekko.protobuf.Descriptors.Descriptor;
|
||||
import org.apache.pekko.protobuf.Descriptors.FieldDescriptor;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A table of known extensions, searchable by name or field number. When
|
||||
* parsing a protocol message that might have extensions, you must provide
|
||||
* an {@code ExtensionRegistry} in which you have registered any extensions
|
||||
* that you want to be able to parse. Otherwise, those extensions will just
|
||||
* be treated like unknown fields.
|
||||
*
|
||||
* <p>For example, if you had the {@code .proto} file:
|
||||
*
|
||||
* <pre>
|
||||
* option java_class = "MyProto";
|
||||
*
|
||||
* message Foo {
|
||||
* extensions 1000 to max;
|
||||
* }
|
||||
*
|
||||
* extend Foo {
|
||||
* optional int32 bar;
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* Then you might write code like:
|
||||
*
|
||||
* <pre>
|
||||
* ExtensionRegistry registry = ExtensionRegistry.newInstance();
|
||||
* registry.add(MyProto.bar);
|
||||
* MyProto.Foo message = MyProto.Foo.parseFrom(input, registry);
|
||||
* </pre>
|
||||
*
|
||||
* <p>Background:
|
||||
*
|
||||
* <p>You might wonder why this is necessary. Two alternatives might come to
|
||||
* mind. First, you might imagine a system where generated extensions are
|
||||
* automatically registered when their containing classes are loaded. This
|
||||
* is a popular technique, but is bad design; among other things, it creates a
|
||||
* situation where behavior can change depending on what classes happen to be
|
||||
* loaded. It also introduces a security vulnerability, because an
|
||||
* unprivileged class could cause its code to be called unexpectedly from a
|
||||
* privileged class by registering itself as an extension of the right type.
|
||||
*
|
||||
* <p>Another option you might consider is lazy parsing: do not parse an
|
||||
* extension until it is first requested, at which point the caller must
|
||||
* provide a type to use. This introduces a different set of problems. First,
|
||||
* it would require a mutex lock any time an extension was accessed, which
|
||||
* would be slow. Second, corrupt data would not be detected until first
|
||||
* access, at which point it would be much harder to deal with it. Third, it
|
||||
* could violate the expectation that message objects are immutable, since the
|
||||
* type provided could be any arbitrary message class. An unprivileged user
|
||||
* could take advantage of this to inject a mutable object into a message
|
||||
* belonging to privileged code and create mischief.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public final class ExtensionRegistry extends ExtensionRegistryLite {
|
||||
/** Construct a new, empty instance. */
|
||||
public static ExtensionRegistry newInstance() {
|
||||
return new ExtensionRegistry();
|
||||
}
|
||||
|
||||
/** Get the unmodifiable singleton empty instance. */
|
||||
public static ExtensionRegistry getEmptyRegistry() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
/** Returns an unmodifiable view of the registry. */
|
||||
@Override
|
||||
public ExtensionRegistry getUnmodifiable() {
|
||||
return new ExtensionRegistry(this);
|
||||
}
|
||||
|
||||
/** A (Descriptor, Message) pair, returned by lookup methods. */
|
||||
public static final class ExtensionInfo {
|
||||
/** The extension's descriptor. */
|
||||
public final FieldDescriptor descriptor;
|
||||
|
||||
/**
|
||||
* A default instance of the extension's type, if it has a message type.
|
||||
* Otherwise, {@code null}.
|
||||
*/
|
||||
public final Message defaultInstance;
|
||||
|
||||
private ExtensionInfo(final FieldDescriptor descriptor) {
|
||||
this.descriptor = descriptor;
|
||||
defaultInstance = null;
|
||||
}
|
||||
private ExtensionInfo(final FieldDescriptor descriptor,
|
||||
final Message defaultInstance) {
|
||||
this.descriptor = descriptor;
|
||||
this.defaultInstance = defaultInstance;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an extension by fully-qualified field name, in the proto namespace.
|
||||
* I.e. {@code result.descriptor.fullName()} will match {@code fullName} if
|
||||
* a match is found.
|
||||
*
|
||||
* @return Information about the extension if found, or {@code null}
|
||||
* otherwise.
|
||||
*/
|
||||
public ExtensionInfo findExtensionByName(final String fullName) {
|
||||
return extensionsByName.get(fullName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an extension by containing type and field number.
|
||||
*
|
||||
* @return Information about the extension if found, or {@code null}
|
||||
* otherwise.
|
||||
*/
|
||||
public ExtensionInfo findExtensionByNumber(final Descriptor containingType,
|
||||
final int fieldNumber) {
|
||||
return extensionsByNumber.get(
|
||||
new DescriptorIntPair(containingType, fieldNumber));
|
||||
}
|
||||
|
||||
/** Add an extension from a generated file to the registry. */
|
||||
public void add(final GeneratedMessage.GeneratedExtension<?, ?> extension) {
|
||||
if (extension.getDescriptor().getJavaType() ==
|
||||
FieldDescriptor.JavaType.MESSAGE) {
|
||||
if (extension.getMessageDefaultInstance() == null) {
|
||||
throw new IllegalStateException(
|
||||
"Registered message-type extension had null default instance: " +
|
||||
extension.getDescriptor().getFullName());
|
||||
}
|
||||
add(new ExtensionInfo(extension.getDescriptor(),
|
||||
extension.getMessageDefaultInstance()));
|
||||
} else {
|
||||
add(new ExtensionInfo(extension.getDescriptor(), null));
|
||||
}
|
||||
}
|
||||
|
||||
/** Add a non-message-type extension to the registry by descriptor. */
|
||||
public void add(final FieldDescriptor type) {
|
||||
if (type.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
throw new IllegalArgumentException(
|
||||
"ExtensionRegistry.add() must be provided a default instance when " +
|
||||
"adding an embedded message extension.");
|
||||
}
|
||||
add(new ExtensionInfo(type, null));
|
||||
}
|
||||
|
||||
/** Add a message-type extension to the registry by descriptor. */
|
||||
public void add(final FieldDescriptor type, final Message defaultInstance) {
|
||||
if (type.getJavaType() != FieldDescriptor.JavaType.MESSAGE) {
|
||||
throw new IllegalArgumentException(
|
||||
"ExtensionRegistry.add() provided a default instance for a " +
|
||||
"non-message extension.");
|
||||
}
|
||||
add(new ExtensionInfo(type, defaultInstance));
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Private stuff.
|
||||
|
||||
private ExtensionRegistry() {
|
||||
this.extensionsByName = new HashMap<String, ExtensionInfo>();
|
||||
this.extensionsByNumber = new HashMap<DescriptorIntPair, ExtensionInfo>();
|
||||
}
|
||||
|
||||
private ExtensionRegistry(ExtensionRegistry other) {
|
||||
super(other);
|
||||
this.extensionsByName = Collections.unmodifiableMap(other.extensionsByName);
|
||||
this.extensionsByNumber =
|
||||
Collections.unmodifiableMap(other.extensionsByNumber);
|
||||
}
|
||||
|
||||
private final Map<String, ExtensionInfo> extensionsByName;
|
||||
private final Map<DescriptorIntPair, ExtensionInfo> extensionsByNumber;
|
||||
|
||||
private ExtensionRegistry(boolean empty) {
|
||||
super(ExtensionRegistryLite.getEmptyRegistry());
|
||||
this.extensionsByName = Collections.<String, ExtensionInfo>emptyMap();
|
||||
this.extensionsByNumber =
|
||||
Collections.<DescriptorIntPair, ExtensionInfo>emptyMap();
|
||||
}
|
||||
private static final ExtensionRegistry EMPTY = new ExtensionRegistry(true);
|
||||
|
||||
private void add(final ExtensionInfo extension) {
|
||||
if (!extension.descriptor.isExtension()) {
|
||||
throw new IllegalArgumentException(
|
||||
"ExtensionRegistry.add() was given a FieldDescriptor for a regular " +
|
||||
"(non-extension) field.");
|
||||
}
|
||||
|
||||
extensionsByName.put(extension.descriptor.getFullName(), extension);
|
||||
extensionsByNumber.put(
|
||||
new DescriptorIntPair(extension.descriptor.getContainingType(),
|
||||
extension.descriptor.getNumber()),
|
||||
extension);
|
||||
|
||||
final FieldDescriptor field = extension.descriptor;
|
||||
if (field.getContainingType().getOptions().getMessageSetWireFormat() &&
|
||||
field.getType() == FieldDescriptor.Type.MESSAGE &&
|
||||
field.isOptional() &&
|
||||
field.getExtensionScope() == field.getMessageType()) {
|
||||
// This is an extension of a MessageSet type defined within the extension
|
||||
// type's own scope. For backwards-compatibility, allow it to be looked
|
||||
// up by type name.
|
||||
extensionsByName.put(field.getMessageType().getFullName(), extension);
|
||||
}
|
||||
}
|
||||
|
||||
/** A (GenericDescriptor, int) pair, used as a map key. */
|
||||
private static final class DescriptorIntPair {
|
||||
private final Descriptor descriptor;
|
||||
private final int number;
|
||||
|
||||
DescriptorIntPair(final Descriptor descriptor, final int number) {
|
||||
this.descriptor = descriptor;
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return descriptor.hashCode() * ((1 << 16) - 1) + number;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (!(obj instanceof DescriptorIntPair)) {
|
||||
return false;
|
||||
}
|
||||
final DescriptorIntPair other = (DescriptorIntPair)obj;
|
||||
return descriptor == other.descriptor && number == other.number;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,198 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Equivalent to {@link ExtensionRegistry} but supports only "lite" types.
|
||||
* <p>
|
||||
* If all of your types are lite types, then you only need to use
|
||||
* {@code ExtensionRegistryLite}. Similarly, if all your types are regular
|
||||
* types, then you only need {@link ExtensionRegistry}. Typically it does not
|
||||
* make sense to mix the two, since if you have any regular types in your
|
||||
* program, you then require the full runtime and lose all the benefits of
|
||||
* the lite runtime, so you might as well make all your types be regular types.
|
||||
* However, in some cases (e.g. when depending on multiple third-party libraries
|
||||
* where one uses lite types and one uses regular), you may find yourself
|
||||
* wanting to mix the two. In this case things get more complicated.
|
||||
* <p>
|
||||
* There are three factors to consider: Whether the type being extended is
|
||||
* lite, whether the embedded type (in the case of a message-typed extension)
|
||||
* is lite, and whether the extension itself is lite. Since all three are
|
||||
* declared in different files, they could all be different. Here are all
|
||||
* the combinations and which type of registry to use:
|
||||
* <pre>
|
||||
* Extended type Inner type Extension Use registry
|
||||
* =======================================================================
|
||||
* lite lite lite ExtensionRegistryLite
|
||||
* lite regular lite ExtensionRegistry
|
||||
* regular regular regular ExtensionRegistry
|
||||
* all other combinations not supported
|
||||
* </pre>
|
||||
* <p>
|
||||
* Note that just as regular types are not allowed to contain lite-type fields,
|
||||
* they are also not allowed to contain lite-type extensions. This is because
|
||||
* regular types must be fully accessible via reflection, which in turn means
|
||||
* that all the inner messages must also support reflection. On the other hand,
|
||||
* since regular types implement the entire lite interface, there is no problem
|
||||
* with embedding regular types inside lite types.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public class ExtensionRegistryLite {
|
||||
|
||||
// Set true to enable lazy parsing feature for MessageSet.
|
||||
//
|
||||
// TODO(xiangl): Now we use a global flag to control whether enable lazy
|
||||
// parsing feature for MessageSet, which may be too crude for some
|
||||
// applications. Need to support this feature on smaller granularity.
|
||||
private static volatile boolean eagerlyParseMessageSets = false;
|
||||
|
||||
public static boolean isEagerlyParseMessageSets() {
|
||||
return eagerlyParseMessageSets;
|
||||
}
|
||||
|
||||
public static void setEagerlyParseMessageSets(boolean isEagerlyParse) {
|
||||
eagerlyParseMessageSets = isEagerlyParse;
|
||||
}
|
||||
|
||||
/** Construct a new, empty instance. */
|
||||
public static ExtensionRegistryLite newInstance() {
|
||||
return new ExtensionRegistryLite();
|
||||
}
|
||||
|
||||
/** Get the unmodifiable singleton empty instance. */
|
||||
public static ExtensionRegistryLite getEmptyRegistry() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
/** Returns an unmodifiable view of the registry. */
|
||||
public ExtensionRegistryLite getUnmodifiable() {
|
||||
return new ExtensionRegistryLite(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an extension by containing type and field number.
|
||||
*
|
||||
* @return Information about the extension if found, or {@code null}
|
||||
* otherwise.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <ContainingType extends MessageLite>
|
||||
GeneratedMessageLite.GeneratedExtension<ContainingType, ?>
|
||||
findLiteExtensionByNumber(
|
||||
final ContainingType containingTypeDefaultInstance,
|
||||
final int fieldNumber) {
|
||||
return (GeneratedMessageLite.GeneratedExtension<ContainingType, ?>)
|
||||
extensionsByNumber.get(
|
||||
new ObjectIntPair(containingTypeDefaultInstance, fieldNumber));
|
||||
}
|
||||
|
||||
/** Add an extension from a lite generated file to the registry. */
|
||||
public final void add(
|
||||
final GeneratedMessageLite.GeneratedExtension<?, ?> extension) {
|
||||
extensionsByNumber.put(
|
||||
new ObjectIntPair(extension.getContainingTypeDefaultInstance(),
|
||||
extension.getNumber()),
|
||||
extension);
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Private stuff.
|
||||
|
||||
// Constructors are package-private so that ExtensionRegistry can subclass
|
||||
// this.
|
||||
|
||||
ExtensionRegistryLite() {
|
||||
this.extensionsByNumber =
|
||||
new HashMap<ObjectIntPair,
|
||||
GeneratedMessageLite.GeneratedExtension<?, ?>>();
|
||||
}
|
||||
|
||||
ExtensionRegistryLite(ExtensionRegistryLite other) {
|
||||
if (other == EMPTY) {
|
||||
this.extensionsByNumber = Collections.emptyMap();
|
||||
} else {
|
||||
this.extensionsByNumber =
|
||||
Collections.unmodifiableMap(other.extensionsByNumber);
|
||||
}
|
||||
}
|
||||
|
||||
private final Map<ObjectIntPair,
|
||||
GeneratedMessageLite.GeneratedExtension<?, ?>>
|
||||
extensionsByNumber;
|
||||
|
||||
private ExtensionRegistryLite(boolean empty) {
|
||||
this.extensionsByNumber = Collections.emptyMap();
|
||||
}
|
||||
private static final ExtensionRegistryLite EMPTY =
|
||||
new ExtensionRegistryLite(true);
|
||||
|
||||
/** A (Object, int) pair, used as a map key. */
|
||||
private static final class ObjectIntPair {
|
||||
private final Object object;
|
||||
private final int number;
|
||||
|
||||
ObjectIntPair(final Object object, final int number) {
|
||||
this.object = object;
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return System.identityHashCode(object) * ((1 << 16) - 1) + number;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (!(obj instanceof ObjectIntPair)) {
|
||||
return false;
|
||||
}
|
||||
final ObjectIntPair other = (ObjectIntPair)obj;
|
||||
return object == other.object && number == other.number;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,874 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
import org.apache.pekko.protobuf.LazyField.LazyIterator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A class which represents an arbitrary set of fields of some message type.
|
||||
* This is used to implement {@link DynamicMessage}, and also to represent
|
||||
* extensions in {@link GeneratedMessage}. This class is package-private,
|
||||
* since outside users should probably be using {@link DynamicMessage}.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
final class FieldSet<FieldDescriptorType extends
|
||||
FieldSet.FieldDescriptorLite<FieldDescriptorType>> {
|
||||
/**
|
||||
* Interface for a FieldDescriptor or lite extension descriptor. This
|
||||
* prevents FieldSet from depending on {@link Descriptors.FieldDescriptor}.
|
||||
*/
|
||||
public interface FieldDescriptorLite<T extends FieldDescriptorLite<T>>
|
||||
extends Comparable<T> {
|
||||
int getNumber();
|
||||
WireFormat.FieldType getLiteType();
|
||||
WireFormat.JavaType getLiteJavaType();
|
||||
boolean isRepeated();
|
||||
boolean isPacked();
|
||||
Internal.EnumLiteMap<?> getEnumType();
|
||||
|
||||
// If getLiteJavaType() == MESSAGE, this merges a message object of the
|
||||
// type into a builder of the type. Returns {@code to}.
|
||||
MessageLite.Builder internalMergeFrom(
|
||||
MessageLite.Builder to, MessageLite from);
|
||||
}
|
||||
|
||||
private final SmallSortedMap<FieldDescriptorType, Object> fields;
|
||||
private boolean isImmutable;
|
||||
private boolean hasLazyField = false;
|
||||
|
||||
/** Construct a new FieldSet. */
|
||||
private FieldSet() {
|
||||
this.fields = SmallSortedMap.newFieldMap(16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an empty FieldSet. This is only used to initialize
|
||||
* DEFAULT_INSTANCE.
|
||||
*/
|
||||
private FieldSet(final boolean dummy) {
|
||||
this.fields = SmallSortedMap.newFieldMap(0);
|
||||
makeImmutable();
|
||||
}
|
||||
|
||||
/** Construct a new FieldSet. */
|
||||
public static <T extends FieldSet.FieldDescriptorLite<T>>
|
||||
FieldSet<T> newFieldSet() {
|
||||
return new FieldSet<T>();
|
||||
}
|
||||
|
||||
/** Get an immutable empty FieldSet. */
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends FieldSet.FieldDescriptorLite<T>>
|
||||
FieldSet<T> emptySet() {
|
||||
return DEFAULT_INSTANCE;
|
||||
}
|
||||
@SuppressWarnings("rawtypes")
|
||||
private static final FieldSet DEFAULT_INSTANCE = new FieldSet(true);
|
||||
|
||||
/** Make this FieldSet immutable from this point forward. */
|
||||
@SuppressWarnings("unchecked")
|
||||
public void makeImmutable() {
|
||||
if (isImmutable) {
|
||||
return;
|
||||
}
|
||||
fields.makeImmutable();
|
||||
isImmutable = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the FieldSet is immutable. This is true if it is the
|
||||
* {@link #emptySet} or if {@link #makeImmutable} were called.
|
||||
*
|
||||
* @return whether the FieldSet is immutable.
|
||||
*/
|
||||
public boolean isImmutable() {
|
||||
return isImmutable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones the FieldSet. The returned FieldSet will be mutable even if the
|
||||
* original FieldSet was immutable.
|
||||
*
|
||||
* @return the newly cloned FieldSet
|
||||
*/
|
||||
@Override
|
||||
public FieldSet<FieldDescriptorType> clone() {
|
||||
// We can't just call fields.clone because List objects in the map
|
||||
// should not be shared.
|
||||
FieldSet<FieldDescriptorType> clone = FieldSet.newFieldSet();
|
||||
for (int i = 0; i < fields.getNumArrayEntries(); i++) {
|
||||
Map.Entry<FieldDescriptorType, Object> entry = fields.getArrayEntryAt(i);
|
||||
FieldDescriptorType descriptor = entry.getKey();
|
||||
clone.setField(descriptor, entry.getValue());
|
||||
}
|
||||
for (Map.Entry<FieldDescriptorType, Object> entry :
|
||||
fields.getOverflowEntries()) {
|
||||
FieldDescriptorType descriptor = entry.getKey();
|
||||
clone.setField(descriptor, entry.getValue());
|
||||
}
|
||||
clone.hasLazyField = hasLazyField;
|
||||
return clone;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
|
||||
/** See {@link Message.Builder#clear()}. */
|
||||
public void clear() {
|
||||
fields.clear();
|
||||
hasLazyField = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a simple map containing all the fields.
|
||||
*/
|
||||
public Map<FieldDescriptorType, Object> getAllFields() {
|
||||
if (hasLazyField) {
|
||||
SmallSortedMap<FieldDescriptorType, Object> result =
|
||||
SmallSortedMap.newFieldMap(16);
|
||||
for (int i = 0; i < fields.getNumArrayEntries(); i++) {
|
||||
cloneFieldEntry(result, fields.getArrayEntryAt(i));
|
||||
}
|
||||
for (Map.Entry<FieldDescriptorType, Object> entry :
|
||||
fields.getOverflowEntries()) {
|
||||
cloneFieldEntry(result, entry);
|
||||
}
|
||||
if (fields.isImmutable()) {
|
||||
result.makeImmutable();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return fields.isImmutable() ? fields : Collections.unmodifiableMap(fields);
|
||||
}
|
||||
|
||||
private void cloneFieldEntry(Map<FieldDescriptorType, Object> map,
|
||||
Map.Entry<FieldDescriptorType, Object> entry) {
|
||||
FieldDescriptorType key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
if (value instanceof LazyField) {
|
||||
map.put(key, ((LazyField) value).getValue());
|
||||
} else {
|
||||
map.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an iterator to the field map. This iterator should not be leaked out
|
||||
* of the protobuf library as it is not protected from mutation when fields
|
||||
* is not immutable.
|
||||
*/
|
||||
public Iterator<Map.Entry<FieldDescriptorType, Object>> iterator() {
|
||||
if (hasLazyField) {
|
||||
return new LazyIterator<FieldDescriptorType>(
|
||||
fields.entrySet().iterator());
|
||||
}
|
||||
return fields.entrySet().iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful for implementing
|
||||
* {@link Message#hasField(Descriptors.FieldDescriptor)}.
|
||||
*/
|
||||
public boolean hasField(final FieldDescriptorType descriptor) {
|
||||
if (descriptor.isRepeated()) {
|
||||
throw new IllegalArgumentException(
|
||||
"hasField() can only be called on non-repeated fields.");
|
||||
}
|
||||
|
||||
return fields.get(descriptor) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful for implementing
|
||||
* {@link Message#getField(Descriptors.FieldDescriptor)}. This method
|
||||
* returns {@code null} if the field is not set; in this case it is up
|
||||
* to the caller to fetch the field's default value.
|
||||
*/
|
||||
public Object getField(final FieldDescriptorType descriptor) {
|
||||
Object o = fields.get(descriptor);
|
||||
if (o instanceof LazyField) {
|
||||
return ((LazyField) o).getValue();
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful for implementing
|
||||
* {@link Message.Builder#setField(Descriptors.FieldDescriptor,Object)}.
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public void setField(final FieldDescriptorType descriptor,
|
||||
Object value) {
|
||||
if (descriptor.isRepeated()) {
|
||||
if (!(value instanceof List)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Wrong object type used with protocol message reflection.");
|
||||
}
|
||||
|
||||
// Wrap the contents in a new list so that the caller cannot change
|
||||
// the list's contents after setting it.
|
||||
final List newList = new ArrayList();
|
||||
newList.addAll((List) value);
|
||||
for (final Object element : newList) {
|
||||
verifyType(descriptor.getLiteType(), element);
|
||||
}
|
||||
value = newList;
|
||||
} else {
|
||||
verifyType(descriptor.getLiteType(), value);
|
||||
}
|
||||
|
||||
if (value instanceof LazyField) {
|
||||
hasLazyField = true;
|
||||
}
|
||||
fields.put(descriptor, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful for implementing
|
||||
* {@link Message.Builder#clearField(Descriptors.FieldDescriptor)}.
|
||||
*/
|
||||
public void clearField(final FieldDescriptorType descriptor) {
|
||||
fields.remove(descriptor);
|
||||
if (fields.isEmpty()) {
|
||||
hasLazyField = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful for implementing
|
||||
* {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)}.
|
||||
*/
|
||||
public int getRepeatedFieldCount(final FieldDescriptorType descriptor) {
|
||||
if (!descriptor.isRepeated()) {
|
||||
throw new IllegalArgumentException(
|
||||
"getRepeatedField() can only be called on repeated fields.");
|
||||
}
|
||||
|
||||
final Object value = getField(descriptor);
|
||||
if (value == null) {
|
||||
return 0;
|
||||
} else {
|
||||
return ((List<?>) value).size();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful for implementing
|
||||
* {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)}.
|
||||
*/
|
||||
public Object getRepeatedField(final FieldDescriptorType descriptor,
|
||||
final int index) {
|
||||
if (!descriptor.isRepeated()) {
|
||||
throw new IllegalArgumentException(
|
||||
"getRepeatedField() can only be called on repeated fields.");
|
||||
}
|
||||
|
||||
final Object value = getField(descriptor);
|
||||
|
||||
if (value == null) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
} else {
|
||||
return ((List<?>) value).get(index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful for implementing
|
||||
* {@link Message.Builder#setRepeatedField(Descriptors.FieldDescriptor,int,Object)}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setRepeatedField(final FieldDescriptorType descriptor,
|
||||
final int index,
|
||||
final Object value) {
|
||||
if (!descriptor.isRepeated()) {
|
||||
throw new IllegalArgumentException(
|
||||
"getRepeatedField() can only be called on repeated fields.");
|
||||
}
|
||||
|
||||
final Object list = getField(descriptor);
|
||||
if (list == null) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
verifyType(descriptor.getLiteType(), value);
|
||||
((List<Object>) list).set(index, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful for implementing
|
||||
* {@link Message.Builder#addRepeatedField(Descriptors.FieldDescriptor,Object)}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void addRepeatedField(final FieldDescriptorType descriptor,
|
||||
final Object value) {
|
||||
if (!descriptor.isRepeated()) {
|
||||
throw new IllegalArgumentException(
|
||||
"addRepeatedField() can only be called on repeated fields.");
|
||||
}
|
||||
|
||||
verifyType(descriptor.getLiteType(), value);
|
||||
|
||||
final Object existingValue = getField(descriptor);
|
||||
List<Object> list;
|
||||
if (existingValue == null) {
|
||||
list = new ArrayList<Object>();
|
||||
fields.put(descriptor, list);
|
||||
} else {
|
||||
list = (List<Object>) existingValue;
|
||||
}
|
||||
|
||||
list.add(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the given object is of the correct type to be a valid
|
||||
* value for the given field. (For repeated fields, this checks if the
|
||||
* object is the right type to be one element of the field.)
|
||||
*
|
||||
* @throws IllegalArgumentException The value is not of the right type.
|
||||
*/
|
||||
private static void verifyType(final WireFormat.FieldType type,
|
||||
final Object value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
boolean isValid = false;
|
||||
switch (type.getJavaType()) {
|
||||
case INT: isValid = value instanceof Integer ; break;
|
||||
case LONG: isValid = value instanceof Long ; break;
|
||||
case FLOAT: isValid = value instanceof Float ; break;
|
||||
case DOUBLE: isValid = value instanceof Double ; break;
|
||||
case BOOLEAN: isValid = value instanceof Boolean ; break;
|
||||
case STRING: isValid = value instanceof String ; break;
|
||||
case BYTE_STRING: isValid = value instanceof ByteString; break;
|
||||
case ENUM:
|
||||
// TODO(kenton): Caller must do type checking here, I guess.
|
||||
isValid = value instanceof Internal.EnumLite;
|
||||
break;
|
||||
case MESSAGE:
|
||||
// TODO(kenton): Caller must do type checking here, I guess.
|
||||
isValid =
|
||||
(value instanceof MessageLite) || (value instanceof LazyField);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
// TODO(kenton): When chaining calls to setField(), it can be hard to
|
||||
// tell from the stack trace which exact call failed, since the whole
|
||||
// chain is considered one line of code. It would be nice to print
|
||||
// more information here, e.g. naming the field. We used to do that.
|
||||
// But we can't now that FieldSet doesn't use descriptors. Maybe this
|
||||
// isn't a big deal, though, since it would only really apply when using
|
||||
// reflection and generally people don't chain reflection setters.
|
||||
throw new IllegalArgumentException(
|
||||
"Wrong object type used with protocol message reflection.");
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Parsing and serialization
|
||||
|
||||
/**
|
||||
* See {@link Message#isInitialized()}. Note: Since {@code FieldSet}
|
||||
* itself does not have any way of knowing about required fields that
|
||||
* aren't actually present in the set, it is up to the caller to check
|
||||
* that all required fields are present.
|
||||
*/
|
||||
public boolean isInitialized() {
|
||||
for (int i = 0; i < fields.getNumArrayEntries(); i++) {
|
||||
if (!isInitialized(fields.getArrayEntryAt(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (final Map.Entry<FieldDescriptorType, Object> entry :
|
||||
fields.getOverflowEntries()) {
|
||||
if (!isInitialized(entry)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private boolean isInitialized(
|
||||
final Map.Entry<FieldDescriptorType, Object> entry) {
|
||||
final FieldDescriptorType descriptor = entry.getKey();
|
||||
if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
|
||||
if (descriptor.isRepeated()) {
|
||||
for (final MessageLite element:
|
||||
(List<MessageLite>) entry.getValue()) {
|
||||
if (!element.isInitialized()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Object value = entry.getValue();
|
||||
if (value instanceof MessageLite) {
|
||||
if (!((MessageLite) value).isInitialized()) {
|
||||
return false;
|
||||
}
|
||||
} else if (value instanceof LazyField) {
|
||||
return true;
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Wrong object type used with protocol message reflection.");
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a field type, return the wire type.
|
||||
*
|
||||
* @returns One of the {@code WIRETYPE_} constants defined in
|
||||
* {@link WireFormat}.
|
||||
*/
|
||||
static int getWireFormatForFieldType(final WireFormat.FieldType type,
|
||||
boolean isPacked) {
|
||||
if (isPacked) {
|
||||
return WireFormat.WIRETYPE_LENGTH_DELIMITED;
|
||||
} else {
|
||||
return type.getWireType();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link Message.Builder#mergeFrom(Message)}, but merges from another
|
||||
* {@link FieldSet}.
|
||||
*/
|
||||
public void mergeFrom(final FieldSet<FieldDescriptorType> other) {
|
||||
for (int i = 0; i < other.fields.getNumArrayEntries(); i++) {
|
||||
mergeFromField(other.fields.getArrayEntryAt(i));
|
||||
}
|
||||
for (final Map.Entry<FieldDescriptorType, Object> entry :
|
||||
other.fields.getOverflowEntries()) {
|
||||
mergeFromField(entry);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private void mergeFromField(
|
||||
final Map.Entry<FieldDescriptorType, Object> entry) {
|
||||
final FieldDescriptorType descriptor = entry.getKey();
|
||||
Object otherValue = entry.getValue();
|
||||
if (otherValue instanceof LazyField) {
|
||||
otherValue = ((LazyField) otherValue).getValue();
|
||||
}
|
||||
|
||||
if (descriptor.isRepeated()) {
|
||||
Object value = getField(descriptor);
|
||||
if (value == null) {
|
||||
// Our list is empty, but we still need to make a defensive copy of
|
||||
// the other list since we don't know if the other FieldSet is still
|
||||
// mutable.
|
||||
fields.put(descriptor, new ArrayList((List) otherValue));
|
||||
} else {
|
||||
// Concatenate the lists.
|
||||
((List) value).addAll((List) otherValue);
|
||||
}
|
||||
} else if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
|
||||
Object value = getField(descriptor);
|
||||
if (value == null) {
|
||||
fields.put(descriptor, otherValue);
|
||||
} else {
|
||||
// Merge the messages.
|
||||
fields.put(
|
||||
descriptor,
|
||||
descriptor.internalMergeFrom(
|
||||
((MessageLite) value).toBuilder(), (MessageLite) otherValue)
|
||||
.build());
|
||||
}
|
||||
} else {
|
||||
fields.put(descriptor, otherValue);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(kenton): Move static parsing and serialization methods into some
|
||||
// other class. Probably WireFormat.
|
||||
|
||||
/**
|
||||
* Read a field of any primitive type from a CodedInputStream. Enums,
|
||||
* groups, and embedded messages are not handled by this method.
|
||||
*
|
||||
* @param input The stream from which to read.
|
||||
* @param type Declared type of the field.
|
||||
* @return An object representing the field's value, of the exact
|
||||
* type which would be returned by
|
||||
* {@link Message#getField(Descriptors.FieldDescriptor)} for
|
||||
* this field.
|
||||
*/
|
||||
public static Object readPrimitiveField(
|
||||
CodedInputStream input,
|
||||
final WireFormat.FieldType type) throws IOException {
|
||||
switch (type) {
|
||||
case DOUBLE : return input.readDouble ();
|
||||
case FLOAT : return input.readFloat ();
|
||||
case INT64 : return input.readInt64 ();
|
||||
case UINT64 : return input.readUInt64 ();
|
||||
case INT32 : return input.readInt32 ();
|
||||
case FIXED64 : return input.readFixed64 ();
|
||||
case FIXED32 : return input.readFixed32 ();
|
||||
case BOOL : return input.readBool ();
|
||||
case STRING : return input.readString ();
|
||||
case BYTES : return input.readBytes ();
|
||||
case UINT32 : return input.readUInt32 ();
|
||||
case SFIXED32: return input.readSFixed32();
|
||||
case SFIXED64: return input.readSFixed64();
|
||||
case SINT32 : return input.readSInt32 ();
|
||||
case SINT64 : return input.readSInt64 ();
|
||||
|
||||
case GROUP:
|
||||
throw new IllegalArgumentException(
|
||||
"readPrimitiveField() cannot handle nested groups.");
|
||||
case MESSAGE:
|
||||
throw new IllegalArgumentException(
|
||||
"readPrimitiveField() cannot handle embedded messages.");
|
||||
case ENUM:
|
||||
// We don't handle enums because we don't know what to do if the
|
||||
// value is not recognized.
|
||||
throw new IllegalArgumentException(
|
||||
"readPrimitiveField() cannot handle enums.");
|
||||
}
|
||||
|
||||
throw new RuntimeException(
|
||||
"There is no way to get here, but the compiler thinks otherwise.");
|
||||
}
|
||||
|
||||
/** See {@link Message#writeTo(CodedOutputStream)}. */
|
||||
public void writeTo(final CodedOutputStream output)
|
||||
throws IOException {
|
||||
for (int i = 0; i < fields.getNumArrayEntries(); i++) {
|
||||
final Map.Entry<FieldDescriptorType, Object> entry =
|
||||
fields.getArrayEntryAt(i);
|
||||
writeField(entry.getKey(), entry.getValue(), output);
|
||||
}
|
||||
for (final Map.Entry<FieldDescriptorType, Object> entry :
|
||||
fields.getOverflowEntries()) {
|
||||
writeField(entry.getKey(), entry.getValue(), output);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #writeTo} but uses MessageSet wire format.
|
||||
*/
|
||||
public void writeMessageSetTo(final CodedOutputStream output)
|
||||
throws IOException {
|
||||
for (int i = 0; i < fields.getNumArrayEntries(); i++) {
|
||||
writeMessageSetTo(fields.getArrayEntryAt(i), output);
|
||||
}
|
||||
for (final Map.Entry<FieldDescriptorType, Object> entry :
|
||||
fields.getOverflowEntries()) {
|
||||
writeMessageSetTo(entry, output);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeMessageSetTo(
|
||||
final Map.Entry<FieldDescriptorType, Object> entry,
|
||||
final CodedOutputStream output) throws IOException {
|
||||
final FieldDescriptorType descriptor = entry.getKey();
|
||||
if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE &&
|
||||
!descriptor.isRepeated() && !descriptor.isPacked()) {
|
||||
output.writeMessageSetExtension(entry.getKey().getNumber(),
|
||||
(MessageLite) entry.getValue());
|
||||
} else {
|
||||
writeField(descriptor, entry.getValue(), output);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a single tag-value pair to the stream.
|
||||
*
|
||||
* @param output The output stream.
|
||||
* @param type The field's type.
|
||||
* @param number The field's number.
|
||||
* @param value Object representing the field's value. Must be of the exact
|
||||
* type which would be returned by
|
||||
* {@link Message#getField(Descriptors.FieldDescriptor)} for
|
||||
* this field.
|
||||
*/
|
||||
private static void writeElement(final CodedOutputStream output,
|
||||
final WireFormat.FieldType type,
|
||||
final int number,
|
||||
final Object value) throws IOException {
|
||||
// Special case for groups, which need a start and end tag; other fields
|
||||
// can just use writeTag() and writeFieldNoTag().
|
||||
if (type == WireFormat.FieldType.GROUP) {
|
||||
output.writeGroup(number, (MessageLite) value);
|
||||
} else {
|
||||
output.writeTag(number, getWireFormatForFieldType(type, false));
|
||||
writeElementNoTag(output, type, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a field of arbitrary type, without its tag, to the stream.
|
||||
*
|
||||
* @param output The output stream.
|
||||
* @param type The field's type.
|
||||
* @param value Object representing the field's value. Must be of the exact
|
||||
* type which would be returned by
|
||||
* {@link Message#getField(Descriptors.FieldDescriptor)} for
|
||||
* this field.
|
||||
*/
|
||||
private static void writeElementNoTag(
|
||||
final CodedOutputStream output,
|
||||
final WireFormat.FieldType type,
|
||||
final Object value) throws IOException {
|
||||
switch (type) {
|
||||
case DOUBLE : output.writeDoubleNoTag ((Double ) value); break;
|
||||
case FLOAT : output.writeFloatNoTag ((Float ) value); break;
|
||||
case INT64 : output.writeInt64NoTag ((Long ) value); break;
|
||||
case UINT64 : output.writeUInt64NoTag ((Long ) value); break;
|
||||
case INT32 : output.writeInt32NoTag ((Integer ) value); break;
|
||||
case FIXED64 : output.writeFixed64NoTag ((Long ) value); break;
|
||||
case FIXED32 : output.writeFixed32NoTag ((Integer ) value); break;
|
||||
case BOOL : output.writeBoolNoTag ((Boolean ) value); break;
|
||||
case STRING : output.writeStringNoTag ((String ) value); break;
|
||||
case GROUP : output.writeGroupNoTag ((MessageLite) value); break;
|
||||
case MESSAGE : output.writeMessageNoTag ((MessageLite) value); break;
|
||||
case BYTES : output.writeBytesNoTag ((ByteString ) value); break;
|
||||
case UINT32 : output.writeUInt32NoTag ((Integer ) value); break;
|
||||
case SFIXED32: output.writeSFixed32NoTag((Integer ) value); break;
|
||||
case SFIXED64: output.writeSFixed64NoTag((Long ) value); break;
|
||||
case SINT32 : output.writeSInt32NoTag ((Integer ) value); break;
|
||||
case SINT64 : output.writeSInt64NoTag ((Long ) value); break;
|
||||
|
||||
case ENUM:
|
||||
output.writeEnumNoTag(((Internal.EnumLite) value).getNumber());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** Write a single field. */
|
||||
public static void writeField(final FieldDescriptorLite<?> descriptor,
|
||||
final Object value,
|
||||
final CodedOutputStream output)
|
||||
throws IOException {
|
||||
WireFormat.FieldType type = descriptor.getLiteType();
|
||||
int number = descriptor.getNumber();
|
||||
if (descriptor.isRepeated()) {
|
||||
final List<?> valueList = (List<?>)value;
|
||||
if (descriptor.isPacked()) {
|
||||
output.writeTag(number, WireFormat.WIRETYPE_LENGTH_DELIMITED);
|
||||
// Compute the total data size so the length can be written.
|
||||
int dataSize = 0;
|
||||
for (final Object element : valueList) {
|
||||
dataSize += computeElementSizeNoTag(type, element);
|
||||
}
|
||||
output.writeRawVarint32(dataSize);
|
||||
// Write the data itself, without any tags.
|
||||
for (final Object element : valueList) {
|
||||
writeElementNoTag(output, type, element);
|
||||
}
|
||||
} else {
|
||||
for (final Object element : valueList) {
|
||||
writeElement(output, type, number, element);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (value instanceof LazyField) {
|
||||
writeElement(output, type, number, ((LazyField) value).getValue());
|
||||
} else {
|
||||
writeElement(output, type, number, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link Message#getSerializedSize()}. It's up to the caller to cache
|
||||
* the resulting size if desired.
|
||||
*/
|
||||
public int getSerializedSize() {
|
||||
int size = 0;
|
||||
for (int i = 0; i < fields.getNumArrayEntries(); i++) {
|
||||
final Map.Entry<FieldDescriptorType, Object> entry =
|
||||
fields.getArrayEntryAt(i);
|
||||
size += computeFieldSize(entry.getKey(), entry.getValue());
|
||||
}
|
||||
for (final Map.Entry<FieldDescriptorType, Object> entry :
|
||||
fields.getOverflowEntries()) {
|
||||
size += computeFieldSize(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #getSerializedSize} but uses MessageSet wire format.
|
||||
*/
|
||||
public int getMessageSetSerializedSize() {
|
||||
int size = 0;
|
||||
for (int i = 0; i < fields.getNumArrayEntries(); i++) {
|
||||
size += getMessageSetSerializedSize(fields.getArrayEntryAt(i));
|
||||
}
|
||||
for (final Map.Entry<FieldDescriptorType, Object> entry :
|
||||
fields.getOverflowEntries()) {
|
||||
size += getMessageSetSerializedSize(entry);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
private int getMessageSetSerializedSize(
|
||||
final Map.Entry<FieldDescriptorType, Object> entry) {
|
||||
final FieldDescriptorType descriptor = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE
|
||||
&& !descriptor.isRepeated() && !descriptor.isPacked()) {
|
||||
if (value instanceof LazyField) {
|
||||
return CodedOutputStream.computeLazyFieldMessageSetExtensionSize(
|
||||
entry.getKey().getNumber(), (LazyField) value);
|
||||
} else {
|
||||
return CodedOutputStream.computeMessageSetExtensionSize(
|
||||
entry.getKey().getNumber(), (MessageLite) value);
|
||||
}
|
||||
} else {
|
||||
return computeFieldSize(descriptor, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the number of bytes that would be needed to encode a
|
||||
* single tag/value pair of arbitrary type.
|
||||
*
|
||||
* @param type The field's type.
|
||||
* @param number The field's number.
|
||||
* @param value Object representing the field's value. Must be of the exact
|
||||
* type which would be returned by
|
||||
* {@link Message#getField(Descriptors.FieldDescriptor)} for
|
||||
* this field.
|
||||
*/
|
||||
private static int computeElementSize(
|
||||
final WireFormat.FieldType type,
|
||||
final int number, final Object value) {
|
||||
int tagSize = CodedOutputStream.computeTagSize(number);
|
||||
if (type == WireFormat.FieldType.GROUP) {
|
||||
tagSize *= 2;
|
||||
}
|
||||
return tagSize + computeElementSizeNoTag(type, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the number of bytes that would be needed to encode a
|
||||
* particular value of arbitrary type, excluding tag.
|
||||
*
|
||||
* @param type The field's type.
|
||||
* @param value Object representing the field's value. Must be of the exact
|
||||
* type which would be returned by
|
||||
* {@link Message#getField(Descriptors.FieldDescriptor)} for
|
||||
* this field.
|
||||
*/
|
||||
private static int computeElementSizeNoTag(
|
||||
final WireFormat.FieldType type, final Object value) {
|
||||
switch (type) {
|
||||
// Note: Minor violation of 80-char limit rule here because this would
|
||||
// actually be harder to read if we wrapped the lines.
|
||||
case DOUBLE : return CodedOutputStream.computeDoubleSizeNoTag ((Double )value);
|
||||
case FLOAT : return CodedOutputStream.computeFloatSizeNoTag ((Float )value);
|
||||
case INT64 : return CodedOutputStream.computeInt64SizeNoTag ((Long )value);
|
||||
case UINT64 : return CodedOutputStream.computeUInt64SizeNoTag ((Long )value);
|
||||
case INT32 : return CodedOutputStream.computeInt32SizeNoTag ((Integer )value);
|
||||
case FIXED64 : return CodedOutputStream.computeFixed64SizeNoTag ((Long )value);
|
||||
case FIXED32 : return CodedOutputStream.computeFixed32SizeNoTag ((Integer )value);
|
||||
case BOOL : return CodedOutputStream.computeBoolSizeNoTag ((Boolean )value);
|
||||
case STRING : return CodedOutputStream.computeStringSizeNoTag ((String )value);
|
||||
case GROUP : return CodedOutputStream.computeGroupSizeNoTag ((MessageLite)value);
|
||||
case BYTES : return CodedOutputStream.computeBytesSizeNoTag ((ByteString )value);
|
||||
case UINT32 : return CodedOutputStream.computeUInt32SizeNoTag ((Integer )value);
|
||||
case SFIXED32: return CodedOutputStream.computeSFixed32SizeNoTag((Integer )value);
|
||||
case SFIXED64: return CodedOutputStream.computeSFixed64SizeNoTag((Long )value);
|
||||
case SINT32 : return CodedOutputStream.computeSInt32SizeNoTag ((Integer )value);
|
||||
case SINT64 : return CodedOutputStream.computeSInt64SizeNoTag ((Long )value);
|
||||
|
||||
case MESSAGE:
|
||||
if (value instanceof LazyField) {
|
||||
return CodedOutputStream.computeLazyFieldSizeNoTag((LazyField) value);
|
||||
} else {
|
||||
return CodedOutputStream.computeMessageSizeNoTag((MessageLite) value);
|
||||
}
|
||||
|
||||
case ENUM:
|
||||
return CodedOutputStream.computeEnumSizeNoTag(
|
||||
((Internal.EnumLite) value).getNumber());
|
||||
}
|
||||
|
||||
throw new RuntimeException(
|
||||
"There is no way to get here, but the compiler thinks otherwise.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the number of bytes needed to encode a particular field.
|
||||
*/
|
||||
public static int computeFieldSize(final FieldDescriptorLite<?> descriptor,
|
||||
final Object value) {
|
||||
WireFormat.FieldType type = descriptor.getLiteType();
|
||||
int number = descriptor.getNumber();
|
||||
if (descriptor.isRepeated()) {
|
||||
if (descriptor.isPacked()) {
|
||||
int dataSize = 0;
|
||||
for (final Object element : (List<?>)value) {
|
||||
dataSize += computeElementSizeNoTag(type, element);
|
||||
}
|
||||
return dataSize +
|
||||
CodedOutputStream.computeTagSize(number) +
|
||||
CodedOutputStream.computeRawVarint32Size(dataSize);
|
||||
} else {
|
||||
int size = 0;
|
||||
for (final Object element : (List<?>)value) {
|
||||
size += computeElementSize(type, number, element);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
} else {
|
||||
return computeElementSize(type, number, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,747 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectStreamException;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Lite version of {@link GeneratedMessage}.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public abstract class GeneratedMessageLite extends AbstractMessageLite implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
protected GeneratedMessageLite() {}
|
||||
|
||||
protected GeneratedMessageLite(Builder builder) {}
|
||||
|
||||
public Parser<? extends MessageLite> getParserForType() {
|
||||
throw new UnsupportedOperationException("This is supposed to be overridden by subclasses.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by subclasses to parse an unknown field.
|
||||
*
|
||||
* @return {@code true} unless the tag is an end-group tag.
|
||||
*/
|
||||
protected boolean parseUnknownField(
|
||||
CodedInputStream input, ExtensionRegistryLite extensionRegistry, int tag) throws IOException {
|
||||
return input.skipField(tag);
|
||||
}
|
||||
|
||||
/** Used by parsing constructors in generated classes. */
|
||||
protected void makeExtensionsImmutable() {
|
||||
// Noop for messages without extensions.
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public abstract static class Builder<
|
||||
MessageType extends GeneratedMessageLite, BuilderType extends Builder>
|
||||
extends AbstractMessageLite.Builder<BuilderType> {
|
||||
protected Builder() {}
|
||||
|
||||
// @Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public BuilderType clear() {
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
// This is implemented here only to work around an apparent bug in the
|
||||
// Java compiler and/or build system. See bug #1898463. The mere presence
|
||||
// of this dummy clone() implementation makes it go away.
|
||||
@Override
|
||||
public BuilderType clone() {
|
||||
throw new UnsupportedOperationException("This is supposed to be overridden by subclasses.");
|
||||
}
|
||||
|
||||
/** All subclasses implement this. */
|
||||
public abstract BuilderType mergeFrom(MessageType message);
|
||||
|
||||
// Defined here for return type covariance.
|
||||
public abstract MessageType getDefaultInstanceForType();
|
||||
|
||||
/**
|
||||
* Called by subclasses to parse an unknown field.
|
||||
*
|
||||
* @return {@code true} unless the tag is an end-group tag.
|
||||
*/
|
||||
protected boolean parseUnknownField(
|
||||
CodedInputStream input, ExtensionRegistryLite extensionRegistry, int tag)
|
||||
throws IOException {
|
||||
return input.skipField(tag);
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Extensions-related stuff
|
||||
|
||||
/** Lite equivalent of {@link org.apache.pekko.protobuf.GeneratedMessage.ExtendableMessageOrBuilder}. */
|
||||
public interface ExtendableMessageOrBuilder<MessageType extends ExtendableMessage>
|
||||
extends MessageLiteOrBuilder {
|
||||
|
||||
/** Check if a singular extension is present. */
|
||||
<Type> boolean hasExtension(GeneratedExtension<MessageType, Type> extension);
|
||||
|
||||
/** Get the number of elements in a repeated extension. */
|
||||
<Type> int getExtensionCount(GeneratedExtension<MessageType, List<Type>> extension);
|
||||
|
||||
/** Get the value of an extension. */
|
||||
<Type> Type getExtension(GeneratedExtension<MessageType, Type> extension);
|
||||
|
||||
/** Get one element of a repeated extension. */
|
||||
<Type> Type getExtension(GeneratedExtension<MessageType, List<Type>> extension, int index);
|
||||
}
|
||||
|
||||
/** Lite equivalent of {@link GeneratedMessage.ExtendableMessage}. */
|
||||
public abstract static class ExtendableMessage<MessageType extends ExtendableMessage<MessageType>>
|
||||
extends GeneratedMessageLite implements ExtendableMessageOrBuilder<MessageType> {
|
||||
|
||||
private final FieldSet<ExtensionDescriptor> extensions;
|
||||
|
||||
protected ExtendableMessage() {
|
||||
this.extensions = FieldSet.newFieldSet();
|
||||
}
|
||||
|
||||
protected ExtendableMessage(ExtendableBuilder<MessageType, ?> builder) {
|
||||
this.extensions = builder.buildExtensions();
|
||||
}
|
||||
|
||||
private void verifyExtensionContainingType(final GeneratedExtension<MessageType, ?> extension) {
|
||||
if (extension.getContainingTypeDefaultInstance() != getDefaultInstanceForType()) {
|
||||
// This can only happen if someone uses unchecked operations.
|
||||
throw new IllegalArgumentException(
|
||||
"This extension is for a different message type. Please make "
|
||||
+ "sure that you are not suppressing any generics type warnings.");
|
||||
}
|
||||
}
|
||||
|
||||
/** Check if a singular extension is present. */
|
||||
// @Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public final <Type> boolean hasExtension(
|
||||
final GeneratedExtension<MessageType, Type> extension) {
|
||||
verifyExtensionContainingType(extension);
|
||||
return extensions.hasField(extension.descriptor);
|
||||
}
|
||||
|
||||
/** Get the number of elements in a repeated extension. */
|
||||
// @Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public final <Type> int getExtensionCount(
|
||||
final GeneratedExtension<MessageType, List<Type>> extension) {
|
||||
verifyExtensionContainingType(extension);
|
||||
return extensions.getRepeatedFieldCount(extension.descriptor);
|
||||
}
|
||||
|
||||
/** Get the value of an extension. */
|
||||
// @Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
@SuppressWarnings("unchecked")
|
||||
public final <Type> Type getExtension(final GeneratedExtension<MessageType, Type> extension) {
|
||||
verifyExtensionContainingType(extension);
|
||||
final Object value = extensions.getField(extension.descriptor);
|
||||
if (value == null) {
|
||||
return extension.defaultValue;
|
||||
} else {
|
||||
return (Type) value;
|
||||
}
|
||||
}
|
||||
|
||||
/** Get one element of a repeated extension. */
|
||||
// @Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
@SuppressWarnings("unchecked")
|
||||
public final <Type> Type getExtension(
|
||||
final GeneratedExtension<MessageType, List<Type>> extension, final int index) {
|
||||
verifyExtensionContainingType(extension);
|
||||
return (Type) extensions.getRepeatedField(extension.descriptor, index);
|
||||
}
|
||||
|
||||
/** Called by subclasses to check if all extensions are initialized. */
|
||||
protected boolean extensionsAreInitialized() {
|
||||
return extensions.isInitialized();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by subclasses to parse an unknown field or an extension.
|
||||
*
|
||||
* @return {@code true} unless the tag is an end-group tag.
|
||||
*/
|
||||
@Override
|
||||
protected boolean parseUnknownField(
|
||||
CodedInputStream input, ExtensionRegistryLite extensionRegistry, int tag)
|
||||
throws IOException {
|
||||
return GeneratedMessageLite.parseUnknownField(
|
||||
extensions, getDefaultInstanceForType(), input, extensionRegistry, tag);
|
||||
}
|
||||
|
||||
/** Used by parsing constructors in generated classes. */
|
||||
@Override
|
||||
protected void makeExtensionsImmutable() {
|
||||
extensions.makeImmutable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by subclasses to serialize extensions. Extension ranges may be interleaved with field
|
||||
* numbers, but we must write them in canonical (sorted by field number) order. ExtensionWriter
|
||||
* helps us write individual ranges of extensions at once.
|
||||
*/
|
||||
protected class ExtensionWriter {
|
||||
// Imagine how much simpler this code would be if Java iterators had
|
||||
// a way to get the next element without advancing the iterator.
|
||||
|
||||
private final Iterator<Map.Entry<ExtensionDescriptor, Object>> iter = extensions.iterator();
|
||||
private Map.Entry<ExtensionDescriptor, Object> next;
|
||||
private final boolean messageSetWireFormat;
|
||||
|
||||
private ExtensionWriter(boolean messageSetWireFormat) {
|
||||
if (iter.hasNext()) {
|
||||
next = iter.next();
|
||||
}
|
||||
this.messageSetWireFormat = messageSetWireFormat;
|
||||
}
|
||||
|
||||
public void writeUntil(final int end, final CodedOutputStream output) throws IOException {
|
||||
while (next != null && next.getKey().getNumber() < end) {
|
||||
ExtensionDescriptor extension = next.getKey();
|
||||
if (messageSetWireFormat
|
||||
&& extension.getLiteJavaType() == WireFormat.JavaType.MESSAGE
|
||||
&& !extension.isRepeated()) {
|
||||
output.writeMessageSetExtension(extension.getNumber(), (MessageLite) next.getValue());
|
||||
} else {
|
||||
FieldSet.writeField(extension, next.getValue(), output);
|
||||
}
|
||||
if (iter.hasNext()) {
|
||||
next = iter.next();
|
||||
} else {
|
||||
next = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected ExtensionWriter newExtensionWriter() {
|
||||
return new ExtensionWriter(false);
|
||||
}
|
||||
|
||||
protected ExtensionWriter newMessageSetExtensionWriter() {
|
||||
return new ExtensionWriter(true);
|
||||
}
|
||||
|
||||
/** Called by subclasses to compute the size of extensions. */
|
||||
protected int extensionsSerializedSize() {
|
||||
return extensions.getSerializedSize();
|
||||
}
|
||||
|
||||
protected int extensionsSerializedSizeAsMessageSet() {
|
||||
return extensions.getMessageSetSerializedSize();
|
||||
}
|
||||
}
|
||||
|
||||
/** Lite equivalent of {@link GeneratedMessage.ExtendableBuilder}. */
|
||||
@SuppressWarnings("unchecked")
|
||||
public abstract static class ExtendableBuilder<
|
||||
MessageType extends ExtendableMessage<MessageType>,
|
||||
BuilderType extends ExtendableBuilder<MessageType, BuilderType>>
|
||||
extends Builder<MessageType, BuilderType> implements ExtendableMessageOrBuilder<MessageType> {
|
||||
protected ExtendableBuilder() {}
|
||||
|
||||
private FieldSet<ExtensionDescriptor> extensions = FieldSet.emptySet();
|
||||
private boolean extensionsIsMutable;
|
||||
|
||||
@Override
|
||||
public BuilderType clear() {
|
||||
extensions.clear();
|
||||
extensionsIsMutable = false;
|
||||
return super.clear();
|
||||
}
|
||||
|
||||
private void ensureExtensionsIsMutable() {
|
||||
if (!extensionsIsMutable) {
|
||||
extensions = extensions.clone();
|
||||
extensionsIsMutable = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the build code path to create a copy of the extensions for building the message.
|
||||
*/
|
||||
private FieldSet<ExtensionDescriptor> buildExtensions() {
|
||||
extensions.makeImmutable();
|
||||
extensionsIsMutable = false;
|
||||
return extensions;
|
||||
}
|
||||
|
||||
private void verifyExtensionContainingType(final GeneratedExtension<MessageType, ?> extension) {
|
||||
if (extension.getContainingTypeDefaultInstance() != getDefaultInstanceForType()) {
|
||||
// This can only happen if someone uses unchecked operations.
|
||||
throw new IllegalArgumentException(
|
||||
"This extension is for a different message type. Please make "
|
||||
+ "sure that you are not suppressing any generics type warnings.");
|
||||
}
|
||||
}
|
||||
|
||||
/** Check if a singular extension is present. */
|
||||
// @Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public final <Type> boolean hasExtension(
|
||||
final GeneratedExtension<MessageType, Type> extension) {
|
||||
verifyExtensionContainingType(extension);
|
||||
return extensions.hasField(extension.descriptor);
|
||||
}
|
||||
|
||||
/** Get the number of elements in a repeated extension. */
|
||||
// @Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public final <Type> int getExtensionCount(
|
||||
final GeneratedExtension<MessageType, List<Type>> extension) {
|
||||
verifyExtensionContainingType(extension);
|
||||
return extensions.getRepeatedFieldCount(extension.descriptor);
|
||||
}
|
||||
|
||||
/** Get the value of an extension. */
|
||||
// @Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
@SuppressWarnings("unchecked")
|
||||
public final <Type> Type getExtension(final GeneratedExtension<MessageType, Type> extension) {
|
||||
verifyExtensionContainingType(extension);
|
||||
final Object value = extensions.getField(extension.descriptor);
|
||||
if (value == null) {
|
||||
return extension.defaultValue;
|
||||
} else {
|
||||
return (Type) value;
|
||||
}
|
||||
}
|
||||
|
||||
/** Get one element of a repeated extension. */
|
||||
@SuppressWarnings("unchecked")
|
||||
// @Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public final <Type> Type getExtension(
|
||||
final GeneratedExtension<MessageType, List<Type>> extension, final int index) {
|
||||
verifyExtensionContainingType(extension);
|
||||
return (Type) extensions.getRepeatedField(extension.descriptor, index);
|
||||
}
|
||||
|
||||
// This is implemented here only to work around an apparent bug in the
|
||||
// Java compiler and/or build system. See bug #1898463. The mere presence
|
||||
// of this dummy clone() implementation makes it go away.
|
||||
@Override
|
||||
public BuilderType clone() {
|
||||
throw new UnsupportedOperationException("This is supposed to be overridden by subclasses.");
|
||||
}
|
||||
|
||||
/** Set the value of an extension. */
|
||||
public final <Type> BuilderType setExtension(
|
||||
final GeneratedExtension<MessageType, Type> extension, final Type value) {
|
||||
verifyExtensionContainingType(extension);
|
||||
ensureExtensionsIsMutable();
|
||||
extensions.setField(extension.descriptor, value);
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
/** Set the value of one element of a repeated extension. */
|
||||
public final <Type> BuilderType setExtension(
|
||||
final GeneratedExtension<MessageType, List<Type>> extension,
|
||||
final int index,
|
||||
final Type value) {
|
||||
verifyExtensionContainingType(extension);
|
||||
ensureExtensionsIsMutable();
|
||||
extensions.setRepeatedField(extension.descriptor, index, value);
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
/** Append a value to a repeated extension. */
|
||||
public final <Type> BuilderType addExtension(
|
||||
final GeneratedExtension<MessageType, List<Type>> extension, final Type value) {
|
||||
verifyExtensionContainingType(extension);
|
||||
ensureExtensionsIsMutable();
|
||||
extensions.addRepeatedField(extension.descriptor, value);
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
/** Clear an extension. */
|
||||
public final <Type> BuilderType clearExtension(
|
||||
final GeneratedExtension<MessageType, ?> extension) {
|
||||
verifyExtensionContainingType(extension);
|
||||
ensureExtensionsIsMutable();
|
||||
extensions.clearField(extension.descriptor);
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
/** Called by subclasses to check if all extensions are initialized. */
|
||||
protected boolean extensionsAreInitialized() {
|
||||
return extensions.isInitialized();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by subclasses to parse an unknown field or an extension.
|
||||
*
|
||||
* @return {@code true} unless the tag is an end-group tag.
|
||||
*/
|
||||
@Override
|
||||
protected boolean parseUnknownField(
|
||||
CodedInputStream input, ExtensionRegistryLite extensionRegistry, int tag)
|
||||
throws IOException {
|
||||
ensureExtensionsIsMutable();
|
||||
return GeneratedMessageLite.parseUnknownField(
|
||||
extensions, getDefaultInstanceForType(), input, extensionRegistry, tag);
|
||||
}
|
||||
|
||||
protected final void mergeExtensionFields(final MessageType other) {
|
||||
ensureExtensionsIsMutable();
|
||||
extensions.mergeFrom(((ExtendableMessage) other).extensions);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Parse an unknown field or an extension.
|
||||
*
|
||||
* @return {@code true} unless the tag is an end-group tag.
|
||||
*/
|
||||
private static <MessageType extends MessageLite> boolean parseUnknownField(
|
||||
FieldSet<ExtensionDescriptor> extensions,
|
||||
MessageType defaultInstance,
|
||||
CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry,
|
||||
int tag)
|
||||
throws IOException {
|
||||
int wireType = WireFormat.getTagWireType(tag);
|
||||
int fieldNumber = WireFormat.getTagFieldNumber(tag);
|
||||
|
||||
GeneratedExtension<MessageType, ?> extension =
|
||||
extensionRegistry.findLiteExtensionByNumber(defaultInstance, fieldNumber);
|
||||
|
||||
boolean unknown = false;
|
||||
boolean packed = false;
|
||||
if (extension == null) {
|
||||
unknown = true; // Unknown field.
|
||||
} else if (wireType
|
||||
== FieldSet.getWireFormatForFieldType(
|
||||
extension.descriptor.getLiteType(), false /* isPacked */)) {
|
||||
packed = false; // Normal, unpacked value.
|
||||
} else if (extension.descriptor.isRepeated
|
||||
&& extension.descriptor.type.isPackable()
|
||||
&& wireType
|
||||
== FieldSet.getWireFormatForFieldType(
|
||||
extension.descriptor.getLiteType(), true /* isPacked */)) {
|
||||
packed = true; // Packed value.
|
||||
} else {
|
||||
unknown = true; // Wrong wire type.
|
||||
}
|
||||
|
||||
if (unknown) { // Unknown field or wrong wire type. Skip.
|
||||
return input.skipField(tag);
|
||||
}
|
||||
|
||||
if (packed) {
|
||||
int length = input.readRawVarint32();
|
||||
int limit = input.pushLimit(length);
|
||||
if (extension.descriptor.getLiteType() == WireFormat.FieldType.ENUM) {
|
||||
while (input.getBytesUntilLimit() > 0) {
|
||||
int rawValue = input.readEnum();
|
||||
Object value = extension.descriptor.getEnumType().findValueByNumber(rawValue);
|
||||
if (value == null) {
|
||||
// If the number isn't recognized as a valid value for this
|
||||
// enum, drop it (don't even add it to unknownFields).
|
||||
return true;
|
||||
}
|
||||
extensions.addRepeatedField(extension.descriptor, value);
|
||||
}
|
||||
} else {
|
||||
while (input.getBytesUntilLimit() > 0) {
|
||||
Object value = FieldSet.readPrimitiveField(input, extension.descriptor.getLiteType());
|
||||
extensions.addRepeatedField(extension.descriptor, value);
|
||||
}
|
||||
}
|
||||
input.popLimit(limit);
|
||||
} else {
|
||||
Object value;
|
||||
switch (extension.descriptor.getLiteJavaType()) {
|
||||
case MESSAGE:
|
||||
{
|
||||
MessageLite.Builder subBuilder = null;
|
||||
if (!extension.descriptor.isRepeated()) {
|
||||
MessageLite existingValue = (MessageLite) extensions.getField(extension.descriptor);
|
||||
if (existingValue != null) {
|
||||
subBuilder = existingValue.toBuilder();
|
||||
}
|
||||
}
|
||||
if (subBuilder == null) {
|
||||
subBuilder = extension.messageDefaultInstance.newBuilderForType();
|
||||
}
|
||||
if (extension.descriptor.getLiteType() == WireFormat.FieldType.GROUP) {
|
||||
input.readGroup(extension.getNumber(), subBuilder, extensionRegistry);
|
||||
} else {
|
||||
input.readMessage(subBuilder, extensionRegistry);
|
||||
}
|
||||
value = subBuilder.build();
|
||||
break;
|
||||
}
|
||||
case ENUM:
|
||||
int rawValue = input.readEnum();
|
||||
value = extension.descriptor.getEnumType().findValueByNumber(rawValue);
|
||||
// If the number isn't recognized as a valid value for this enum,
|
||||
// drop it.
|
||||
if (value == null) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
value = FieldSet.readPrimitiveField(input, extension.descriptor.getLiteType());
|
||||
break;
|
||||
}
|
||||
|
||||
if (extension.descriptor.isRepeated()) {
|
||||
extensions.addRepeatedField(extension.descriptor, value);
|
||||
} else {
|
||||
extensions.setField(extension.descriptor, value);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
/** For use by generated code only. */
|
||||
public static <ContainingType extends MessageLite, Type>
|
||||
GeneratedExtension<ContainingType, Type> newSingularGeneratedExtension(
|
||||
final ContainingType containingTypeDefaultInstance,
|
||||
final Type defaultValue,
|
||||
final MessageLite messageDefaultInstance,
|
||||
final Internal.EnumLiteMap<?> enumTypeMap,
|
||||
final int number,
|
||||
final WireFormat.FieldType type) {
|
||||
return new GeneratedExtension<ContainingType, Type>(
|
||||
containingTypeDefaultInstance,
|
||||
defaultValue,
|
||||
messageDefaultInstance,
|
||||
new ExtensionDescriptor(
|
||||
enumTypeMap, number, type, false /* isRepeated */, false /* isPacked */));
|
||||
}
|
||||
|
||||
/** For use by generated code only. */
|
||||
public static <ContainingType extends MessageLite, Type>
|
||||
GeneratedExtension<ContainingType, Type> newRepeatedGeneratedExtension(
|
||||
final ContainingType containingTypeDefaultInstance,
|
||||
final MessageLite messageDefaultInstance,
|
||||
final Internal.EnumLiteMap<?> enumTypeMap,
|
||||
final int number,
|
||||
final WireFormat.FieldType type,
|
||||
final boolean isPacked) {
|
||||
@SuppressWarnings("unchecked") // Subclasses ensure Type is a List
|
||||
Type emptyList = (Type) Collections.emptyList();
|
||||
return new GeneratedExtension<ContainingType, Type>(
|
||||
containingTypeDefaultInstance,
|
||||
emptyList,
|
||||
messageDefaultInstance,
|
||||
new ExtensionDescriptor(enumTypeMap, number, type, true /* isRepeated */, isPacked));
|
||||
}
|
||||
|
||||
private static final class ExtensionDescriptor
|
||||
implements FieldSet.FieldDescriptorLite<ExtensionDescriptor> {
|
||||
private ExtensionDescriptor(
|
||||
final Internal.EnumLiteMap<?> enumTypeMap,
|
||||
final int number,
|
||||
final WireFormat.FieldType type,
|
||||
final boolean isRepeated,
|
||||
final boolean isPacked) {
|
||||
this.enumTypeMap = enumTypeMap;
|
||||
this.number = number;
|
||||
this.type = type;
|
||||
this.isRepeated = isRepeated;
|
||||
this.isPacked = isPacked;
|
||||
}
|
||||
|
||||
private final Internal.EnumLiteMap<?> enumTypeMap;
|
||||
private final int number;
|
||||
private final WireFormat.FieldType type;
|
||||
private final boolean isRepeated;
|
||||
private final boolean isPacked;
|
||||
|
||||
public int getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
public WireFormat.FieldType getLiteType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public WireFormat.JavaType getLiteJavaType() {
|
||||
return type.getJavaType();
|
||||
}
|
||||
|
||||
public boolean isRepeated() {
|
||||
return isRepeated;
|
||||
}
|
||||
|
||||
public boolean isPacked() {
|
||||
return isPacked;
|
||||
}
|
||||
|
||||
public Internal.EnumLiteMap<?> getEnumType() {
|
||||
return enumTypeMap;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public MessageLite.Builder internalMergeFrom(MessageLite.Builder to, MessageLite from) {
|
||||
return ((Builder) to).mergeFrom((GeneratedMessageLite) from);
|
||||
}
|
||||
|
||||
public int compareTo(ExtensionDescriptor other) {
|
||||
return number - other.number;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lite equivalent to {@link GeneratedMessage.GeneratedExtension}.
|
||||
*
|
||||
* <p>Users should ignore the contents of this class and only use objects of this type as
|
||||
* parameters to extension accessors and ExtensionRegistry.add().
|
||||
*/
|
||||
public static final class GeneratedExtension<ContainingType extends MessageLite, Type> {
|
||||
|
||||
private GeneratedExtension(
|
||||
final ContainingType containingTypeDefaultInstance,
|
||||
final Type defaultValue,
|
||||
final MessageLite messageDefaultInstance,
|
||||
final ExtensionDescriptor descriptor) {
|
||||
// Defensive checks to verify the correct initialization order of
|
||||
// GeneratedExtensions and their related GeneratedMessages.
|
||||
if (containingTypeDefaultInstance == null) {
|
||||
throw new IllegalArgumentException("Null containingTypeDefaultInstance");
|
||||
}
|
||||
if (descriptor.getLiteType() == WireFormat.FieldType.MESSAGE
|
||||
&& messageDefaultInstance == null) {
|
||||
throw new IllegalArgumentException("Null messageDefaultInstance");
|
||||
}
|
||||
this.containingTypeDefaultInstance = containingTypeDefaultInstance;
|
||||
this.defaultValue = defaultValue;
|
||||
this.messageDefaultInstance = messageDefaultInstance;
|
||||
this.descriptor = descriptor;
|
||||
}
|
||||
|
||||
private final ContainingType containingTypeDefaultInstance;
|
||||
private final Type defaultValue;
|
||||
private final MessageLite messageDefaultInstance;
|
||||
private final ExtensionDescriptor descriptor;
|
||||
|
||||
/** Default instance of the type being extended, used to identify that type. */
|
||||
public ContainingType getContainingTypeDefaultInstance() {
|
||||
return containingTypeDefaultInstance;
|
||||
}
|
||||
|
||||
/** Get the field number. */
|
||||
public int getNumber() {
|
||||
return descriptor.getNumber();
|
||||
}
|
||||
|
||||
/** If the extension is an embedded message, this is the default instance of that type. */
|
||||
public MessageLite getMessageDefaultInstance() {
|
||||
return messageDefaultInstance;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A serialized (serializable) form of the generated message. Stores the message as a class name
|
||||
* and a byte array.
|
||||
*/
|
||||
static final class SerializedForm implements Serializable {
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
private String messageClassName;
|
||||
private byte[] asBytes;
|
||||
|
||||
/**
|
||||
* Creates the serialized form by calling {@link org.apache.pekko.protobuf.MessageLite#toByteArray}.
|
||||
*
|
||||
* @param regularForm the message to serialize
|
||||
*/
|
||||
SerializedForm(MessageLite regularForm) {
|
||||
messageClassName = regularForm.getClass().getName();
|
||||
asBytes = regularForm.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* When read from an ObjectInputStream, this method converts this object back to the regular
|
||||
* form. Part of Java's serialization magic.
|
||||
*
|
||||
* @return a GeneratedMessage of the type that was serialized
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Object readResolve() throws ObjectStreamException {
|
||||
try {
|
||||
Class messageClass = Class.forName(messageClassName);
|
||||
Method newBuilder = messageClass.getMethod("newBuilder");
|
||||
MessageLite.Builder builder = (MessageLite.Builder) newBuilder.invoke(null);
|
||||
builder.mergeFrom(asBytes);
|
||||
return builder.buildPartial();
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException("Unable to find proto buffer class", e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeException("Unable to find newBuilder method", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Unable to call newBuilder method", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException("Error calling newBuilder", e.getCause());
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw new RuntimeException("Unable to understand proto buffer", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces this object in the output stream with a serialized form. Part of Java's serialization
|
||||
* magic. Generated sub-classes must override this method by calling {@code return
|
||||
* super.writeReplace();}
|
||||
*
|
||||
* @return a SerializedForm of this message
|
||||
*/
|
||||
protected Object writeReplace() throws ObjectStreamException {
|
||||
return new SerializedForm(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,166 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/**
|
||||
* The classes contained within are used internally by the Protocol Buffer
|
||||
* library and generated message implementations. They are public only because
|
||||
* those generated messages do not reside in the {@code protobuf} package.
|
||||
* Others should not use this class directly.
|
||||
*
|
||||
* @author kenton@google.com (Kenton Varda)
|
||||
*/
|
||||
public class Internal {
|
||||
/**
|
||||
* Helper called by generated code to construct default values for string
|
||||
* fields.
|
||||
* <p>
|
||||
* The protocol compiler does not actually contain a UTF-8 decoder -- it
|
||||
* just pushes UTF-8-encoded text around without touching it. The one place
|
||||
* where this presents a problem is when generating Java string literals.
|
||||
* Unicode characters in the string literal would normally need to be encoded
|
||||
* using a Unicode escape sequence, which would require decoding them.
|
||||
* To get around this, protoc instead embeds the UTF-8 bytes into the
|
||||
* generated code and leaves it to the runtime library to decode them.
|
||||
* <p>
|
||||
* It gets worse, though. If protoc just generated a byte array, like:
|
||||
* new byte[] {0x12, 0x34, 0x56, 0x78}
|
||||
* Java actually generates *code* which allocates an array and then fills
|
||||
* in each value. This is much less efficient than just embedding the bytes
|
||||
* directly into the bytecode. To get around this, we need another
|
||||
* work-around. String literals are embedded directly, so protoc actually
|
||||
* generates a string literal corresponding to the bytes. The easiest way
|
||||
* to do this is to use the ISO-8859-1 character set, which corresponds to
|
||||
* the first 256 characters of the Unicode range. Protoc can then use
|
||||
* good old CEscape to generate the string.
|
||||
* <p>
|
||||
* So we have a string literal which represents a set of bytes which
|
||||
* represents another string. This function -- stringDefaultValue --
|
||||
* converts from the generated string to the string we actually want. The
|
||||
* generated code calls this automatically.
|
||||
*/
|
||||
public static String stringDefaultValue(String bytes) {
|
||||
try {
|
||||
return new String(bytes.getBytes("ISO-8859-1"), "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// This should never happen since all JVMs are required to implement
|
||||
// both of the above character sets.
|
||||
throw new IllegalStateException(
|
||||
"Java VM does not support a standard character set.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper called by generated code to construct default values for bytes
|
||||
* fields.
|
||||
* <p>
|
||||
* This is a lot like {@link #stringDefaultValue}, but for bytes fields.
|
||||
* In this case we only need the second of the two hacks -- allowing us to
|
||||
* embed raw bytes as a string literal with ISO-8859-1 encoding.
|
||||
*/
|
||||
public static ByteString bytesDefaultValue(String bytes) {
|
||||
try {
|
||||
return ByteString.copyFrom(bytes.getBytes("ISO-8859-1"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// This should never happen since all JVMs are required to implement
|
||||
// ISO-8859-1.
|
||||
throw new IllegalStateException(
|
||||
"Java VM does not support a standard character set.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper called by generated code to determine if a byte array is a valid
|
||||
* UTF-8 encoded string such that the original bytes can be converted to
|
||||
* a String object and then back to a byte array round tripping the bytes
|
||||
* without loss. More precisely, returns {@code true} whenever:
|
||||
* <pre> {@code
|
||||
* Arrays.equals(byteString.toByteArray(),
|
||||
* new String(byteString.toByteArray(), "UTF-8").getBytes("UTF-8"))
|
||||
* }</pre>
|
||||
*
|
||||
* <p>This method rejects "overlong" byte sequences, as well as
|
||||
* 3-byte sequences that would map to a surrogate character, in
|
||||
* accordance with the restricted definition of UTF-8 introduced in
|
||||
* Unicode 3.1. Note that the UTF-8 decoder included in Oracle's
|
||||
* JDK has been modified to also reject "overlong" byte sequences,
|
||||
* but currently (2011) still accepts 3-byte surrogate character
|
||||
* byte sequences.
|
||||
*
|
||||
* <p>See the Unicode Standard,<br>
|
||||
* Table 3-6. <em>UTF-8 Bit Distribution</em>,<br>
|
||||
* Table 3-7. <em>Well Formed UTF-8 Byte Sequences</em>.
|
||||
*
|
||||
* <p>As of 2011-02, this method simply returns the result of {@link
|
||||
* ByteString#isValidUtf8()}. Calling that method directly is preferred.
|
||||
*
|
||||
* @param byteString the string to check
|
||||
* @return whether the byte array is round trippable
|
||||
*/
|
||||
public static boolean isValidUtf8(ByteString byteString) {
|
||||
return byteString.isValidUtf8();
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for an enum value or value descriptor, to be used in FieldSet.
|
||||
* The lite library stores enum values directly in FieldSets but the full
|
||||
* library stores EnumValueDescriptors in order to better support reflection.
|
||||
*/
|
||||
public interface EnumLite {
|
||||
int getNumber();
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for an object which maps integers to {@link EnumLite}s.
|
||||
* {@link Descriptors.EnumDescriptor} implements this interface by mapping
|
||||
* numbers to {@link Descriptors.EnumValueDescriptor}s. Additionally,
|
||||
* every generated enum type has a static method internalGetValueMap() which
|
||||
* returns an implementation of this type that maps numbers to enum values.
|
||||
*/
|
||||
public interface EnumLiteMap<T extends EnumLite> {
|
||||
T findValueByNumber(int number);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,127 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Thrown when a protocol message being parsed is invalid in some way,
|
||||
* e.g. it contains a malformed varint or a negative byte length.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public class InvalidProtocolBufferException extends IOException {
|
||||
private static final long serialVersionUID = -1616151763072450476L;
|
||||
private MessageLite unfinishedMessage = null;
|
||||
|
||||
public InvalidProtocolBufferException(final String description) {
|
||||
super(description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches an unfinished message to the exception to support best-effort
|
||||
* parsing in {@code Parser} interface.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public InvalidProtocolBufferException setUnfinishedMessage(
|
||||
MessageLite unfinishedMessage) {
|
||||
this.unfinishedMessage = unfinishedMessage;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unfinished message attached to the exception, or null if
|
||||
* no message is attached.
|
||||
*/
|
||||
public MessageLite getUnfinishedMessage() {
|
||||
return unfinishedMessage;
|
||||
}
|
||||
|
||||
static InvalidProtocolBufferException truncatedMessage() {
|
||||
return new InvalidProtocolBufferException(
|
||||
"While parsing a protocol message, the input ended unexpectedly " +
|
||||
"in the middle of a field. This could mean either than the " +
|
||||
"input has been truncated or that an embedded message " +
|
||||
"misreported its own length.");
|
||||
}
|
||||
|
||||
static InvalidProtocolBufferException negativeSize() {
|
||||
return new InvalidProtocolBufferException(
|
||||
"CodedInputStream encountered an embedded string or message " +
|
||||
"which claimed to have negative size.");
|
||||
}
|
||||
|
||||
static InvalidProtocolBufferException malformedVarint() {
|
||||
return new InvalidProtocolBufferException(
|
||||
"CodedInputStream encountered a malformed varint.");
|
||||
}
|
||||
|
||||
static InvalidProtocolBufferException invalidTag() {
|
||||
return new InvalidProtocolBufferException(
|
||||
"Protocol message contained an invalid tag (zero).");
|
||||
}
|
||||
|
||||
static InvalidProtocolBufferException invalidEndTag() {
|
||||
return new InvalidProtocolBufferException(
|
||||
"Protocol message end-group tag did not match expected tag.");
|
||||
}
|
||||
|
||||
static InvalidProtocolBufferException invalidWireType() {
|
||||
return new InvalidProtocolBufferException(
|
||||
"Protocol message tag had invalid wire type.");
|
||||
}
|
||||
|
||||
static InvalidProtocolBufferException recursionLimitExceeded() {
|
||||
return new InvalidProtocolBufferException(
|
||||
"Protocol message had too many levels of nesting. May be malicious. " +
|
||||
"Use CodedInputStream.setRecursionLimit() to increase the depth limit.");
|
||||
}
|
||||
|
||||
static InvalidProtocolBufferException sizeLimitExceeded() {
|
||||
return new InvalidProtocolBufferException(
|
||||
"Protocol message was too large. May be malicious. " +
|
||||
"Use CodedInputStream.setSizeLimit() to increase the size limit.");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,223 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* LazyField encapsulates the logic of lazily parsing message fields. It stores
|
||||
* the message in a ByteString initially and then parse it on-demand.
|
||||
*
|
||||
* LazyField is thread-compatible e.g. concurrent read are safe, however,
|
||||
* synchronizations are needed under read/write situations.
|
||||
*
|
||||
* Now LazyField is only used to lazily load MessageSet.
|
||||
* TODO(xiangl): Use LazyField to lazily load all messages.
|
||||
*
|
||||
* @author xiangl@google.com (Xiang Li)
|
||||
*/
|
||||
class LazyField {
|
||||
|
||||
final private MessageLite defaultInstance;
|
||||
final private ExtensionRegistryLite extensionRegistry;
|
||||
|
||||
// Mutable because it is initialized lazily.
|
||||
private ByteString bytes;
|
||||
private volatile MessageLite value;
|
||||
private volatile boolean isDirty = false;
|
||||
|
||||
public LazyField(MessageLite defaultInstance,
|
||||
ExtensionRegistryLite extensionRegistry, ByteString bytes) {
|
||||
this.defaultInstance = defaultInstance;
|
||||
this.extensionRegistry = extensionRegistry;
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
public MessageLite getValue() {
|
||||
ensureInitialized();
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* LazyField is not thread-safe for write access. Synchronizations are needed
|
||||
* under read/write situations.
|
||||
*/
|
||||
public MessageLite setValue(MessageLite value) {
|
||||
MessageLite originalValue = this.value;
|
||||
this.value = value;
|
||||
bytes = null;
|
||||
isDirty = true;
|
||||
return originalValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Due to the optional field can be duplicated at the end of serialized
|
||||
* bytes, which will make the serialized size changed after LazyField
|
||||
* parsed. Be careful when using this method.
|
||||
*/
|
||||
public int getSerializedSize() {
|
||||
if (isDirty) {
|
||||
return value.getSerializedSize();
|
||||
}
|
||||
return bytes.size();
|
||||
}
|
||||
|
||||
public ByteString toByteString() {
|
||||
if (!isDirty) {
|
||||
return bytes;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (!isDirty) {
|
||||
return bytes;
|
||||
}
|
||||
bytes = value.toByteString();
|
||||
isDirty = false;
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
ensureInitialized();
|
||||
return value.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
ensureInitialized();
|
||||
return value.equals(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
ensureInitialized();
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
private void ensureInitialized() {
|
||||
if (value != null) {
|
||||
return;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (value != null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (bytes != null) {
|
||||
value = defaultInstance.getParserForType()
|
||||
.parseFrom(bytes, extensionRegistry);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// TODO(xiangl): Refactory the API to support the exception thrown from
|
||||
// lazily load messages.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================
|
||||
|
||||
/**
|
||||
* LazyEntry and LazyIterator are used to encapsulate the LazyField, when
|
||||
* users iterate all fields from FieldSet.
|
||||
*/
|
||||
static class LazyEntry<K> implements Entry<K, Object> {
|
||||
private Entry<K, LazyField> entry;
|
||||
|
||||
private LazyEntry(Entry<K, LazyField> entry) {
|
||||
this.entry = entry;
|
||||
}
|
||||
|
||||
public K getKey() {
|
||||
return entry.getKey();
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
LazyField field = entry.getValue();
|
||||
if (field == null) {
|
||||
return null;
|
||||
}
|
||||
return field.getValue();
|
||||
}
|
||||
|
||||
public LazyField getField() {
|
||||
return entry.getValue();
|
||||
}
|
||||
|
||||
public Object setValue(Object value) {
|
||||
if (!(value instanceof MessageLite)) {
|
||||
throw new IllegalArgumentException(
|
||||
"LazyField now only used for MessageSet, "
|
||||
+ "and the value of MessageSet must be an instance of MessageLite");
|
||||
}
|
||||
return entry.getValue().setValue((MessageLite) value);
|
||||
}
|
||||
}
|
||||
|
||||
static class LazyIterator<K> implements Iterator<Entry<K, Object>> {
|
||||
private Iterator<Entry<K, Object>> iterator;
|
||||
|
||||
public LazyIterator(Iterator<Entry<K, Object>> iterator) {
|
||||
this.iterator = iterator;
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Entry<K, Object> next() {
|
||||
Entry<K, ?> entry = iterator.next();
|
||||
if (entry.getValue() instanceof LazyField) {
|
||||
return new LazyEntry<K>((Entry<K, LazyField>) entry);
|
||||
}
|
||||
return (Entry<K, Object>) entry;
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,191 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.RandomAccess;
|
||||
|
||||
/**
|
||||
* An implementation of {@link LazyStringList} that wraps an ArrayList. Each
|
||||
* element is either a ByteString or a String. It caches the last one requested
|
||||
* which is most likely the one needed next. This minimizes memory usage while
|
||||
* satisfying the most common use cases.
|
||||
* <p>
|
||||
* <strong>Note that this implementation is not synchronized.</strong>
|
||||
* If multiple threads access an <tt>ArrayList</tt> instance concurrently,
|
||||
* and at least one of the threads modifies the list structurally, it
|
||||
* <i>must</i> be synchronized externally. (A structural modification is
|
||||
* any operation that adds or deletes one or more elements, or explicitly
|
||||
* resizes the backing array; merely setting the value of an element is not
|
||||
* a structural modification.) This is typically accomplished by
|
||||
* synchronizing on some object that naturally encapsulates the list.
|
||||
* <p>
|
||||
* If the implementation is accessed via concurrent reads, this is thread safe.
|
||||
* Conversions are done in a thread safe manner. It's possible that the
|
||||
* conversion may happen more than once if two threads attempt to access the
|
||||
* same element and the modifications were not visible to each other, but this
|
||||
* will not result in any corruption of the list or change in behavior other
|
||||
* than performance.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public class LazyStringArrayList extends AbstractList<String>
|
||||
implements LazyStringList, RandomAccess {
|
||||
|
||||
public final static LazyStringList EMPTY = new UnmodifiableLazyStringList(
|
||||
new LazyStringArrayList());
|
||||
|
||||
private final List<Object> list;
|
||||
|
||||
public LazyStringArrayList() {
|
||||
list = new ArrayList<Object>();
|
||||
}
|
||||
|
||||
public LazyStringArrayList(LazyStringList from) {
|
||||
list = new ArrayList<Object>(from.size());
|
||||
addAll(from);
|
||||
}
|
||||
|
||||
public LazyStringArrayList(List<String> from) {
|
||||
list = new ArrayList<Object>(from);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(int index) {
|
||||
Object o = list.get(index);
|
||||
if (o instanceof String) {
|
||||
return (String) o;
|
||||
} else {
|
||||
ByteString bs = (ByteString) o;
|
||||
String s = bs.toStringUtf8();
|
||||
if (bs.isValidUtf8()) {
|
||||
list.set(index, s);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return list.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String set(int index, String s) {
|
||||
Object o = list.set(index, s);
|
||||
return asString(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, String element) {
|
||||
list.add(index, element);
|
||||
modCount++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends String> c) {
|
||||
// The default implementation of AbstractCollection.addAll(Collection)
|
||||
// delegates to add(Object). This implementation instead delegates to
|
||||
// addAll(int, Collection), which makes a special case for Collections
|
||||
// which are instances of LazyStringList.
|
||||
return addAll(size(), c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int index, Collection<? extends String> c) {
|
||||
// When copying from another LazyStringList, directly copy the underlying
|
||||
// elements rather than forcing each element to be decoded to a String.
|
||||
Collection<?> collection = c instanceof LazyStringList
|
||||
? ((LazyStringList) c).getUnderlyingElements() : c;
|
||||
boolean ret = list.addAll(index, collection);
|
||||
modCount++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String remove(int index) {
|
||||
Object o = list.remove(index);
|
||||
modCount++;
|
||||
return asString(o);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
list.clear();
|
||||
modCount++;
|
||||
}
|
||||
|
||||
// @Override
|
||||
public void add(ByteString element) {
|
||||
list.add(element);
|
||||
modCount++;
|
||||
}
|
||||
|
||||
// @Override
|
||||
public ByteString getByteString(int index) {
|
||||
Object o = list.get(index);
|
||||
if (o instanceof String) {
|
||||
ByteString b = ByteString.copyFromUtf8((String) o);
|
||||
list.set(index, b);
|
||||
return b;
|
||||
} else {
|
||||
return (ByteString) o;
|
||||
}
|
||||
}
|
||||
|
||||
private String asString(Object o) {
|
||||
if (o instanceof String) {
|
||||
return (String) o;
|
||||
} else {
|
||||
return ((ByteString) o).toStringUtf8();
|
||||
}
|
||||
}
|
||||
|
||||
public List<?> getUnderlyingElements() {
|
||||
return Collections.unmodifiableList(list);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An interface extending {@code List<String>} that also provides access to the
|
||||
* items of the list as UTF8-encoded ByteString objects. This is used by the
|
||||
* protocol buffer implementation to support lazily converting bytes parsed
|
||||
* over the wire to String objects until needed and also increases the
|
||||
* efficiency of serialization if the String was never requested as the
|
||||
* ByteString is already cached.
|
||||
* <p>
|
||||
* This only adds additional methods that are required for the use in the
|
||||
* protocol buffer code in order to be able successfully round trip byte arrays
|
||||
* through parsing and serialization without conversion to strings. It's not
|
||||
* attempting to support the functionality of say {@code List<ByteString>}, hence
|
||||
* why only these two very specific methods are added.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public interface LazyStringList extends List<String> {
|
||||
|
||||
/**
|
||||
* Returns the element at the specified position in this list as a ByteString.
|
||||
*
|
||||
* @param index index of the element to return
|
||||
* @return the element at the specified position in this list
|
||||
* @throws IndexOutOfBoundsException if the index is out of range
|
||||
* ({@code index < 0 || index >= size()})
|
||||
*/
|
||||
ByteString getByteString(int index);
|
||||
|
||||
/**
|
||||
* Appends the specified element to the end of this list (optional
|
||||
* operation).
|
||||
*
|
||||
* @param element element to be appended to this list
|
||||
* @throws UnsupportedOperationException if the <tt>add</tt> operation
|
||||
* is not supported by this list
|
||||
*/
|
||||
void add(ByteString element);
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable List of the underlying elements, each of
|
||||
* which is either a {@code String} or its equivalent UTF-8 encoded
|
||||
* {@code ByteString}. It is an error for the caller to modify the returned
|
||||
* List, and attempting to do so will result in an
|
||||
* {@link UnsupportedOperationException}.
|
||||
*/
|
||||
List<?> getUnderlyingElements();
|
||||
}
|
||||
|
|
@ -1,362 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* This class implements a {@link org.apache.pekko.protobuf.ByteString} backed by a
|
||||
* single array of bytes, contiguous in memory. It supports substring by
|
||||
* pointing to only a sub-range of the underlying byte array, meaning that a
|
||||
* substring will reference the full byte-array of the string it's made from,
|
||||
* exactly as with {@link String}.
|
||||
*
|
||||
* @author carlanton@google.com (Carl Haverl)
|
||||
*/
|
||||
class LiteralByteString extends ByteString {
|
||||
|
||||
protected final byte[] bytes;
|
||||
|
||||
/**
|
||||
* Creates a {@code LiteralByteString} backed by the given array, without
|
||||
* copying.
|
||||
*
|
||||
* @param bytes array to wrap
|
||||
*/
|
||||
LiteralByteString(byte[] bytes) {
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte byteAt(int index) {
|
||||
// Unlike most methods in this class, this one is a direct implementation
|
||||
// ignoring the potential offset because we need to do range-checking in the
|
||||
// substring case anyway.
|
||||
return bytes[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return bytes.length;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ByteString -> substring
|
||||
|
||||
@Override
|
||||
public ByteString substring(int beginIndex, int endIndex) {
|
||||
if (beginIndex < 0) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"Beginning index: " + beginIndex + " < 0");
|
||||
}
|
||||
if (endIndex > size()) {
|
||||
throw new IndexOutOfBoundsException("End index: " + endIndex + " > " +
|
||||
size());
|
||||
}
|
||||
int substringLength = endIndex - beginIndex;
|
||||
if (substringLength < 0) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"Beginning index larger than ending index: " + beginIndex + ", "
|
||||
+ endIndex);
|
||||
}
|
||||
|
||||
ByteString result;
|
||||
if (substringLength == 0) {
|
||||
result = ByteString.EMPTY;
|
||||
} else {
|
||||
result = new BoundedByteString(bytes, getOffsetIntoBytes() + beginIndex,
|
||||
substringLength);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ByteString -> byte[]
|
||||
|
||||
@Override
|
||||
protected void copyToInternal(byte[] target, int sourceOffset,
|
||||
int targetOffset, int numberToCopy) {
|
||||
// Optimized form, not for subclasses, since we don't call
|
||||
// getOffsetIntoBytes() or check the 'numberToCopy' parameter.
|
||||
System.arraycopy(bytes, sourceOffset, target, targetOffset, numberToCopy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyTo(ByteBuffer target) {
|
||||
target.put(bytes, getOffsetIntoBytes(), size()); // Copies bytes
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer asReadOnlyByteBuffer() {
|
||||
ByteBuffer byteBuffer =
|
||||
ByteBuffer.wrap(bytes, getOffsetIntoBytes(), size());
|
||||
return byteBuffer.asReadOnlyBuffer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ByteBuffer> asReadOnlyByteBufferList() {
|
||||
// Return the ByteBuffer generated by asReadOnlyByteBuffer() as a singleton
|
||||
List<ByteBuffer> result = new ArrayList<ByteBuffer>(1);
|
||||
result.add(asReadOnlyByteBuffer());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(OutputStream outputStream) throws IOException {
|
||||
outputStream.write(toByteArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(String charsetName)
|
||||
throws UnsupportedEncodingException {
|
||||
return new String(bytes, getOffsetIntoBytes(), size(), charsetName);
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// UTF-8 decoding
|
||||
|
||||
@Override
|
||||
public boolean isValidUtf8() {
|
||||
int offset = getOffsetIntoBytes();
|
||||
return Utf8.isValidUtf8(bytes, offset, offset + size());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int partialIsValidUtf8(int state, int offset, int length) {
|
||||
int index = getOffsetIntoBytes() + offset;
|
||||
return Utf8.partialIsValidUtf8(state, bytes, index, index + length);
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// equals() and hashCode()
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof ByteString)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (size() != ((ByteString) other).size()) {
|
||||
return false;
|
||||
}
|
||||
if (size() == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (other instanceof LiteralByteString) {
|
||||
return equalsRange((LiteralByteString) other, 0, size());
|
||||
} else if (other instanceof RopeByteString) {
|
||||
return other.equals(this);
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Has a new type of ByteString been created? Found "
|
||||
+ other.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check equality of the substring of given length of this object starting at
|
||||
* zero with another {@code LiteralByteString} substring starting at offset.
|
||||
*
|
||||
* @param other what to compare a substring in
|
||||
* @param offset offset into other
|
||||
* @param length number of bytes to compare
|
||||
* @return true for equality of substrings, else false.
|
||||
*/
|
||||
boolean equalsRange(LiteralByteString other, int offset, int length) {
|
||||
if (length > other.size()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Length too large: " + length + size());
|
||||
}
|
||||
if (offset + length > other.size()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Ran off end of other: " + offset + ", " + length + ", " +
|
||||
other.size());
|
||||
}
|
||||
|
||||
byte[] thisBytes = bytes;
|
||||
byte[] otherBytes = other.bytes;
|
||||
int thisLimit = getOffsetIntoBytes() + length;
|
||||
for (int thisIndex = getOffsetIntoBytes(), otherIndex =
|
||||
other.getOffsetIntoBytes() + offset;
|
||||
(thisIndex < thisLimit); ++thisIndex, ++otherIndex) {
|
||||
if (thisBytes[thisIndex] != otherBytes[otherIndex]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cached hash value. Intentionally accessed via a data race, which
|
||||
* is safe because of the Java Memory Model's "no out-of-thin-air values"
|
||||
* guarantees for ints.
|
||||
*/
|
||||
private int hash = 0;
|
||||
|
||||
/**
|
||||
* Compute the hashCode using the traditional algorithm from {@link
|
||||
* ByteString}.
|
||||
*
|
||||
* @return hashCode value
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int h = hash;
|
||||
|
||||
if (h == 0) {
|
||||
int size = size();
|
||||
h = partialHash(size, 0, size);
|
||||
if (h == 0) {
|
||||
h = 1;
|
||||
}
|
||||
hash = h;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int peekCachedHashCode() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int partialHash(int h, int offset, int length) {
|
||||
byte[] thisBytes = bytes;
|
||||
for (int i = getOffsetIntoBytes() + offset, limit = i + length; i < limit;
|
||||
i++) {
|
||||
h = h * 31 + thisBytes[i];
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Input stream
|
||||
|
||||
@Override
|
||||
public InputStream newInput() {
|
||||
return new ByteArrayInputStream(bytes, getOffsetIntoBytes(),
|
||||
size()); // No copy
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodedInputStream newCodedInput() {
|
||||
// We trust CodedInputStream not to modify the bytes, or to give anyone
|
||||
// else access to them.
|
||||
return CodedInputStream
|
||||
.newInstance(bytes, getOffsetIntoBytes(), size()); // No copy
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ByteIterator
|
||||
|
||||
@Override
|
||||
public ByteIterator iterator() {
|
||||
return new LiteralByteIterator();
|
||||
}
|
||||
|
||||
private class LiteralByteIterator implements ByteIterator {
|
||||
private int position;
|
||||
private final int limit;
|
||||
|
||||
private LiteralByteIterator() {
|
||||
position = 0;
|
||||
limit = size();
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return (position < limit);
|
||||
}
|
||||
|
||||
public Byte next() {
|
||||
// Boxing calls Byte.valueOf(byte), which does not instantiate.
|
||||
return nextByte();
|
||||
}
|
||||
|
||||
public byte nextByte() {
|
||||
try {
|
||||
return bytes[position++];
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
throw new NoSuchElementException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Internal methods
|
||||
|
||||
@Override
|
||||
protected int getTreeDepth() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isBalanced() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset into {@code bytes[]} to use, non-zero for substrings.
|
||||
*
|
||||
* @return always 0 for this class
|
||||
*/
|
||||
protected int getOffsetIntoBytes() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,250 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
// TODO(kenton): Use generics? E.g. Builder<BuilderType extends Builder>, then
|
||||
// mergeFrom*() could return BuilderType for better type-safety.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Abstract interface implemented by Protocol Message objects.
|
||||
* <p>
|
||||
* See also {@link MessageLite}, which defines most of the methods that typical
|
||||
* users care about. {@link Message} adds to it methods that are not available
|
||||
* in the "lite" runtime. The biggest added features are introspection and
|
||||
* reflection -- i.e., getting descriptors for the message type and accessing
|
||||
* the field values dynamically.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public interface Message extends MessageLite, MessageOrBuilder {
|
||||
|
||||
// (From MessageLite, re-declared here only for return type covariance.)
|
||||
Parser<? extends Message> getParserForType();
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Comparison and hashing
|
||||
|
||||
/**
|
||||
* Compares the specified object with this message for equality. Returns
|
||||
* {@code true} if the given object is a message of the same type (as
|
||||
* defined by {@code getDescriptorForType()}) and has identical values for
|
||||
* all of its fields. Subclasses must implement this; inheriting
|
||||
* {@code Object.equals()} is incorrect.
|
||||
*
|
||||
* @param other object to be compared for equality with this message
|
||||
* @return {@code true} if the specified object is equal to this message
|
||||
*/
|
||||
@Override
|
||||
boolean equals(Object other);
|
||||
|
||||
/**
|
||||
* Returns the hash code value for this message. The hash code of a message
|
||||
* should mix the message's type (object identity of the descriptor) with its
|
||||
* contents (known and unknown field values). Subclasses must implement this;
|
||||
* inheriting {@code Object.hashCode()} is incorrect.
|
||||
*
|
||||
* @return the hash code value for this message
|
||||
* @see Map#hashCode()
|
||||
*/
|
||||
@Override
|
||||
int hashCode();
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Convenience methods.
|
||||
|
||||
/**
|
||||
* Converts the message to a string in protocol buffer text format. This is
|
||||
* just a trivial wrapper around {@link
|
||||
* TextFormat#printToString(MessageOrBuilder)}.
|
||||
*/
|
||||
@Override
|
||||
String toString();
|
||||
|
||||
// =================================================================
|
||||
// Builders
|
||||
|
||||
// (From MessageLite, re-declared here only for return type covariance.)
|
||||
Builder newBuilderForType();
|
||||
Builder toBuilder();
|
||||
|
||||
/**
|
||||
* Abstract interface implemented by Protocol Message builders.
|
||||
*/
|
||||
interface Builder extends MessageLite.Builder, MessageOrBuilder {
|
||||
// (From MessageLite.Builder, re-declared here only for return type
|
||||
// covariance.)
|
||||
Builder clear();
|
||||
|
||||
/**
|
||||
* Merge {@code other} into the message being built. {@code other} must
|
||||
* have the exact same type as {@code this} (i.e.
|
||||
* {@code getDescriptorForType() == other.getDescriptorForType()}).
|
||||
*
|
||||
* Merging occurs as follows. For each field:<br>
|
||||
* * For singular primitive fields, if the field is set in {@code other},
|
||||
* then {@code other}'s value overwrites the value in this message.<br>
|
||||
* * For singular message fields, if the field is set in {@code other},
|
||||
* it is merged into the corresponding sub-message of this message
|
||||
* using the same merging rules.<br>
|
||||
* * For repeated fields, the elements in {@code other} are concatenated
|
||||
* with the elements in this message.
|
||||
*
|
||||
* This is equivalent to the {@code Message::MergeFrom} method in C++.
|
||||
*/
|
||||
Builder mergeFrom(Message other);
|
||||
|
||||
// (From MessageLite.Builder, re-declared here only for return type
|
||||
// covariance.)
|
||||
Message build();
|
||||
Message buildPartial();
|
||||
Builder clone();
|
||||
Builder mergeFrom(CodedInputStream input) throws IOException;
|
||||
Builder mergeFrom(CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Get the message's type's descriptor.
|
||||
* See {@link Message#getDescriptorForType()}.
|
||||
*/
|
||||
Descriptors.Descriptor getDescriptorForType();
|
||||
|
||||
/**
|
||||
* Create a Builder for messages of the appropriate type for the given
|
||||
* field. Messages built with this can then be passed to setField(),
|
||||
* setRepeatedField(), or addRepeatedField().
|
||||
*/
|
||||
Builder newBuilderForField(Descriptors.FieldDescriptor field);
|
||||
|
||||
/**
|
||||
* Get a nested builder instance for the given field.
|
||||
* <p>
|
||||
* Normally, we hold a reference to the immutable message object for the
|
||||
* message type field. Some implementations(the generated message builders),
|
||||
* however, can also hold a reference to the builder object (a nested
|
||||
* builder) for the field.
|
||||
* <p>
|
||||
* If the field is already backed up by a nested builder, the nested builder
|
||||
* will be returned. Otherwise, a new field builder will be created and
|
||||
* returned. The original message field (if exist) will be merged into the
|
||||
* field builder, which will then be nested into its parent builder.
|
||||
* <p>
|
||||
* NOTE: implementations that do not support nested builders will throw
|
||||
* <code>UnsupportedException</code>.
|
||||
*/
|
||||
Builder getFieldBuilder(Descriptors.FieldDescriptor field);
|
||||
|
||||
/**
|
||||
* Sets a field to the given value. The value must be of the correct type
|
||||
* for this field, i.e. the same type that
|
||||
* {@link Message#getField(Descriptors.FieldDescriptor)} would return.
|
||||
*/
|
||||
Builder setField(Descriptors.FieldDescriptor field, Object value);
|
||||
|
||||
/**
|
||||
* Clears the field. This is exactly equivalent to calling the generated
|
||||
* "clear" accessor method corresponding to the field.
|
||||
*/
|
||||
Builder clearField(Descriptors.FieldDescriptor field);
|
||||
|
||||
/**
|
||||
* Sets an element of a repeated field to the given value. The value must
|
||||
* be of the correct type for this field, i.e. the same type that
|
||||
* {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)} would
|
||||
* return.
|
||||
* @throws IllegalArgumentException The field is not a repeated field, or
|
||||
* {@code field.getContainingType() != getDescriptorForType()}.
|
||||
*/
|
||||
Builder setRepeatedField(Descriptors.FieldDescriptor field,
|
||||
int index, Object value);
|
||||
|
||||
/**
|
||||
* Like {@code setRepeatedField}, but appends the value as a new element.
|
||||
* @throws IllegalArgumentException The field is not a repeated field, or
|
||||
* {@code field.getContainingType() != getDescriptorForType()}.
|
||||
*/
|
||||
Builder addRepeatedField(Descriptors.FieldDescriptor field, Object value);
|
||||
|
||||
/** Set the {@link UnknownFieldSet} for this message. */
|
||||
Builder setUnknownFields(UnknownFieldSet unknownFields);
|
||||
|
||||
/**
|
||||
* Merge some unknown fields into the {@link UnknownFieldSet} for this
|
||||
* message.
|
||||
*/
|
||||
Builder mergeUnknownFields(UnknownFieldSet unknownFields);
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Convenience methods.
|
||||
|
||||
// (From MessageLite.Builder, re-declared here only for return type
|
||||
// covariance.)
|
||||
Builder mergeFrom(ByteString data) throws InvalidProtocolBufferException;
|
||||
Builder mergeFrom(ByteString data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
Builder mergeFrom(byte[] data) throws InvalidProtocolBufferException;
|
||||
Builder mergeFrom(byte[] data, int off, int len)
|
||||
throws InvalidProtocolBufferException;
|
||||
Builder mergeFrom(byte[] data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
Builder mergeFrom(byte[] data, int off, int len,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
Builder mergeFrom(InputStream input) throws IOException;
|
||||
Builder mergeFrom(InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException;
|
||||
boolean mergeDelimitedFrom(InputStream input)
|
||||
throws IOException;
|
||||
boolean mergeDelimitedFrom(InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,332 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
// TODO(kenton): Use generics? E.g. Builder<BuilderType extends Builder>, then
|
||||
// mergeFrom*() could return BuilderType for better type-safety.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Abstract interface implemented by Protocol Message objects.
|
||||
*
|
||||
* <p>This interface is implemented by all protocol message objects. Non-lite
|
||||
* messages additionally implement the Message interface, which is a subclass
|
||||
* of MessageLite. Use MessageLite instead when you only need the subset of
|
||||
* features which it supports -- namely, nothing that uses descriptors or
|
||||
* reflection. You can instruct the protocol compiler to generate classes
|
||||
* which implement only MessageLite, not the full Message interface, by adding
|
||||
* the follow line to the .proto file:
|
||||
* <pre>
|
||||
* option optimize_for = LITE_RUNTIME;
|
||||
* </pre>
|
||||
*
|
||||
* <p>This is particularly useful on resource-constrained systems where the
|
||||
* full protocol buffers runtime library is too big.
|
||||
*
|
||||
* <p>Note that on non-constrained systems (e.g. servers) when you need to link
|
||||
* in lots of protocol definitions, a better way to reduce total code footprint
|
||||
* is to use {@code optimize_for = CODE_SIZE}. This will make the generated
|
||||
* code smaller while still supporting all the same features (at the expense of
|
||||
* speed). {@code optimize_for = LITE_RUNTIME} is best when you only have a
|
||||
* small number of message types linked into your binary, in which case the
|
||||
* size of the protocol buffers runtime itself is the biggest problem.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public interface MessageLite extends MessageLiteOrBuilder {
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the message and writes it to {@code output}. This does not
|
||||
* flush or close the stream.
|
||||
*/
|
||||
void writeTo(CodedOutputStream output) throws IOException;
|
||||
|
||||
/**
|
||||
* Get the number of bytes required to encode this message. The result
|
||||
* is only computed on the first call and memoized after that.
|
||||
*/
|
||||
int getSerializedSize();
|
||||
|
||||
|
||||
/**
|
||||
* Gets the parser for a message of the same type as this message.
|
||||
*/
|
||||
Parser<? extends MessageLite> getParserForType();
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Convenience methods.
|
||||
|
||||
/**
|
||||
* Serializes the message to a {@code ByteString} and returns it. This is
|
||||
* just a trivial wrapper around
|
||||
* {@link #writeTo(CodedOutputStream)}.
|
||||
*/
|
||||
ByteString toByteString();
|
||||
|
||||
/**
|
||||
* Serializes the message to a {@code byte} array and returns it. This is
|
||||
* just a trivial wrapper around
|
||||
* {@link #writeTo(CodedOutputStream)}.
|
||||
*/
|
||||
byte[] toByteArray();
|
||||
|
||||
/**
|
||||
* Serializes the message and writes it to {@code output}. This is just a
|
||||
* trivial wrapper around {@link #writeTo(CodedOutputStream)}. This does
|
||||
* not flush or close the stream.
|
||||
* <p>
|
||||
* NOTE: Protocol Buffers are not self-delimiting. Therefore, if you write
|
||||
* any more data to the stream after the message, you must somehow ensure
|
||||
* that the parser on the receiving end does not interpret this as being
|
||||
* part of the protocol message. This can be done e.g. by writing the size
|
||||
* of the message before the data, then making sure to limit the input to
|
||||
* that size on the receiving end (e.g. by wrapping the InputStream in one
|
||||
* which limits the input). Alternatively, just use
|
||||
* {@link #writeDelimitedTo(OutputStream)}.
|
||||
*/
|
||||
void writeTo(OutputStream output) throws IOException;
|
||||
|
||||
/**
|
||||
* Like {@link #writeTo(OutputStream)}, but writes the size of the message
|
||||
* as a varint before writing the data. This allows more data to be written
|
||||
* to the stream after the message without the need to delimit the message
|
||||
* data yourself. Use {@link Builder#mergeDelimitedFrom(InputStream)} (or
|
||||
* the static method {@code YourMessageType.parseDelimitedFrom(InputStream)})
|
||||
* to parse messages written by this method.
|
||||
*/
|
||||
void writeDelimitedTo(OutputStream output) throws IOException;
|
||||
|
||||
// =================================================================
|
||||
// Builders
|
||||
|
||||
/**
|
||||
* Constructs a new builder for a message of the same type as this message.
|
||||
*/
|
||||
Builder newBuilderForType();
|
||||
|
||||
/**
|
||||
* Constructs a builder initialized with the current message. Use this to
|
||||
* derive a new message from the current one.
|
||||
*/
|
||||
Builder toBuilder();
|
||||
|
||||
/**
|
||||
* Abstract interface implemented by Protocol Message builders.
|
||||
*/
|
||||
interface Builder extends MessageLiteOrBuilder, Cloneable {
|
||||
/** Resets all fields to their default values. */
|
||||
Builder clear();
|
||||
|
||||
/**
|
||||
* Constructs the message based on the state of the Builder. Subsequent
|
||||
* changes to the Builder will not affect the returned message.
|
||||
* @throws UninitializedMessageException The message is missing one or more
|
||||
* required fields (i.e. {@link #isInitialized()} returns false).
|
||||
* Use {@link #buildPartial()} to bypass this check.
|
||||
*/
|
||||
MessageLite build();
|
||||
|
||||
/**
|
||||
* Like {@link #build()}, but does not throw an exception if the message
|
||||
* is missing required fields. Instead, a partial message is returned.
|
||||
* Subsequent changes to the Builder will not affect the returned message.
|
||||
*/
|
||||
MessageLite buildPartial();
|
||||
|
||||
/**
|
||||
* Clones the Builder.
|
||||
* @see Object#clone()
|
||||
*/
|
||||
Builder clone();
|
||||
|
||||
/**
|
||||
* Parses a message of this type from the input and merges it with this
|
||||
* message.
|
||||
*
|
||||
* <p>Warning: This does not verify that all required fields are present in
|
||||
* the input message. If you call {@link #build()} without setting all
|
||||
* required fields, it will throw an {@link UninitializedMessageException},
|
||||
* which is a {@code RuntimeException} and thus might not be caught. There
|
||||
* are a few good ways to deal with this:
|
||||
* <ul>
|
||||
* <li>Call {@link #isInitialized()} to verify that all required fields
|
||||
* are set before building.
|
||||
* <li>Use {@code buildPartial()} to build, which ignores missing
|
||||
* required fields.
|
||||
* </ul>
|
||||
*
|
||||
* <p>Note: The caller should call
|
||||
* {@link CodedInputStream#checkLastTagWas(int)} after calling this to
|
||||
* verify that the last tag seen was the appropriate end-group tag,
|
||||
* or zero for EOF.
|
||||
*/
|
||||
Builder mergeFrom(CodedInputStream input) throws IOException;
|
||||
|
||||
/**
|
||||
* Like {@link Builder#mergeFrom(CodedInputStream)}, but also
|
||||
* parses extensions. The extensions that you want to be able to parse
|
||||
* must be registered in {@code extensionRegistry}. Extensions not in
|
||||
* the registry will be treated as unknown fields.
|
||||
*/
|
||||
Builder mergeFrom(CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException;
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Convenience methods.
|
||||
|
||||
/**
|
||||
* Parse {@code data} as a message of this type and merge it with the
|
||||
* message being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream)}.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
Builder mergeFrom(ByteString data) throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parse {@code data} as a message of this type and merge it with the
|
||||
* message being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream,ExtensionRegistryLite)}.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
Builder mergeFrom(ByteString data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parse {@code data} as a message of this type and merge it with the
|
||||
* message being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream)}.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
Builder mergeFrom(byte[] data) throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parse {@code data} as a message of this type and merge it with the
|
||||
* message being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream)}.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
Builder mergeFrom(byte[] data, int off, int len)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parse {@code data} as a message of this type and merge it with the
|
||||
* message being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream,ExtensionRegistryLite)}.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
Builder mergeFrom(byte[] data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parse {@code data} as a message of this type and merge it with the
|
||||
* message being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream,ExtensionRegistryLite)}.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
Builder mergeFrom(byte[] data, int off, int len,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parse a message of this type from {@code input} and merge it with the
|
||||
* message being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream)}. Note that this method always
|
||||
* reads the <i>entire</i> input (unless it throws an exception). If you
|
||||
* want it to stop earlier, you will need to wrap your input in some
|
||||
* wrapper stream that limits reading. Or, use
|
||||
* {@link MessageLite#writeDelimitedTo(OutputStream)} to write your message
|
||||
* and {@link #mergeDelimitedFrom(InputStream)} to read it.
|
||||
* <p>
|
||||
* Despite usually reading the entire input, this does not close the stream.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
Builder mergeFrom(InputStream input) throws IOException;
|
||||
|
||||
/**
|
||||
* Parse a message of this type from {@code input} and merge it with the
|
||||
* message being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream,ExtensionRegistryLite)}.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
Builder mergeFrom(InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Like {@link #mergeFrom(InputStream)}, but does not read until EOF.
|
||||
* Instead, the size of the message (encoded as a varint) is read first,
|
||||
* then the message data. Use
|
||||
* {@link MessageLite#writeDelimitedTo(OutputStream)} to write messages in
|
||||
* this format.
|
||||
*
|
||||
* @return True if successful, or false if the stream is at EOF when the
|
||||
* method starts. Any other error (including reaching EOF during
|
||||
* parsing) will cause an exception to be thrown.
|
||||
*/
|
||||
boolean mergeDelimitedFrom(InputStream input)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Like {@link #mergeDelimitedFrom(InputStream)} but supporting extensions.
|
||||
*/
|
||||
boolean mergeDelimitedFrom(InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
/**
|
||||
* Base interface for methods common to {@link MessageLite}
|
||||
* and {@link MessageLite.Builder} to provide type equivalency.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public interface MessageLiteOrBuilder {
|
||||
/**
|
||||
* Get an instance of the type with no fields set. Because no fields are set,
|
||||
* all getters for singular fields will return default values and repeated
|
||||
* fields will appear empty.
|
||||
* This may or may not be a singleton. This differs from the
|
||||
* {@code getDefaultInstance()} method of generated message classes in that
|
||||
* this method is an abstract method of the {@code MessageLite} interface
|
||||
* whereas {@code getDefaultInstance()} is a static method of a specific
|
||||
* class. They return the same thing.
|
||||
*/
|
||||
MessageLite getDefaultInstanceForType();
|
||||
|
||||
/**
|
||||
* Returns true if all required fields in the message and all embedded
|
||||
* messages are set, false otherwise.
|
||||
*
|
||||
* <p>See also: {@link MessageOrBuilder#getInitializationErrorString()}
|
||||
*/
|
||||
boolean isInitialized();
|
||||
|
||||
}
|
||||
|
|
@ -1,142 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Base interface for methods common to {@link Message} and
|
||||
* {@link Message.Builder} to provide type equivalency.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public interface MessageOrBuilder extends MessageLiteOrBuilder {
|
||||
|
||||
// (From MessageLite, re-declared here only for return type covariance.)
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
Message getDefaultInstanceForType();
|
||||
|
||||
/**
|
||||
* Returns a list of field paths (e.g. "foo.bar.baz") of required fields
|
||||
* which are not set in this message. You should call
|
||||
* {@link MessageLiteOrBuilder#isInitialized()} first to check if there
|
||||
* are any missing fields, as that method is likely to be much faster
|
||||
* than this one even when the message is fully-initialized.
|
||||
*/
|
||||
List<String> findInitializationErrors();
|
||||
|
||||
/**
|
||||
* Returns a comma-delimited list of required fields which are not set
|
||||
* in this message object. You should call
|
||||
* {@link MessageLiteOrBuilder#isInitialized()} first to check if there
|
||||
* are any missing fields, as that method is likely to be much faster
|
||||
* than this one even when the message is fully-initialized.
|
||||
*/
|
||||
String getInitializationErrorString();
|
||||
|
||||
/**
|
||||
* Get the message's type's descriptor. This differs from the
|
||||
* {@code getDescriptor()} method of generated message classes in that
|
||||
* this method is an abstract method of the {@code Message} interface
|
||||
* whereas {@code getDescriptor()} is a static method of a specific class.
|
||||
* They return the same thing.
|
||||
*/
|
||||
Descriptors.Descriptor getDescriptorForType();
|
||||
|
||||
/**
|
||||
* Returns a collection of all the fields in this message which are set
|
||||
* and their corresponding values. A singular ("required" or "optional")
|
||||
* field is set iff hasField() returns true for that field. A "repeated"
|
||||
* field is set iff getRepeatedFieldSize() is greater than zero. The
|
||||
* values are exactly what would be returned by calling
|
||||
* {@link #getField(Descriptors.FieldDescriptor)} for each field. The map
|
||||
* is guaranteed to be a sorted map, so iterating over it will return fields
|
||||
* in order by field number.
|
||||
* <br>
|
||||
* If this is for a builder, the returned map may or may not reflect future
|
||||
* changes to the builder. Either way, the returned map is itself
|
||||
* unmodifiable.
|
||||
*/
|
||||
Map<Descriptors.FieldDescriptor, Object> getAllFields();
|
||||
|
||||
/**
|
||||
* Returns true if the given field is set. This is exactly equivalent to
|
||||
* calling the generated "has" accessor method corresponding to the field.
|
||||
* @throws IllegalArgumentException The field is a repeated field, or
|
||||
* {@code field.getContainingType() != getDescriptorForType()}.
|
||||
*/
|
||||
boolean hasField(Descriptors.FieldDescriptor field);
|
||||
|
||||
/**
|
||||
* Obtains the value of the given field, or the default value if it is
|
||||
* not set. For primitive fields, the boxed primitive value is returned.
|
||||
* For enum fields, the EnumValueDescriptor for the value is returned. For
|
||||
* embedded message fields, the sub-message is returned. For repeated
|
||||
* fields, a java.util.List is returned.
|
||||
*/
|
||||
Object getField(Descriptors.FieldDescriptor field);
|
||||
|
||||
/**
|
||||
* Gets the number of elements of a repeated field. This is exactly
|
||||
* equivalent to calling the generated "Count" accessor method corresponding
|
||||
* to the field.
|
||||
* @throws IllegalArgumentException The field is not a repeated field, or
|
||||
* {@code field.getContainingType() != getDescriptorForType()}.
|
||||
*/
|
||||
int getRepeatedFieldCount(Descriptors.FieldDescriptor field);
|
||||
|
||||
/**
|
||||
* Gets an element of a repeated field. For primitive fields, the boxed
|
||||
* primitive value is returned. For enum fields, the EnumValueDescriptor
|
||||
* for the value is returned. For embedded message fields, the sub-message
|
||||
* is returned.
|
||||
* @throws IllegalArgumentException The field is not a repeated field, or
|
||||
* {@code field.getContainingType() != getDescriptorForType()}.
|
||||
*/
|
||||
Object getRepeatedField(Descriptors.FieldDescriptor field, int index);
|
||||
|
||||
/** Get the {@link UnknownFieldSet} for this message. */
|
||||
UnknownFieldSet getUnknownFields();
|
||||
}
|
||||
|
|
@ -1,272 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Abstract interface for parsing Protocol Messages.
|
||||
*
|
||||
* @author liujisi@google.com (Pherl Liu)
|
||||
*/
|
||||
public interface Parser<MessageType> {
|
||||
/**
|
||||
* Parses a message of {@code MessageType} from the input.
|
||||
*
|
||||
* <p>Note: The caller should call
|
||||
* {@link CodedInputStream#checkLastTagWas(int)} after calling this to
|
||||
* verify that the last tag seen was the appropriate end-group tag,
|
||||
* or zero for EOF.
|
||||
*/
|
||||
public MessageType parseFrom(CodedInputStream input)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(CodedInputStream)}, but also parses extensions.
|
||||
* The extensions that you want to be able to parse must be registered in
|
||||
* {@code extensionRegistry}. Extensions not in the registry will be treated
|
||||
* as unknown fields.
|
||||
*/
|
||||
public MessageType parseFrom(CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(CodedInputStream)}, but does not throw an
|
||||
* exception if the message is missing required fields. Instead, a partial
|
||||
* message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(CodedInputStream input)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(CodedInputStream input, ExtensionRegistryLite)},
|
||||
* but does not throw an exception if the message is missing required fields.
|
||||
* Instead, a partial message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Convenience methods.
|
||||
|
||||
/**
|
||||
* Parses {@code data} as a message of {@code MessageType}.
|
||||
* This is just a small wrapper around {@link #parseFrom(CodedInputStream)}.
|
||||
*/
|
||||
public MessageType parseFrom(ByteString data)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parses {@code data} as a message of {@code MessageType}.
|
||||
* This is just a small wrapper around
|
||||
* {@link #parseFrom(CodedInputStream, ExtensionRegistryLite)}.
|
||||
*/
|
||||
public MessageType parseFrom(ByteString data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(ByteString)}, but does not throw an
|
||||
* exception if the message is missing required fields. Instead, a partial
|
||||
* message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(ByteString data)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(ByteString, ExtensionRegistryLite)},
|
||||
* but does not throw an exception if the message is missing required fields.
|
||||
* Instead, a partial message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(ByteString data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parses {@code data} as a message of {@code MessageType}.
|
||||
* This is just a small wrapper around {@link #parseFrom(CodedInputStream)}.
|
||||
*/
|
||||
public MessageType parseFrom(byte[] data, int off, int len)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parses {@code data} as a message of {@code MessageType}.
|
||||
* This is just a small wrapper around
|
||||
* {@link #parseFrom(CodedInputStream, ExtensionRegistryLite)}.
|
||||
*/
|
||||
public MessageType parseFrom(byte[] data, int off, int len,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parses {@code data} as a message of {@code MessageType}.
|
||||
* This is just a small wrapper around {@link #parseFrom(CodedInputStream)}.
|
||||
*/
|
||||
public MessageType parseFrom(byte[] data)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parses {@code data} as a message of {@code MessageType}.
|
||||
* This is just a small wrapper around
|
||||
* {@link #parseFrom(CodedInputStream, ExtensionRegistryLite)}.
|
||||
*/
|
||||
public MessageType parseFrom(byte[] data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(byte[], int, int)}, but does not throw an
|
||||
* exception if the message is missing required fields. Instead, a partial
|
||||
* message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(byte[] data, int off, int len)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(ByteString, ExtensionRegistryLite)},
|
||||
* but does not throw an exception if the message is missing required fields.
|
||||
* Instead, a partial message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(byte[] data, int off, int len,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(byte[])}, but does not throw an
|
||||
* exception if the message is missing required fields. Instead, a partial
|
||||
* message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(byte[] data)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(byte[], ExtensionRegistryLite)},
|
||||
* but does not throw an exception if the message is missing required fields.
|
||||
* Instead, a partial message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(byte[] data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parse a message of {@code MessageType} from {@code input}.
|
||||
* This is just a small wrapper around {@link #parseFrom(CodedInputStream)}.
|
||||
* Note that this method always reads the <i>entire</i> input (unless it
|
||||
* throws an exception). If you want it to stop earlier, you will need to
|
||||
* wrap your input in some wrapper stream that limits reading. Or, use
|
||||
* {@link MessageLite#writeDelimitedTo(java.io.OutputStream)} to write your
|
||||
* message and {@link #parseDelimitedFrom(InputStream)} to read it.
|
||||
* <p>
|
||||
* Despite usually reading the entire input, this does not close the stream.
|
||||
*/
|
||||
public MessageType parseFrom(InputStream input)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parses a message of {@code MessageType} from {@code input}.
|
||||
* This is just a small wrapper around
|
||||
* {@link #parseFrom(CodedInputStream, ExtensionRegistryLite)}.
|
||||
*/
|
||||
public MessageType parseFrom(InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(InputStream)}, but does not throw an
|
||||
* exception if the message is missing required fields. Instead, a partial
|
||||
* message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(InputStream input)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(InputStream, ExtensionRegistryLite)},
|
||||
* but does not throw an exception if the message is missing required fields.
|
||||
* Instead, a partial message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(InputStream)}, but does not read util EOF.
|
||||
* Instead, the size of message (encoded as a varint) is read first,
|
||||
* then the message data. Use
|
||||
* {@link MessageLite#writeDelimitedTo(java.io.OutputStream)} to write
|
||||
* messages in this format.
|
||||
*
|
||||
* @return True if successful, or false if the stream is at EOF when the
|
||||
* method starts. Any other error (including reaching EOF during
|
||||
* parsing) will cause an exception to be thrown.
|
||||
*/
|
||||
public MessageType parseDelimitedFrom(InputStream input)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseDelimitedFrom(InputStream)} but supporting extensions.
|
||||
*/
|
||||
public MessageType parseDelimitedFrom(InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseDelimitedFrom(InputStream)}, but does not throw an
|
||||
* exception if the message is missing required fields. Instead, a partial
|
||||
* message is returned.
|
||||
*/
|
||||
public MessageType parsePartialDelimitedFrom(InputStream input)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseDelimitedFrom(InputStream, ExtensionRegistryLite)},
|
||||
* but does not throw an exception if the message is missing required fields.
|
||||
* Instead, a partial message is returned.
|
||||
*/
|
||||
public MessageType parsePartialDelimitedFrom(
|
||||
InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
import org.apache.pekko.protobuf.Descriptors.EnumDescriptor;
|
||||
import org.apache.pekko.protobuf.Descriptors.EnumValueDescriptor;
|
||||
|
||||
/**
|
||||
* Interface of useful methods added to all enums generated by the protocol
|
||||
* compiler.
|
||||
*/
|
||||
public interface ProtocolMessageEnum extends Internal.EnumLite {
|
||||
|
||||
/**
|
||||
* Return the value's numeric value as defined in the .proto file.
|
||||
*/
|
||||
int getNumber();
|
||||
|
||||
/**
|
||||
* Return the value's descriptor, which contains information such as
|
||||
* value name, number, and type.
|
||||
*/
|
||||
EnumValueDescriptor getValueDescriptor();
|
||||
|
||||
/**
|
||||
* Return the enum type's descriptor, which contains information
|
||||
* about each defined value, etc.
|
||||
*/
|
||||
EnumDescriptor getDescriptorForType();
|
||||
}
|
||||
|
|
@ -1,709 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@code RepeatedFieldBuilder} implements a structure that a protocol
|
||||
* message uses to hold a repeated field of other protocol messages. It supports
|
||||
* the classical use case of adding immutable {@link Message}'s to the
|
||||
* repeated field and is highly optimized around this (no extra memory
|
||||
* allocations and sharing of immutable arrays).
|
||||
* <br>
|
||||
* It also supports the additional use case of adding a {@link Message.Builder}
|
||||
* to the repeated field and deferring conversion of that {@code Builder}
|
||||
* to an immutable {@code Message}. In this way, it's possible to maintain
|
||||
* a tree of {@code Builder}'s that acts as a fully read/write data
|
||||
* structure.
|
||||
* <br>
|
||||
* Logically, one can think of a tree of builders as converting the entire tree
|
||||
* to messages when build is called on the root or when any method is called
|
||||
* that desires a Message instead of a Builder. In terms of the implementation,
|
||||
* the {@code SingleFieldBuilder} and {@code RepeatedFieldBuilder}
|
||||
* classes cache messages that were created so that messages only need to be
|
||||
* created when some change occurred in its builder or a builder for one of its
|
||||
* descendants.
|
||||
*
|
||||
* @param <MType> the type of message for the field
|
||||
* @param <BType> the type of builder for the field
|
||||
* @param <IType> the common interface for the message and the builder
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public class RepeatedFieldBuilder
|
||||
<MType extends GeneratedMessage,
|
||||
BType extends GeneratedMessage.Builder,
|
||||
IType extends MessageOrBuilder>
|
||||
implements GeneratedMessage.BuilderParent {
|
||||
|
||||
// Parent to send changes to.
|
||||
private GeneratedMessage.BuilderParent parent;
|
||||
|
||||
// List of messages. Never null. It may be immutable, in which case
|
||||
// isMessagesListImmutable will be true. See note below.
|
||||
private List<MType> messages;
|
||||
|
||||
// Whether messages is an mutable array that can be modified.
|
||||
private boolean isMessagesListMutable;
|
||||
|
||||
// List of builders. May be null, in which case, no nested builders were
|
||||
// created. If not null, entries represent the builder for that index.
|
||||
private List<SingleFieldBuilder<MType, BType, IType>> builders;
|
||||
|
||||
// Here are the invariants for messages and builders:
|
||||
// 1. messages is never null and its count corresponds to the number of items
|
||||
// in the repeated field.
|
||||
// 2. If builders is non-null, messages and builders MUST always
|
||||
// contain the same number of items.
|
||||
// 3. Entries in either array can be null, but for any index, there MUST be
|
||||
// either a Message in messages or a builder in builders.
|
||||
// 4. If the builder at an index is non-null, the builder is
|
||||
// authoritative. This is the case where a Builder was set on the index.
|
||||
// Any message in the messages array MUST be ignored.
|
||||
// t. If the builder at an index is null, the message in the messages
|
||||
// list is authoritative. This is the case where a Message (not a Builder)
|
||||
// was set directly for an index.
|
||||
|
||||
// Indicates that we've built a message and so we are now obligated
|
||||
// to dispatch dirty invalidations. See GeneratedMessage.BuilderListener.
|
||||
private boolean isClean;
|
||||
|
||||
// A view of this builder that exposes a List interface of messages. This is
|
||||
// initialized on demand. This is fully backed by this object and all changes
|
||||
// are reflected in it. Access to any item converts it to a message if it
|
||||
// was a builder.
|
||||
private MessageExternalList<MType, BType, IType> externalMessageList;
|
||||
|
||||
// A view of this builder that exposes a List interface of builders. This is
|
||||
// initialized on demand. This is fully backed by this object and all changes
|
||||
// are reflected in it. Access to any item converts it to a builder if it
|
||||
// was a message.
|
||||
private BuilderExternalList<MType, BType, IType> externalBuilderList;
|
||||
|
||||
// A view of this builder that exposes a List interface of the interface
|
||||
// implemented by messages and builders. This is initialized on demand. This
|
||||
// is fully backed by this object and all changes are reflected in it.
|
||||
// Access to any item returns either a builder or message depending on
|
||||
// what is most efficient.
|
||||
private MessageOrBuilderExternalList<MType, BType, IType>
|
||||
externalMessageOrBuilderList;
|
||||
|
||||
/**
|
||||
* Constructs a new builder with an empty list of messages.
|
||||
*
|
||||
* @param messages the current list of messages
|
||||
* @param isMessagesListMutable Whether the messages list is mutable
|
||||
* @param parent a listener to notify of changes
|
||||
* @param isClean whether the builder is initially marked clean
|
||||
*/
|
||||
public RepeatedFieldBuilder(
|
||||
List<MType> messages,
|
||||
boolean isMessagesListMutable,
|
||||
GeneratedMessage.BuilderParent parent,
|
||||
boolean isClean) {
|
||||
this.messages = messages;
|
||||
this.isMessagesListMutable = isMessagesListMutable;
|
||||
this.parent = parent;
|
||||
this.isClean = isClean;
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
// Null out parent so we stop sending it invalidations.
|
||||
parent = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the list of messages is mutable so it can be updated. If it's
|
||||
* immutable, a copy is made.
|
||||
*/
|
||||
private void ensureMutableMessageList() {
|
||||
if (!isMessagesListMutable) {
|
||||
messages = new ArrayList<MType>(messages);
|
||||
isMessagesListMutable = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the list of builders is not null. If it's null, the list is
|
||||
* created and initialized to be the same size as the messages list with
|
||||
* null entries.
|
||||
*/
|
||||
private void ensureBuilders() {
|
||||
if (this.builders == null) {
|
||||
this.builders =
|
||||
new ArrayList<SingleFieldBuilder<MType, BType, IType>>(
|
||||
messages.size());
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
builders.add(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the count of items in the list.
|
||||
*
|
||||
* @return the count of items in the list.
|
||||
*/
|
||||
public int getCount() {
|
||||
return messages.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether the list is empty.
|
||||
*
|
||||
* @return whether the list is empty
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return messages.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message at the specified index. If the message is currently stored
|
||||
* as a {@code Builder}, it is converted to a {@code Message} by
|
||||
* calling {@link Message.Builder#buildPartial} on it.
|
||||
*
|
||||
* @param index the index of the message to get
|
||||
* @return the message for the specified index
|
||||
*/
|
||||
public MType getMessage(int index) {
|
||||
return getMessage(index, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message at the specified index. If the message is currently stored
|
||||
* as a {@code Builder}, it is converted to a {@code Message} by
|
||||
* calling {@link Message.Builder#buildPartial} on it.
|
||||
*
|
||||
* @param index the index of the message to get
|
||||
* @param forBuild this is being called for build so we want to make sure
|
||||
* we SingleFieldBuilder.build to send dirty invalidations
|
||||
* @return the message for the specified index
|
||||
*/
|
||||
private MType getMessage(int index, boolean forBuild) {
|
||||
if (this.builders == null) {
|
||||
// We don't have any builders -- return the current Message.
|
||||
// This is the case where no builder was created, so we MUST have a
|
||||
// Message.
|
||||
return messages.get(index);
|
||||
}
|
||||
|
||||
SingleFieldBuilder<MType, BType, IType> builder = builders.get(index);
|
||||
if (builder == null) {
|
||||
// We don't have a builder -- return the current message.
|
||||
// This is the case where no builder was created for the entry at index,
|
||||
// so we MUST have a message.
|
||||
return messages.get(index);
|
||||
|
||||
} else {
|
||||
return forBuild ? builder.build() : builder.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a builder for the specified index. If no builder has been created for
|
||||
* that index, a builder is created on demand by calling
|
||||
* {@link Message#toBuilder}.
|
||||
*
|
||||
* @param index the index of the message to get
|
||||
* @return The builder for that index
|
||||
*/
|
||||
public BType getBuilder(int index) {
|
||||
ensureBuilders();
|
||||
SingleFieldBuilder<MType, BType, IType> builder = builders.get(index);
|
||||
if (builder == null) {
|
||||
MType message = messages.get(index);
|
||||
builder = new SingleFieldBuilder<MType, BType, IType>(
|
||||
message, this, isClean);
|
||||
builders.set(index, builder);
|
||||
}
|
||||
return builder.getBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the base class interface for the specified index. This may either be
|
||||
* a builder or a message. It will return whatever is more efficient.
|
||||
*
|
||||
* @param index the index of the message to get
|
||||
* @return the message or builder for the index as the base class interface
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public IType getMessageOrBuilder(int index) {
|
||||
if (this.builders == null) {
|
||||
// We don't have any builders -- return the current Message.
|
||||
// This is the case where no builder was created, so we MUST have a
|
||||
// Message.
|
||||
return (IType) messages.get(index);
|
||||
}
|
||||
|
||||
SingleFieldBuilder<MType, BType, IType> builder = builders.get(index);
|
||||
if (builder == null) {
|
||||
// We don't have a builder -- return the current message.
|
||||
// This is the case where no builder was created for the entry at index,
|
||||
// so we MUST have a message.
|
||||
return (IType) messages.get(index);
|
||||
|
||||
} else {
|
||||
return builder.getMessageOrBuilder();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a message at the specified index replacing the existing item at
|
||||
* that index.
|
||||
*
|
||||
* @param index the index to set.
|
||||
* @param message the message to set
|
||||
* @return the builder
|
||||
*/
|
||||
public RepeatedFieldBuilder<MType, BType, IType> setMessage(
|
||||
int index, MType message) {
|
||||
if (message == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
ensureMutableMessageList();
|
||||
messages.set(index, message);
|
||||
if (builders != null) {
|
||||
SingleFieldBuilder<MType, BType, IType> entry =
|
||||
builders.set(index, null);
|
||||
if (entry != null) {
|
||||
entry.dispose();
|
||||
}
|
||||
}
|
||||
onChanged();
|
||||
incrementModCounts();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the specified element to the end of this list.
|
||||
*
|
||||
* @param message the message to add
|
||||
* @return the builder
|
||||
*/
|
||||
public RepeatedFieldBuilder<MType, BType, IType> addMessage(
|
||||
MType message) {
|
||||
if (message == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
ensureMutableMessageList();
|
||||
messages.add(message);
|
||||
if (builders != null) {
|
||||
builders.add(null);
|
||||
}
|
||||
onChanged();
|
||||
incrementModCounts();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the specified message at the specified position in this list.
|
||||
* Shifts the element currently at that position (if any) and any subsequent
|
||||
* elements to the right (adds one to their indices).
|
||||
*
|
||||
* @param index the index at which to insert the message
|
||||
* @param message the message to add
|
||||
* @return the builder
|
||||
*/
|
||||
public RepeatedFieldBuilder<MType, BType, IType> addMessage(
|
||||
int index, MType message) {
|
||||
if (message == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
ensureMutableMessageList();
|
||||
messages.add(index, message);
|
||||
if (builders != null) {
|
||||
builders.add(index, null);
|
||||
}
|
||||
onChanged();
|
||||
incrementModCounts();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends all of the messages in the specified collection to the end of
|
||||
* this list, in the order that they are returned by the specified
|
||||
* collection's iterator.
|
||||
*
|
||||
* @param values the messages to add
|
||||
* @return the builder
|
||||
*/
|
||||
public RepeatedFieldBuilder<MType, BType, IType> addAllMessages(
|
||||
Iterable<? extends MType> values) {
|
||||
for (final MType value : values) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
}
|
||||
if (values instanceof Collection) {
|
||||
@SuppressWarnings("unchecked") final
|
||||
Collection<MType> collection = (Collection<MType>) values;
|
||||
if (collection.size() == 0) {
|
||||
return this;
|
||||
}
|
||||
ensureMutableMessageList();
|
||||
for (MType value : values) {
|
||||
addMessage(value);
|
||||
}
|
||||
} else {
|
||||
ensureMutableMessageList();
|
||||
for (MType value : values) {
|
||||
addMessage(value);
|
||||
}
|
||||
}
|
||||
onChanged();
|
||||
incrementModCounts();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a new builder to the end of this list and returns the builder.
|
||||
*
|
||||
* @param message the message to add which is the basis of the builder
|
||||
* @return the new builder
|
||||
*/
|
||||
public BType addBuilder(MType message) {
|
||||
ensureMutableMessageList();
|
||||
ensureBuilders();
|
||||
SingleFieldBuilder<MType, BType, IType> builder =
|
||||
new SingleFieldBuilder<MType, BType, IType>(
|
||||
message, this, isClean);
|
||||
messages.add(null);
|
||||
builders.add(builder);
|
||||
onChanged();
|
||||
incrementModCounts();
|
||||
return builder.getBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a new builder at the specified position in this list.
|
||||
* Shifts the element currently at that position (if any) and any subsequent
|
||||
* elements to the right (adds one to their indices).
|
||||
*
|
||||
* @param index the index at which to insert the builder
|
||||
* @param message the message to add which is the basis of the builder
|
||||
* @return the builder
|
||||
*/
|
||||
public BType addBuilder(int index, MType message) {
|
||||
ensureMutableMessageList();
|
||||
ensureBuilders();
|
||||
SingleFieldBuilder<MType, BType, IType> builder =
|
||||
new SingleFieldBuilder<MType, BType, IType>(
|
||||
message, this, isClean);
|
||||
messages.add(index, null);
|
||||
builders.add(index, builder);
|
||||
onChanged();
|
||||
incrementModCounts();
|
||||
return builder.getBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the element at the specified position in this list. Shifts any
|
||||
* subsequent elements to the left (subtracts one from their indices).
|
||||
* Returns the element that was removed from the list.
|
||||
*
|
||||
* @param index the index at which to remove the message
|
||||
*/
|
||||
public void remove(int index) {
|
||||
ensureMutableMessageList();
|
||||
messages.remove(index);
|
||||
if (builders != null) {
|
||||
SingleFieldBuilder<MType, BType, IType> entry =
|
||||
builders.remove(index);
|
||||
if (entry != null) {
|
||||
entry.dispose();
|
||||
}
|
||||
}
|
||||
onChanged();
|
||||
incrementModCounts();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all of the elements from this list.
|
||||
* The list will be empty after this call returns.
|
||||
*/
|
||||
public void clear() {
|
||||
messages = Collections.emptyList();
|
||||
isMessagesListMutable = false;
|
||||
if (builders != null) {
|
||||
for (SingleFieldBuilder<MType, BType, IType> entry :
|
||||
builders) {
|
||||
if (entry != null) {
|
||||
entry.dispose();
|
||||
}
|
||||
}
|
||||
builders = null;
|
||||
}
|
||||
onChanged();
|
||||
incrementModCounts();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the list of messages from the builder and returns them.
|
||||
*
|
||||
* @return an immutable list of messages
|
||||
*/
|
||||
public List<MType> build() {
|
||||
// Now that build has been called, we are required to dispatch
|
||||
// invalidations.
|
||||
isClean = true;
|
||||
|
||||
if (!isMessagesListMutable && builders == null) {
|
||||
// We still have an immutable list and we never created a builder.
|
||||
return messages;
|
||||
}
|
||||
|
||||
boolean allMessagesInSync = true;
|
||||
if (!isMessagesListMutable) {
|
||||
// We still have an immutable list. Let's see if any of them are out
|
||||
// of sync with their builders.
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
Message message = messages.get(i);
|
||||
SingleFieldBuilder<MType, BType, IType> builder = builders.get(i);
|
||||
if (builder != null) {
|
||||
if (builder.build() != message) {
|
||||
allMessagesInSync = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (allMessagesInSync) {
|
||||
// Immutable list is still in sync.
|
||||
return messages;
|
||||
}
|
||||
}
|
||||
|
||||
// Need to make sure messages is up to date
|
||||
ensureMutableMessageList();
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
messages.set(i, getMessage(i, true));
|
||||
}
|
||||
|
||||
// We're going to return our list as immutable so we mark that we can
|
||||
// no longer update it.
|
||||
messages = Collections.unmodifiableList(messages);
|
||||
isMessagesListMutable = false;
|
||||
return messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a view of the builder as a list of messages. The returned list is live
|
||||
* and will reflect any changes to the underlying builder.
|
||||
*
|
||||
* @return the messages in the list
|
||||
*/
|
||||
public List<MType> getMessageList() {
|
||||
if (externalMessageList == null) {
|
||||
externalMessageList =
|
||||
new MessageExternalList<MType, BType, IType>(this);
|
||||
}
|
||||
return externalMessageList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a view of the builder as a list of builders. This returned list is
|
||||
* live and will reflect any changes to the underlying builder.
|
||||
*
|
||||
* @return the builders in the list
|
||||
*/
|
||||
public List<BType> getBuilderList() {
|
||||
if (externalBuilderList == null) {
|
||||
externalBuilderList =
|
||||
new BuilderExternalList<MType, BType, IType>(this);
|
||||
}
|
||||
return externalBuilderList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a view of the builder as a list of MessageOrBuilders. This returned
|
||||
* list is live and will reflect any changes to the underlying builder.
|
||||
*
|
||||
* @return the builders in the list
|
||||
*/
|
||||
public List<IType> getMessageOrBuilderList() {
|
||||
if (externalMessageOrBuilderList == null) {
|
||||
externalMessageOrBuilderList =
|
||||
new MessageOrBuilderExternalList<MType, BType, IType>(this);
|
||||
}
|
||||
return externalMessageOrBuilderList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a the builder or one of its nested children has changed
|
||||
* and any parent should be notified of its invalidation.
|
||||
*/
|
||||
private void onChanged() {
|
||||
if (isClean && parent != null) {
|
||||
parent.markDirty();
|
||||
|
||||
// Don't keep dispatching invalidations until build is called again.
|
||||
isClean = false;
|
||||
}
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public void markDirty() {
|
||||
onChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the mod counts so that an ConcurrentModificationException can
|
||||
* be thrown if calling code tries to modify the builder while its iterating
|
||||
* the list.
|
||||
*/
|
||||
private void incrementModCounts() {
|
||||
if (externalMessageList != null) {
|
||||
externalMessageList.incrementModCount();
|
||||
}
|
||||
if (externalBuilderList != null) {
|
||||
externalBuilderList.incrementModCount();
|
||||
}
|
||||
if (externalMessageOrBuilderList != null) {
|
||||
externalMessageOrBuilderList.incrementModCount();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a live view of the builder as a list of messages.
|
||||
*
|
||||
* @param <MType> the type of message for the field
|
||||
* @param <BType> the type of builder for the field
|
||||
* @param <IType> the common interface for the message and the builder
|
||||
*/
|
||||
private static class MessageExternalList<
|
||||
MType extends GeneratedMessage,
|
||||
BType extends GeneratedMessage.Builder,
|
||||
IType extends MessageOrBuilder>
|
||||
extends AbstractList<MType> implements List<MType> {
|
||||
|
||||
RepeatedFieldBuilder<MType, BType, IType> builder;
|
||||
|
||||
MessageExternalList(
|
||||
RepeatedFieldBuilder<MType, BType, IType> builder) {
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return this.builder.getCount();
|
||||
}
|
||||
|
||||
public MType get(int index) {
|
||||
return builder.getMessage(index);
|
||||
}
|
||||
|
||||
void incrementModCount() {
|
||||
modCount++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a live view of the builder as a list of builders.
|
||||
*
|
||||
* @param <MType> the type of message for the field
|
||||
* @param <BType> the type of builder for the field
|
||||
* @param <IType> the common interface for the message and the builder
|
||||
*/
|
||||
private static class BuilderExternalList<
|
||||
MType extends GeneratedMessage,
|
||||
BType extends GeneratedMessage.Builder,
|
||||
IType extends MessageOrBuilder>
|
||||
extends AbstractList<BType> implements List<BType> {
|
||||
|
||||
RepeatedFieldBuilder<MType, BType, IType> builder;
|
||||
|
||||
BuilderExternalList(
|
||||
RepeatedFieldBuilder<MType, BType, IType> builder) {
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return this.builder.getCount();
|
||||
}
|
||||
|
||||
public BType get(int index) {
|
||||
return builder.getBuilder(index);
|
||||
}
|
||||
|
||||
void incrementModCount() {
|
||||
modCount++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a live view of the builder as a list of builders.
|
||||
*
|
||||
* @param <MType> the type of message for the field
|
||||
* @param <BType> the type of builder for the field
|
||||
* @param <IType> the common interface for the message and the builder
|
||||
*/
|
||||
private static class MessageOrBuilderExternalList<
|
||||
MType extends GeneratedMessage,
|
||||
BType extends GeneratedMessage.Builder,
|
||||
IType extends MessageOrBuilder>
|
||||
extends AbstractList<IType> implements List<IType> {
|
||||
|
||||
RepeatedFieldBuilder<MType, BType, IType> builder;
|
||||
|
||||
MessageOrBuilderExternalList(
|
||||
RepeatedFieldBuilder<MType, BType, IType> builder) {
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return this.builder.getCount();
|
||||
}
|
||||
|
||||
public IType get(int index) {
|
||||
return builder.getMessageOrBuilder(index);
|
||||
}
|
||||
|
||||
void incrementModCount() {
|
||||
modCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,956 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* Class to represent {@code ByteStrings} formed by concatenation of other
|
||||
* ByteStrings, without copying the data in the pieces. The concatenation is
|
||||
* represented as a tree whose leaf nodes are each a {@link LiteralByteString}.
|
||||
*
|
||||
* <p>Most of the operation here is inspired by the now-famous paper <a
|
||||
* href="https://www.cs.ubc.ca/local/reading/proceedings/spe91-95/spe/vol25/issue12/spe986.pdf">
|
||||
* BAP95 </a> Ropes: an Alternative to Strings hans-j. boehm, russ atkinson and
|
||||
* michael plass
|
||||
*
|
||||
* <p>The algorithms described in the paper have been implemented for character
|
||||
* strings in {@link com.google.common.string.Rope} and in the c++ class {@code
|
||||
* cord.cc}.
|
||||
*
|
||||
* <p>Fundamentally the Rope algorithm represents the collection of pieces as a
|
||||
* binary tree. BAP95 uses a Fibonacci bound relating depth to a minimum
|
||||
* sequence length, sequences that are too short relative to their depth cause a
|
||||
* tree rebalance. More precisely, a tree of depth d is "balanced" in the
|
||||
* terminology of BAP95 if its length is at least F(d+2), where F(n) is the
|
||||
* n-the Fibonacci number. Thus for depths 0, 1, 2, 3, 4, 5,... we have minimum
|
||||
* lengths 1, 2, 3, 5, 8, 13,...
|
||||
*
|
||||
* @author carlanton@google.com (Carl Haverl)
|
||||
*/
|
||||
class RopeByteString extends ByteString {
|
||||
|
||||
/**
|
||||
* BAP95. Let Fn be the nth Fibonacci number. A {@link RopeByteString} of
|
||||
* depth n is "balanced", i.e flat enough, if its length is at least Fn+2,
|
||||
* e.g. a "balanced" {@link RopeByteString} of depth 1 must have length at
|
||||
* least 2, of depth 4 must have length >= 8, etc.
|
||||
*
|
||||
* <p>There's nothing special about using the Fibonacci numbers for this, but
|
||||
* they are a reasonable sequence for encapsulating the idea that we are OK
|
||||
* with longer strings being encoded in deeper binary trees.
|
||||
*
|
||||
* <p>For 32-bit integers, this array has length 46.
|
||||
*/
|
||||
private static final int[] minLengthByDepth;
|
||||
|
||||
static {
|
||||
// Dynamically generate the list of Fibonacci numbers the first time this
|
||||
// class is accessed.
|
||||
List<Integer> numbers = new ArrayList<Integer>();
|
||||
|
||||
// we skip the first Fibonacci number (1). So instead of: 1 1 2 3 5 8 ...
|
||||
// we have: 1 2 3 5 8 ...
|
||||
int f1 = 1;
|
||||
int f2 = 1;
|
||||
|
||||
// get all the values until we roll over.
|
||||
while (f2 > 0) {
|
||||
numbers.add(f2);
|
||||
int temp = f1 + f2;
|
||||
f1 = f2;
|
||||
f2 = temp;
|
||||
}
|
||||
|
||||
// we include this here so that we can index this array to [x + 1] in the
|
||||
// loops below.
|
||||
numbers.add(Integer.MAX_VALUE);
|
||||
minLengthByDepth = new int[numbers.size()];
|
||||
for (int i = 0; i < minLengthByDepth.length; i++) {
|
||||
// unbox all the values
|
||||
minLengthByDepth[i] = numbers.get(i);
|
||||
}
|
||||
}
|
||||
|
||||
private final int totalLength;
|
||||
private final ByteString left;
|
||||
private final ByteString right;
|
||||
private final int leftLength;
|
||||
private final int treeDepth;
|
||||
|
||||
/**
|
||||
* Create a new RopeByteString, which can be thought of as a new tree node, by
|
||||
* recording references to the two given strings.
|
||||
*
|
||||
* @param left string on the left of this node, should have {@code size() >
|
||||
* 0}
|
||||
* @param right string on the right of this node, should have {@code size() >
|
||||
* 0}
|
||||
*/
|
||||
private RopeByteString(ByteString left, ByteString right) {
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
leftLength = left.size();
|
||||
totalLength = leftLength + right.size();
|
||||
treeDepth = Math.max(left.getTreeDepth(), right.getTreeDepth()) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenate the given strings while performing various optimizations to
|
||||
* slow the growth rate of tree depth and tree node count. The result is
|
||||
* either a {@link LiteralByteString} or a {@link RopeByteString}
|
||||
* depending on which optimizations, if any, were applied.
|
||||
*
|
||||
* <p>Small pieces of length less than {@link
|
||||
* ByteString#CONCATENATE_BY_COPY_SIZE} may be copied by value here, as in
|
||||
* BAP95. Large pieces are referenced without copy.
|
||||
*
|
||||
* @param left string on the left
|
||||
* @param right string on the right
|
||||
* @return concatenation representing the same sequence as the given strings
|
||||
*/
|
||||
static ByteString concatenate(ByteString left, ByteString right) {
|
||||
ByteString result;
|
||||
RopeByteString leftRope =
|
||||
(left instanceof RopeByteString) ? (RopeByteString) left : null;
|
||||
if (right.size() == 0) {
|
||||
result = left;
|
||||
} else if (left.size() == 0) {
|
||||
result = right;
|
||||
} else {
|
||||
int newLength = left.size() + right.size();
|
||||
if (newLength < ByteString.CONCATENATE_BY_COPY_SIZE) {
|
||||
// Optimization from BAP95: For short (leaves in paper, but just short
|
||||
// here) total length, do a copy of data to a new leaf.
|
||||
result = concatenateBytes(left, right);
|
||||
} else if (leftRope != null
|
||||
&& leftRope.right.size() + right.size() < CONCATENATE_BY_COPY_SIZE) {
|
||||
// Optimization from BAP95: As an optimization of the case where the
|
||||
// ByteString is constructed by repeated concatenate, recognize the case
|
||||
// where a short string is concatenated to a left-hand node whose
|
||||
// right-hand branch is short. In the paper this applies to leaves, but
|
||||
// we just look at the length here. This has the advantage of shedding
|
||||
// references to unneeded data when substrings have been taken.
|
||||
//
|
||||
// When we recognize this case, we do a copy of the data and create a
|
||||
// new parent node so that the depth of the result is the same as the
|
||||
// given left tree.
|
||||
ByteString newRight = concatenateBytes(leftRope.right, right);
|
||||
result = new RopeByteString(leftRope.left, newRight);
|
||||
} else if (leftRope != null
|
||||
&& leftRope.left.getTreeDepth() > leftRope.right.getTreeDepth()
|
||||
&& leftRope.getTreeDepth() > right.getTreeDepth()) {
|
||||
// Typically for concatenate-built strings the left-side is deeper than
|
||||
// the right. This is our final attempt to concatenate without
|
||||
// increasing the tree depth. We'll redo the node on the RHS. This
|
||||
// is yet another optimization for building the string by repeatedly
|
||||
// concatenating on the right.
|
||||
ByteString newRight = new RopeByteString(leftRope.right, right);
|
||||
result = new RopeByteString(leftRope.left, newRight);
|
||||
} else {
|
||||
// Fine, we'll add a node and increase the tree depth--unless we
|
||||
// rebalance ;^)
|
||||
int newDepth = Math.max(left.getTreeDepth(), right.getTreeDepth()) + 1;
|
||||
if (newLength >= minLengthByDepth[newDepth]) {
|
||||
// The tree is shallow enough, so don't rebalance
|
||||
result = new RopeByteString(left, right);
|
||||
} else {
|
||||
result = new Balancer().balance(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates two strings by copying data values. This is called in a few
|
||||
* cases in order to reduce the growth of the number of tree nodes.
|
||||
*
|
||||
* @param left string on the left
|
||||
* @param right string on the right
|
||||
* @return string formed by copying data bytes
|
||||
*/
|
||||
private static LiteralByteString concatenateBytes(ByteString left,
|
||||
ByteString right) {
|
||||
int leftSize = left.size();
|
||||
int rightSize = right.size();
|
||||
byte[] bytes = new byte[leftSize + rightSize];
|
||||
left.copyTo(bytes, 0, 0, leftSize);
|
||||
right.copyTo(bytes, 0, leftSize, rightSize);
|
||||
return new LiteralByteString(bytes); // Constructor wraps bytes
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new RopeByteString for testing only while bypassing all the
|
||||
* defenses of {@link #concatenate(ByteString, ByteString)}. This allows
|
||||
* testing trees of specific structure. We are also able to insert empty
|
||||
* leaves, though these are dis-allowed, so that we can make sure the
|
||||
* implementation can withstand their presence.
|
||||
*
|
||||
* @param left string on the left of this node
|
||||
* @param right string on the right of this node
|
||||
* @return an unsafe instance for testing only
|
||||
*/
|
||||
static RopeByteString newInstanceForTest(ByteString left, ByteString right) {
|
||||
return new RopeByteString(left, right);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the byte at the given index.
|
||||
* Throws {@link ArrayIndexOutOfBoundsException} for backwards-compatibility
|
||||
* reasons although it would more properly be {@link
|
||||
* IndexOutOfBoundsException}.
|
||||
*
|
||||
* @param index index of byte
|
||||
* @return the value
|
||||
* @throws ArrayIndexOutOfBoundsException {@code index} is < 0 or >= size
|
||||
*/
|
||||
@Override
|
||||
public byte byteAt(int index) {
|
||||
if (index < 0) {
|
||||
throw new ArrayIndexOutOfBoundsException("Index < 0: " + index);
|
||||
}
|
||||
if (index > totalLength) {
|
||||
throw new ArrayIndexOutOfBoundsException(
|
||||
"Index > length: " + index + ", " + totalLength);
|
||||
}
|
||||
|
||||
byte result;
|
||||
// Find the relevant piece by recursive descent
|
||||
if (index < leftLength) {
|
||||
result = left.byteAt(index);
|
||||
} else {
|
||||
result = right.byteAt(index - leftLength);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return totalLength;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Pieces
|
||||
|
||||
@Override
|
||||
protected int getTreeDepth() {
|
||||
return treeDepth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the tree is balanced according to BAP95, which means the tree
|
||||
* is flat-enough with respect to the bounds. Note that this definition of
|
||||
* balanced is one where sub-trees of balanced trees are not necessarily
|
||||
* balanced.
|
||||
*
|
||||
* @return true if the tree is balanced
|
||||
*/
|
||||
@Override
|
||||
protected boolean isBalanced() {
|
||||
return totalLength >= minLengthByDepth[treeDepth];
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a substring of this one. This involves recursive descent along the
|
||||
* left and right edges of the substring, and referencing any wholly contained
|
||||
* segments in between. Any leaf nodes entirely uninvolved in the substring
|
||||
* will not be referenced by the substring.
|
||||
*
|
||||
* <p>Substrings of {@code length < 2} should result in at most a single
|
||||
* recursive call chain, terminating at a leaf node. Thus the result will be a
|
||||
* {@link LiteralByteString}. {@link #RopeByteString(ByteString,
|
||||
* ByteString)}.
|
||||
*
|
||||
* @param beginIndex start at this index
|
||||
* @param endIndex the last character is the one before this index
|
||||
* @return substring leaf node or tree
|
||||
*/
|
||||
@Override
|
||||
public ByteString substring(int beginIndex, int endIndex) {
|
||||
if (beginIndex < 0) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"Beginning index: " + beginIndex + " < 0");
|
||||
}
|
||||
if (endIndex > totalLength) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"End index: " + endIndex + " > " + totalLength);
|
||||
}
|
||||
int substringLength = endIndex - beginIndex;
|
||||
if (substringLength < 0) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"Beginning index larger than ending index: " + beginIndex + ", "
|
||||
+ endIndex);
|
||||
}
|
||||
|
||||
ByteString result;
|
||||
if (substringLength == 0) {
|
||||
// Empty substring
|
||||
result = ByteString.EMPTY;
|
||||
} else if (substringLength == totalLength) {
|
||||
// The whole string
|
||||
result = this;
|
||||
} else {
|
||||
// Proper substring
|
||||
if (endIndex <= leftLength) {
|
||||
// Substring on the left
|
||||
result = left.substring(beginIndex, endIndex);
|
||||
} else if (beginIndex >= leftLength) {
|
||||
// Substring on the right
|
||||
result = right
|
||||
.substring(beginIndex - leftLength, endIndex - leftLength);
|
||||
} else {
|
||||
// Split substring
|
||||
ByteString leftSub = left.substring(beginIndex);
|
||||
ByteString rightSub = right.substring(0, endIndex - leftLength);
|
||||
// Intentionally not rebalancing, since in many cases these two
|
||||
// substrings will already be less deep than the top-level
|
||||
// RopeByteString we're taking a substring of.
|
||||
result = new RopeByteString(leftSub, rightSub);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ByteString -> byte[]
|
||||
|
||||
@Override
|
||||
protected void copyToInternal(byte[] target, int sourceOffset,
|
||||
int targetOffset, int numberToCopy) {
|
||||
if (sourceOffset + numberToCopy <= leftLength) {
|
||||
left.copyToInternal(target, sourceOffset, targetOffset, numberToCopy);
|
||||
} else if (sourceOffset >= leftLength) {
|
||||
right.copyToInternal(target, sourceOffset - leftLength, targetOffset,
|
||||
numberToCopy);
|
||||
} else {
|
||||
int leftLength = this.leftLength - sourceOffset;
|
||||
left.copyToInternal(target, sourceOffset, targetOffset, leftLength);
|
||||
right.copyToInternal(target, 0, targetOffset + leftLength,
|
||||
numberToCopy - leftLength);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyTo(ByteBuffer target) {
|
||||
left.copyTo(target);
|
||||
right.copyTo(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer asReadOnlyByteBuffer() {
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(toByteArray());
|
||||
return byteBuffer.asReadOnlyBuffer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ByteBuffer> asReadOnlyByteBufferList() {
|
||||
// Walk through the list of LiteralByteString's that make up this
|
||||
// rope, and add each one as a read-only ByteBuffer.
|
||||
List<ByteBuffer> result = new ArrayList<ByteBuffer>();
|
||||
PieceIterator pieces = new PieceIterator(this);
|
||||
while (pieces.hasNext()) {
|
||||
LiteralByteString byteString = pieces.next();
|
||||
result.add(byteString.asReadOnlyByteBuffer());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(OutputStream outputStream) throws IOException {
|
||||
left.writeTo(outputStream);
|
||||
right.writeTo(outputStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(String charsetName)
|
||||
throws UnsupportedEncodingException {
|
||||
return new String(toByteArray(), charsetName);
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// UTF-8 decoding
|
||||
|
||||
@Override
|
||||
public boolean isValidUtf8() {
|
||||
int leftPartial = left.partialIsValidUtf8(Utf8.COMPLETE, 0, leftLength);
|
||||
int state = right.partialIsValidUtf8(leftPartial, 0, right.size());
|
||||
return state == Utf8.COMPLETE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int partialIsValidUtf8(int state, int offset, int length) {
|
||||
int toIndex = offset + length;
|
||||
if (toIndex <= leftLength) {
|
||||
return left.partialIsValidUtf8(state, offset, length);
|
||||
} else if (offset >= leftLength) {
|
||||
return right.partialIsValidUtf8(state, offset - leftLength, length);
|
||||
} else {
|
||||
int leftLength = this.leftLength - offset;
|
||||
int leftPartial = left.partialIsValidUtf8(state, offset, leftLength);
|
||||
return right.partialIsValidUtf8(leftPartial, 0, length - leftLength);
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// equals() and hashCode()
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof ByteString)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ByteString otherByteString = (ByteString) other;
|
||||
if (totalLength != otherByteString.size()) {
|
||||
return false;
|
||||
}
|
||||
if (totalLength == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// You don't really want to be calling equals on long strings, but since
|
||||
// we cache the hashCode, we effectively cache inequality. We use the cached
|
||||
// hashCode if it's already computed. It's arguable we should compute the
|
||||
// hashCode here, and if we're going to be testing a bunch of byteStrings,
|
||||
// it might even make sense.
|
||||
if (hash != 0) {
|
||||
int cachedOtherHash = otherByteString.peekCachedHashCode();
|
||||
if (cachedOtherHash != 0 && hash != cachedOtherHash) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return equalsFragments(otherByteString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this string is equal to another of the same length by
|
||||
* iterating over the leaf nodes. On each step of the iteration, the
|
||||
* overlapping segments of the leaves are compared.
|
||||
*
|
||||
* @param other string of the same length as this one
|
||||
* @return true if the values of this string equals the value of the given
|
||||
* one
|
||||
*/
|
||||
private boolean equalsFragments(ByteString other) {
|
||||
int thisOffset = 0;
|
||||
Iterator<LiteralByteString> thisIter = new PieceIterator(this);
|
||||
LiteralByteString thisString = thisIter.next();
|
||||
|
||||
int thatOffset = 0;
|
||||
Iterator<LiteralByteString> thatIter = new PieceIterator(other);
|
||||
LiteralByteString thatString = thatIter.next();
|
||||
|
||||
int pos = 0;
|
||||
while (true) {
|
||||
int thisRemaining = thisString.size() - thisOffset;
|
||||
int thatRemaining = thatString.size() - thatOffset;
|
||||
int bytesToCompare = Math.min(thisRemaining, thatRemaining);
|
||||
|
||||
// At least one of the offsets will be zero
|
||||
boolean stillEqual = (thisOffset == 0)
|
||||
? thisString.equalsRange(thatString, thatOffset, bytesToCompare)
|
||||
: thatString.equalsRange(thisString, thisOffset, bytesToCompare);
|
||||
if (!stillEqual) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pos += bytesToCompare;
|
||||
if (pos >= totalLength) {
|
||||
if (pos == totalLength) {
|
||||
return true;
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
// We always get to the end of at least one of the pieces
|
||||
if (bytesToCompare == thisRemaining) { // If reached end of this
|
||||
thisOffset = 0;
|
||||
thisString = thisIter.next();
|
||||
} else {
|
||||
thisOffset += bytesToCompare;
|
||||
}
|
||||
if (bytesToCompare == thatRemaining) { // If reached end of that
|
||||
thatOffset = 0;
|
||||
thatString = thatIter.next();
|
||||
} else {
|
||||
thatOffset += bytesToCompare;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cached hash value. Intentionally accessed via a data race, which is safe
|
||||
* because of the Java Memory Model's "no out-of-thin-air values" guarantees
|
||||
* for ints.
|
||||
*/
|
||||
private int hash = 0;
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int h = hash;
|
||||
|
||||
if (h == 0) {
|
||||
h = totalLength;
|
||||
h = partialHash(h, 0, totalLength);
|
||||
if (h == 0) {
|
||||
h = 1;
|
||||
}
|
||||
hash = h;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int peekCachedHashCode() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int partialHash(int h, int offset, int length) {
|
||||
int toIndex = offset + length;
|
||||
if (toIndex <= leftLength) {
|
||||
return left.partialHash(h, offset, length);
|
||||
} else if (offset >= leftLength) {
|
||||
return right.partialHash(h, offset - leftLength, length);
|
||||
} else {
|
||||
int leftLength = this.leftLength - offset;
|
||||
int leftPartial = left.partialHash(h, offset, leftLength);
|
||||
return right.partialHash(leftPartial, 0, length - leftLength);
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Input stream
|
||||
|
||||
@Override
|
||||
public CodedInputStream newCodedInput() {
|
||||
return CodedInputStream.newInstance(new RopeInputStream());
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream newInput() {
|
||||
return new RopeInputStream();
|
||||
}
|
||||
|
||||
/**
|
||||
* This class implements the balancing algorithm of BAP95. In the paper the
|
||||
* authors use an array to keep track of pieces, while here we use a stack.
|
||||
* The tree is balanced by traversing subtrees in left to right order, and the
|
||||
* stack always contains the part of the string we've traversed so far.
|
||||
*
|
||||
* <p>One surprising aspect of the algorithm is the result of balancing is not
|
||||
* necessarily balanced, though it is nearly balanced. For details, see
|
||||
* BAP95.
|
||||
*/
|
||||
private static class Balancer {
|
||||
// Stack containing the part of the string, starting from the left, that
|
||||
// we've already traversed. The final string should be the equivalent of
|
||||
// concatenating the strings on the stack from bottom to top.
|
||||
private final Stack<ByteString> prefixesStack = new Stack<ByteString>();
|
||||
|
||||
private ByteString balance(ByteString left, ByteString right) {
|
||||
doBalance(left);
|
||||
doBalance(right);
|
||||
|
||||
// Sweep stack to gather the result
|
||||
ByteString partialString = prefixesStack.pop();
|
||||
while (!prefixesStack.isEmpty()) {
|
||||
ByteString newLeft = prefixesStack.pop();
|
||||
partialString = new RopeByteString(newLeft, partialString);
|
||||
}
|
||||
// We should end up with a RopeByteString since at a minimum we will
|
||||
// create one from concatenating left and right
|
||||
return partialString;
|
||||
}
|
||||
|
||||
private void doBalance(ByteString root) {
|
||||
// BAP95: Insert balanced subtrees whole. This means the result might not
|
||||
// be balanced, leading to repeated rebalancings on concatenate. However,
|
||||
// these rebalancings are shallow due to ignoring balanced subtrees, and
|
||||
// relatively few calls to insert() result.
|
||||
if (root.isBalanced()) {
|
||||
insert(root);
|
||||
} else if (root instanceof RopeByteString) {
|
||||
RopeByteString rbs = (RopeByteString) root;
|
||||
doBalance(rbs.left);
|
||||
doBalance(rbs.right);
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Has a new type of ByteString been created? Found " +
|
||||
root.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a string on the balance stack (BAP95). BAP95 uses an array and
|
||||
* calls the elements in the array 'bins'. We instead use a stack, so the
|
||||
* 'bins' of lengths are represented by differences between the elements of
|
||||
* minLengthByDepth.
|
||||
*
|
||||
* <p>If the length bin for our string, and all shorter length bins, are
|
||||
* empty, we just push it on the stack. Otherwise, we need to start
|
||||
* concatenating, putting the given string in the "middle" and continuing
|
||||
* until we land in an empty length bin that matches the length of our
|
||||
* concatenation.
|
||||
*
|
||||
* @param byteString string to place on the balance stack
|
||||
*/
|
||||
private void insert(ByteString byteString) {
|
||||
int depthBin = getDepthBinForLength(byteString.size());
|
||||
int binEnd = minLengthByDepth[depthBin + 1];
|
||||
|
||||
// BAP95: Concatenate all trees occupying bins representing the length of
|
||||
// our new piece or of shorter pieces, to the extent that is possible.
|
||||
// The goal is to clear the bin which our piece belongs in, but that may
|
||||
// not be entirely possible if there aren't enough longer bins occupied.
|
||||
if (prefixesStack.isEmpty() || prefixesStack.peek().size() >= binEnd) {
|
||||
prefixesStack.push(byteString);
|
||||
} else {
|
||||
int binStart = minLengthByDepth[depthBin];
|
||||
|
||||
// Concatenate the subtrees of shorter length
|
||||
ByteString newTree = prefixesStack.pop();
|
||||
while (!prefixesStack.isEmpty()
|
||||
&& prefixesStack.peek().size() < binStart) {
|
||||
ByteString left = prefixesStack.pop();
|
||||
newTree = new RopeByteString(left, newTree);
|
||||
}
|
||||
|
||||
// Concatenate the given string
|
||||
newTree = new RopeByteString(newTree, byteString);
|
||||
|
||||
// Continue concatenating until we land in an empty bin
|
||||
while (!prefixesStack.isEmpty()) {
|
||||
depthBin = getDepthBinForLength(newTree.size());
|
||||
binEnd = minLengthByDepth[depthBin + 1];
|
||||
if (prefixesStack.peek().size() < binEnd) {
|
||||
ByteString left = prefixesStack.pop();
|
||||
newTree = new RopeByteString(left, newTree);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
prefixesStack.push(newTree);
|
||||
}
|
||||
}
|
||||
|
||||
private int getDepthBinForLength(int length) {
|
||||
int depth = Arrays.binarySearch(minLengthByDepth, length);
|
||||
if (depth < 0) {
|
||||
// It wasn't an exact match, so convert to the index of the containing
|
||||
// fragment, which is one less even than the insertion point.
|
||||
int insertionPoint = -(depth + 1);
|
||||
depth = insertionPoint - 1;
|
||||
}
|
||||
|
||||
return depth;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is a continuable tree traversal, which keeps the state
|
||||
* information which would exist on the stack in a recursive traversal instead
|
||||
* on a stack of "Bread Crumbs". The maximum depth of the stack in this
|
||||
* iterator is the same as the depth of the tree being traversed.
|
||||
*
|
||||
* <p>This iterator is used to implement
|
||||
* {@link RopeByteString#equalsFragments(ByteString)}.
|
||||
*/
|
||||
private static class PieceIterator implements Iterator<LiteralByteString> {
|
||||
|
||||
private final Stack<RopeByteString> breadCrumbs =
|
||||
new Stack<RopeByteString>();
|
||||
private LiteralByteString next;
|
||||
|
||||
private PieceIterator(ByteString root) {
|
||||
next = getLeafByLeft(root);
|
||||
}
|
||||
|
||||
private LiteralByteString getLeafByLeft(ByteString root) {
|
||||
ByteString pos = root;
|
||||
while (pos instanceof RopeByteString) {
|
||||
RopeByteString rbs = (RopeByteString) pos;
|
||||
breadCrumbs.push(rbs);
|
||||
pos = rbs.left;
|
||||
}
|
||||
return (LiteralByteString) pos;
|
||||
}
|
||||
|
||||
private LiteralByteString getNextNonEmptyLeaf() {
|
||||
while (true) {
|
||||
// Almost always, we go through this loop exactly once. However, if
|
||||
// we discover an empty string in the rope, we toss it and try again.
|
||||
if (breadCrumbs.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
LiteralByteString result = getLeafByLeft(breadCrumbs.pop().right);
|
||||
if (!result.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return next != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next item and advances one {@code LiteralByteString}.
|
||||
*
|
||||
* @return next non-empty LiteralByteString or {@code null}
|
||||
*/
|
||||
public LiteralByteString next() {
|
||||
if (next == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
LiteralByteString result = next;
|
||||
next = getNextNonEmptyLeaf();
|
||||
return result;
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ByteIterator
|
||||
|
||||
@Override
|
||||
public ByteIterator iterator() {
|
||||
return new RopeByteIterator();
|
||||
}
|
||||
|
||||
private class RopeByteIterator implements ByteString.ByteIterator {
|
||||
|
||||
private final PieceIterator pieces;
|
||||
private ByteIterator bytes;
|
||||
int bytesRemaining;
|
||||
|
||||
private RopeByteIterator() {
|
||||
pieces = new PieceIterator(RopeByteString.this);
|
||||
bytes = pieces.next().iterator();
|
||||
bytesRemaining = size();
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return (bytesRemaining > 0);
|
||||
}
|
||||
|
||||
public Byte next() {
|
||||
return nextByte(); // Does not instantiate a Byte
|
||||
}
|
||||
|
||||
public byte nextByte() {
|
||||
if (!bytes.hasNext()) {
|
||||
bytes = pieces.next().iterator();
|
||||
}
|
||||
--bytesRemaining;
|
||||
return bytes.nextByte();
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is the {@link RopeByteString} equivalent for
|
||||
* {@link ByteArrayInputStream}.
|
||||
*/
|
||||
private class RopeInputStream extends InputStream {
|
||||
// Iterates through the pieces of the rope
|
||||
private PieceIterator pieceIterator;
|
||||
// The current piece
|
||||
private LiteralByteString currentPiece;
|
||||
// The size of the current piece
|
||||
private int currentPieceSize;
|
||||
// The index of the next byte to read in the current piece
|
||||
private int currentPieceIndex;
|
||||
// The offset of the start of the current piece in the rope byte string
|
||||
private int currentPieceOffsetInRope;
|
||||
// Offset in the buffer at which user called mark();
|
||||
private int mark;
|
||||
|
||||
public RopeInputStream() {
|
||||
initialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte b[], int offset, int length) {
|
||||
if (b == null) {
|
||||
throw new NullPointerException();
|
||||
} else if (offset < 0 || length < 0 || length > b.length - offset) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
return readSkipInternal(b, offset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long length) {
|
||||
if (length < 0) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
} else if (length > Integer.MAX_VALUE) {
|
||||
length = Integer.MAX_VALUE;
|
||||
}
|
||||
return readSkipInternal(null, 0, (int) length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal implementation of read and skip. If b != null, then read the
|
||||
* next {@code length} bytes into the buffer {@code b} at
|
||||
* offset {@code offset}. If b == null, then skip the next {@code length)
|
||||
* bytes.
|
||||
* <p>
|
||||
* This method assumes that all error checking has already happened.
|
||||
* <p>
|
||||
* Returns the actual number of bytes read or skipped.
|
||||
*/
|
||||
private int readSkipInternal(byte b[], int offset, int length) {
|
||||
int bytesRemaining = length;
|
||||
while (bytesRemaining > 0) {
|
||||
advanceIfCurrentPieceFullyRead();
|
||||
if (currentPiece == null) {
|
||||
if (bytesRemaining == length) {
|
||||
// We didn't manage to read anything
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
// Copy the bytes from this piece.
|
||||
int currentPieceRemaining = currentPieceSize - currentPieceIndex;
|
||||
int count = Math.min(currentPieceRemaining, bytesRemaining);
|
||||
if (b != null) {
|
||||
currentPiece.copyTo(b, currentPieceIndex, offset, count);
|
||||
offset += count;
|
||||
}
|
||||
currentPieceIndex += count;
|
||||
bytesRemaining -= count;
|
||||
}
|
||||
}
|
||||
// Return the number of bytes read.
|
||||
return length - bytesRemaining;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
advanceIfCurrentPieceFullyRead();
|
||||
if (currentPiece == null) {
|
||||
return -1;
|
||||
} else {
|
||||
return currentPiece.byteAt(currentPieceIndex++) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
int bytesRead = currentPieceOffsetInRope + currentPieceIndex;
|
||||
return RopeByteString.this.size() - bytesRead;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean markSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mark(int readAheadLimit) {
|
||||
// Set the mark to our position in the byte string
|
||||
mark = currentPieceOffsetInRope + currentPieceIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void reset() {
|
||||
// Just reinitialize and skip the specified number of bytes.
|
||||
initialize();
|
||||
readSkipInternal(null, 0, mark);
|
||||
}
|
||||
|
||||
/** Common initialization code used by both the constructor and reset() */
|
||||
private void initialize() {
|
||||
pieceIterator = new PieceIterator(RopeByteString.this);
|
||||
currentPiece = pieceIterator.next();
|
||||
currentPieceSize = currentPiece.size();
|
||||
currentPieceIndex = 0;
|
||||
currentPieceOffsetInRope = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips to the next piece if we have read all the data in the current
|
||||
* piece. Sets currentPiece to null if we have reached the end of the
|
||||
* input.
|
||||
*/
|
||||
private void advanceIfCurrentPieceFullyRead() {
|
||||
if (currentPiece != null && currentPieceIndex == currentPieceSize) {
|
||||
// Generally, we can only go through this loop at most once, since
|
||||
// empty strings can't end up in a rope. But better to test.
|
||||
currentPieceOffsetInRope += currentPieceSize;
|
||||
currentPieceIndex = 0;
|
||||
if (pieceIterator.hasNext()) {
|
||||
currentPiece = pieceIterator.next();
|
||||
currentPieceSize = currentPiece.size();
|
||||
} else {
|
||||
currentPiece = null;
|
||||
currentPieceSize = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
/**
|
||||
* Interface for an RPC callback, normally called when an RPC completes.
|
||||
* {@code ParameterType} is normally the method's response message type.
|
||||
*
|
||||
* <p>Starting with version 2.3.0, RPC implementations should not try to build
|
||||
* on this, but should instead provide code generator plugins which generate
|
||||
* code specific to the particular RPC implementation. This way the generated
|
||||
* code can be more appropriate for the implementation in use and can avoid
|
||||
* unnecessary layers of indirection.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public interface RpcCallback<ParameterType> {
|
||||
void run(ParameterType parameter);
|
||||
}
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
/**
|
||||
* <p>Abstract interface for an RPC channel. An {@code RpcChannel} represents a
|
||||
* communication line to a {@link Service} which can be used to call that
|
||||
* {@link Service}'s methods. The {@link Service} may be running on another
|
||||
* machine. Normally, you should not call an {@code RpcChannel} directly, but
|
||||
* instead construct a stub {@link Service} wrapping it. Example:
|
||||
*
|
||||
* <pre>
|
||||
* RpcChannel channel = rpcImpl.newChannel("remotehost.example.com:1234");
|
||||
* RpcController controller = rpcImpl.newController();
|
||||
* MyService service = MyService.newStub(channel);
|
||||
* service.myMethod(controller, request, callback);
|
||||
* </pre>
|
||||
*
|
||||
* <p>Starting with version 2.3.0, RPC implementations should not try to build
|
||||
* on this, but should instead provide code generator plugins which generate
|
||||
* code specific to the particular RPC implementation. This way the generated
|
||||
* code can be more appropriate for the implementation in use and can avoid
|
||||
* unnecessary layers of indirection.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public interface RpcChannel {
|
||||
/**
|
||||
* Call the given method of the remote service. This method is similar to
|
||||
* {@code Service.callMethod()} with one important difference: the caller
|
||||
* decides the types of the {@code Message} objects, not the callee. The
|
||||
* request may be of any type as long as
|
||||
* {@code request.getDescriptor() == method.getInputType()}.
|
||||
* The response passed to the callback will be of the same type as
|
||||
* {@code responsePrototype} (which must have
|
||||
* {@code getDescriptor() == method.getOutputType()}).
|
||||
*/
|
||||
void callMethod(Descriptors.MethodDescriptor method,
|
||||
RpcController controller,
|
||||
Message request,
|
||||
Message responsePrototype,
|
||||
RpcCallback<Message> done);
|
||||
}
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
/**
|
||||
* <p>An {@code RpcController} mediates a single method call. The primary
|
||||
* purpose of the controller is to provide a way to manipulate settings
|
||||
* specific to the RPC implementation and to find out about RPC-level errors.
|
||||
*
|
||||
* <p>Starting with version 2.3.0, RPC implementations should not try to build
|
||||
* on this, but should instead provide code generator plugins which generate
|
||||
* code specific to the particular RPC implementation. This way the generated
|
||||
* code can be more appropriate for the implementation in use and can avoid
|
||||
* unnecessary layers of indirection.
|
||||
*
|
||||
* <p>The methods provided by the {@code RpcController} interface are intended
|
||||
* to be a "least common denominator" set of features which we expect all
|
||||
* implementations to support. Specific implementations may provide more
|
||||
* advanced features (e.g. deadline propagation).
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public interface RpcController {
|
||||
// -----------------------------------------------------------------
|
||||
// These calls may be made from the client side only. Their results
|
||||
// are undefined on the server side (may throw RuntimeExceptions).
|
||||
|
||||
/**
|
||||
* Resets the RpcController to its initial state so that it may be reused in
|
||||
* a new call. This can be called from the client side only. It must not
|
||||
* be called while an RPC is in progress.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* After a call has finished, returns true if the call failed. The possible
|
||||
* reasons for failure depend on the RPC implementation. {@code failed()}
|
||||
* most only be called on the client side, and must not be called before a
|
||||
* call has finished.
|
||||
*/
|
||||
boolean failed();
|
||||
|
||||
/**
|
||||
* If {@code failed()} is {@code true}, returns a human-readable description
|
||||
* of the error.
|
||||
*/
|
||||
String errorText();
|
||||
|
||||
/**
|
||||
* Advises the RPC system that the caller desires that the RPC call be
|
||||
* canceled. The RPC system may cancel it immediately, may wait awhile and
|
||||
* then cancel it, or may not even cancel the call at all. If the call is
|
||||
* canceled, the "done" callback will still be called and the RpcController
|
||||
* will indicate that the call failed at that time.
|
||||
*/
|
||||
void startCancel();
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// These calls may be made from the server side only. Their results
|
||||
// are undefined on the client side (may throw RuntimeExceptions).
|
||||
|
||||
/**
|
||||
* Causes {@code failed()} to return true on the client side. {@code reason}
|
||||
* will be incorporated into the message returned by {@code errorText()}.
|
||||
* If you find you need to return machine-readable information about
|
||||
* failures, you should incorporate it into your response protocol buffer
|
||||
* and should NOT call {@code setFailed()}.
|
||||
*/
|
||||
void setFailed(String reason);
|
||||
|
||||
/**
|
||||
* If {@code true}, indicates that the client canceled the RPC, so the server
|
||||
* may as well give up on replying to it. This method must be called on the
|
||||
* server side only. The server should still call the final "done" callback.
|
||||
*/
|
||||
boolean isCanceled();
|
||||
|
||||
/**
|
||||
* Asks that the given callback be called when the RPC is canceled. The
|
||||
* parameter passed to the callback will always be {@code null}. The
|
||||
* callback will always be called exactly once. If the RPC completes without
|
||||
* being canceled, the callback will be called after completion. If the RPC
|
||||
* has already been canceled when NotifyOnCancel() is called, the callback
|
||||
* will be called immediately.
|
||||
*
|
||||
* <p>{@code notifyOnCancel()} must be called no more than once per request.
|
||||
* It must be called on the server side only.
|
||||
*/
|
||||
void notifyOnCancel(RpcCallback<Object> callback);
|
||||
}
|
||||
|
|
@ -1,148 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
/**
|
||||
* Grab-bag of utility functions useful when dealing with RPCs.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public final class RpcUtil {
|
||||
private RpcUtil() {}
|
||||
|
||||
/**
|
||||
* Take an {@code RpcCallback<Message>} and convert it to an
|
||||
* {@code RpcCallback} accepting a specific message type. This is always
|
||||
* type-safe (parameter type contravariance).
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <Type extends Message> RpcCallback<Type>
|
||||
specializeCallback(final RpcCallback<Message> originalCallback) {
|
||||
return (RpcCallback<Type>)originalCallback;
|
||||
// The above cast works, but only due to technical details of the Java
|
||||
// implementation. A more theoretically correct -- but less efficient --
|
||||
// implementation would be as follows:
|
||||
// return new RpcCallback<Type>() {
|
||||
// public void run(Type parameter) {
|
||||
// originalCallback.run(parameter);
|
||||
// }
|
||||
// };
|
||||
}
|
||||
|
||||
/**
|
||||
* Take an {@code RpcCallback} accepting a specific message type and convert
|
||||
* it to an {@code RpcCallback<Message>}. The generalized callback will
|
||||
* accept any message object which has the same descriptor, and will convert
|
||||
* it to the correct class before calling the original callback. However,
|
||||
* if the generalized callback is given a message with a different descriptor,
|
||||
* an exception will be thrown.
|
||||
*/
|
||||
public static <Type extends Message>
|
||||
RpcCallback<Message> generalizeCallback(
|
||||
final RpcCallback<Type> originalCallback,
|
||||
final Class<Type> originalClass,
|
||||
final Type defaultInstance) {
|
||||
return new RpcCallback<Message>() {
|
||||
public void run(final Message parameter) {
|
||||
Type typedParameter;
|
||||
try {
|
||||
typedParameter = originalClass.cast(parameter);
|
||||
} catch (ClassCastException ignored) {
|
||||
typedParameter = copyAsType(defaultInstance, parameter);
|
||||
}
|
||||
originalCallback.run(typedParameter);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new message of type "Type" which is a copy of "source". "source"
|
||||
* must have the same descriptor but may be a different class (e.g.
|
||||
* DynamicMessage).
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <Type extends Message> Type copyAsType(
|
||||
final Type typeDefaultInstance, final Message source) {
|
||||
return (Type)typeDefaultInstance.newBuilderForType()
|
||||
.mergeFrom(source)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a callback which can only be called once. This may be useful for
|
||||
* security, when passing a callback to untrusted code: most callbacks do
|
||||
* not expect to be called more than once, so doing so may expose bugs if it
|
||||
* is not prevented.
|
||||
*/
|
||||
public static <ParameterType>
|
||||
RpcCallback<ParameterType> newOneTimeCallback(
|
||||
final RpcCallback<ParameterType> originalCallback) {
|
||||
return new RpcCallback<ParameterType>() {
|
||||
private boolean alreadyCalled = false;
|
||||
|
||||
public void run(final ParameterType parameter) {
|
||||
synchronized(this) {
|
||||
if (alreadyCalled) {
|
||||
throw new AlreadyCalledException();
|
||||
}
|
||||
alreadyCalled = true;
|
||||
}
|
||||
|
||||
originalCallback.run(parameter);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception thrown when a one-time callback is called more than once.
|
||||
*/
|
||||
public static final class AlreadyCalledException extends RuntimeException {
|
||||
private static final long serialVersionUID = 5469741279507848266L;
|
||||
|
||||
public AlreadyCalledException() {
|
||||
super("This RpcCallback was already called and cannot be called " +
|
||||
"multiple times.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
/**
|
||||
* Abstract base interface for protocol-buffer-based RPC services. Services
|
||||
* themselves are abstract classes (implemented either by servers or as
|
||||
* stubs), but they subclass this base interface. The methods of this
|
||||
* interface can be used to call the methods of the service without knowing
|
||||
* its exact type at compile time (analogous to the Message interface).
|
||||
*
|
||||
* <p>Starting with version 2.3.0, RPC implementations should not try to build
|
||||
* on this, but should instead provide code generator plugins which generate
|
||||
* code specific to the particular RPC implementation. This way the generated
|
||||
* code can be more appropriate for the implementation in use and can avoid
|
||||
* unnecessary layers of indirection.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public interface Service {
|
||||
/**
|
||||
* Get the {@code ServiceDescriptor} describing this service and its methods.
|
||||
*/
|
||||
Descriptors.ServiceDescriptor getDescriptorForType();
|
||||
|
||||
/**
|
||||
* <p>Call a method of the service specified by MethodDescriptor. This is
|
||||
* normally implemented as a simple {@code switch()} that calls the standard
|
||||
* definitions of the service's methods.
|
||||
*
|
||||
* <p>Preconditions:
|
||||
* <ul>
|
||||
* <li>{@code method.getService() == getDescriptorForType()}
|
||||
* <li>{@code request} is of the exact same class as the object returned by
|
||||
* {@code getRequestPrototype(method)}.
|
||||
* <li>{@code controller} is of the correct type for the RPC implementation
|
||||
* being used by this Service. For stubs, the "correct type" depends
|
||||
* on the RpcChannel which the stub is using. Server-side Service
|
||||
* implementations are expected to accept whatever type of
|
||||
* {@code RpcController} the server-side RPC implementation uses.
|
||||
* </ul>
|
||||
*
|
||||
* <p>Postconditions:
|
||||
* <ul>
|
||||
* <li>{@code done} will be called when the method is complete. This may be
|
||||
* before {@code callMethod()} returns or it may be at some point in
|
||||
* the future.
|
||||
* <li>The parameter to {@code done} is the response. It must be of the
|
||||
* exact same type as would be returned by
|
||||
* {@code getResponsePrototype(method)}.
|
||||
* <li>If the RPC failed, the parameter to {@code done} will be
|
||||
* {@code null}. Further details about the failure can be found by
|
||||
* querying {@code controller}.
|
||||
* </ul>
|
||||
*/
|
||||
void callMethod(Descriptors.MethodDescriptor method,
|
||||
RpcController controller,
|
||||
Message request,
|
||||
RpcCallback<Message> done);
|
||||
|
||||
/**
|
||||
* <p>{@code callMethod()} requires that the request passed in is of a
|
||||
* particular subclass of {@code Message}. {@code getRequestPrototype()}
|
||||
* gets the default instances of this type for a given method. You can then
|
||||
* call {@code Message.newBuilderForType()} on this instance to
|
||||
* construct a builder to build an object which you can then pass to
|
||||
* {@code callMethod()}.
|
||||
*
|
||||
* <p>Example:
|
||||
* <pre>
|
||||
* MethodDescriptor method =
|
||||
* service.getDescriptorForType().findMethodByName("Foo");
|
||||
* Message request =
|
||||
* stub.getRequestPrototype(method).newBuilderForType()
|
||||
* .mergeFrom(input).build();
|
||||
* service.callMethod(method, request, callback);
|
||||
* </pre>
|
||||
*/
|
||||
Message getRequestPrototype(Descriptors.MethodDescriptor method);
|
||||
|
||||
/**
|
||||
* Like {@code getRequestPrototype()}, but gets a prototype of the response
|
||||
* message. {@code getResponsePrototype()} is generally not needed because
|
||||
* the {@code Service} implementation constructs the response message itself,
|
||||
* but it may be useful in some cases to know ahead of time what type of
|
||||
* object will be returned.
|
||||
*/
|
||||
Message getResponsePrototype(Descriptors.MethodDescriptor method);
|
||||
}
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
/**
|
||||
* Thrown by blocking RPC methods when a failure occurs.
|
||||
*
|
||||
* @author cpovirk@google.com (Chris Povirk)
|
||||
*/
|
||||
public class ServiceException extends Exception {
|
||||
private static final long serialVersionUID = -1219262335729891920L;
|
||||
|
||||
public ServiceException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ServiceException(final Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public ServiceException(final String message, final Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,254 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
/**
|
||||
* {@code SingleFieldBuilder} implements a structure that a protocol
|
||||
* message uses to hold a single field of another protocol message. It supports
|
||||
* the classical use case of setting an immutable {@link Message} as the value
|
||||
* of the field and is highly optimized around this.
|
||||
* <br>
|
||||
* It also supports the additional use case of setting a {@link Message.Builder}
|
||||
* as the field and deferring conversion of that {@code Builder}
|
||||
* to an immutable {@code Message}. In this way, it's possible to maintain
|
||||
* a tree of {@code Builder}'s that acts as a fully read/write data
|
||||
* structure.
|
||||
* <br>
|
||||
* Logically, one can think of a tree of builders as converting the entire tree
|
||||
* to messages when build is called on the root or when any method is called
|
||||
* that desires a Message instead of a Builder. In terms of the implementation,
|
||||
* the {@code SingleFieldBuilder} and {@code RepeatedFieldBuilder}
|
||||
* classes cache messages that were created so that messages only need to be
|
||||
* created when some change occurred in its builder or a builder for one of its
|
||||
* descendants.
|
||||
*
|
||||
* @param <MType> the type of message for the field
|
||||
* @param <BType> the type of builder for the field
|
||||
* @param <IType> the common interface for the message and the builder
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public class SingleFieldBuilder
|
||||
<MType extends GeneratedMessage,
|
||||
BType extends GeneratedMessage.Builder,
|
||||
IType extends MessageOrBuilder>
|
||||
implements GeneratedMessage.BuilderParent {
|
||||
|
||||
// Parent to send changes to.
|
||||
private GeneratedMessage.BuilderParent parent;
|
||||
|
||||
// Invariant: one of builder or message fields must be non-null.
|
||||
|
||||
// If set, this is the case where we are backed by a builder. In this case,
|
||||
// message field represents a cached message for the builder (or null if
|
||||
// there is no cached message).
|
||||
private BType builder;
|
||||
|
||||
// If builder is non-null, this represents a cached message from the builder.
|
||||
// If builder is null, this is the authoritative message for the field.
|
||||
private MType message;
|
||||
|
||||
// Indicates that we've built a message and so we are now obligated
|
||||
// to dispatch dirty invalidations. See GeneratedMessage.BuilderListener.
|
||||
private boolean isClean;
|
||||
|
||||
public SingleFieldBuilder(
|
||||
MType message,
|
||||
GeneratedMessage.BuilderParent parent,
|
||||
boolean isClean) {
|
||||
if (message == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
this.message = message;
|
||||
this.parent = parent;
|
||||
this.isClean = isClean;
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
// Null out parent so we stop sending it invalidations.
|
||||
parent = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message for the field. If the message is currently stored
|
||||
* as a {@code Builder}, it is converted to a {@code Message} by
|
||||
* calling {@link Message.Builder#buildPartial} on it. If no message has
|
||||
* been set, returns the default instance of the message.
|
||||
*
|
||||
* @return the message for the field
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public MType getMessage() {
|
||||
if (message == null) {
|
||||
// If message is null, the invariant is that we must be have a builder.
|
||||
message = (MType) builder.buildPartial();
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the message and returns it.
|
||||
*
|
||||
* @return the message
|
||||
*/
|
||||
public MType build() {
|
||||
// Now that build has been called, we are required to dispatch
|
||||
// invalidations.
|
||||
isClean = true;
|
||||
return getMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a builder for the field. If no builder has been created yet, a
|
||||
* builder is created on demand by calling {@link Message#toBuilder}.
|
||||
*
|
||||
* @return The builder for the field
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public BType getBuilder() {
|
||||
if (builder == null) {
|
||||
// builder.mergeFrom() on a fresh builder
|
||||
// does not create any sub-objects with independent clean/dirty states,
|
||||
// therefore setting the builder itself to clean without actually calling
|
||||
// build() cannot break any invariants.
|
||||
builder = (BType) message.newBuilderForType(this);
|
||||
builder.mergeFrom(message); // no-op if message is the default message
|
||||
builder.markClean();
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the base class interface for the field. This may either be a builder
|
||||
* or a message. It will return whatever is more efficient.
|
||||
*
|
||||
* @return the message or builder for the field as the base class interface
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public IType getMessageOrBuilder() {
|
||||
if (builder != null) {
|
||||
return (IType) builder;
|
||||
} else {
|
||||
return (IType) message;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a message for the field replacing any existing value.
|
||||
*
|
||||
* @param message the message to set
|
||||
* @return the builder
|
||||
*/
|
||||
public SingleFieldBuilder<MType, BType, IType> setMessage(
|
||||
MType message) {
|
||||
if (message == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
this.message = message;
|
||||
if (builder != null) {
|
||||
builder.dispose();
|
||||
builder = null;
|
||||
}
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the field from another field.
|
||||
*
|
||||
* @param value the value to merge from
|
||||
* @return the builder
|
||||
*/
|
||||
public SingleFieldBuilder<MType, BType, IType> mergeFrom(
|
||||
MType value) {
|
||||
if (builder == null && message == message.getDefaultInstanceForType()) {
|
||||
message = value;
|
||||
} else {
|
||||
getBuilder().mergeFrom(value);
|
||||
}
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the value of the field.
|
||||
*
|
||||
* @return the builder
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public SingleFieldBuilder<MType, BType, IType> clear() {
|
||||
message = (MType) (message != null ?
|
||||
message.getDefaultInstanceForType() :
|
||||
builder.getDefaultInstanceForType());
|
||||
if (builder != null) {
|
||||
builder.dispose();
|
||||
builder = null;
|
||||
}
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a the builder or one of its nested children has changed
|
||||
* and any parent should be notified of its invalidation.
|
||||
*/
|
||||
private void onChanged() {
|
||||
// If builder is null, this is the case where onChanged is being called
|
||||
// from setMessage or clear.
|
||||
if (builder != null) {
|
||||
message = null;
|
||||
}
|
||||
if (isClean && parent != null) {
|
||||
parent.markDirty();
|
||||
|
||||
// Don't keep dispatching invalidations until build is called again.
|
||||
isClean = false;
|
||||
}
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public void markDirty() {
|
||||
onChanged();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,631 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.TreeMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
|
||||
/**
|
||||
* A custom map implementation from FieldDescriptor to Object optimized to
|
||||
* minimize the number of memory allocations for instances with a small number
|
||||
* of mappings. The implementation stores the first {@code k} mappings in an
|
||||
* array for a configurable value of {@code k}, allowing direct access to the
|
||||
* corresponding {@code Entry}s without the need to create an Iterator. The
|
||||
* remaining entries are stored in an overflow map. Iteration over the entries
|
||||
* in the map should be done as follows:
|
||||
*
|
||||
* <pre> {@code
|
||||
* for (int i = 0; i < fieldMap.getNumArrayEntries(); i++) {
|
||||
* process(fieldMap.getArrayEntryAt(i));
|
||||
* }
|
||||
* for (Map.Entry<K, V> entry : fieldMap.getOverflowEntries()) {
|
||||
* process(entry);
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* The resulting iteration is in order of ascending field tag number. The
|
||||
* object returned by {@link #entrySet()} adheres to the same contract but is
|
||||
* less efficient as it necessarily involves creating an object for iteration.
|
||||
* <p>
|
||||
* The tradeoff for this memory efficiency is that the worst case running time
|
||||
* of the {@code put()} operation is {@code O(k + lg n)}, which happens when
|
||||
* entries are added in descending order. {@code k} should be chosen such that
|
||||
* it covers enough common cases without adversely affecting larger maps. In
|
||||
* practice, the worst case scenario does not happen for extensions because
|
||||
* extension fields are serialized and deserialized in order of ascending tag
|
||||
* number, but the worst case scenario can happen for DynamicMessages.
|
||||
* <p>
|
||||
* The running time for all other operations is similar to that of
|
||||
* {@code TreeMap}.
|
||||
* <p>
|
||||
* Instances are not thread-safe until {@link #makeImmutable()} is called,
|
||||
* after which any modifying operation will result in an
|
||||
* {@link UnsupportedOperationException}.
|
||||
*
|
||||
* @author darick@google.com Darick Tong
|
||||
*/
|
||||
// This class is final for all intents and purposes because the constructor is
|
||||
// private. However, the FieldDescriptor-specific logic is encapsulated in
|
||||
// a subclass to aid testability of the core logic.
|
||||
class SmallSortedMap<K extends Comparable<K>, V> extends AbstractMap<K, V> {
|
||||
|
||||
/**
|
||||
* Creates a new instance for mapping FieldDescriptors to their values.
|
||||
* The {@link #makeImmutable()} implementation will convert the List values
|
||||
* of any repeated fields to unmodifiable lists.
|
||||
*
|
||||
* @param arraySize The size of the entry array containing the
|
||||
* lexicographically smallest mappings.
|
||||
*/
|
||||
static <FieldDescriptorType extends
|
||||
FieldSet.FieldDescriptorLite<FieldDescriptorType>>
|
||||
SmallSortedMap<FieldDescriptorType, Object> newFieldMap(int arraySize) {
|
||||
return new SmallSortedMap<FieldDescriptorType, Object>(arraySize) {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void makeImmutable() {
|
||||
if (!isImmutable()) {
|
||||
for (int i = 0; i < getNumArrayEntries(); i++) {
|
||||
final Map.Entry<FieldDescriptorType, Object> entry =
|
||||
getArrayEntryAt(i);
|
||||
if (entry.getKey().isRepeated()) {
|
||||
final List value = (List) entry.getValue();
|
||||
entry.setValue(Collections.unmodifiableList(value));
|
||||
}
|
||||
}
|
||||
for (Map.Entry<FieldDescriptorType, Object> entry :
|
||||
getOverflowEntries()) {
|
||||
if (entry.getKey().isRepeated()) {
|
||||
final List value = (List) entry.getValue();
|
||||
entry.setValue(Collections.unmodifiableList(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
super.makeImmutable();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance for testing.
|
||||
*
|
||||
* @param arraySize The size of the entry array containing the
|
||||
* lexicographically smallest mappings.
|
||||
*/
|
||||
static <K extends Comparable<K>, V> SmallSortedMap<K, V> newInstanceForTest(
|
||||
int arraySize) {
|
||||
return new SmallSortedMap<K, V>(arraySize);
|
||||
}
|
||||
|
||||
private final int maxArraySize;
|
||||
// The "entry array" is actually a List because generic arrays are not
|
||||
// allowed. ArrayList also nicely handles the entry shifting on inserts and
|
||||
// removes.
|
||||
private List<Entry> entryList;
|
||||
private Map<K, V> overflowEntries;
|
||||
private boolean isImmutable;
|
||||
// The EntrySet is a stateless view of the Map. It's initialized the first
|
||||
// time it is requested and reused henceforth.
|
||||
private volatile EntrySet lazyEntrySet;
|
||||
|
||||
/**
|
||||
* @code arraySize Size of the array in which the lexicographically smallest
|
||||
* mappings are stored. (i.e. the {@code k} referred to in the class
|
||||
* documentation).
|
||||
*/
|
||||
private SmallSortedMap(int arraySize) {
|
||||
this.maxArraySize = arraySize;
|
||||
this.entryList = Collections.emptyList();
|
||||
this.overflowEntries = Collections.emptyMap();
|
||||
}
|
||||
|
||||
/** Make this map immutable from this point forward. */
|
||||
public void makeImmutable() {
|
||||
if (!isImmutable) {
|
||||
// Note: There's no need to wrap the entryList in an unmodifiableList
|
||||
// because none of the list's accessors are exposed. The iterator() of
|
||||
// overflowEntries, on the other hand, is exposed so it must be made
|
||||
// unmodifiable.
|
||||
overflowEntries = overflowEntries.isEmpty() ?
|
||||
Collections.<K, V>emptyMap() :
|
||||
Collections.unmodifiableMap(overflowEntries);
|
||||
isImmutable = true;
|
||||
}
|
||||
}
|
||||
|
||||
/** @return Whether {@link #makeImmutable()} has been called. */
|
||||
public boolean isImmutable() {
|
||||
return isImmutable;
|
||||
}
|
||||
|
||||
/** @return The number of entries in the entry array. */
|
||||
public int getNumArrayEntries() {
|
||||
return entryList.size();
|
||||
}
|
||||
|
||||
/** @return The array entry at the given {@code index}. */
|
||||
public Map.Entry<K, V> getArrayEntryAt(int index) {
|
||||
return entryList.get(index);
|
||||
}
|
||||
|
||||
/** @return There number of overflow entries. */
|
||||
public int getNumOverflowEntries() {
|
||||
return overflowEntries.size();
|
||||
}
|
||||
|
||||
/** @return An iterable over the overflow entries. */
|
||||
public Iterable<Map.Entry<K, V>> getOverflowEntries() {
|
||||
return overflowEntries.isEmpty() ?
|
||||
EmptySet.<Map.Entry<K, V>>iterable() :
|
||||
overflowEntries.entrySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return entryList.size() + overflowEntries.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation throws a {@code ClassCastException} if o is not an
|
||||
* object of type {@code K}.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean containsKey(Object o) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final K key = (K) o;
|
||||
return binarySearchInArray(key) >= 0 || overflowEntries.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation throws a {@code ClassCastException} if o is not an
|
||||
* object of type {@code K}.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public V get(Object o) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final K key = (K) o;
|
||||
final int index = binarySearchInArray(key);
|
||||
if (index >= 0) {
|
||||
return entryList.get(index).getValue();
|
||||
}
|
||||
return overflowEntries.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V put(K key, V value) {
|
||||
checkMutable();
|
||||
final int index = binarySearchInArray(key);
|
||||
if (index >= 0) {
|
||||
// Replace existing array entry.
|
||||
return entryList.get(index).setValue(value);
|
||||
}
|
||||
ensureEntryArrayMutable();
|
||||
final int insertionPoint = -(index + 1);
|
||||
if (insertionPoint >= maxArraySize) {
|
||||
// Put directly in overflow.
|
||||
return getOverflowEntriesMutable().put(key, value);
|
||||
}
|
||||
// Insert new Entry in array.
|
||||
if (entryList.size() == maxArraySize) {
|
||||
// Shift the last array entry into overflow.
|
||||
final Entry lastEntryInArray = entryList.remove(maxArraySize - 1);
|
||||
getOverflowEntriesMutable().put(lastEntryInArray.getKey(),
|
||||
lastEntryInArray.getValue());
|
||||
}
|
||||
entryList.add(insertionPoint, new Entry(key, value));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
checkMutable();
|
||||
if (!entryList.isEmpty()) {
|
||||
entryList.clear();
|
||||
}
|
||||
if (!overflowEntries.isEmpty()) {
|
||||
overflowEntries.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation throws a {@code ClassCastException} if o is not an
|
||||
* object of type {@code K}.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public V remove(Object o) {
|
||||
checkMutable();
|
||||
@SuppressWarnings("unchecked")
|
||||
final K key = (K) o;
|
||||
final int index = binarySearchInArray(key);
|
||||
if (index >= 0) {
|
||||
return removeArrayEntryAt(index);
|
||||
}
|
||||
// overflowEntries might be Collections.unmodifiableMap(), so only
|
||||
// call remove() if it is non-empty.
|
||||
if (overflowEntries.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
return overflowEntries.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
private V removeArrayEntryAt(int index) {
|
||||
checkMutable();
|
||||
final V removed = entryList.remove(index).getValue();
|
||||
if (!overflowEntries.isEmpty()) {
|
||||
// Shift the first entry in the overflow to be the last entry in the
|
||||
// array.
|
||||
final Iterator<Map.Entry<K, V>> iterator =
|
||||
getOverflowEntriesMutable().entrySet().iterator();
|
||||
entryList.add(new Entry(iterator.next()));
|
||||
iterator.remove();
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key The key to find in the entry array.
|
||||
* @return The returned integer position follows the same semantics as the
|
||||
* value returned by {@link java.util.Arrays#binarySearch()}.
|
||||
*/
|
||||
private int binarySearchInArray(K key) {
|
||||
int left = 0;
|
||||
int right = entryList.size() - 1;
|
||||
|
||||
// Optimization: For the common case in which entries are added in
|
||||
// ascending tag order, check the largest element in the array before
|
||||
// doing a full binary search.
|
||||
if (right >= 0) {
|
||||
int cmp = key.compareTo(entryList.get(right).getKey());
|
||||
if (cmp > 0) {
|
||||
return -(right + 2); // Insert point is after "right".
|
||||
} else if (cmp == 0) {
|
||||
return right;
|
||||
}
|
||||
}
|
||||
|
||||
while (left <= right) {
|
||||
int mid = (left + right) / 2;
|
||||
int cmp = key.compareTo(entryList.get(mid).getKey());
|
||||
if (cmp < 0) {
|
||||
right = mid - 1;
|
||||
} else if (cmp > 0) {
|
||||
left = mid + 1;
|
||||
} else {
|
||||
return mid;
|
||||
}
|
||||
}
|
||||
return -(left + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to the AbstractMap implementation of {@code keySet()} and
|
||||
* {@code values()}, the entry set is created the first time this method is
|
||||
* called, and returned in response to all subsequent calls.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Set<Map.Entry<K, V>> entrySet() {
|
||||
if (lazyEntrySet == null) {
|
||||
lazyEntrySet = new EntrySet();
|
||||
}
|
||||
return lazyEntrySet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws UnsupportedOperationException if {@link #makeImmutable()} has
|
||||
* has been called.
|
||||
*/
|
||||
private void checkMutable() {
|
||||
if (isImmutable) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a {@link SortedMap} to which overflow entries mappings can be
|
||||
* added or removed.
|
||||
* @throws UnsupportedOperationException if {@link #makeImmutable()} has been
|
||||
* called.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private SortedMap<K, V> getOverflowEntriesMutable() {
|
||||
checkMutable();
|
||||
if (overflowEntries.isEmpty() && !(overflowEntries instanceof TreeMap)) {
|
||||
overflowEntries = new TreeMap<K, V>();
|
||||
}
|
||||
return (SortedMap<K, V>) overflowEntries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazily creates the entry list. Any code that adds to the list must first
|
||||
* call this method.
|
||||
*/
|
||||
private void ensureEntryArrayMutable() {
|
||||
checkMutable();
|
||||
if (entryList.isEmpty() && !(entryList instanceof ArrayList)) {
|
||||
entryList = new ArrayList<Entry>(maxArraySize);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry implementation that implements Comparable in order to support
|
||||
* binary search within the entry array. Also checks mutability in
|
||||
* {@link #setValue()}.
|
||||
*/
|
||||
private class Entry implements Map.Entry<K, V>, Comparable<Entry> {
|
||||
|
||||
private final K key;
|
||||
private V value;
|
||||
|
||||
Entry(Map.Entry<K, V> copy) {
|
||||
this(copy.getKey(), copy.getValue());
|
||||
}
|
||||
|
||||
Entry(K key, V value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public K getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public V getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public int compareTo(Entry other) {
|
||||
return getKey().compareTo(other.getKey());
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public V setValue(V newValue) {
|
||||
checkMutable();
|
||||
final V oldValue = this.value;
|
||||
this.value = newValue;
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof Map.Entry)) {
|
||||
return false;
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
Map.Entry<?, ?> other = (Map.Entry<?, ?>) o;
|
||||
return equals(key, other.getKey()) && equals(value, other.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (key == null ? 0 : key.hashCode()) ^
|
||||
(value == null ? 0 : value.hashCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return key + "=" + value;
|
||||
}
|
||||
|
||||
/** equals() that handles null values. */
|
||||
private boolean equals(Object o1, Object o2) {
|
||||
return o1 == null ? o2 == null : o1.equals(o2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stateless view of the entries in the field map.
|
||||
*/
|
||||
private class EntrySet extends AbstractSet<Map.Entry<K, V>> {
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<K, V>> iterator() {
|
||||
return new EntryIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return SmallSortedMap.this.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws a {@link ClassCastException} if o is not of the expected type.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Map.Entry<K, V> entry = (Map.Entry<K, V>) o;
|
||||
final V existing = get(entry.getKey());
|
||||
final V value = entry.getValue();
|
||||
return existing == value ||
|
||||
(existing != null && existing.equals(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(Map.Entry<K, V> entry) {
|
||||
if (!contains(entry)) {
|
||||
put(entry.getKey(), entry.getValue());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws a {@link ClassCastException} if o is not of the expected type.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Map.Entry<K, V> entry = (Map.Entry<K, V>) o;
|
||||
if (contains(entry)) {
|
||||
SmallSortedMap.this.remove(entry.getKey());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
SmallSortedMap.this.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator implementation that switches from the entry array to the overflow
|
||||
* entries appropriately.
|
||||
*/
|
||||
private class EntryIterator implements Iterator<Map.Entry<K, V>> {
|
||||
|
||||
private int pos = -1;
|
||||
private boolean nextCalledBeforeRemove;
|
||||
private Iterator<Map.Entry<K, V>> lazyOverflowIterator;
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public boolean hasNext() {
|
||||
return (pos + 1) < entryList.size() ||
|
||||
getOverflowIterator().hasNext();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public Map.Entry<K, V> next() {
|
||||
nextCalledBeforeRemove = true;
|
||||
// Always increment pos so that we know whether the last returned value
|
||||
// was from the array or from overflow.
|
||||
if (++pos < entryList.size()) {
|
||||
return entryList.get(pos);
|
||||
}
|
||||
return getOverflowIterator().next();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public void remove() {
|
||||
if (!nextCalledBeforeRemove) {
|
||||
throw new IllegalStateException("remove() was called before next()");
|
||||
}
|
||||
nextCalledBeforeRemove = false;
|
||||
checkMutable();
|
||||
|
||||
if (pos < entryList.size()) {
|
||||
removeArrayEntryAt(pos--);
|
||||
} else {
|
||||
getOverflowIterator().remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* It is important to create the overflow iterator only after the array
|
||||
* entries have been iterated over because the overflow entry set changes
|
||||
* when the client calls remove() on the array entries, which invalidates
|
||||
* any existing iterators.
|
||||
*/
|
||||
private Iterator<Map.Entry<K, V>> getOverflowIterator() {
|
||||
if (lazyOverflowIterator == null) {
|
||||
lazyOverflowIterator = overflowEntries.entrySet().iterator();
|
||||
}
|
||||
return lazyOverflowIterator;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class that holds immutable instances of an Iterable/Iterator that
|
||||
* we return when the overflow entries is empty. This eliminates the creation
|
||||
* of an Iterator object when there is nothing to iterate over.
|
||||
*/
|
||||
private static class EmptySet {
|
||||
|
||||
private static final Iterator<Object> ITERATOR = new Iterator<Object>() {
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public boolean hasNext() {
|
||||
return false;
|
||||
}
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public Object next() {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
|
||||
private static final Iterable<Object> ITERABLE = new Iterable<Object>() {
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public Iterator<Object> iterator() {
|
||||
return ITERATOR;
|
||||
}
|
||||
};
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T> Iterable<T> iterable() {
|
||||
return (Iterable<T>) ITERABLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,112 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Thrown when attempting to build a protocol message that is missing required
|
||||
* fields. This is a {@code RuntimeException} because it normally represents
|
||||
* a programming error: it happens when some code which constructs a message
|
||||
* fails to set all the fields. {@code parseFrom()} methods <b>do not</b>
|
||||
* throw this; they throw an {@link InvalidProtocolBufferException} if
|
||||
* required fields are missing, because it is not a programming error to
|
||||
* receive an incomplete message. In other words,
|
||||
* {@code UninitializedMessageException} should never be thrown by correct
|
||||
* code, but {@code InvalidProtocolBufferException} might be.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public class UninitializedMessageException extends RuntimeException {
|
||||
private static final long serialVersionUID = -7466929953374883507L;
|
||||
|
||||
public UninitializedMessageException(final MessageLite message) {
|
||||
super("Message was missing required fields. (Lite runtime could not " +
|
||||
"determine which fields were missing).");
|
||||
missingFields = null;
|
||||
}
|
||||
|
||||
public UninitializedMessageException(final List<String> missingFields) {
|
||||
super(buildDescription(missingFields));
|
||||
this.missingFields = missingFields;
|
||||
}
|
||||
|
||||
private final List<String> missingFields;
|
||||
|
||||
/**
|
||||
* Get a list of human-readable names of required fields missing from this
|
||||
* message. Each name is a full path to a field, e.g. "foo.bar[5].baz".
|
||||
* Returns null if the lite runtime was used, since it lacks the ability to
|
||||
* find missing fields.
|
||||
*/
|
||||
public List<String> getMissingFields() {
|
||||
return Collections.unmodifiableList(missingFields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this exception to an {@link InvalidProtocolBufferException}.
|
||||
* When a parsed message is missing required fields, this should be thrown
|
||||
* instead of {@code UninitializedMessageException}.
|
||||
*/
|
||||
public InvalidProtocolBufferException asInvalidProtocolBufferException() {
|
||||
return new InvalidProtocolBufferException(getMessage());
|
||||
}
|
||||
|
||||
/** Construct the description string for this exception. */
|
||||
private static String buildDescription(final List<String> missingFields) {
|
||||
final StringBuilder description =
|
||||
new StringBuilder("Message missing required fields: ");
|
||||
boolean first = true;
|
||||
for (final String field : missingFields) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
description.append(", ");
|
||||
}
|
||||
description.append(field);
|
||||
}
|
||||
return description.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,991 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
import org.apache.pekko.protobuf.AbstractMessageLite.Builder.LimitedInputStream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* {@code UnknownFieldSet} is used to keep track of fields which were seen when
|
||||
* parsing a protocol message but whose field numbers or types are unrecognized.
|
||||
* This most frequently occurs when new fields are added to a message type
|
||||
* and then messages containing those fields are read by old software that was
|
||||
* compiled before the new types were added.
|
||||
*
|
||||
* <p>Every {@link Message} contains an {@code UnknownFieldSet} (and every
|
||||
* {@link Message.Builder} contains an {@link Builder}).
|
||||
*
|
||||
* <p>Most users will never need to use this class.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public final class UnknownFieldSet implements MessageLite {
|
||||
private UnknownFieldSet() {}
|
||||
|
||||
/** Create a new {@link Builder}. */
|
||||
public static Builder newBuilder() {
|
||||
return Builder.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Builder} and initialize it to be a copy
|
||||
* of {@code copyFrom}.
|
||||
*/
|
||||
public static Builder newBuilder(final UnknownFieldSet copyFrom) {
|
||||
return newBuilder().mergeFrom(copyFrom);
|
||||
}
|
||||
|
||||
/** Get an empty {@code UnknownFieldSet}. */
|
||||
public static UnknownFieldSet getDefaultInstance() {
|
||||
return defaultInstance;
|
||||
}
|
||||
public UnknownFieldSet getDefaultInstanceForType() {
|
||||
return defaultInstance;
|
||||
}
|
||||
private static final UnknownFieldSet defaultInstance =
|
||||
new UnknownFieldSet(Collections.<Integer, Field>emptyMap());
|
||||
|
||||
/**
|
||||
* Construct an {@code UnknownFieldSet} around the given map. The map is
|
||||
* expected to be immutable.
|
||||
*/
|
||||
private UnknownFieldSet(final Map<Integer, Field> fields) {
|
||||
this.fields = fields;
|
||||
}
|
||||
private Map<Integer, Field> fields;
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
return (other instanceof UnknownFieldSet) &&
|
||||
fields.equals(((UnknownFieldSet) other).fields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return fields.hashCode();
|
||||
}
|
||||
|
||||
/** Get a map of fields in the set by number. */
|
||||
public Map<Integer, Field> asMap() {
|
||||
return fields;
|
||||
}
|
||||
|
||||
/** Check if the given field number is present in the set. */
|
||||
public boolean hasField(final int number) {
|
||||
return fields.containsKey(number);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a field by number. Returns an empty field if not present. Never
|
||||
* returns {@code null}.
|
||||
*/
|
||||
public Field getField(final int number) {
|
||||
final Field result = fields.get(number);
|
||||
return (result == null) ? Field.getDefaultInstance() : result;
|
||||
}
|
||||
|
||||
/** Serializes the set and writes it to {@code output}. */
|
||||
public void writeTo(final CodedOutputStream output) throws IOException {
|
||||
for (final Map.Entry<Integer, Field> entry : fields.entrySet()) {
|
||||
entry.getValue().writeTo(entry.getKey(), output);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the set to a string in protocol buffer text format. This is
|
||||
* just a trivial wrapper around
|
||||
* {@link TextFormat#printToString(UnknownFieldSet)}.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return TextFormat.printToString(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the message to a {@code ByteString} and returns it. This is
|
||||
* just a trivial wrapper around {@link #writeTo(CodedOutputStream)}.
|
||||
*/
|
||||
public ByteString toByteString() {
|
||||
try {
|
||||
final ByteString.CodedBuilder out =
|
||||
ByteString.newCodedBuilder(getSerializedSize());
|
||||
writeTo(out.getCodedOutput());
|
||||
return out.build();
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Serializing to a ByteString threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the message to a {@code byte} array and returns it. This is
|
||||
* just a trivial wrapper around {@link #writeTo(CodedOutputStream)}.
|
||||
*/
|
||||
public byte[] toByteArray() {
|
||||
try {
|
||||
final byte[] result = new byte[getSerializedSize()];
|
||||
final CodedOutputStream output = CodedOutputStream.newInstance(result);
|
||||
writeTo(output);
|
||||
output.checkNoSpaceLeft();
|
||||
return result;
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Serializing to a byte array threw an IOException " +
|
||||
"(should never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the message and writes it to {@code output}. This is just a
|
||||
* trivial wrapper around {@link #writeTo(CodedOutputStream)}.
|
||||
*/
|
||||
public void writeTo(final OutputStream output) throws IOException {
|
||||
final CodedOutputStream codedOutput = CodedOutputStream.newInstance(output);
|
||||
writeTo(codedOutput);
|
||||
codedOutput.flush();
|
||||
}
|
||||
|
||||
public void writeDelimitedTo(OutputStream output) throws IOException {
|
||||
final CodedOutputStream codedOutput = CodedOutputStream.newInstance(output);
|
||||
codedOutput.writeRawVarint32(getSerializedSize());
|
||||
writeTo(codedOutput);
|
||||
codedOutput.flush();
|
||||
}
|
||||
|
||||
/** Get the number of bytes required to encode this set. */
|
||||
public int getSerializedSize() {
|
||||
int result = 0;
|
||||
for (final Map.Entry<Integer, Field> entry : fields.entrySet()) {
|
||||
result += entry.getValue().getSerializedSize(entry.getKey());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the set and writes it to {@code output} using
|
||||
* {@code MessageSet} wire format.
|
||||
*/
|
||||
public void writeAsMessageSetTo(final CodedOutputStream output)
|
||||
throws IOException {
|
||||
for (final Map.Entry<Integer, Field> entry : fields.entrySet()) {
|
||||
entry.getValue().writeAsMessageSetExtensionTo(
|
||||
entry.getKey(), output);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of bytes required to encode this set using
|
||||
* {@code MessageSet} wire format.
|
||||
*/
|
||||
public int getSerializedSizeAsMessageSet() {
|
||||
int result = 0;
|
||||
for (final Map.Entry<Integer, Field> entry : fields.entrySet()) {
|
||||
result += entry.getValue().getSerializedSizeAsMessageSetExtension(
|
||||
entry.getKey());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isInitialized() {
|
||||
// UnknownFieldSets do not have required fields, so they are always
|
||||
// initialized.
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Parse an {@code UnknownFieldSet} from the given input stream. */
|
||||
public static UnknownFieldSet parseFrom(final CodedInputStream input)
|
||||
throws IOException {
|
||||
return newBuilder().mergeFrom(input).build();
|
||||
}
|
||||
|
||||
/** Parse {@code data} as an {@code UnknownFieldSet} and return it. */
|
||||
public static UnknownFieldSet parseFrom(final ByteString data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return newBuilder().mergeFrom(data).build();
|
||||
}
|
||||
|
||||
/** Parse {@code data} as an {@code UnknownFieldSet} and return it. */
|
||||
public static UnknownFieldSet parseFrom(final byte[] data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return newBuilder().mergeFrom(data).build();
|
||||
}
|
||||
|
||||
/** Parse an {@code UnknownFieldSet} from {@code input} and return it. */
|
||||
public static UnknownFieldSet parseFrom(final InputStream input)
|
||||
throws IOException {
|
||||
return newBuilder().mergeFrom(input).build();
|
||||
}
|
||||
|
||||
public Builder newBuilderForType() {
|
||||
return newBuilder();
|
||||
}
|
||||
|
||||
public Builder toBuilder() {
|
||||
return newBuilder().mergeFrom(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link UnknownFieldSet}s.
|
||||
*
|
||||
* <p>Note that this class maintains {@link Field.Builder}s for all fields
|
||||
* in the set. Thus, adding one element to an existing {@link Field} does not
|
||||
* require making a copy. This is important for efficient parsing of
|
||||
* unknown repeated fields. However, it implies that {@link Field}s cannot
|
||||
* be constructed independently, nor can two {@link UnknownFieldSet}s share
|
||||
* the same {@code Field} object.
|
||||
*
|
||||
* <p>Use {@link UnknownFieldSet#newBuilder()} to construct a {@code Builder}.
|
||||
*/
|
||||
public static final class Builder implements MessageLite.Builder {
|
||||
// This constructor should never be called directly (except from 'create').
|
||||
private Builder() {}
|
||||
|
||||
private Map<Integer, Field> fields;
|
||||
|
||||
// Optimization: We keep around a builder for the last field that was
|
||||
// modified so that we can efficiently add to it multiple times in a
|
||||
// row (important when parsing an unknown repeated field).
|
||||
private int lastFieldNumber;
|
||||
private Field.Builder lastField;
|
||||
|
||||
private static Builder create() {
|
||||
Builder builder = new Builder();
|
||||
builder.reinitialize();
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a field builder for the given field number which includes any
|
||||
* values that already exist.
|
||||
*/
|
||||
private Field.Builder getFieldBuilder(final int number) {
|
||||
if (lastField != null) {
|
||||
if (number == lastFieldNumber) {
|
||||
return lastField;
|
||||
}
|
||||
// Note: addField() will reset lastField and lastFieldNumber.
|
||||
addField(lastFieldNumber, lastField.build());
|
||||
}
|
||||
if (number == 0) {
|
||||
return null;
|
||||
} else {
|
||||
final Field existing = fields.get(number);
|
||||
lastFieldNumber = number;
|
||||
lastField = Field.newBuilder();
|
||||
if (existing != null) {
|
||||
lastField.mergeFrom(existing);
|
||||
}
|
||||
return lastField;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the {@link UnknownFieldSet} and return it.
|
||||
*
|
||||
* <p>Once {@code build()} has been called, the {@code Builder} will no
|
||||
* longer be usable. Calling any method after {@code build()} will result
|
||||
* in undefined behavior and can cause a {@code NullPointerException} to be
|
||||
* thrown.
|
||||
*/
|
||||
public UnknownFieldSet build() {
|
||||
getFieldBuilder(0); // Force lastField to be built.
|
||||
final UnknownFieldSet result;
|
||||
if (fields.isEmpty()) {
|
||||
result = getDefaultInstance();
|
||||
} else {
|
||||
result = new UnknownFieldSet(Collections.unmodifiableMap(fields));
|
||||
}
|
||||
fields = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
public UnknownFieldSet buildPartial() {
|
||||
// No required fields, so this is the same as build().
|
||||
return build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder clone() {
|
||||
getFieldBuilder(0); // Force lastField to be built.
|
||||
return UnknownFieldSet.newBuilder().mergeFrom(
|
||||
new UnknownFieldSet(fields));
|
||||
}
|
||||
|
||||
public UnknownFieldSet getDefaultInstanceForType() {
|
||||
return UnknownFieldSet.getDefaultInstance();
|
||||
}
|
||||
|
||||
private void reinitialize() {
|
||||
fields = Collections.emptyMap();
|
||||
lastFieldNumber = 0;
|
||||
lastField = null;
|
||||
}
|
||||
|
||||
/** Reset the builder to an empty set. */
|
||||
public Builder clear() {
|
||||
reinitialize();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the fields from {@code other} into this set. If a field number
|
||||
* exists in both sets, {@code other}'s values for that field will be
|
||||
* appended to the values in this set.
|
||||
*/
|
||||
public Builder mergeFrom(final UnknownFieldSet other) {
|
||||
if (other != getDefaultInstance()) {
|
||||
for (final Map.Entry<Integer, Field> entry : other.fields.entrySet()) {
|
||||
mergeField(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a field to the {@code UnknownFieldSet}. If a field with the same
|
||||
* number already exists, the two are merged.
|
||||
*/
|
||||
public Builder mergeField(final int number, final Field field) {
|
||||
if (number == 0) {
|
||||
throw new IllegalArgumentException("Zero is not a valid field number.");
|
||||
}
|
||||
if (hasField(number)) {
|
||||
getFieldBuilder(number).mergeFrom(field);
|
||||
} else {
|
||||
// Optimization: We could call getFieldBuilder(number).mergeFrom(field)
|
||||
// in this case, but that would create a copy of the Field object.
|
||||
// We'd rather reuse the one passed to us, so call addField() instead.
|
||||
addField(number, field);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for merging a new field containing a single varint
|
||||
* value. This is used in particular when an unknown enum value is
|
||||
* encountered.
|
||||
*/
|
||||
public Builder mergeVarintField(final int number, final int value) {
|
||||
if (number == 0) {
|
||||
throw new IllegalArgumentException("Zero is not a valid field number.");
|
||||
}
|
||||
getFieldBuilder(number).addVarint(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Check if the given field number is present in the set. */
|
||||
public boolean hasField(final int number) {
|
||||
if (number == 0) {
|
||||
throw new IllegalArgumentException("Zero is not a valid field number.");
|
||||
}
|
||||
return number == lastFieldNumber || fields.containsKey(number);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a field to the {@code UnknownFieldSet}. If a field with the same
|
||||
* number already exists, it is removed.
|
||||
*/
|
||||
public Builder addField(final int number, final Field field) {
|
||||
if (number == 0) {
|
||||
throw new IllegalArgumentException("Zero is not a valid field number.");
|
||||
}
|
||||
if (lastField != null && lastFieldNumber == number) {
|
||||
// Discard this.
|
||||
lastField = null;
|
||||
lastFieldNumber = 0;
|
||||
}
|
||||
if (fields.isEmpty()) {
|
||||
fields = new TreeMap<Integer,Field>();
|
||||
}
|
||||
fields.put(number, field);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all present {@code Field}s as an immutable {@code Map}. If more
|
||||
* fields are added, the changes may or may not be reflected in this map.
|
||||
*/
|
||||
public Map<Integer, Field> asMap() {
|
||||
getFieldBuilder(0); // Force lastField to be built.
|
||||
return Collections.unmodifiableMap(fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an entire message from {@code input} and merge its fields into
|
||||
* this set.
|
||||
*/
|
||||
public Builder mergeFrom(final CodedInputStream input) throws IOException {
|
||||
while (true) {
|
||||
final int tag = input.readTag();
|
||||
if (tag == 0 || !mergeFieldFrom(tag, input)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a single field from {@code input} and merge it into this set.
|
||||
* @param tag The field's tag number, which was already parsed.
|
||||
* @return {@code false} if the tag is an end group tag.
|
||||
*/
|
||||
public boolean mergeFieldFrom(final int tag, final CodedInputStream input)
|
||||
throws IOException {
|
||||
final int number = WireFormat.getTagFieldNumber(tag);
|
||||
switch (WireFormat.getTagWireType(tag)) {
|
||||
case WireFormat.WIRETYPE_VARINT:
|
||||
getFieldBuilder(number).addVarint(input.readInt64());
|
||||
return true;
|
||||
case WireFormat.WIRETYPE_FIXED64:
|
||||
getFieldBuilder(number).addFixed64(input.readFixed64());
|
||||
return true;
|
||||
case WireFormat.WIRETYPE_LENGTH_DELIMITED:
|
||||
getFieldBuilder(number).addLengthDelimited(input.readBytes());
|
||||
return true;
|
||||
case WireFormat.WIRETYPE_START_GROUP:
|
||||
final Builder subBuilder = newBuilder();
|
||||
input.readGroup(number, subBuilder,
|
||||
ExtensionRegistry.getEmptyRegistry());
|
||||
getFieldBuilder(number).addGroup(subBuilder.build());
|
||||
return true;
|
||||
case WireFormat.WIRETYPE_END_GROUP:
|
||||
return false;
|
||||
case WireFormat.WIRETYPE_FIXED32:
|
||||
getFieldBuilder(number).addFixed32(input.readFixed32());
|
||||
return true;
|
||||
default:
|
||||
throw InvalidProtocolBufferException.invalidWireType();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse {@code data} as an {@code UnknownFieldSet} and merge it with the
|
||||
* set being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream)}.
|
||||
*/
|
||||
public Builder mergeFrom(final ByteString data)
|
||||
throws InvalidProtocolBufferException {
|
||||
try {
|
||||
final CodedInputStream input = data.newCodedInput();
|
||||
mergeFrom(input);
|
||||
input.checkLastTagWas(0);
|
||||
return this;
|
||||
} catch (final InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Reading from a ByteString threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse {@code data} as an {@code UnknownFieldSet} and merge it with the
|
||||
* set being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream)}.
|
||||
*/
|
||||
public Builder mergeFrom(final byte[] data)
|
||||
throws InvalidProtocolBufferException {
|
||||
try {
|
||||
final CodedInputStream input = CodedInputStream.newInstance(data);
|
||||
mergeFrom(input);
|
||||
input.checkLastTagWas(0);
|
||||
return this;
|
||||
} catch (final InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Reading from a byte array threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an {@code UnknownFieldSet} from {@code input} and merge it with the
|
||||
* set being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream)}.
|
||||
*/
|
||||
public Builder mergeFrom(final InputStream input) throws IOException {
|
||||
final CodedInputStream codedInput = CodedInputStream.newInstance(input);
|
||||
mergeFrom(codedInput);
|
||||
codedInput.checkLastTagWas(0);
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean mergeDelimitedFrom(InputStream input)
|
||||
throws IOException {
|
||||
final int firstByte = input.read();
|
||||
if (firstByte == -1) {
|
||||
return false;
|
||||
}
|
||||
final int size = CodedInputStream.readRawVarint32(firstByte, input);
|
||||
final InputStream limitedInput = new LimitedInputStream(input, size);
|
||||
mergeFrom(limitedInput);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean mergeDelimitedFrom(
|
||||
InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry) throws IOException {
|
||||
// UnknownFieldSet has no extensions.
|
||||
return mergeDelimitedFrom(input);
|
||||
}
|
||||
|
||||
public Builder mergeFrom(
|
||||
CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry) throws IOException {
|
||||
// UnknownFieldSet has no extensions.
|
||||
return mergeFrom(input);
|
||||
}
|
||||
|
||||
public Builder mergeFrom(
|
||||
ByteString data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
// UnknownFieldSet has no extensions.
|
||||
return mergeFrom(data);
|
||||
}
|
||||
|
||||
public Builder mergeFrom(byte[] data, int off, int len)
|
||||
throws InvalidProtocolBufferException {
|
||||
try {
|
||||
final CodedInputStream input =
|
||||
CodedInputStream.newInstance(data, off, len);
|
||||
mergeFrom(input);
|
||||
input.checkLastTagWas(0);
|
||||
return this;
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Reading from a byte array threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
public Builder mergeFrom(
|
||||
byte[] data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
// UnknownFieldSet has no extensions.
|
||||
return mergeFrom(data);
|
||||
}
|
||||
|
||||
public Builder mergeFrom(
|
||||
byte[] data, int off, int len,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
// UnknownFieldSet has no extensions.
|
||||
return mergeFrom(data, off, len);
|
||||
}
|
||||
|
||||
public Builder mergeFrom(
|
||||
InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry) throws IOException {
|
||||
// UnknownFieldSet has no extensions.
|
||||
return mergeFrom(input);
|
||||
}
|
||||
|
||||
public boolean isInitialized() {
|
||||
// UnknownFieldSets do not have required fields, so they are always
|
||||
// initialized.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a single field in an {@code UnknownFieldSet}.
|
||||
*
|
||||
* <p>A {@code Field} consists of five lists of values. The lists correspond
|
||||
* to the five "wire types" used in the protocol buffer binary format.
|
||||
* The wire type of each field can be determined from the encoded form alone,
|
||||
* without knowing the field's declared type. So, we are able to parse
|
||||
* unknown values at least this far and separate them. Normally, only one
|
||||
* of the five lists will contain any values, since it is impossible to
|
||||
* define a valid message type that declares two different types for the
|
||||
* same field number. However, the code is designed to allow for the case
|
||||
* where the same unknown field number is encountered using multiple different
|
||||
* wire types.
|
||||
*
|
||||
* <p>{@code Field} is an immutable class. To construct one, you must use a
|
||||
* {@link Builder}.
|
||||
*
|
||||
* @see UnknownFieldSet
|
||||
*/
|
||||
public static final class Field {
|
||||
private Field() {}
|
||||
|
||||
/** Construct a new {@link Builder}. */
|
||||
public static Builder newBuilder() {
|
||||
return Builder.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@link Builder} and initialize it to a copy of
|
||||
* {@code copyFrom}.
|
||||
*/
|
||||
public static Builder newBuilder(final Field copyFrom) {
|
||||
return newBuilder().mergeFrom(copyFrom);
|
||||
}
|
||||
|
||||
/** Get an empty {@code Field}. */
|
||||
public static Field getDefaultInstance() {
|
||||
return fieldDefaultInstance;
|
||||
}
|
||||
private static final Field fieldDefaultInstance = newBuilder().build();
|
||||
|
||||
/** Get the list of varint values for this field. */
|
||||
public List<Long> getVarintList() { return varint; }
|
||||
|
||||
/** Get the list of fixed32 values for this field. */
|
||||
public List<Integer> getFixed32List() { return fixed32; }
|
||||
|
||||
/** Get the list of fixed64 values for this field. */
|
||||
public List<Long> getFixed64List() { return fixed64; }
|
||||
|
||||
/** Get the list of length-delimited values for this field. */
|
||||
public List<ByteString> getLengthDelimitedList() { return lengthDelimited; }
|
||||
|
||||
/**
|
||||
* Get the list of embedded group values for this field. These are
|
||||
* represented using {@link UnknownFieldSet}s rather than {@link Message}s
|
||||
* since the group's type is presumably unknown.
|
||||
*/
|
||||
public List<UnknownFieldSet> getGroupList() { return group; }
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof Field)) {
|
||||
return false;
|
||||
}
|
||||
return Arrays.equals(getIdentityArray(),
|
||||
((Field) other).getIdentityArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(getIdentityArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array of objects to be used to uniquely identify this
|
||||
* {@link Field} instance.
|
||||
*/
|
||||
private Object[] getIdentityArray() {
|
||||
return new Object[] {
|
||||
varint,
|
||||
fixed32,
|
||||
fixed64,
|
||||
lengthDelimited,
|
||||
group};
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the field, including field number, and writes it to
|
||||
* {@code output}.
|
||||
*/
|
||||
public void writeTo(final int fieldNumber, final CodedOutputStream output)
|
||||
throws IOException {
|
||||
for (final long value : varint) {
|
||||
output.writeUInt64(fieldNumber, value);
|
||||
}
|
||||
for (final int value : fixed32) {
|
||||
output.writeFixed32(fieldNumber, value);
|
||||
}
|
||||
for (final long value : fixed64) {
|
||||
output.writeFixed64(fieldNumber, value);
|
||||
}
|
||||
for (final ByteString value : lengthDelimited) {
|
||||
output.writeBytes(fieldNumber, value);
|
||||
}
|
||||
for (final UnknownFieldSet value : group) {
|
||||
output.writeGroup(fieldNumber, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of bytes required to encode this field, including field
|
||||
* number.
|
||||
*/
|
||||
public int getSerializedSize(final int fieldNumber) {
|
||||
int result = 0;
|
||||
for (final long value : varint) {
|
||||
result += CodedOutputStream.computeUInt64Size(fieldNumber, value);
|
||||
}
|
||||
for (final int value : fixed32) {
|
||||
result += CodedOutputStream.computeFixed32Size(fieldNumber, value);
|
||||
}
|
||||
for (final long value : fixed64) {
|
||||
result += CodedOutputStream.computeFixed64Size(fieldNumber, value);
|
||||
}
|
||||
for (final ByteString value : lengthDelimited) {
|
||||
result += CodedOutputStream.computeBytesSize(fieldNumber, value);
|
||||
}
|
||||
for (final UnknownFieldSet value : group) {
|
||||
result += CodedOutputStream.computeGroupSize(fieldNumber, value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the field, including field number, and writes it to
|
||||
* {@code output}, using {@code MessageSet} wire format.
|
||||
*/
|
||||
public void writeAsMessageSetExtensionTo(
|
||||
final int fieldNumber,
|
||||
final CodedOutputStream output)
|
||||
throws IOException {
|
||||
for (final ByteString value : lengthDelimited) {
|
||||
output.writeRawMessageSetExtension(fieldNumber, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of bytes required to encode this field, including field
|
||||
* number, using {@code MessageSet} wire format.
|
||||
*/
|
||||
public int getSerializedSizeAsMessageSetExtension(final int fieldNumber) {
|
||||
int result = 0;
|
||||
for (final ByteString value : lengthDelimited) {
|
||||
result += CodedOutputStream.computeRawMessageSetExtensionSize(
|
||||
fieldNumber, value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Long> varint;
|
||||
private List<Integer> fixed32;
|
||||
private List<Long> fixed64;
|
||||
private List<ByteString> lengthDelimited;
|
||||
private List<UnknownFieldSet> group;
|
||||
|
||||
/**
|
||||
* Used to build a {@link Field} within an {@link UnknownFieldSet}.
|
||||
*
|
||||
* <p>Use {@link Field#newBuilder()} to construct a {@code Builder}.
|
||||
*/
|
||||
public static final class Builder {
|
||||
// This constructor should never be called directly (except from 'create').
|
||||
private Builder() {}
|
||||
|
||||
private static Builder create() {
|
||||
Builder builder = new Builder();
|
||||
builder.result = new Field();
|
||||
return builder;
|
||||
}
|
||||
|
||||
private Field result;
|
||||
|
||||
/**
|
||||
* Build the field. After {@code build()} has been called, the
|
||||
* {@code Builder} is no longer usable. Calling any other method will
|
||||
* result in undefined behavior and can cause a
|
||||
* {@code NullPointerException} to be thrown.
|
||||
*/
|
||||
public Field build() {
|
||||
if (result.varint == null) {
|
||||
result.varint = Collections.emptyList();
|
||||
} else {
|
||||
result.varint = Collections.unmodifiableList(result.varint);
|
||||
}
|
||||
if (result.fixed32 == null) {
|
||||
result.fixed32 = Collections.emptyList();
|
||||
} else {
|
||||
result.fixed32 = Collections.unmodifiableList(result.fixed32);
|
||||
}
|
||||
if (result.fixed64 == null) {
|
||||
result.fixed64 = Collections.emptyList();
|
||||
} else {
|
||||
result.fixed64 = Collections.unmodifiableList(result.fixed64);
|
||||
}
|
||||
if (result.lengthDelimited == null) {
|
||||
result.lengthDelimited = Collections.emptyList();
|
||||
} else {
|
||||
result.lengthDelimited =
|
||||
Collections.unmodifiableList(result.lengthDelimited);
|
||||
}
|
||||
if (result.group == null) {
|
||||
result.group = Collections.emptyList();
|
||||
} else {
|
||||
result.group = Collections.unmodifiableList(result.group);
|
||||
}
|
||||
|
||||
final Field returnMe = result;
|
||||
result = null;
|
||||
return returnMe;
|
||||
}
|
||||
|
||||
/** Discard the field's contents. */
|
||||
public Builder clear() {
|
||||
result = new Field();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the values in {@code other} into this field. For each list
|
||||
* of values, {@code other}'s values are append to the ones in this
|
||||
* field.
|
||||
*/
|
||||
public Builder mergeFrom(final Field other) {
|
||||
if (!other.varint.isEmpty()) {
|
||||
if (result.varint == null) {
|
||||
result.varint = new ArrayList<Long>();
|
||||
}
|
||||
result.varint.addAll(other.varint);
|
||||
}
|
||||
if (!other.fixed32.isEmpty()) {
|
||||
if (result.fixed32 == null) {
|
||||
result.fixed32 = new ArrayList<Integer>();
|
||||
}
|
||||
result.fixed32.addAll(other.fixed32);
|
||||
}
|
||||
if (!other.fixed64.isEmpty()) {
|
||||
if (result.fixed64 == null) {
|
||||
result.fixed64 = new ArrayList<Long>();
|
||||
}
|
||||
result.fixed64.addAll(other.fixed64);
|
||||
}
|
||||
if (!other.lengthDelimited.isEmpty()) {
|
||||
if (result.lengthDelimited == null) {
|
||||
result.lengthDelimited = new ArrayList<ByteString>();
|
||||
}
|
||||
result.lengthDelimited.addAll(other.lengthDelimited);
|
||||
}
|
||||
if (!other.group.isEmpty()) {
|
||||
if (result.group == null) {
|
||||
result.group = new ArrayList<UnknownFieldSet>();
|
||||
}
|
||||
result.group.addAll(other.group);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Add a varint value. */
|
||||
public Builder addVarint(final long value) {
|
||||
if (result.varint == null) {
|
||||
result.varint = new ArrayList<Long>();
|
||||
}
|
||||
result.varint.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Add a fixed32 value. */
|
||||
public Builder addFixed32(final int value) {
|
||||
if (result.fixed32 == null) {
|
||||
result.fixed32 = new ArrayList<Integer>();
|
||||
}
|
||||
result.fixed32.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Add a fixed64 value. */
|
||||
public Builder addFixed64(final long value) {
|
||||
if (result.fixed64 == null) {
|
||||
result.fixed64 = new ArrayList<Long>();
|
||||
}
|
||||
result.fixed64.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Add a length-delimited value. */
|
||||
public Builder addLengthDelimited(final ByteString value) {
|
||||
if (result.lengthDelimited == null) {
|
||||
result.lengthDelimited = new ArrayList<ByteString>();
|
||||
}
|
||||
result.lengthDelimited.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Add an embedded group. */
|
||||
public Builder addGroup(final UnknownFieldSet value) {
|
||||
if (result.group == null) {
|
||||
result.group = new ArrayList<UnknownFieldSet>();
|
||||
}
|
||||
result.group.add(value);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parser to implement MessageLite interface.
|
||||
*/
|
||||
public static final class Parser extends AbstractParser<UnknownFieldSet> {
|
||||
public UnknownFieldSet parsePartialFrom(
|
||||
CodedInputStream input, ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
Builder builder = newBuilder();
|
||||
try {
|
||||
builder.mergeFrom(input);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e.setUnfinishedMessage(builder.buildPartial());
|
||||
} catch (IOException e) {
|
||||
throw new InvalidProtocolBufferException(e.getMessage())
|
||||
.setUnfinishedMessage(builder.buildPartial());
|
||||
}
|
||||
return builder.buildPartial();
|
||||
}
|
||||
}
|
||||
|
||||
private static final Parser PARSER = new Parser();
|
||||
public final Parser getParserForType() {
|
||||
return PARSER;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,165 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
import java.util.AbstractList;
|
||||
import java.util.RandomAccess;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* An implementation of {@link LazyStringList} that wraps another
|
||||
* {@link LazyStringList} such that it cannot be modified via the wrapper.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public class UnmodifiableLazyStringList extends AbstractList<String>
|
||||
implements LazyStringList, RandomAccess {
|
||||
|
||||
private final LazyStringList list;
|
||||
|
||||
public UnmodifiableLazyStringList(LazyStringList list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(int index) {
|
||||
return list.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return list.size();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public ByteString getByteString(int index) {
|
||||
return list.getByteString(index);
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public void add(ByteString element) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public ListIterator<String> listIterator(final int index) {
|
||||
return new ListIterator<String>() {
|
||||
ListIterator<String> iter = list.listIterator(index);
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public boolean hasNext() {
|
||||
return iter.hasNext();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public String next() {
|
||||
return iter.next();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public boolean hasPrevious() {
|
||||
return iter.hasPrevious();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public String previous() {
|
||||
return iter.previous();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public int nextIndex() {
|
||||
return iter.nextIndex();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public int previousIndex() {
|
||||
return iter.previousIndex();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public void set(String o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public void add(String o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<String> iterator() {
|
||||
return new Iterator<String>() {
|
||||
Iterator<String> iter = list.iterator();
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public boolean hasNext() {
|
||||
return iter.hasNext();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public String next() {
|
||||
return iter.next();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public List<?> getUnderlyingElements() {
|
||||
// The returned value is already unmodifiable.
|
||||
return list.getUnderlyingElements();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,362 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
/**
|
||||
* A set of low-level, high-performance static utility methods related
|
||||
* to the UTF-8 character encoding. This class has no dependencies
|
||||
* outside of the core JDK libraries.
|
||||
*
|
||||
* <p>There are several variants of UTF-8. The one implemented by
|
||||
* this class is the restricted definition of UTF-8 introduced in
|
||||
* Unicode 3.1, which mandates the rejection of "overlong" byte
|
||||
* sequences as well as rejection of 3-byte surrogate codepoint byte
|
||||
* sequences. Note that the UTF-8 decoder included in Oracle's JDK
|
||||
* has been modified to also reject "overlong" byte sequences, but (as
|
||||
* of 2011) still accepts 3-byte surrogate codepoint byte sequences.
|
||||
*
|
||||
* <p>The byte sequences considered valid by this class are exactly
|
||||
* those that can be roundtrip converted to Strings and back to bytes
|
||||
* using the UTF-8 charset, without loss: <pre> {@code
|
||||
* Arrays.equals(bytes, new String(bytes, "UTF-8").getBytes("UTF-8"))
|
||||
* }</pre>
|
||||
*
|
||||
* <p>See the Unicode Standard,</br>
|
||||
* Table 3-6. <em>UTF-8 Bit Distribution</em>,</br>
|
||||
* Table 3-7. <em>Well Formed UTF-8 Byte Sequences</em>.
|
||||
*
|
||||
* <p>This class supports decoding of partial byte sequences, so that the
|
||||
* bytes in a complete UTF-8 byte sequences can be stored in multiple
|
||||
* segments. Methods typically return {@link #MALFORMED} if the partial
|
||||
* byte sequence is definitely not well-formed, {@link #COMPLETE} if it is
|
||||
* well-formed in the absence of additional input, or if the byte sequence
|
||||
* apparently terminated in the middle of a character, an opaque integer
|
||||
* "state" value containing enough information to decode the character when
|
||||
* passed to a subsequent invocation of a partial decoding method.
|
||||
*
|
||||
* @author martinrb@google.com (Martin Buchholz)
|
||||
*/
|
||||
final class Utf8 {
|
||||
private Utf8() {}
|
||||
|
||||
/**
|
||||
* State value indicating that the byte sequence is well-formed and
|
||||
* complete (no further bytes are needed to complete a character).
|
||||
*/
|
||||
public static final int COMPLETE = 0;
|
||||
|
||||
/**
|
||||
* State value indicating that the byte sequence is definitely not
|
||||
* well-formed.
|
||||
*/
|
||||
public static final int MALFORMED = -1;
|
||||
|
||||
// Other state values include the partial bytes of the incomplete
|
||||
// character to be decoded in the simplest way: we pack the bytes
|
||||
// into the state int in little-endian order. For example:
|
||||
//
|
||||
// int state = byte1 ^ (byte2 << 8) ^ (byte3 << 16);
|
||||
//
|
||||
// Such a state is unpacked thus (note the ~ operation for byte2 to
|
||||
// undo byte1's sign-extension bits):
|
||||
//
|
||||
// int byte1 = (byte) state;
|
||||
// int byte2 = (byte) ~(state >> 8);
|
||||
// int byte3 = (byte) (state >> 16);
|
||||
//
|
||||
// We cannot store a zero byte in the state because it would be
|
||||
// indistinguishable from the absence of a byte. But we don't need
|
||||
// to, because partial bytes must always be negative. When building
|
||||
// a state, we ensure that byte1 is negative and subsequent bytes
|
||||
// are valid trailing bytes.
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the given byte array is a well-formed
|
||||
* UTF-8 byte sequence.
|
||||
*
|
||||
* <p>This is a convenience method, equivalent to a call to {@code
|
||||
* isValidUtf8(bytes, 0, bytes.length)}.
|
||||
*/
|
||||
public static boolean isValidUtf8(byte[] bytes) {
|
||||
return isValidUtf8(bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the given byte array slice is a
|
||||
* well-formed UTF-8 byte sequence. The range of bytes to be
|
||||
* checked extends from index {@code index}, inclusive, to {@code
|
||||
* limit}, exclusive.
|
||||
*
|
||||
* <p>This is a convenience method, equivalent to {@code
|
||||
* partialIsValidUtf8(bytes, index, limit) == Utf8.COMPLETE}.
|
||||
*/
|
||||
public static boolean isValidUtf8(byte[] bytes, int index, int limit) {
|
||||
return partialIsValidUtf8(bytes, index, limit) == COMPLETE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether the given byte array slice is a well-formed,
|
||||
* malformed, or incomplete UTF-8 byte sequence. The range of bytes
|
||||
* to be checked extends from index {@code index}, inclusive, to
|
||||
* {@code limit}, exclusive.
|
||||
*
|
||||
* @param state either {@link Utf8#COMPLETE} (if this is the initial decoding
|
||||
* operation) or the value returned from a call to a partial decoding method
|
||||
* for the previous bytes
|
||||
*
|
||||
* @return {@link #MALFORMED} if the partial byte sequence is
|
||||
* definitely not well-formed, {@link #COMPLETE} if it is well-formed
|
||||
* (no additional input needed), or if the byte sequence is
|
||||
* "incomplete", i.e. apparently terminated in the middle of a character,
|
||||
* an opaque integer "state" value containing enough information to
|
||||
* decode the character when passed to a subsequent invocation of a
|
||||
* partial decoding method.
|
||||
*/
|
||||
public static int partialIsValidUtf8(
|
||||
int state, byte[] bytes, int index, int limit) {
|
||||
if (state != COMPLETE) {
|
||||
// The previous decoding operation was incomplete (or malformed).
|
||||
// We look for a well-formed sequence consisting of bytes from
|
||||
// the previous decoding operation (stored in state) together
|
||||
// with bytes from the array slice.
|
||||
//
|
||||
// We expect such "straddler characters" to be rare.
|
||||
|
||||
if (index >= limit) { // No bytes? No progress.
|
||||
return state;
|
||||
}
|
||||
int byte1 = (byte) state;
|
||||
// byte1 is never ASCII.
|
||||
if (byte1 < (byte) 0xE0) {
|
||||
// two-byte form
|
||||
|
||||
// Simultaneously checks for illegal trailing-byte in
|
||||
// leading position and overlong 2-byte form.
|
||||
if (byte1 < (byte) 0xC2 ||
|
||||
// byte2 trailing-byte test
|
||||
bytes[index++] > (byte) 0xBF) {
|
||||
return MALFORMED;
|
||||
}
|
||||
} else if (byte1 < (byte) 0xF0) {
|
||||
// three-byte form
|
||||
|
||||
// Get byte2 from saved state or array
|
||||
int byte2 = (byte) ~(state >> 8);
|
||||
if (byte2 == 0) {
|
||||
byte2 = bytes[index++];
|
||||
if (index >= limit) {
|
||||
return incompleteStateFor(byte1, byte2);
|
||||
}
|
||||
}
|
||||
if (byte2 > (byte) 0xBF ||
|
||||
// overlong? 5 most significant bits must not all be zero
|
||||
(byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0) ||
|
||||
// illegal surrogate codepoint?
|
||||
(byte1 == (byte) 0xED && byte2 >= (byte) 0xA0) ||
|
||||
// byte3 trailing-byte test
|
||||
bytes[index++] > (byte) 0xBF) {
|
||||
return MALFORMED;
|
||||
}
|
||||
} else {
|
||||
// four-byte form
|
||||
|
||||
// Get byte2 and byte3 from saved state or array
|
||||
int byte2 = (byte) ~(state >> 8);
|
||||
int byte3 = 0;
|
||||
if (byte2 == 0) {
|
||||
byte2 = bytes[index++];
|
||||
if (index >= limit) {
|
||||
return incompleteStateFor(byte1, byte2);
|
||||
}
|
||||
} else {
|
||||
byte3 = (byte) (state >> 16);
|
||||
}
|
||||
if (byte3 == 0) {
|
||||
byte3 = bytes[index++];
|
||||
if (index >= limit) {
|
||||
return incompleteStateFor(byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
|
||||
// If we were called with state == MALFORMED, then byte1 is 0xFF,
|
||||
// which never occurs in well-formed UTF-8, and so we will return
|
||||
// MALFORMED again below.
|
||||
|
||||
if (byte2 > (byte) 0xBF ||
|
||||
// Check that 1 <= plane <= 16. Tricky optimized form of:
|
||||
// if (byte1 > (byte) 0xF4 ||
|
||||
// byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 ||
|
||||
// byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F)
|
||||
(((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0 ||
|
||||
// byte3 trailing-byte test
|
||||
byte3 > (byte) 0xBF ||
|
||||
// byte4 trailing-byte test
|
||||
bytes[index++] > (byte) 0xBF) {
|
||||
return MALFORMED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return partialIsValidUtf8(bytes, index, limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether the given byte array slice is a well-formed,
|
||||
* malformed, or incomplete UTF-8 byte sequence. The range of bytes
|
||||
* to be checked extends from index {@code index}, inclusive, to
|
||||
* {@code limit}, exclusive.
|
||||
*
|
||||
* <p>This is a convenience method, equivalent to a call to {@code
|
||||
* partialIsValidUtf8(Utf8.COMPLETE, bytes, index, limit)}.
|
||||
*
|
||||
* @return {@link #MALFORMED} if the partial byte sequence is
|
||||
* definitely not well-formed, {@link #COMPLETE} if it is well-formed
|
||||
* (no additional input needed), or if the byte sequence is
|
||||
* "incomplete", i.e. apparently terminated in the middle of a character,
|
||||
* an opaque integer "state" value containing enough information to
|
||||
* decode the character when passed to a subsequent invocation of a
|
||||
* partial decoding method.
|
||||
*/
|
||||
public static int partialIsValidUtf8(
|
||||
byte[] bytes, int index, int limit) {
|
||||
// Optimize for 100% ASCII.
|
||||
// Hotspot loves small simple top-level loops like this.
|
||||
while (index < limit && bytes[index] >= 0) {
|
||||
index++;
|
||||
}
|
||||
|
||||
return (index >= limit) ? COMPLETE :
|
||||
partialIsValidUtf8NonAscii(bytes, index, limit);
|
||||
}
|
||||
|
||||
private static int partialIsValidUtf8NonAscii(
|
||||
byte[] bytes, int index, int limit) {
|
||||
for (;;) {
|
||||
int byte1, byte2;
|
||||
|
||||
// Optimize for interior runs of ASCII bytes.
|
||||
do {
|
||||
if (index >= limit) {
|
||||
return COMPLETE;
|
||||
}
|
||||
} while ((byte1 = bytes[index++]) >= 0);
|
||||
|
||||
if (byte1 < (byte) 0xE0) {
|
||||
// two-byte form
|
||||
|
||||
if (index >= limit) {
|
||||
return byte1;
|
||||
}
|
||||
|
||||
// Simultaneously checks for illegal trailing-byte in
|
||||
// leading position and overlong 2-byte form.
|
||||
if (byte1 < (byte) 0xC2 ||
|
||||
bytes[index++] > (byte) 0xBF) {
|
||||
return MALFORMED;
|
||||
}
|
||||
} else if (byte1 < (byte) 0xF0) {
|
||||
// three-byte form
|
||||
|
||||
if (index >= limit - 1) { // incomplete sequence
|
||||
return incompleteStateFor(bytes, index, limit);
|
||||
}
|
||||
if ((byte2 = bytes[index++]) > (byte) 0xBF ||
|
||||
// overlong? 5 most significant bits must not all be zero
|
||||
(byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0) ||
|
||||
// check for illegal surrogate codepoints
|
||||
(byte1 == (byte) 0xED && byte2 >= (byte) 0xA0) ||
|
||||
// byte3 trailing-byte test
|
||||
bytes[index++] > (byte) 0xBF) {
|
||||
return MALFORMED;
|
||||
}
|
||||
} else {
|
||||
// four-byte form
|
||||
|
||||
if (index >= limit - 2) { // incomplete sequence
|
||||
return incompleteStateFor(bytes, index, limit);
|
||||
}
|
||||
if ((byte2 = bytes[index++]) > (byte) 0xBF ||
|
||||
// Check that 1 <= plane <= 16. Tricky optimized form of:
|
||||
// if (byte1 > (byte) 0xF4 ||
|
||||
// byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 ||
|
||||
// byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F)
|
||||
(((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0 ||
|
||||
// byte3 trailing-byte test
|
||||
bytes[index++] > (byte) 0xBF ||
|
||||
// byte4 trailing-byte test
|
||||
bytes[index++] > (byte) 0xBF) {
|
||||
return MALFORMED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int incompleteStateFor(int byte1) {
|
||||
return (byte1 > (byte) 0xF4) ?
|
||||
MALFORMED : byte1;
|
||||
}
|
||||
|
||||
private static int incompleteStateFor(int byte1, int byte2) {
|
||||
return (byte1 > (byte) 0xF4 ||
|
||||
byte2 > (byte) 0xBF) ?
|
||||
MALFORMED : byte1 ^ (byte2 << 8);
|
||||
}
|
||||
|
||||
private static int incompleteStateFor(int byte1, int byte2, int byte3) {
|
||||
return (byte1 > (byte) 0xF4 ||
|
||||
byte2 > (byte) 0xBF ||
|
||||
byte3 > (byte) 0xBF) ?
|
||||
MALFORMED : byte1 ^ (byte2 << 8) ^ (byte3 << 16);
|
||||
}
|
||||
|
||||
private static int incompleteStateFor(byte[] bytes, int index, int limit) {
|
||||
int byte1 = bytes[index - 1];
|
||||
switch (limit - index) {
|
||||
case 0: return incompleteStateFor(byte1);
|
||||
case 1: return incompleteStateFor(byte1, bytes[index]);
|
||||
case 2: return incompleteStateFor(byte1, bytes[index], bytes[index + 1]);
|
||||
default: throw new AssertionError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,176 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* license agreements; and to You under the Apache License, version 2.0:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* This file is part of the Apache Pekko project, which was derived from Akka.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018-2022 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.apache.pekko.protobuf;
|
||||
|
||||
/**
|
||||
* This class is used internally by the Protocol Buffer library and generated
|
||||
* message implementations. It is public only because those generated messages
|
||||
* do not reside in the {@code protobuf} package. Others should not use this
|
||||
* class directly.
|
||||
*
|
||||
* This class contains constants and helper functions useful for dealing with
|
||||
* the Protocol Buffer wire format.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public final class WireFormat {
|
||||
// Do not allow instantiation.
|
||||
private WireFormat() {}
|
||||
|
||||
public static final int WIRETYPE_VARINT = 0;
|
||||
public static final int WIRETYPE_FIXED64 = 1;
|
||||
public static final int WIRETYPE_LENGTH_DELIMITED = 2;
|
||||
public static final int WIRETYPE_START_GROUP = 3;
|
||||
public static final int WIRETYPE_END_GROUP = 4;
|
||||
public static final int WIRETYPE_FIXED32 = 5;
|
||||
|
||||
static final int TAG_TYPE_BITS = 3;
|
||||
static final int TAG_TYPE_MASK = (1 << TAG_TYPE_BITS) - 1;
|
||||
|
||||
/** Given a tag value, determines the wire type (the lower 3 bits). */
|
||||
static int getTagWireType(final int tag) {
|
||||
return tag & TAG_TYPE_MASK;
|
||||
}
|
||||
|
||||
/** Given a tag value, determines the field number (the upper 29 bits). */
|
||||
public static int getTagFieldNumber(final int tag) {
|
||||
return tag >>> TAG_TYPE_BITS;
|
||||
}
|
||||
|
||||
/** Makes a tag value given a field number and wire type. */
|
||||
static int makeTag(final int fieldNumber, final int wireType) {
|
||||
return (fieldNumber << TAG_TYPE_BITS) | wireType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lite equivalent to {@link Descriptors.FieldDescriptor.JavaType}. This is
|
||||
* only here to support the lite runtime and should not be used by users.
|
||||
*/
|
||||
public enum JavaType {
|
||||
INT(0),
|
||||
LONG(0L),
|
||||
FLOAT(0F),
|
||||
DOUBLE(0D),
|
||||
BOOLEAN(false),
|
||||
STRING(""),
|
||||
BYTE_STRING(ByteString.EMPTY),
|
||||
ENUM(null),
|
||||
MESSAGE(null);
|
||||
|
||||
JavaType(final Object defaultDefault) {
|
||||
this.defaultDefault = defaultDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* The default default value for fields of this type, if it's a primitive
|
||||
* type.
|
||||
*/
|
||||
Object getDefaultDefault() {
|
||||
return defaultDefault;
|
||||
}
|
||||
|
||||
private final Object defaultDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lite equivalent to {@link Descriptors.FieldDescriptor.Type}. This is
|
||||
* only here to support the lite runtime and should not be used by users.
|
||||
*/
|
||||
public enum FieldType {
|
||||
DOUBLE (JavaType.DOUBLE , WIRETYPE_FIXED64 ),
|
||||
FLOAT (JavaType.FLOAT , WIRETYPE_FIXED32 ),
|
||||
INT64 (JavaType.LONG , WIRETYPE_VARINT ),
|
||||
UINT64 (JavaType.LONG , WIRETYPE_VARINT ),
|
||||
INT32 (JavaType.INT , WIRETYPE_VARINT ),
|
||||
FIXED64 (JavaType.LONG , WIRETYPE_FIXED64 ),
|
||||
FIXED32 (JavaType.INT , WIRETYPE_FIXED32 ),
|
||||
BOOL (JavaType.BOOLEAN , WIRETYPE_VARINT ),
|
||||
STRING (JavaType.STRING , WIRETYPE_LENGTH_DELIMITED) {
|
||||
public boolean isPackable() { return false; }
|
||||
},
|
||||
GROUP (JavaType.MESSAGE , WIRETYPE_START_GROUP ) {
|
||||
public boolean isPackable() { return false; }
|
||||
},
|
||||
MESSAGE (JavaType.MESSAGE , WIRETYPE_LENGTH_DELIMITED) {
|
||||
public boolean isPackable() { return false; }
|
||||
},
|
||||
BYTES (JavaType.BYTE_STRING, WIRETYPE_LENGTH_DELIMITED) {
|
||||
public boolean isPackable() { return false; }
|
||||
},
|
||||
UINT32 (JavaType.INT , WIRETYPE_VARINT ),
|
||||
ENUM (JavaType.ENUM , WIRETYPE_VARINT ),
|
||||
SFIXED32(JavaType.INT , WIRETYPE_FIXED32 ),
|
||||
SFIXED64(JavaType.LONG , WIRETYPE_FIXED64 ),
|
||||
SINT32 (JavaType.INT , WIRETYPE_VARINT ),
|
||||
SINT64 (JavaType.LONG , WIRETYPE_VARINT );
|
||||
|
||||
FieldType(final JavaType javaType, final int wireType) {
|
||||
this.javaType = javaType;
|
||||
this.wireType = wireType;
|
||||
}
|
||||
|
||||
private final JavaType javaType;
|
||||
private final int wireType;
|
||||
|
||||
public JavaType getJavaType() { return javaType; }
|
||||
public int getWireType() { return wireType; }
|
||||
|
||||
public boolean isPackable() { return true; }
|
||||
}
|
||||
|
||||
// Field numbers for fields in MessageSet wire format.
|
||||
static final int MESSAGE_SET_ITEM = 1;
|
||||
static final int MESSAGE_SET_TYPE_ID = 2;
|
||||
static final int MESSAGE_SET_MESSAGE = 3;
|
||||
|
||||
// Tag numbers.
|
||||
static final int MESSAGE_SET_ITEM_TAG =
|
||||
makeTag(MESSAGE_SET_ITEM, WIRETYPE_START_GROUP);
|
||||
static final int MESSAGE_SET_ITEM_END_TAG =
|
||||
makeTag(MESSAGE_SET_ITEM, WIRETYPE_END_GROUP);
|
||||
static final int MESSAGE_SET_TYPE_ID_TAG =
|
||||
makeTag(MESSAGE_SET_TYPE_ID, WIRETYPE_VARINT);
|
||||
static final int MESSAGE_SET_MESSAGE_TAG =
|
||||
makeTag(MESSAGE_SET_MESSAGE, WIRETYPE_LENGTH_DELIMITED);
|
||||
}
|
||||
|
|
@ -34,15 +34,6 @@ pekko {
|
|||
|
||||
"org.apache.pekko.remote.artery.ArteryMessage" = artery
|
||||
|
||||
# Since org.apache.pekko.protobuf.Message does not extend Serializable but
|
||||
# GeneratedMessage does, need to use the more specific one here in order
|
||||
# to avoid ambiguity.
|
||||
# This is only loaded if pekko-protobuf is on the classpath
|
||||
# It should not be used and users should migrate to using the protobuf classes
|
||||
# directly
|
||||
# Remove in 2.7
|
||||
"org.apache.pekko.protobuf.GeneratedMessage" = proto
|
||||
|
||||
"org.apache.pekko.protobufv3.internal.GeneratedMessageV3" = proto
|
||||
|
||||
# Since com.google.protobuf.Message does not extend Serializable but
|
||||
|
|
|
|||
|
|
@ -48,9 +48,11 @@ object ProtobufSerializer {
|
|||
}
|
||||
|
||||
/**
|
||||
* This Serializer serializes `org.apache.pekko.protobuf.Message` and `org.apache.pekko.google.protobuf.Message`
|
||||
* This Serializer serializes `org.apache.pekko.protobufv3.internal.Message`
|
||||
* It is using reflection to find the `parseFrom` and `toByteArray` methods to avoid
|
||||
* dependency to `com.google.protobuf`.
|
||||
*
|
||||
* This is related to the config property `pekko.serialization.protobuf.allowed-classes`.
|
||||
*/
|
||||
class ProtobufSerializer(val system: ExtendedActorSystem) extends BaseSerializer {
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,6 @@ class ProtobufSerializerSpec extends PekkoSpec(s"""
|
|||
"com.google.protobuf.GeneratedMessage",
|
||||
"com.google.protobuf.GeneratedMessageV3",
|
||||
"scalapb.GeneratedMessageCompanion",
|
||||
"org.apache.pekko.protobuf.GeneratedMessage",
|
||||
"org.apache.pekko.protobufv3.internal.GeneratedMessageV3",
|
||||
"${classOf[AnotherMessage].getName}",
|
||||
"${classOf[ProtobufSerializerSpec.AnotherInterface].getName}",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue