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
|
- serialization-jackson/test
|
||||||
- stream/test stream-testkit/test stream-tests/test stream-typed/test
|
- stream/test stream-testkit/test stream-tests/test stream-typed/test
|
||||||
- stream-tests-tck/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
|
fail-fast: true
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- 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
|
- serialization-jackson/Test/compile
|
||||||
- stream/Test/compile stream-testkit/Test/compile stream-tests/Test/compile stream-typed/Test/compile
|
- stream/Test/compile stream-testkit/Test/compile stream-tests/Test/compile stream-typed/Test/compile
|
||||||
- stream-tests-tck/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
|
fail-fast: true
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- 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.
|
pekko-persistence-typed contains AuctionEntity.java in its test source.
|
||||||
This code is derived from a class in Lagom <https://github.com/lagom>,
|
This code is derived from a class in Lagom <https://github.com/lagom>,
|
||||||
licensed under the Apache 2.0 license.
|
licensed under the Apache 2.0 license.
|
||||||
|
|
|
||||||
|
|
@ -800,7 +800,6 @@ pekko {
|
||||||
"com.google.protobuf.GeneratedMessage",
|
"com.google.protobuf.GeneratedMessage",
|
||||||
"com.google.protobuf.GeneratedMessageV3",
|
"com.google.protobuf.GeneratedMessageV3",
|
||||||
"scalapb.GeneratedMessageCompanion",
|
"scalapb.GeneratedMessageCompanion",
|
||||||
"org.apache.pekko.protobuf.GeneratedMessage",
|
|
||||||
"org.apache.pekko.protobufv3.internal.GeneratedMessageV3"
|
"org.apache.pekko.protobufv3.internal.GeneratedMessageV3"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -447,7 +447,7 @@ class Serialization(val system: ExtendedActorSystem) extends Extension {
|
||||||
private[pekko] val bindings: immutable.Seq[ClassSerializer] = {
|
private[pekko] val bindings: immutable.Seq[ClassSerializer] = {
|
||||||
val fromConfig = for {
|
val fromConfig = for {
|
||||||
(className: String, alias: String) <- settings.SerializationBindings
|
(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))
|
} yield (system.dynamicAccess.getClassFor[Any](className).get, serializers(alias))
|
||||||
|
|
||||||
val fromSettings = serializerDetails.flatMap { detail =>
|
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.
|
// include "com.google.protobuf.GeneratedMessage" = proto in configured serialization-bindings.
|
||||||
private def checkGoogleProtobuf(className: String): Boolean = checkClass("com.google.protobuf", className)
|
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 =
|
private def checkClass(prefix: String, className: String): Boolean =
|
||||||
!className.startsWith(prefix) || system.dynamicAccess.getClassFor[Any](className).isSuccess
|
!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,
|
persistenceQuery,
|
||||||
persistenceTyped,
|
persistenceTyped,
|
||||||
persistenceTestkit,
|
persistenceTestkit,
|
||||||
protobuf,
|
|
||||||
protobufV3,
|
protobufV3,
|
||||||
pki,
|
pki,
|
||||||
remote,
|
remote,
|
||||||
|
|
@ -116,7 +115,6 @@ lazy val root = Project(id = "pekko", base = file("."))
|
||||||
UnidocRoot.autoImport.unidocRootIgnoreProjects := Seq(
|
UnidocRoot.autoImport.unidocRootIgnoreProjects := Seq(
|
||||||
remoteTests,
|
remoteTests,
|
||||||
benchJmh,
|
benchJmh,
|
||||||
protobuf,
|
|
||||||
protobufV3,
|
protobufV3,
|
||||||
pekkoScalaNightly,
|
pekkoScalaNightly,
|
||||||
docs,
|
docs,
|
||||||
|
|
@ -362,14 +360,6 @@ lazy val persistenceTypedTests = pekkoModule("persistence-typed-tests")
|
||||||
.disablePlugins(MimaPlugin)
|
.disablePlugins(MimaPlugin)
|
||||||
.enablePlugins(NoPublish)
|
.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")
|
lazy val protobufV3 = pekkoModule("protobuf-v3")
|
||||||
.settings(OSGi.protobufV3)
|
.settings(OSGi.protobufV3)
|
||||||
.settings(AutomaticModuleName.settings("pekko.protobuf.v3"))
|
.settings(AutomaticModuleName.settings("pekko.protobuf.v3"))
|
||||||
|
|
@ -413,7 +403,6 @@ lazy val remote =
|
||||||
actor,
|
actor,
|
||||||
stream,
|
stream,
|
||||||
pki,
|
pki,
|
||||||
protobuf % "test",
|
|
||||||
actorTests % "test->test",
|
actorTests % "test->test",
|
||||||
testkit % "test->test",
|
testkit % "test->test",
|
||||||
streamTestkit % "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",
|
apacheSonatypeLicenseFile := baseDir.value / "legal" / "pekko-remote-jar-license.txt",
|
||||||
apacheSonatypeNoticeFile := baseDir.value / "legal" / "pekko-remote-jar-notice.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
|
* Settings specific for Pekko protobuf-v3 subproject which requires a different license file
|
||||||
* as well as an additional "COPYING.protobuf" file.
|
* as well as an additional "COPYING.protobuf" file.
|
||||||
|
|
|
||||||
|
|
@ -70,8 +70,6 @@ object OSGi {
|
||||||
|
|
||||||
val osgi = exports(Seq("org.apache.pekko.osgi.*"))
|
val osgi = exports(Seq("org.apache.pekko.osgi.*"))
|
||||||
|
|
||||||
val protobuf = exports(Seq("org.apache.pekko.protobuf.*"))
|
|
||||||
|
|
||||||
val protobufV3 = osgiSettings ++ Seq(
|
val protobufV3 = osgiSettings ++ Seq(
|
||||||
OsgiKeys.importPackage := Seq(
|
OsgiKeys.importPackage := Seq(
|
||||||
"!sun.misc",
|
"!sun.misc",
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,6 @@ object PekkoDisciplinePlugin extends AutoPlugin {
|
||||||
"pekko-actor-typed-tests",
|
"pekko-actor-typed-tests",
|
||||||
// references to deprecated PARSER fields in generated message formats?
|
// references to deprecated PARSER fields in generated message formats?
|
||||||
"pekko-cluster-typed",
|
"pekko-cluster-typed",
|
||||||
// use of deprecated org.apache.pekko.protobuf.GeneratedMessage
|
|
||||||
"pekko-protobuf",
|
|
||||||
"pekko-protobuf-v3",
|
"pekko-protobuf-v3",
|
||||||
// references to deprecated PARSER fields in generated message formats?
|
// references to deprecated PARSER fields in generated message formats?
|
||||||
"pekko-remote",
|
"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
|
"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
|
"org.apache.pekko.protobufv3.internal.GeneratedMessageV3" = proto
|
||||||
|
|
||||||
# Since com.google.protobuf.Message does not extend Serializable but
|
# 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
|
* It is using reflection to find the `parseFrom` and `toByteArray` methods to avoid
|
||||||
* dependency to `com.google.protobuf`.
|
* dependency to `com.google.protobuf`.
|
||||||
|
*
|
||||||
|
* This is related to the config property `pekko.serialization.protobuf.allowed-classes`.
|
||||||
*/
|
*/
|
||||||
class ProtobufSerializer(val system: ExtendedActorSystem) extends BaseSerializer {
|
class ProtobufSerializer(val system: ExtendedActorSystem) extends BaseSerializer {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,6 @@ class ProtobufSerializerSpec extends PekkoSpec(s"""
|
||||||
"com.google.protobuf.GeneratedMessage",
|
"com.google.protobuf.GeneratedMessage",
|
||||||
"com.google.protobuf.GeneratedMessageV3",
|
"com.google.protobuf.GeneratedMessageV3",
|
||||||
"scalapb.GeneratedMessageCompanion",
|
"scalapb.GeneratedMessageCompanion",
|
||||||
"org.apache.pekko.protobuf.GeneratedMessage",
|
|
||||||
"org.apache.pekko.protobufv3.internal.GeneratedMessageV3",
|
"org.apache.pekko.protobufv3.internal.GeneratedMessageV3",
|
||||||
"${classOf[AnotherMessage].getName}",
|
"${classOf[AnotherMessage].getName}",
|
||||||
"${classOf[ProtobufSerializerSpec.AnotherInterface].getName}",
|
"${classOf[ProtobufSerializerSpec.AnotherInterface].getName}",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue