commit
32bea52ee6
319 changed files with 45881 additions and 46930 deletions
@ -0,0 +1,135 @@ |
||||
# How to use `protobuf_generate` |
||||
|
||||
This document explains how to use the function `protobuf_generate` which is provided by protobuf's CMake module. |
||||
|
||||
## Usage |
||||
|
||||
In the same directory that called `find_package(protobuf CONFIG)` and any of its subdirectories, the CMake function `protobuf_generate` is made available by |
||||
[`protobuf-generate.cmake`](../cmake/protobuf-generate.cmake). It can be used to automatically generate source files from `.proto` schema files at build time. |
||||
|
||||
### Basic example |
||||
|
||||
Let us see how `protobuf_generate` can be used to generate and compile the source files of a proto schema whenever an object target called `proto-objects` is built. |
||||
|
||||
Given the following directory structure: |
||||
|
||||
- `proto/helloworld/helloworld.proto` |
||||
- `CMakeLists.txt` |
||||
|
||||
where `helloworld.proto` is a protobuf schema file and `CMakeLists.txt` contains: |
||||
|
||||
```cmake |
||||
find_package(protobuf CONFIG REQUIRED) |
||||
|
||||
add_library(proto-objects OBJECT "${CMAKE_CURRENT_LIST_DIR}/proto/helloworld/helloworld.proto") |
||||
|
||||
target_link_libraries(proto-objects PUBLIC protobuf::libprotobuf) |
||||
|
||||
set(PROTO_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated") |
||||
|
||||
target_include_directories(proto-objects PUBLIC "$<BUILD_INTERFACE:${PROTO_BINARY_DIR}>") |
||||
|
||||
protobuf_generate( |
||||
TARGET proto-objects |
||||
IMPORT_DIRS "${CMAKE_CURRENT_LIST_DIR}/proto" |
||||
PROTOC_OUT_DIR "${PROTO_BINARY_DIR}") |
||||
``` |
||||
|
||||
Building the target `proto-objects` will generate the files: |
||||
|
||||
- `${CMAKE_CURRENT_BINARY_DIR}/generated/helloworld/helloworld.pb.h` |
||||
- `${CMAKE_CURRENT_BINARY_DIR}/generated/helloworld/helloworld.pb.cc` |
||||
|
||||
and (depending on the build system) output: |
||||
|
||||
```shell |
||||
[build] [1/2] Running cpp protocol buffer compiler on /proto/helloworld/helloworld.proto |
||||
[build] [2/2] Building CXX object /build/generated/helloworld/helloworld.pb.cc.o |
||||
``` |
||||
|
||||
### gRPC example |
||||
|
||||
`protobuf_generate` can also be customized to invoke plugins like gRPC's `grpc_cpp_plugin`. Given the same directory structure as in the [basic example](#basic-example) |
||||
and let `CMakeLists.txt` contain: |
||||
|
||||
```cmake |
||||
find_package(gRPC CONFIG REQUIRED) |
||||
|
||||
add_library(proto-objects OBJECT "${CMAKE_CURRENT_LIST_DIR}/proto/helloworld/helloworld.proto") |
||||
|
||||
target_link_libraries(proto-objects PUBLIC protobuf::libprotobuf gRPC::grpc++) |
||||
|
||||
set(PROTO_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated") |
||||
set(PROTO_IMPORT_DIRS "${CMAKE_CURRENT_LIST_DIR}/proto") |
||||
|
||||
target_include_directories(proto-objects PUBLIC "$<BUILD_INTERFACE:${PROTO_BINARY_DIR}>") |
||||
|
||||
protobuf_generate( |
||||
TARGET proto-objects |
||||
IMPORT_DIRS ${PROTO_IMPORT_DIRS} |
||||
PROTOC_OUT_DIR "${PROTO_BINARY_DIR}") |
||||
|
||||
protobuf_generate( |
||||
TARGET proto-objects |
||||
LANGUAGE grpc |
||||
GENERATE_EXTENSIONS .grpc.pb.h .grpc.pb.cc |
||||
PLUGIN "protoc-gen-grpc=\$<TARGET_FILE:gRPC::grpc_cpp_plugin>" |
||||
IMPORT_DIRS ${PROTO_IMPORT_DIRS} |
||||
PROTOC_OUT_DIR "${PROTO_BINARY_DIR}") |
||||
``` |
||||
|
||||
Then building `proto-objects` will generate and compile: |
||||
|
||||
- `${CMAKE_CURRENT_BINARY_DIR}/generated/helloworld/helloworld.pb.h` |
||||
- `${CMAKE_CURRENT_BINARY_DIR}/generated/helloworld/helloworld.pb.cc` |
||||
- `${CMAKE_CURRENT_BINARY_DIR}/generated/helloworld/helloworld.grpc.pb.h` |
||||
- `${CMAKE_CURRENT_BINARY_DIR}/generated/helloworld/helloworld.grpc.pb.cc` |
||||
|
||||
And `protoc` will automatically be re-run whenever the schema files change and `proto-objects` is built. |
||||
|
||||
### Note on unity builds |
||||
|
||||
Since protobuf's generated source files are unsuited for [jumbo/unity builds](https://cmake.org/cmake/help/latest/prop_tgt/UNITY_BUILD.html) it is recommended |
||||
to exclude them from such builds which can be achieved by adjusting their properties: |
||||
|
||||
```cmake |
||||
protobuf_generate( |
||||
OUT_VAR PROTO_GENERATED_FILES |
||||
...) |
||||
|
||||
set_source_files_properties(${PROTO_GENERATED_FILES} PROPERTIES SKIP_UNITY_BUILD_INCLUSION on) |
||||
``` |
||||
|
||||
## How it works |
||||
|
||||
For each source file ending in `proto` of the argument provided to `TARGET` or each file provided through `PROTOS`, `protobuf_generate` will set up |
||||
a [add_custom_command](https://cmake.org/cmake/help/latest/command/add_custom_command.html) which depends on `protobuf::protoc` and the proto files. |
||||
It declares the generated source files as `OUTPUT` which means that any target that depends on them will automatically cause the custom command to execute |
||||
when it is brought up to date. The command itself is made up of the arguments for `protoc`, like the output directory, the schema files, the language to |
||||
generate for, the plugins to use, etc. |
||||
|
||||
## Reference |
||||
|
||||
Arguments accepted by `protobuf_generate`. |
||||
|
||||
Flag arguments: |
||||
|
||||
- `APPEND_PATH` — A flag that causes the base path of all proto schema files to be added to `IMPORT_DIRS`. |
||||
|
||||
Single-value arguments: |
||||
|
||||
- `LANGUAGE` — A single value: cpp or python. Determines what kind of source files are being generated. |
||||
- `OUT_VAR` — Name of a CMake variable that will be filled with the paths to the generated source files. |
||||
- `EXPORT_MACRO` — Name of a macro that is applied to all generated Protobuf message classes and extern variables. It can, for example, be used to declare DLL exports. |
||||
- `PROTOC_OUT_DIR` — Output directory of generated source files. Defaults to `CMAKE_CURRENT_BINARY_DIR`. |
||||
- `PLUGIN` — An optional plugin executable. This could, for example, be the path to `grpc_cpp_plugin`. |
||||
- `PLUGIN_OPTIONS` — Additional options provided to the plugin, such as `generate_mock_code=true` for the gRPC cpp plugin. |
||||
- `DEPENDENCIES` — Arguments forwarded to the `DEPENDS` of the underlying `add_custom_command` invocation. |
||||
- `TARGET` — CMake target that will have the generated files added as sources. |
||||
|
||||
Multi-value arguments: |
||||
|
||||
- `PROTOS` — List of proto schema files. If omitted, then every source file ending in *proto* of `TARGET` will be used. |
||||
- `IMPORT_DIRS` — A common parent directory for the schema files. For example, if the schema file is `proto/helloworld/helloworld.proto` and the import directory `proto/` then the generated files are `${PROTOC_OUT_DIR}/helloworld/helloworld.pb.h` and `${PROTOC_OUT_DIR}/helloworld/helloworld.pb.cc`. |
||||
- `GENERATE_EXTENSIONS` — If LANGUAGE is omitted then this must be set to the extensions that protoc generates. |
||||
- `PROTOC_OPTIONS` — Additional arguments that are forwarded to protoc. |
@ -0,0 +1,42 @@ |
||||
# Publish pre-compiled protoc artifacts |
||||
``protoc`` is the compiler for ``.proto`` files. It generates language bindings |
||||
for the messages and/or RPC services from ``.proto`` files. |
||||
|
||||
Because ``protoc`` is a native executable, the scripts under this directory |
||||
publish a ``protoc`` executable (a.k.a. artifact) to Maven repositories. The |
||||
artifact can be used by build automation tools so that users would not need to |
||||
compile and install ``protoc`` for their systems. |
||||
|
||||
If you would like us to publish protoc artifact for a new platform, please |
||||
open an issue to request it. |
||||
|
||||
## Maven Location |
||||
The published protoc artifacts are available on Maven here: |
||||
|
||||
https://repo.maven.apache.org/maven2/com/google/protobuf/protoc/ |
||||
|
||||
## Versioning |
||||
The version of the ``protoc`` artifact must be the same as the version of the |
||||
Protobuf project. |
||||
|
||||
## Artifact name |
||||
The name of a published ``protoc`` artifact is in the following format: |
||||
``protoc-<version>-<os>-<arch>.exe``, e.g., ``protoc-3.6.1-linux-x86_64.exe``. |
||||
|
||||
Note that artifacts for linux/macos also have the `.exe` suffix but they are |
||||
not windows binaries. |
||||
|
||||
## System requirement |
||||
Install [Apache Maven](http://maven.apache.org/) if you don't have it. |
||||
|
||||
The scripts only work under Unix-like environments, e.g., Linux, MacOSX, and |
||||
Cygwin or MinGW for Windows. Please see ``README.md`` of the Protobuf project |
||||
for how to set up the build environment. |
||||
|
||||
## Tested build environments |
||||
We have successfully built artifacts on the following environments: |
||||
- Linux x86_32 and x86_64: |
||||
- Centos 6.9 (within Docker 1.6.1) |
||||
- Ubuntu 14.04.5 64-bit |
||||
- Linux aarch_64: Cross compiled with `g++-aarch64-linux-gnu` on Ubuntu 14.04.5 64-bit |
||||
- Mac OS X x86_32 and x86_64: Mac OS X 10.9.5 |
@ -0,0 +1,18 @@ |
||||
# We run our staleness tests as release-type jobs only. They are not really |
||||
# part of the release process, but the release job type allows us to run the |
||||
# tests on a schedule only (not presubmit or postsubmit). |
||||
|
||||
# Location of the build script in repository |
||||
build_file: "protobuf/kokoro/linux/bazel.sh" |
||||
timeout_mins: 15 |
||||
|
||||
env_vars { |
||||
key: "BAZEL_TARGETS" |
||||
value: "//src:cmake_lists_staleness_test" |
||||
} |
||||
|
||||
action { |
||||
define_artifacts { |
||||
regex: "**/sponge_log.*" |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,3 +1,3 @@ |
||||
PROTOC_VERSION = '21.6' |
||||
PROTOBUF_JAVA_VERSION = '3.21.6' |
||||
PROTOBUF_PYTHON_VERSION = '4.21.6' |
||||
PROTOC_VERSION = "21.7" |
||||
PROTOBUF_JAVA_VERSION = "3.21.7" |
||||
PROTOBUF_PYTHON_VERSION = "4.21.7" |
||||
|
@ -0,0 +1,215 @@ |
||||
# Protocol Buffers - Google's data interchange format |
||||
# Copyright 2008 Google Inc. All rights reserved. |
||||
# https://developers.google.com/protocol-buffers/ |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Test use of numpy types with repeated and non-repeated scalar fields.""" |
||||
|
||||
import unittest |
||||
|
||||
import numpy as np |
||||
|
||||
from google.protobuf import unittest_pb2 |
||||
from google.protobuf.internal import testing_refleaks |
||||
|
||||
message = unittest_pb2.TestAllTypes() |
||||
np_float_scalar = np.float64(0.0) |
||||
np_1_float_array = np.zeros(shape=(1,), dtype=np.float64) |
||||
np_2_float_array = np.zeros(shape=(2,), dtype=np.float64) |
||||
np_11_float_array = np.zeros(shape=(1, 1), dtype=np.float64) |
||||
np_22_float_array = np.zeros(shape=(2, 2), dtype=np.float64) |
||||
|
||||
np_int_scalar = np.int64(0) |
||||
np_1_int_array = np.zeros(shape=(1,), dtype=np.int64) |
||||
np_2_int_array = np.zeros(shape=(2,), dtype=np.int64) |
||||
np_11_int_array = np.zeros(shape=(1, 1), dtype=np.int64) |
||||
np_22_int_array = np.zeros(shape=(2, 2), dtype=np.int64) |
||||
|
||||
np_uint_scalar = np.uint64(0) |
||||
np_1_uint_array = np.zeros(shape=(1,), dtype=np.uint64) |
||||
np_2_uint_array = np.zeros(shape=(2,), dtype=np.uint64) |
||||
np_11_uint_array = np.zeros(shape=(1, 1), dtype=np.uint64) |
||||
np_22_uint_array = np.zeros(shape=(2, 2), dtype=np.uint64) |
||||
|
||||
np_bool_scalar = np.bool_(False) |
||||
np_1_bool_array = np.zeros(shape=(1,), dtype=np.bool_) |
||||
np_2_bool_array = np.zeros(shape=(2,), dtype=np.bool_) |
||||
np_11_bool_array = np.zeros(shape=(1, 1), dtype=np.bool_) |
||||
np_22_bool_array = np.zeros(shape=(2, 2), dtype=np.bool_) |
||||
|
||||
|
||||
@testing_refleaks.TestCase |
||||
class NumpyIntProtoTest(unittest.TestCase): |
||||
|
||||
# Assigning dim 1 ndarray of ints to repeated field should pass |
||||
def testNumpyDim1IntArrayToRepeated_IsValid(self): |
||||
message.repeated_int64[:] = np_1_int_array |
||||
message.repeated_int64[:] = np_2_int_array |
||||
|
||||
message.repeated_uint64[:] = np_1_uint_array |
||||
message.repeated_uint64[:] = np_2_uint_array |
||||
|
||||
# Assigning dim 2 ndarray of ints to repeated field should fail |
||||
def testNumpyDim2IntArrayToRepeated_RaisesTypeError(self): |
||||
with self.assertRaises(TypeError): |
||||
message.repeated_int64[:] = np_11_int_array |
||||
with self.assertRaises(TypeError): |
||||
message.repeated_int64[:] = np_22_int_array |
||||
|
||||
with self.assertRaises(TypeError): |
||||
message.repeated_uint64[:] = np_11_uint_array |
||||
with self.assertRaises(TypeError): |
||||
message.repeated_uint64[:] = np_22_uint_array |
||||
|
||||
# Assigning any ndarray of floats to repeated int field should fail |
||||
def testNumpyFloatArrayToRepeated_RaisesTypeError(self): |
||||
with self.assertRaises(TypeError): |
||||
message.repeated_int64[:] = np_1_float_array |
||||
with self.assertRaises(TypeError): |
||||
message.repeated_int64[:] = np_11_float_array |
||||
with self.assertRaises(TypeError): |
||||
message.repeated_int64[:] = np_22_float_array |
||||
|
||||
# Assigning any np int to scalar field should pass |
||||
def testNumpyIntScalarToScalar_IsValid(self): |
||||
message.optional_int64 = np_int_scalar |
||||
message.optional_uint64 = np_uint_scalar |
||||
|
||||
# Assigning any ndarray of ints to scalar field should fail |
||||
def testNumpyIntArrayToScalar_RaisesTypeError(self): |
||||
with self.assertRaises(TypeError): |
||||
message.optional_int64 = np_1_int_array |
||||
with self.assertRaises(TypeError): |
||||
message.optional_int64 = np_11_int_array |
||||
with self.assertRaises(TypeError): |
||||
message.optional_int64 = np_22_int_array |
||||
|
||||
with self.assertRaises(TypeError): |
||||
message.optional_uint64 = np_1_uint_array |
||||
with self.assertRaises(TypeError): |
||||
message.optional_uint64 = np_11_uint_array |
||||
with self.assertRaises(TypeError): |
||||
message.optional_uint64 = np_22_uint_array |
||||
|
||||
# Assigning any ndarray of floats to scalar field should fail |
||||
def testNumpyFloatArrayToScalar_RaisesTypeError(self): |
||||
with self.assertRaises(TypeError): |
||||
message.optional_int64 = np_1_float_array |
||||
with self.assertRaises(TypeError): |
||||
message.optional_int64 = np_11_float_array |
||||
with self.assertRaises(TypeError): |
||||
message.optional_int64 = np_22_float_array |
||||
|
||||
|
||||
@testing_refleaks.TestCase |
||||
class NumpyFloatProtoTest(unittest.TestCase): |
||||
|
||||
# Assigning dim 1 ndarray of floats to repeated field should pass |
||||
def testNumpyDim1FloatArrayToRepeated_IsValid(self): |
||||
message.repeated_float[:] = np_1_float_array |
||||
message.repeated_float[:] = np_2_float_array |
||||
|
||||
# Assigning dim 2 ndarray of floats to repeated field should fail |
||||
def testNumpyDim2FloatArrayToRepeated_RaisesTypeError(self): |
||||
with self.assertRaises(TypeError): |
||||
message.repeated_float[:] = np_11_float_array |
||||
with self.assertRaises(TypeError): |
||||
message.repeated_float[:] = np_22_float_array |
||||
|
||||
# Assigning any np float to scalar field should pass |
||||
def testNumpyFloatScalarToScalar_IsValid(self): |
||||
message.optional_float = np_float_scalar |
||||
|
||||
# Assigning any ndarray of float to scalar field should fail |
||||
def testNumpyFloatArrayToScalar_RaisesTypeError(self): |
||||
with self.assertRaises(TypeError): |
||||
message.optional_float = np_1_float_array |
||||
with self.assertRaises(TypeError): |
||||
message.optional_float = np_11_float_array |
||||
with self.assertRaises(TypeError): |
||||
message.optional_float = np_22_float_array |
||||
|
||||
|
||||
@testing_refleaks.TestCase |
||||
class NumpyBoolProtoTest(unittest.TestCase): |
||||
|
||||
# Assigning dim 1 ndarray of bool to repeated field should pass |
||||
def testNumpyDim1BoolArrayToRepeated_IsValid(self): |
||||
message.repeated_bool[:] = np_1_bool_array |
||||
message.repeated_bool[:] = np_2_bool_array |
||||
|
||||
# Assigning dim 2 ndarray of bool to repeated field should fail |
||||
def testNumpyDim2BoolArrayToRepeated_RaisesTypeError(self): |
||||
with self.assertRaises(TypeError): |
||||
message.repeated_bool[:] = np_11_bool_array |
||||
with self.assertRaises(TypeError): |
||||
message.repeated_bool[:] = np_22_bool_array |
||||
|
||||
# Assigning any np bool to scalar field should pass |
||||
def testNumpyBoolScalarToScalar_IsValid(self): |
||||
message.optional_bool = np_bool_scalar |
||||
|
||||
# Assigning any ndarray of bool to scalar field should fail |
||||
def testNumpyBoolArrayToScalar_RaisesTypeError(self): |
||||
with self.assertRaises(TypeError): |
||||
message.optional_bool = np_1_bool_array |
||||
with self.assertRaises(TypeError): |
||||
message.optional_bool = np_11_bool_array |
||||
with self.assertRaises(TypeError): |
||||
message.optional_bool = np_22_bool_array |
||||
|
||||
|
||||
@testing_refleaks.TestCase |
||||
class NumpyProtoIndexingTest(unittest.TestCase): |
||||
|
||||
def testNumpyIntScalarIndexing_Passes(self): |
||||
data = unittest_pb2.TestAllTypes(repeated_int64=[0, 1, 2]) |
||||
self.assertEqual(0, data.repeated_int64[np.int64(0)]) |
||||
|
||||
def testNumpyNegative1IntScalarIndexing_Passes(self): |
||||
data = unittest_pb2.TestAllTypes(repeated_int64=[0, 1, 2]) |
||||
self.assertEqual(2, data.repeated_int64[np.int64(-1)]) |
||||
|
||||
def testNumpyFloatScalarIndexing_Fails(self): |
||||
data = unittest_pb2.TestAllTypes(repeated_int64=[0, 1, 2]) |
||||
with self.assertRaises(TypeError): |
||||
_ = data.repeated_int64[np.float64(0.0)] |
||||
|
||||
def testNumpyIntArrayIndexing_Fails(self): |
||||
data = unittest_pb2.TestAllTypes(repeated_int64=[0, 1, 2]) |
||||
with self.assertRaises(TypeError): |
||||
_ = data.repeated_int64[np.array([0])] |
||||
with self.assertRaises(TypeError): |
||||
_ = data.repeated_int64[np.ndarray((1,), buffer=np.array([0]), dtype=int)] |
||||
with self.assertRaises(TypeError): |
||||
_ = data.repeated_int64[np.ndarray((1, 1), |
||||
buffer=np.array([0]), |
||||
dtype=int)] |
||||
|
||||
if __name__ == '__main__': |
||||
unittest.main() |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,155 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// This file provides alignment utilities for use in arenas.
|
||||
//
|
||||
// `ArenaAlign` constains a single `align` data member and provides
|
||||
// the below functions which operate on the given alignment.
|
||||
//
|
||||
// Ceil(size_t n) - rounds `n` up to the nearest `align` boundary.
|
||||
// Floor(size_t n) - rounds `n` down to the nearest `align` boundary.
|
||||
// Ceil(T* P) - rounds `p` up to the nearest `align` boundary.
|
||||
// IsAligned(size_t n) - returns true if `n` is aligned to `align`
|
||||
// IsAligned(T* p) - returns true if `p` is aligned to `align`
|
||||
// CheckAligned(T* p) - returns `p`. Checks alignment of `p` in debug.
|
||||
//
|
||||
// Additionally there is an optimized `CeilDefaultAligned(T*)` method which is
|
||||
// equivalent to `Ceil(ArenaAlignDefault().CheckAlign(p))` but more efficiently
|
||||
// implemented as a 'check only' for ArenaAlignDefault.
|
||||
//
|
||||
// These classes allow for generic arena logic using 'alignment policies'.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// template <Align>
|
||||
// void* NaiveAlloc(size_t n, Align align) {
|
||||
// align.CheckAligned(n);
|
||||
// uint8_t* ptr = align.CeilDefaultAligned(ptr_);
|
||||
// ptr_ += n;
|
||||
// return ptr;
|
||||
// }
|
||||
//
|
||||
// void CallSites() {
|
||||
// void *p1 = NaiveAlloc(n, ArenaAlignDefault());
|
||||
// void *p2 = NaiveAlloc(n, ArenaAlignAs(32));
|
||||
// }
|
||||
//
|
||||
#ifndef GOOGLE_PROTOBUF_ARENA_ALIGN_H__ |
||||
#define GOOGLE_PROTOBUF_ARENA_ALIGN_H__ |
||||
|
||||
#include <cstddef> |
||||
#include <cstdint> |
||||
|
||||
#include "google/protobuf/stubs/logging.h" |
||||
#include "google/protobuf/stubs/common.h" |
||||
#include "absl/numeric/bits.h" |
||||
|
||||
namespace google { |
||||
namespace protobuf { |
||||
namespace internal { |
||||
|
||||
struct ArenaAlignDefault { |
||||
static constexpr size_t align = 8; // NOLINT
|
||||
|
||||
static constexpr bool IsAligned(size_t n) { return (n & (align - 1)) == 0; } |
||||
|
||||
template <typename T> |
||||
static bool IsAligned(T* ptr) { |
||||
return (reinterpret_cast<uintptr_t>(ptr) & (align - 1)) == 0; |
||||
} |
||||
|
||||
static constexpr size_t Ceil(size_t n) { return (n + align - 1) & -align; } |
||||
static constexpr size_t Floor(size_t n) { return (n & ~(align - 1)); } |
||||
|
||||
template <typename T> |
||||
T* Ceil(T* ptr) const { |
||||
uintptr_t intptr = reinterpret_cast<uintptr_t>(ptr); |
||||
return reinterpret_cast<T*>((intptr + align - 1) & -align); |
||||
} |
||||
|
||||
template <typename T> |
||||
T* CeilDefaultAligned(T* ptr) const { |
||||
return ArenaAlignDefault().CheckAligned(ptr); |
||||
} |
||||
|
||||
// Address sanitizer enabled alignment check
|
||||
template <typename T> |
||||
static T* CheckAligned(T* ptr) { |
||||
GOOGLE_DCHECK(IsAligned(ptr)) << static_cast<void*>(ptr); |
||||
return ptr; |
||||
} |
||||
}; |
||||
|
||||
struct ArenaAlign { |
||||
static constexpr bool IsDefault() { return false; }; |
||||
|
||||
size_t align = 8; |
||||
|
||||
constexpr bool IsAligned(size_t n) const { return (n & (align - 1)) == 0; } |
||||
|
||||
template <typename T> |
||||
bool IsAligned(T* ptr) const { |
||||
return (reinterpret_cast<uintptr_t>(ptr) & (align - 1)) == 0; |
||||
} |
||||
|
||||
constexpr size_t Ceil(size_t n) const { return (n + align - 1) & -align; } |
||||
constexpr size_t Floor(size_t n) const { return (n & ~(align - 1)); } |
||||
|
||||
template <typename T> |
||||
T* Ceil(T* ptr) const { |
||||
uintptr_t intptr = reinterpret_cast<uintptr_t>(ptr); |
||||
return reinterpret_cast<T*>((intptr + align - 1) & -align); |
||||
} |
||||
|
||||
template <typename T> |
||||
T* CeilDefaultAligned(T* ptr) const { |
||||
return Ceil(ArenaAlignDefault().CheckAligned(ptr)); |
||||
} |
||||
|
||||
// Address sanitizer enabled alignment check
|
||||
template <typename T> |
||||
T* CheckAligned(T* ptr) const { |
||||
GOOGLE_DCHECK(IsAligned(ptr)) << static_cast<void*>(ptr); |
||||
return ptr; |
||||
} |
||||
}; |
||||
|
||||
inline ArenaAlign ArenaAlignAs(size_t align) { |
||||
// align must be a non zero power of 2 >= 8
|
||||
GOOGLE_DCHECK_NE(align, 0); |
||||
GOOGLE_DCHECK(absl::has_single_bit(align)) << "Invalid alignment " << align; |
||||
return ArenaAlign{align}; |
||||
} |
||||
|
||||
} // namespace internal
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
||||
|
||||
#endif // GOOGLE_PROTOBUF_ARENA_ALIGN_H__
|
@ -0,0 +1,215 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "google/protobuf/arena_align.h" |
||||
|
||||
#include <gmock/gmock.h> |
||||
#include <gtest/gtest.h> |
||||
|
||||
namespace google { |
||||
namespace protobuf { |
||||
namespace internal { |
||||
namespace { |
||||
|
||||
using ::testing::Eq; |
||||
|
||||
TEST(ArenaAlignDefault, Align) { |
||||
auto align_default = ArenaAlignDefault(); |
||||
EXPECT_THAT(align_default.align, Eq(8)); |
||||
} |
||||
|
||||
TEST(ArenaAlignDefault, Floor) { |
||||
auto align_default = ArenaAlignDefault(); |
||||
EXPECT_THAT(align_default.Floor(0), Eq(0)); |
||||
EXPECT_THAT(align_default.Floor(1), Eq(0)); |
||||
EXPECT_THAT(align_default.Floor(7), Eq(0)); |
||||
EXPECT_THAT(align_default.Floor(8), Eq(8)); |
||||
EXPECT_THAT(align_default.Floor(9), Eq(8)); |
||||
EXPECT_THAT(align_default.Floor(15), Eq(8)); |
||||
EXPECT_THAT(align_default.Floor(16), Eq(16)); |
||||
} |
||||
|
||||
TEST(ArenaAlignDefault, Ceil) { |
||||
auto align_default = ArenaAlignDefault(); |
||||
EXPECT_THAT(align_default.Ceil(0), Eq(0)); |
||||
EXPECT_THAT(align_default.Ceil(1), Eq(8)); |
||||
EXPECT_THAT(align_default.Ceil(7), Eq(8)); |
||||
EXPECT_THAT(align_default.Ceil(8), Eq(8)); |
||||
EXPECT_THAT(align_default.Ceil(9), Eq(16)); |
||||
EXPECT_THAT(align_default.Ceil(15), Eq(16)); |
||||
EXPECT_THAT(align_default.Ceil(16), Eq(16)); |
||||
} |
||||
|
||||
TEST(ArenaAlignDefault, CeilPtr) { |
||||
char p[17] = {0}; |
||||
auto align_default = ArenaAlignDefault(); |
||||
EXPECT_THAT(align_default.Ceil(p + 0), Eq(p + 0)); |
||||
EXPECT_THAT(align_default.Ceil(p + 1), Eq(p + 8)); |
||||
EXPECT_THAT(align_default.Ceil(p + 7), Eq(p + 8)); |
||||
EXPECT_THAT(align_default.Ceil(p + 8), Eq(p + 8)); |
||||
EXPECT_THAT(align_default.Ceil(p + 9), Eq(p + 16)); |
||||
EXPECT_THAT(align_default.Ceil(p + 15), Eq(p + 16)); |
||||
EXPECT_THAT(align_default.Ceil(p + 16), Eq(p + 16)); |
||||
} |
||||
|
||||
TEST(ArenaAlignDefault, CheckAligned) { |
||||
char p[17] = {0}; |
||||
auto align_default = ArenaAlignDefault(); |
||||
EXPECT_THAT(align_default.CheckAligned(p + 0), Eq(p + 0)); |
||||
EXPECT_THAT(align_default.CheckAligned(p + 8), Eq(p + 8)); |
||||
EXPECT_THAT(align_default.CheckAligned(p + 16), Eq(p + 16)); |
||||
#ifdef PROTOBUF_HAS_DEATH_TEST |
||||
EXPECT_DEBUG_DEATH(align_default.CheckAligned(p + 1), ".*"); |
||||
EXPECT_DEBUG_DEATH(align_default.CheckAligned(p + 7), ".*"); |
||||
EXPECT_DEBUG_DEATH(align_default.CheckAligned(p + 9), ".*"); |
||||
EXPECT_DEBUG_DEATH(align_default.CheckAligned(p + 15), ".*"); |
||||
EXPECT_DEBUG_DEATH(align_default.CheckAligned(p + 17), ".*"); |
||||
#endif // PROTOBUF_HAS_DEATH_TEST
|
||||
} |
||||
|
||||
TEST(ArenaAlignDefault, CeilDefaultAligned) { |
||||
char p[17] = {0}; |
||||
auto align_default = ArenaAlignDefault(); |
||||
EXPECT_THAT(align_default.CeilDefaultAligned(p + 0), Eq(p + 0)); |
||||
EXPECT_THAT(align_default.CeilDefaultAligned(p + 8), Eq(p + 8)); |
||||
EXPECT_THAT(align_default.CeilDefaultAligned(p + 16), Eq(p + 16)); |
||||
#ifdef PROTOBUF_HAS_DEATH_TEST |
||||
EXPECT_DEBUG_DEATH(align_default.CeilDefaultAligned(p + 1), ".*"); |
||||
EXPECT_DEBUG_DEATH(align_default.CeilDefaultAligned(p + 7), ".*"); |
||||
EXPECT_DEBUG_DEATH(align_default.CeilDefaultAligned(p + 9), ".*"); |
||||
EXPECT_DEBUG_DEATH(align_default.CeilDefaultAligned(p + 15), ".*"); |
||||
EXPECT_DEBUG_DEATH(align_default.CeilDefaultAligned(p + 17), ".*"); |
||||
#endif // PROTOBUF_HAS_DEATH_TEST
|
||||
} |
||||
|
||||
TEST(ArenaAlignDefault, IsAligned) { |
||||
auto align_default = ArenaAlignDefault(); |
||||
EXPECT_TRUE(align_default.IsAligned(0)); |
||||
EXPECT_FALSE(align_default.IsAligned(1)); |
||||
EXPECT_FALSE(align_default.IsAligned(7)); |
||||
EXPECT_TRUE(align_default.IsAligned(8)); |
||||
EXPECT_FALSE(align_default.IsAligned(9)); |
||||
EXPECT_FALSE(align_default.IsAligned(15)); |
||||
EXPECT_TRUE(align_default.IsAligned(16)); |
||||
} |
||||
|
||||
TEST(ArenaAlign, Align) { |
||||
auto align_64 = ArenaAlignAs(64); |
||||
EXPECT_THAT(align_64.align, Eq(64)); |
||||
} |
||||
|
||||
TEST(ArenaAlign, Floor) { |
||||
auto align_64 = ArenaAlignAs(64); |
||||
EXPECT_THAT(align_64.Floor(0), Eq(0)); |
||||
EXPECT_THAT(align_64.Floor(1), Eq(0)); |
||||
EXPECT_THAT(align_64.Floor(63), Eq(0)); |
||||
EXPECT_THAT(align_64.Floor(64), Eq(64)); |
||||
EXPECT_THAT(align_64.Floor(65), Eq(64)); |
||||
EXPECT_THAT(align_64.Floor(127), Eq(64)); |
||||
EXPECT_THAT(align_64.Floor(128), Eq(128)); |
||||
} |
||||
|
||||
TEST(ArenaAlign, Ceil) { |
||||
auto align_64 = ArenaAlignAs(64); |
||||
EXPECT_THAT(align_64.Ceil(0), Eq(0)); |
||||
EXPECT_THAT(align_64.Ceil(1), Eq(64)); |
||||
EXPECT_THAT(align_64.Ceil(63), Eq(64)); |
||||
EXPECT_THAT(align_64.Ceil(64), Eq(64)); |
||||
EXPECT_THAT(align_64.Ceil(65), Eq(128)); |
||||
EXPECT_THAT(align_64.Ceil(127), Eq(128)); |
||||
EXPECT_THAT(align_64.Ceil(128), Eq(128)); |
||||
} |
||||
|
||||
TEST(ArenaAlign, CeilPtr) { |
||||
alignas(64) char p[129] = {0}; |
||||
auto align_64 = ArenaAlignAs(64); |
||||
EXPECT_THAT(align_64.Ceil(p + 0), Eq(p)); |
||||
EXPECT_THAT(align_64.Ceil(p + 1), Eq(p + 64)); |
||||
EXPECT_THAT(align_64.Ceil(p + 63), Eq(p + 64)); |
||||
EXPECT_THAT(align_64.Ceil(p + 64), Eq(p + 64)); |
||||
EXPECT_THAT(align_64.Ceil(p + 65), Eq(p + 128)); |
||||
EXPECT_THAT(align_64.Ceil(p + 127), Eq(p + 128)); |
||||
EXPECT_THAT(align_64.Ceil(p + 128), Eq(p + 128)); |
||||
} |
||||
|
||||
TEST(ArenaAlign, CheckAligned) { |
||||
alignas(128) char p[129] = {0}; |
||||
auto align_64 = ArenaAlignAs(64); |
||||
EXPECT_THAT(align_64.CheckAligned(p + 0), Eq(p)); |
||||
EXPECT_THAT(align_64.CheckAligned(p + 64), Eq(p + 64)); |
||||
EXPECT_THAT(align_64.CheckAligned(p + 128), Eq(p + 128)); |
||||
#ifdef PROTOBUF_HAS_DEATH_TEST |
||||
EXPECT_DEBUG_DEATH(align_64.CheckAligned(p + 1), ".*"); |
||||
EXPECT_DEBUG_DEATH(align_64.CheckAligned(p + 7), ".*"); |
||||
EXPECT_DEBUG_DEATH(align_64.CheckAligned(p + 8), ".*"); |
||||
EXPECT_DEBUG_DEATH(align_64.CheckAligned(p + 56), ".*"); |
||||
EXPECT_DEBUG_DEATH(align_64.CheckAligned(p + 63), ".*"); |
||||
EXPECT_DEBUG_DEATH(align_64.CheckAligned(p + 65), ".*"); |
||||
EXPECT_DEBUG_DEATH(align_64.CheckAligned(p + 72), ".*"); |
||||
EXPECT_DEBUG_DEATH(align_64.CheckAligned(p + 120), ".*"); |
||||
EXPECT_DEBUG_DEATH(align_64.CheckAligned(p + 129), ".*"); |
||||
#endif // PROTOBUF_HAS_DEATH_TEST
|
||||
} |
||||
|
||||
TEST(ArenaAlign, CeilDefaultAligned) { |
||||
alignas(128) char p[129] = {0}; |
||||
auto align_64 = ArenaAlignAs(64); |
||||
EXPECT_THAT(align_64.CeilDefaultAligned(p + 0), Eq(p)); |
||||
EXPECT_THAT(align_64.CeilDefaultAligned(p + 8), Eq(p + 64)); |
||||
EXPECT_THAT(align_64.CeilDefaultAligned(p + 56), Eq(p + 64)); |
||||
EXPECT_THAT(align_64.CeilDefaultAligned(p + 64), Eq(p + 64)); |
||||
EXPECT_THAT(align_64.CeilDefaultAligned(p + 72), Eq(p + 128)); |
||||
EXPECT_THAT(align_64.CeilDefaultAligned(p + 120), Eq(p + 128)); |
||||
EXPECT_THAT(align_64.CeilDefaultAligned(p + 128), Eq(p + 128)); |
||||
#ifdef PROTOBUF_HAS_DEATH_TEST |
||||
EXPECT_DEBUG_DEATH(align_64.CeilDefaultAligned(p + 1), ".*"); |
||||
EXPECT_DEBUG_DEATH(align_64.CeilDefaultAligned(p + 7), ".*"); |
||||
EXPECT_DEBUG_DEATH(align_64.CeilDefaultAligned(p + 63), ".*"); |
||||
EXPECT_DEBUG_DEATH(align_64.CeilDefaultAligned(p + 65), ".*"); |
||||
EXPECT_DEBUG_DEATH(align_64.CeilDefaultAligned(p + 127), ".*"); |
||||
EXPECT_DEBUG_DEATH(align_64.CeilDefaultAligned(p + 129), ".*"); |
||||
#endif // PROTOBUF_HAS_DEATH_TEST
|
||||
} |
||||
|
||||
TEST(ArenaAlign, IsAligned) { |
||||
auto align_64 = ArenaAlignAs(64); |
||||
EXPECT_TRUE(align_64.IsAligned(0)); |
||||
EXPECT_FALSE(align_64.IsAligned(1)); |
||||
EXPECT_FALSE(align_64.IsAligned(63)); |
||||
EXPECT_TRUE(align_64.IsAligned(64)); |
||||
EXPECT_FALSE(align_64.IsAligned(65)); |
||||
EXPECT_FALSE(align_64.IsAligned(127)); |
||||
EXPECT_TRUE(align_64.IsAligned(128)); |
||||
} |
||||
|
||||
} // namespace
|
||||
} // namespace internal
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
@ -0,0 +1,126 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef GOOGLE_PROTOBUF_ARENA_ALLOCATION_POLICY_H__ |
||||
#define GOOGLE_PROTOBUF_ARENA_ALLOCATION_POLICY_H__ |
||||
|
||||
#include <cstddef> |
||||
#include <cstdint> |
||||
|
||||
#include "google/protobuf/arena_config.h" |
||||
|
||||
namespace google { |
||||
namespace protobuf { |
||||
namespace internal { |
||||
|
||||
// `AllocationPolicy` defines `Arena` allocation policies. Applications can
|
||||
// customize the inital and maximum sizes for arena allocation, as well as set
|
||||
// custom allocation and deallocation functions. `AllocationPolicy` is for
|
||||
// protocol buffer internal use only, and typically created from a user facing
|
||||
// public configuration class such as `ArenaOptions`.
|
||||
struct AllocationPolicy { |
||||
static constexpr size_t kDefaultStartBlockSize = 256; |
||||
|
||||
size_t start_block_size = kDefaultStartBlockSize; |
||||
size_t max_block_size = GetDefaultArenaMaxBlockSize(); |
||||
|
||||
void* (*block_alloc)(size_t) = nullptr; |
||||
void (*block_dealloc)(void*, size_t) = nullptr; |
||||
|
||||
bool IsDefault() const { |
||||
return start_block_size == kDefaultStartBlockSize && |
||||
max_block_size == GetDefaultArenaMaxBlockSize() && |
||||
block_alloc == nullptr && block_dealloc == nullptr; |
||||
} |
||||
}; |
||||
|
||||
// Tagged pointer to an AllocationPolicy.
|
||||
class TaggedAllocationPolicyPtr { |
||||
public: |
||||
constexpr TaggedAllocationPolicyPtr() : policy_(0) {} |
||||
|
||||
explicit TaggedAllocationPolicyPtr(AllocationPolicy* policy) |
||||
: policy_(reinterpret_cast<uintptr_t>(policy)) {} |
||||
|
||||
void set_policy(AllocationPolicy* policy) { |
||||
auto bits = policy_ & kTagsMask; |
||||
policy_ = reinterpret_cast<uintptr_t>(policy) | bits; |
||||
} |
||||
|
||||
AllocationPolicy* get() { |
||||
return reinterpret_cast<AllocationPolicy*>(policy_ & kPtrMask); |
||||
} |
||||
const AllocationPolicy* get() const { |
||||
return reinterpret_cast<const AllocationPolicy*>(policy_ & kPtrMask); |
||||
} |
||||
|
||||
AllocationPolicy& operator*() { return *get(); } |
||||
const AllocationPolicy& operator*() const { return *get(); } |
||||
|
||||
AllocationPolicy* operator->() { return get(); } |
||||
const AllocationPolicy* operator->() const { return get(); } |
||||
|
||||
bool is_user_owned_initial_block() const { |
||||
return static_cast<bool>(get_mask<kUserOwnedInitialBlock>()); |
||||
} |
||||
void set_is_user_owned_initial_block(bool v) { |
||||
set_mask<kUserOwnedInitialBlock>(v); |
||||
} |
||||
|
||||
uintptr_t get_raw() const { return policy_; } |
||||
|
||||
private: |
||||
enum : uintptr_t { |
||||
kUserOwnedInitialBlock = 1, |
||||
}; |
||||
|
||||
static constexpr uintptr_t kTagsMask = 7; |
||||
static constexpr uintptr_t kPtrMask = ~kTagsMask; |
||||
|
||||
template <uintptr_t kMask> |
||||
uintptr_t get_mask() const { |
||||
return policy_ & kMask; |
||||
} |
||||
template <uintptr_t kMask> |
||||
void set_mask(bool v) { |
||||
if (v) { |
||||
policy_ |= kMask; |
||||
} else { |
||||
policy_ &= ~kMask; |
||||
} |
||||
} |
||||
uintptr_t policy_; |
||||
}; |
||||
|
||||
} // namespace internal
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
||||
|
||||
#endif // GOOGLE_PROTOBUF_ARENA_ALLOCATION_POLICY_H__
|
@ -0,0 +1,189 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef GOOGLE_PROTOBUF_ARENA_CLEANUP_H__ |
||||
#define GOOGLE_PROTOBUF_ARENA_CLEANUP_H__ |
||||
|
||||
#include <cstddef> |
||||
#include <cstdint> |
||||
#include <string> |
||||
|
||||
#include "google/protobuf/stubs/logging.h" |
||||
#include "google/protobuf/stubs/common.h" |
||||
#include "absl/base/attributes.h" |
||||
|
||||
|
||||
|
||||
namespace google { |
||||
namespace protobuf { |
||||
namespace internal { |
||||
namespace cleanup { |
||||
|
||||
// Helper function invoking the destructor of `object`
|
||||
template <typename T> |
||||
void arena_destruct_object(void* object) { |
||||
reinterpret_cast<T*>(object)->~T(); |
||||
} |
||||
|
||||
// Tag defines the type of cleanup / cleanup object. This tag is stored in the
|
||||
// lowest 2 bits of the `elem` value identifying the type of node. All node
|
||||
// types must start with a `uintptr_t` that stores `Tag` in its low two bits.
|
||||
enum class Tag : uintptr_t { |
||||
kDynamic = 0, // DynamicNode
|
||||
kString = 1, // StringNode (std::string)
|
||||
}; |
||||
|
||||
// DynamicNode contains the object (`elem`) that needs to be
|
||||
// destroyed, and the function to destroy it (`destructor`)
|
||||
// elem must be aligned at minimum on a 4 byte boundary.
|
||||
struct DynamicNode { |
||||
uintptr_t elem; |
||||
void (*destructor)(void*); |
||||
}; |
||||
|
||||
// StringNode contains a `std::string` object (`elem`) that needs to be
|
||||
// destroyed. The lowest 2 bits of `elem` contain the non-zero kString tag.
|
||||
struct StringNode { |
||||
uintptr_t elem; |
||||
}; |
||||
|
||||
|
||||
// EnableSpecializedTags() return true if the alignment of tagged objects
|
||||
// such as std::string allow us to poke tags in the 2 LSB bits.
|
||||
inline constexpr bool EnableSpecializedTags() { |
||||
// For now we require 2 bits
|
||||
return alignof(std::string) >= 8; |
||||
} |
||||
|
||||
// Adds a cleanup entry identified by `tag` at memory location `pos`.
|
||||
inline ABSL_ATTRIBUTE_ALWAYS_INLINE void CreateNode(Tag tag, void* pos, |
||||
const void* elem_raw, |
||||
void (*destructor)(void*)) { |
||||
auto elem = reinterpret_cast<uintptr_t>(elem_raw); |
||||
if (EnableSpecializedTags()) { |
||||
GOOGLE_DCHECK_EQ(elem & 3, 0ULL); // Must be aligned
|
||||
switch (tag) { |
||||
case Tag::kString: { |
||||
StringNode n = {elem | static_cast<uintptr_t>(Tag::kString)}; |
||||
memcpy(pos, &n, sizeof(n)); |
||||
return; |
||||
} |
||||
default: |
||||
break; |
||||
} |
||||
} |
||||
DynamicNode n = {elem, destructor}; |
||||
memcpy(pos, &n, sizeof(n)); |
||||
} |
||||
|
||||
// Optimization: performs a prefetch on `elem_address`.
|
||||
inline ABSL_ATTRIBUTE_ALWAYS_INLINE void PrefetchNode( |
||||
const void* elem_address) { |
||||
(void)elem_address; |
||||
} |
||||
|
||||
// Destroys the node idenitfied by `tag` stored at memory location `pos`.
|
||||
inline ABSL_ATTRIBUTE_ALWAYS_INLINE void DestroyNode(Tag tag, const void* pos) { |
||||
if (EnableSpecializedTags()) { |
||||
switch (tag) { |
||||
case Tag::kString: { |
||||
StringNode n; |
||||
memcpy(&n, pos, sizeof(n)); |
||||
auto* s = reinterpret_cast<std::string*>(n.elem & ~0x7ULL); |
||||
// Some compilers don't like fully qualified explicit dtor calls,
|
||||
// so use an alias to avoid having to type `::`.
|
||||
using string_type = std::string; |
||||
s->~string_type(); |
||||
return; |
||||
} |
||||
default: |
||||
break; |
||||
} |
||||
} |
||||
DynamicNode n; |
||||
memcpy(&n, pos, sizeof(n)); |
||||
n.destructor(reinterpret_cast<void*>(n.elem)); |
||||
} |
||||
|
||||
// Returns the `tag` identifying the type of object for `destructor` or
|
||||
// kDynamic if `destructor` does not identify a well know object type.
|
||||
inline ABSL_ATTRIBUTE_ALWAYS_INLINE Tag Type(void (*destructor)(void*)) { |
||||
if (EnableSpecializedTags()) { |
||||
if (destructor == &arena_destruct_object<std::string>) { |
||||
return Tag::kString; |
||||
} |
||||
} |
||||
return Tag::kDynamic; |
||||
} |
||||
|
||||
// Returns the `tag` identifying the type of object stored at memory location
|
||||
// `elem`, which represents the first uintptr_t value in the node.
|
||||
inline ABSL_ATTRIBUTE_ALWAYS_INLINE Tag Type(void* raw) { |
||||
if (!EnableSpecializedTags()) return Tag::kDynamic; |
||||
|
||||
uintptr_t elem; |
||||
memcpy(&elem, raw, sizeof(elem)); |
||||
switch (static_cast<Tag>(elem & 0x7ULL)) { |
||||
case Tag::kDynamic: |
||||
return Tag::kDynamic; |
||||
case Tag::kString: |
||||
return Tag::kString; |
||||
default: |
||||
GOOGLE_LOG(FATAL) << "Corrupted cleanup tag: " << (elem & 0x7ULL); |
||||
return Tag::kDynamic; |
||||
} |
||||
} |
||||
|
||||
// Returns the required size in bytes off the node type identified by `tag`.
|
||||
inline ABSL_ATTRIBUTE_ALWAYS_INLINE size_t Size(Tag tag) { |
||||
if (!EnableSpecializedTags()) return sizeof(DynamicNode); |
||||
|
||||
switch (tag) { |
||||
case Tag::kDynamic: |
||||
return sizeof(DynamicNode); |
||||
case Tag::kString: |
||||
return sizeof(StringNode); |
||||
default: |
||||
GOOGLE_LOG(FATAL) << "Corrupted cleanup tag: " << static_cast<int>(tag); |
||||
return sizeof(DynamicNode); |
||||
} |
||||
} |
||||
|
||||
// Returns the required size in bytes off the node type for `destructor`.
|
||||
inline ABSL_ATTRIBUTE_ALWAYS_INLINE size_t Size(void (*destructor)(void*)) { |
||||
return destructor == nullptr ? 0 : Size(Type(destructor)); |
||||
} |
||||
|
||||
} // namespace cleanup
|
||||
} // namespace internal
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
||||
|
||||
#endif // GOOGLE_PROTOBUF_ARENA_CLEANUP_H__
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue