summaryrefslogtreecommitdiffstats
path: root/src/fluent-bit/lib/cmetrics
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 12:08:03 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 12:08:18 +0000
commit5da14042f70711ea5cf66e034699730335462f66 (patch)
tree0f6354ccac934ed87a2d555f45be4c831cf92f4a /src/fluent-bit/lib/cmetrics
parentReleasing debian version 1.44.3-2. (diff)
downloadnetdata-5da14042f70711ea5cf66e034699730335462f66.tar.xz
netdata-5da14042f70711ea5cf66e034699730335462f66.zip
Merging upstream version 1.45.3+dfsg.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/fluent-bit/lib/cmetrics')
-rw-r--r--src/fluent-bit/lib/cmetrics/.github/actionlint-matcher.json17
-rw-r--r--src/fluent-bit/lib/cmetrics/.github/dependabot.yml12
-rw-r--r--src/fluent-bit/lib/cmetrics/.github/workflows/build.yaml144
-rw-r--r--src/fluent-bit/lib/cmetrics/.github/workflows/lint.yaml28
-rw-r--r--src/fluent-bit/lib/cmetrics/.github/workflows/packages.yaml108
-rw-r--r--src/fluent-bit/lib/cmetrics/.gitignore7
-rw-r--r--src/fluent-bit/lib/cmetrics/.gitmodules6
-rw-r--r--src/fluent-bit/lib/cmetrics/.mdlrc1
-rw-r--r--src/fluent-bit/lib/cmetrics/CMakeLists.txt415
-rw-r--r--src/fluent-bit/lib/cmetrics/LICENSE177
-rw-r--r--src/fluent-bit/lib/cmetrics/README.md22
-rw-r--r--src/fluent-bit/lib/cmetrics/appveyor.yml34
-rw-r--r--src/fluent-bit/lib/cmetrics/cmake/headers.cmake4
-rw-r--r--src/fluent-bit/lib/cmetrics/cmake/libraries.cmake3
-rw-r--r--src/fluent-bit/lib/cmetrics/cmake/macros.cmake10
-rw-r--r--src/fluent-bit/lib/cmetrics/cpack/description1
-rw-r--r--src/fluent-bit/lib/cmetrics/cpack/macos/welcome.txt.cmakein7
-rw-r--r--src/fluent-bit/lib/cmetrics/create-submoduled-tarball.sh23
-rw-r--r--src/fluent-bit/lib/cmetrics/include/CMakeLists.txt11
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmetrics.h82
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_atomic.h11
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_cat.h27
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_compat.h42
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_counter.h50
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_decode_msgpack.h64
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_decode_opentelemetry.h48
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_decode_prometheus.h112
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_encode_influx.h29
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_encode_msgpack.h31
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_encode_opentelemetry.h44
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_encode_prometheus.h29
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_encode_prometheus_remote_write.h59
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_encode_splunk_hec.h44
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_encode_text.h29
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_gauge.h51
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_histogram.h71
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_info.h.in28
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_label.h40
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_log.h71
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_map.h61
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_math.h52
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_metric.h79
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_mpack_utils.h46
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_mpack_utils_defs.h40
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_opts.h44
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_summary.h70
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_time.h28
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_untyped.h46
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_variant_utils.h626
-rw-r--r--src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_version.h.in36
-rw-r--r--src/fluent-bit/lib/cmetrics/include/prometheus_remote_write/protobuf-c.h1110
-rw-r--r--src/fluent-bit/lib/cmetrics/include/prometheus_remote_write/remote.pb-c.h304
-rw-r--r--src/fluent-bit/lib/cmetrics/include/prometheus_remote_write/types.pb-c.h441
-rw-r--r--src/fluent-bit/lib/cmetrics/lib/mpack/CMakeLists.txt31
-rw-r--r--src/fluent-bit/lib/cmetrics/lib/mpack/src/mpack/mpack.c6472
-rw-r--r--src/fluent-bit/lib/cmetrics/lib/mpack/src/mpack/mpack.h7203
-rw-r--r--src/fluent-bit/lib/cmetrics/markdownlint.rb16
-rwxr-xr-xsrc/fluent-bit/lib/cmetrics/scripts/win_build.bat8
-rw-r--r--src/fluent-bit/lib/cmetrics/src/CMakeLists.txt80
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmetrics.c147
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_atomic_clang.c42
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_atomic_gcc.c42
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_atomic_generic.c123
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_atomic_msvc.c131
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_cat.c454
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_counter.c180
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_decode_msgpack.c1474
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_decode_opentelemetry.c1289
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_decode_prometheus.c1160
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_decode_prometheus.l195
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_decode_prometheus.y151
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_encode_influx.c374
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_encode_msgpack.c552
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_encode_opentelemetry.c2592
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_encode_prometheus.c543
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_encode_prometheus_remote_write.c1131
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_encode_splunk_hec.c710
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_encode_text.c332
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_gauge.c198
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_histogram.c403
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_label.c100
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_log.c91
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_map.c318
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_metric.c142
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_metric_histogram.c192
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_mpack_utils.c286
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_opts.c118
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_summary.c306
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_time.c34
-rw-r--r--src/fluent-bit/lib/cmetrics/src/cmt_untyped.c140
-rw-r--r--src/fluent-bit/lib/cmetrics/src/external/LICENSE32
-rw-r--r--src/fluent-bit/lib/cmetrics/src/external/opentelemetry_common.pb-c.c624
-rw-r--r--src/fluent-bit/lib/cmetrics/src/external/opentelemetry_metrics.pb-c.c1996
-rw-r--r--src/fluent-bit/lib/cmetrics/src/external/opentelemetry_metrics_service.pb-c.c189
-rw-r--r--src/fluent-bit/lib/cmetrics/src/external/opentelemetry_resource.pb-c.c105
-rw-r--r--src/fluent-bit/lib/cmetrics/src/external/protobuf-c.c3667
-rw-r--r--src/fluent-bit/lib/cmetrics/src/external/remote.pb-c.c615
-rw-r--r--src/fluent-bit/lib/cmetrics/src/external/types.pb-c.c1092
-rw-r--r--src/fluent-bit/lib/cmetrics/tests/CMakeLists.txt51
-rw-r--r--src/fluent-bit/lib/cmetrics/tests/atomic_operations.c120
-rw-r--r--src/fluent-bit/lib/cmetrics/tests/basic.c130
-rw-r--r--src/fluent-bit/lib/cmetrics/tests/cat.c238
-rw-r--r--src/fluent-bit/lib/cmetrics/tests/cmt_tests.h35
-rw-r--r--src/fluent-bit/lib/cmetrics/tests/cmt_tests_config.h.in25
-rw-r--r--src/fluent-bit/lib/cmetrics/tests/counter.c264
-rw-r--r--src/fluent-bit/lib/cmetrics/tests/data/histogram_different_label_count.txt14
-rw-r--r--src/fluent-bit/lib/cmetrics/tests/data/issue_6534.txt165
-rw-r--r--src/fluent-bit/lib/cmetrics/tests/data/issue_71.txt11
-rw-r--r--src/fluent-bit/lib/cmetrics/tests/data/issue_fluent_bit_5541.txt19
-rw-r--r--src/fluent-bit/lib/cmetrics/tests/data/issue_fluent_bit_5894.txt141
-rw-r--r--src/fluent-bit/lib/cmetrics/tests/data/issue_fluent_bit_6021.txt221
-rw-r--r--src/fluent-bit/lib/cmetrics/tests/data/pr_168.txt22
-rw-r--r--src/fluent-bit/lib/cmetrics/tests/decoding.c184
-rw-r--r--src/fluent-bit/lib/cmetrics/tests/encode_output.c67
-rw-r--r--src/fluent-bit/lib/cmetrics/tests/encode_output.h28
-rw-r--r--src/fluent-bit/lib/cmetrics/tests/encoding.c1056
-rw-r--r--src/fluent-bit/lib/cmetrics/tests/gauge.c174
-rw-r--r--src/fluent-bit/lib/cmetrics/tests/histogram.c212
-rw-r--r--src/fluent-bit/lib/cmetrics/tests/issues.c95
-rw-r--r--src/fluent-bit/lib/cmetrics/tests/lib/acutest/acutest.h1794
-rw-r--r--src/fluent-bit/lib/cmetrics/tests/null_label.c167
-rw-r--r--src/fluent-bit/lib/cmetrics/tests/prometheus_lexer.c218
-rw-r--r--src/fluent-bit/lib/cmetrics/tests/prometheus_parser.c1701
-rw-r--r--src/fluent-bit/lib/cmetrics/tests/summary.c172
-rw-r--r--src/fluent-bit/lib/cmetrics/tests/untyped.c122
-rw-r--r--src/fluent-bit/lib/cmetrics/tests/util.c68
126 files changed, 48569 insertions, 0 deletions
diff --git a/src/fluent-bit/lib/cmetrics/.github/actionlint-matcher.json b/src/fluent-bit/lib/cmetrics/.github/actionlint-matcher.json
new file mode 100644
index 000000000..7b1ba251e
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/.github/actionlint-matcher.json
@@ -0,0 +1,17 @@
+{
+ "problemMatcher": [
+ {
+ "owner": "actionlint",
+ "pattern": [
+ {
+ "regexp": "^(?:\\x1b\\[\\d+m)?(.+?)(?:\\x1b\\[\\d+m)*:(?:\\x1b\\[\\d+m)*(\\d+)(?:\\x1b\\[\\d+m)*:(?:\\x1b\\[\\d+m)*(\\d+)(?:\\x1b\\[\\d+m)*: (?:\\x1b\\[\\d+m)*(.+?)(?:\\x1b\\[\\d+m)* \\[(.+?)\\]$",
+ "file": 1,
+ "line": 2,
+ "column": 3,
+ "message": 4,
+ "code": 5
+ }
+ ]
+ }
+ ]
+ } \ No newline at end of file
diff --git a/src/fluent-bit/lib/cmetrics/.github/dependabot.yml b/src/fluent-bit/lib/cmetrics/.github/dependabot.yml
new file mode 100644
index 000000000..12f9b4a0b
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/.github/dependabot.yml
@@ -0,0 +1,12 @@
+# To get started with Dependabot version updates, you'll need to specify which
+# package ecosystems to update and where the package manifests are located.
+# Please see the documentation for all configuration options:
+# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
+
+version: 2
+updates:
+ # Maintain dependencies for GitHub Actions
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "weekly" \ No newline at end of file
diff --git a/src/fluent-bit/lib/cmetrics/.github/workflows/build.yaml b/src/fluent-bit/lib/cmetrics/.github/workflows/build.yaml
new file mode 100644
index 000000000..88bfb3fa3
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/.github/workflows/build.yaml
@@ -0,0 +1,144 @@
+name: Build PR(s) and master branch.
+on:
+ workflow_dispatch:
+ push:
+ branches:
+ - master
+ pull_request:
+ branches:
+ - master
+ types: [opened, reopened, synchronize]
+jobs:
+ build-windows:
+ name: Build sources on amd64 for ${{ matrix.os }}
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [windows-latest, windows-2019]
+ permissions:
+ contents: read
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ submodules: true
+
+ - name: Build on ${{ matrix.os }} with vs-2019
+ run: |
+ .\scripts\win_build.bat
+ - name: Run unit tests.
+ run: |
+ ctest --rerun-failed --output-on-failure -C Debug --test-dir .\tests\
+
+ build-centos:
+ name: CentOS 7 build to confirm no issues once used downstream
+ runs-on: ubuntu-latest
+ container: centos:7
+ permissions:
+ contents: read
+ steps:
+ - name: Set up base image dependencies
+ run: |
+ yum -y update && \
+ yum install -y ca-certificates cmake gcc gcc-c++ git make wget && \
+ wget http://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm && \
+ rpm -ivh epel-release-latest-7.noarch.rpm && \
+ yum install -y cmake3
+ shell: bash
+
+ - name: Clone repo with submodules (1.8.3 version of Git)
+ run: |
+ git clone --recursive https://github.com/calyptia/cmetrics.git
+ shell: bash
+
+ - name: Run compilation
+ run: |
+ cmake3 -DCMT_TESTS=on -DCMT_DEV=on .
+ make
+ shell: bash
+ working-directory: cmetrics
+
+ build-unix-arm64:
+ name: Build sources on arm64 for ${{ matrix.os }} - ${{ matrix.compiler }}
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest]
+ compiler: [ gcc, clang ]
+ permissions:
+ contents: read
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ submodules: true
+
+ - name: Build on ${{ matrix.os }} with ${{ matrix.compiler }}
+ uses: uraimo/run-on-arch-action@v2.3.0
+ with:
+ arch: aarch64
+ distro: ubuntu20.04
+ run: |
+ apt-get update && \
+ apt-get install -y --no-install-recommends \
+ build-essential \
+ cmake \
+ file \
+ make
+ export CC=${{ env.compiler }}
+ cmake -DCMT_TESTS=On .
+ make all
+ CTEST_OUTPUT_ON_FAILURE=1 make test
+ env:
+ CC: ${{ matrix.compiler }}
+
+ build-unix-amd64:
+ name: Build sources on amd64 for ${{ matrix.os }} - ${{ matrix.compiler }}
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, macos-latest]
+ compiler: [ gcc, clang ]
+ permissions:
+ contents: read
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ submodules: true
+
+ - name: Build on ${{ matrix.os }} with ${{ matrix.compiler }}
+ run: |
+ echo "CC = $CC, CXX = $CXX"
+ cmake -DCMT_TESTS=On .
+ make all
+ CTEST_OUTPUT_ON_FAILURE=1 make test
+ env:
+ CC: ${{ matrix.compiler }}
+
+ build-analysis-tests:
+ name: Build with various code analysis tools
+ strategy:
+ fail-fast: false
+ matrix:
+ preset:
+ - clang-sanitize-address
+ - clang-sanitize-memory
+ - clang-sanitize-undefined
+ - clang-sanitize-dataflow
+ - clang-sanitize-safe-stack
+ - valgrind
+ permissions:
+ contents: read
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ submodules: true
+
+ - uses: docker://lpenz/ghaction-cmake:0.19
+ with:
+ preset: ${{ matrix.preset }}
+ # dependencies_debian: ''
+ cmakeflags: '-DCMT_TESTS=On -DCMT_DEV=on .'
+ build_command: make all
diff --git a/src/fluent-bit/lib/cmetrics/.github/workflows/lint.yaml b/src/fluent-bit/lib/cmetrics/.github/workflows/lint.yaml
new file mode 100644
index 000000000..75cdd122e
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/.github/workflows/lint.yaml
@@ -0,0 +1,28 @@
+name: Lint PRs
+on:
+ pull_request:
+ workflow_dispatch:
+
+jobs:
+ shellcheck:
+ runs-on: ubuntu-latest
+ name: Shellcheck
+ permissions:
+ contents: read
+ steps:
+ - uses: actions/checkout@v3
+ - uses: ludeeus/action-shellcheck@master
+
+ actionlint:
+ runs-on: ubuntu-latest
+ name: Actionlint
+ permissions:
+ contents: read
+ steps:
+ - uses: actions/checkout@v3
+ - run: |
+ echo "::add-matcher::.github/actionlint-matcher.json"
+ bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)
+ ./actionlint -color -shellcheck=
+ shell: bash
+
diff --git a/src/fluent-bit/lib/cmetrics/.github/workflows/packages.yaml b/src/fluent-bit/lib/cmetrics/.github/workflows/packages.yaml
new file mode 100644
index 000000000..41e913ec8
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/.github/workflows/packages.yaml
@@ -0,0 +1,108 @@
+name: Build packages for master or a tagged release
+
+on:
+ push:
+ branches:
+ - master
+ # Any tag starting with 'v'
+ tags:
+ - 'v*'
+ workflow_dispatch:
+
+jobs:
+ build-distro-packages-arm64:
+ runs-on: ubuntu-latest
+ name: build arm64 packages
+ strategy:
+ fail-fast: true
+ matrix:
+ format: [ rpm, deb ]
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ submodules: true
+
+ - uses: uraimo/run-on-arch-action@v2.3.0
+ name: Build the ${{matrix.format}} packages
+ with:
+ arch: aarch64
+ distro: ubuntu20.04
+ run: |
+ apt-get update && \
+ apt-get install -y --no-install-recommends \
+ build-essential \
+ cmake \
+ file \
+ rpm \
+ make
+ cmake .
+ echo ${{ matrix.format }} | awk '{print toupper($0)}' | xargs -I{} cpack -G {}
+
+ - name: Store the master package artifacts
+ uses: actions/upload-artifact@v3
+ with:
+ name: ${{ matrix.format }}-arm64
+ path: |
+ ./*.${{matrix.format}}
+
+ build-distro-packages-amd64:
+ name: build amd64 packages
+ strategy:
+ fail-fast: true
+ matrix:
+ format: [ rpm, deb ]
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ submodules: true
+
+ - name: Build the ${{matrix.format}} packages
+ run: |
+ cmake .
+ echo ${{ matrix.format }} | awk '{print toupper($0)}' | xargs -I{} cpack -G {}
+
+ - name: Store the master package artifacts
+ uses: actions/upload-artifact@v3
+ with:
+ name: ${{ matrix.format }}-amd64
+ path: |
+ ./*.${{matrix.format}}
+
+ release:
+ name: Create release and upload packages
+ needs:
+ - build-distro-packages-amd64
+ - build-distro-packages-arm64
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ steps:
+ - name: Download all artefacts
+ uses: actions/download-artifact@v3
+ with:
+ path: artifacts/
+
+ - name: Display structure of downloaded files
+ run: ls -R
+ working-directory: artifacts
+ shell: bash
+
+ - name: Unstable release on push to master to make it easier to download
+ uses: pyTooling/Actions/releaser@r0
+ continue-on-error: true
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ tag: 'unstable'
+ rm: true
+ files: |
+ artifacts/**/*
+
+ - name: Release on tag
+ uses: softprops/action-gh-release@v1
+ if: startsWith(github.ref, 'refs/tags/')
+ with:
+ generate_release_notes: true
+ draft: false
+ files: |
+ artifacts/**/*
diff --git a/src/fluent-bit/lib/cmetrics/.gitignore b/src/fluent-bit/lib/cmetrics/.gitignore
new file mode 100644
index 000000000..89ea35d6e
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/.gitignore
@@ -0,0 +1,7 @@
+*~
+build/*
+include/cmetrics/cmt_info.h
+include/cmetrics/cmt_version.h
+LICENSE.txt
+README.html
+welcome.txt \ No newline at end of file
diff --git a/src/fluent-bit/lib/cmetrics/.gitmodules b/src/fluent-bit/lib/cmetrics/.gitmodules
new file mode 100644
index 000000000..3ae54b77c
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "lib/cfl"]
+ path = lib/cfl
+ url = https://github.com/fluent/cfl
+[submodule "lib/fluent-otel-proto"]
+ path = lib/fluent-otel-proto
+ url = https://github.com/fluent/fluent-otel-proto
diff --git a/src/fluent-bit/lib/cmetrics/.mdlrc b/src/fluent-bit/lib/cmetrics/.mdlrc
new file mode 100644
index 000000000..406517849
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/.mdlrc
@@ -0,0 +1 @@
+style "#{File.dirname(__FILE__)}/markdownlint.rb" \ No newline at end of file
diff --git a/src/fluent-bit/lib/cmetrics/CMakeLists.txt b/src/fluent-bit/lib/cmetrics/CMakeLists.txt
new file mode 100644
index 000000000..38a86e5f4
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/CMakeLists.txt
@@ -0,0 +1,415 @@
+cmake_minimum_required(VERSION 3.0)
+project(cmetrics C)
+set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+
+# CMetrics Version
+set(CMT_VERSION_MAJOR 0)
+set(CMT_VERSION_MINOR 6)
+set(CMT_VERSION_PATCH 3)
+set(CMT_VERSION_STR "${CMT_VERSION_MAJOR}.${CMT_VERSION_MINOR}.${CMT_VERSION_PATCH}")
+
+# Include helpers
+include(cmake/macros.cmake)
+include(CheckCSourceCompiles)
+include(GNUInstallDirs)
+
+# On macOS, search Homebrew for keg-only versions of Bison and Flex. Xcode does
+# not provide new enough versions for us to use.
+if (CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin")
+ execute_process(
+ COMMAND brew --prefix bison
+ RESULT_VARIABLE BREW_BISON
+ OUTPUT_VARIABLE BREW_BISON_PREFIX
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+ if (BREW_BISON EQUAL 0 AND EXISTS "${BREW_BISON_PREFIX}")
+ message(STATUS "Found Bison keg installed by Homebrew at ${BREW_BISON_PREFIX}")
+ set(BISON_EXECUTABLE "${BREW_BISON_PREFIX}/bin/bison")
+ endif()
+
+ execute_process(
+ COMMAND brew --prefix flex
+ RESULT_VARIABLE BREW_FLEX
+ OUTPUT_VARIABLE BREW_FLEX_PREFIX
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+ if (BREW_FLEX EQUAL 0 AND EXISTS "${BREW_FLEX_PREFIX}")
+ message(STATUS "Found Flex keg installed by Homebrew at ${BREW_FLEX_PREFIX}")
+ set(FLEX_EXECUTABLE "${BREW_FLEX_PREFIX}/bin/flex")
+ endif()
+endif()
+
+# Define macro to identify Windows system (without Cygwin)
+if(CMAKE_SYSTEM_NAME MATCHES "Windows")
+ set(CMT_SYSTEM_WINDOWS On)
+ add_definitions(-DCMT_SYSTEM_WINDOWS)
+
+ # Disable unistd.h for flex/bison
+ CMT_DEFINITION(YY_NO_UNISTD_H)
+ message(STATUS "Specifying YY_NO_UNISTD_H")
+endif()
+
+# Define macro to identify macOS system
+if(CMAKE_SYSTEM_NAME MATCHES "Darwin")
+ set(CMT_SYSTEM_MACOS On)
+ add_definitions(-DCMT_SYSTEM_MACOS)
+endif()
+
+if(NOT MSVC)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
+endif()
+
+# Define __CMT_FILENAME__ consistently across Operating Systems
+if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Windows")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D__CMT_FILENAME__='\"$(subst ${CMAKE_SOURCE_DIR}/,,$(abspath $<))\"'")
+else()
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D__CMT_FILENAME__=__FILE__")
+endif()
+
+# Configuration options
+option(CMT_DEV "Enable development mode" No)
+option(CMT_TESTS "Enable unit testing" No)
+option(CMT_INSTALL_TARGETS "Enable subdirectory library installations" Yes)
+option(CMT_ENABLE_PROMETHEUS_DECODER "Enable prometheus decoder" Yes)
+
+if(CMT_DEV)
+ set(CMT_TESTS Yes)
+endif()
+
+set(FLEX_BISON_GENERATED_DIR ${CMAKE_CURRENT_BINARY_DIR})
+
+# Bundled libraries
+include(cmake/libraries.cmake)
+include(cmake/headers.cmake)
+
+# Include headers and dependency headers
+include_directories(
+ src
+ include
+ ${FLEX_BISON_GENERATED_DIR}
+ )
+
+# timespec_get() support
+check_c_source_compiles("
+ #include <time.h>
+ int main() {
+ struct tm tm;
+ return timespec_get(&tm, TIME_UTC);
+ }" CMT_HAVE_TIMESPEC_GET)
+if(CMT_HAVE_TIMESPEC_GET)
+ CMT_DEFINITION(CMT_HAVE_TIMESPEC_GET)
+endif()
+
+# gmtime_r() support
+check_c_source_compiles("
+ #include <time.h>
+ int main() {
+ struct tm tm;
+ struct timespec tms;
+ return gmtime_r(&tms.tv_sec, &tm);
+ }" CMT_HAVE_GMTIME_R)
+if(CMT_HAVE_GMTIME_R)
+ CMT_DEFINITION(CMT_HAVE_GMTIME_R)
+endif()
+
+# gmtime_s() support
+check_c_source_compiles("
+ #include <time.h>
+ int main() {
+ struct tm tm;
+ struct timespec tms;
+ return gmtime_s(&tm, &tms.tv_sec);
+ }" CMT_HAVE_GMTIME_S)
+if(CMT_HAVE_GMTIME_S)
+ CMT_DEFINITION(CMT_HAVE_GMTIME_S)
+endif()
+
+# clock_get_time() support for macOS.
+check_c_source_compiles("
+ #include <mach/clock.h>
+ #include <mach/mach.h>
+ int main() {
+ clock_serv_t cclock;
+ mach_timespec_t mts;
+ host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
+ clock_get_time(cclock, &mts);
+ return mach_port_deallocate(mach_task_self(), cclock);
+ }" CMT_HAVE_CLOCK_GET_TIME)
+if(CMT_HAVE_CLOCK_GET_TIME)
+ CMT_DEFINITION(CMT_HAVE_CLOCK_GET_TIME)
+endif()
+
+# FIXME: MessagePack support
+check_c_source_compiles("
+ #include \"../../../lib/msgpack-c/include/msgpack.h\"
+ int main() {
+ msgpack_packer pck;
+ msgpack_sbuffer sbuf;
+ return 0;
+ }" CMT_HAVE_MSGPACK)
+
+if(CMT_ENABLE_PROMETHEUS_DECODER)
+ # Flex and Bison: check if the variables has not been defined before by
+ # a parent project to avoid conflicts.
+ if(NOT FLEX_FOUND)
+ find_package(FLEX 2)
+ endif()
+
+ if(NOT BISON_FOUND)
+ find_package(BISON 3)
+ endif()
+
+ if(FLEX_FOUND AND BISON_FOUND)
+ set(CMT_BUILD_PROMETHEUS_DECODER 1)
+ endif()
+endif()
+
+# Check if 'C Floppy' library is available in the environment, if not,
+# we will try to build a local copy at a later stage
+check_c_source_compiles("
+ #include <cfl/cfl_found.h>
+ int main() {
+ return cfl_found();
+ }" CMT_HAVE_CFL)
+if(CMT_HAVE_CFL)
+ CMT_DEFINITION(CMT_HAVE_CFL)
+ message(STATUS "CFL found in the system. OK")
+endif()
+
+# Check if fluent-otel-proto library is available in the environment, if not,
+# we will try to build a local copy at a later stage
+check_c_source_compiles("
+ #include <fluent-otel-proto/fluent-otel_found.h>
+ int main() {
+ return fluent_otel_found();
+ }" CMT_HAVE_FLUENT_OTEL_PROTO)
+if(CMT_HAVE_FLUENT_OTEL_PROTO)
+ CMT_DEFINITION(CMT_HAVE_FLUENT_OTEL_PROTO)
+endif()
+
+# Configure header files
+configure_file(
+ "${PROJECT_SOURCE_DIR}/include/cmetrics/cmt_info.h.in"
+ "${PROJECT_SOURCE_DIR}/include/cmetrics/cmt_info.h"
+ )
+
+configure_file(
+ "${PROJECT_SOURCE_DIR}/include/cmetrics/cmt_version.h.in"
+ "${PROJECT_SOURCE_DIR}/include/cmetrics/cmt_version.h"
+ )
+
+# Installation Directories
+# ========================
+if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
+ set(CMT_INSTALL_LIBDIR "lib")
+ set(CMT_INSTALL_INCLUDEDIR "include")
+else()
+ set(CMT_INSTALL_LIBDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
+ set(CMT_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_PREFIX}/include")
+endif()
+
+# mpack
+if(NOT TARGET mpack-static)
+ include_directories(lib/mpack/src/)
+ add_subdirectory(lib/mpack EXCLUDE_FROM_ALL)
+
+ if (CMT_INSTALL_TARGETS)
+ install(TARGETS mpack-static
+ RUNTIME DESTINATION ${CMT_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMT_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMT_INSTALL_LIBDIR}
+ COMPONENT library)
+
+ install(FILES lib/mpack/src/mpack/mpack.h
+ DESTINATION ${CMT_INSTALL_INCLUDEDIR}/mpack
+ COMPONENT headers
+ PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)
+ endif()
+endif()
+
+# C Floppy
+if (NOT CMT_HAVE_CFL)
+ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/${CMT_PATH_LIB_CFL}/include)
+ add_subdirectory(lib/cfl)
+ CMT_DEFINITION(CMT_HAVE_CFL)
+ CMT_DEFINITION(CMT_HAVE_CFL_INTERNAL)
+ if (CMT_INSTALL_TARGETS)
+ file(GLOB bundledCFLHeaders "lib/cfl/include/cfl/*.h")
+ install(FILES ${bundledCFLHeaders}
+ DESTINATION ${CMT_INSTALL_INCLUDEDIR}/cfl
+ COMPONENT headers
+ PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)
+
+ # xxHash
+ install(FILES lib/cfl/lib/xxhash/xxh3.h
+ DESTINATION ${CMT_INSTALL_INCLUDEDIR}
+ COMPONENT headers
+ PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)
+ install(FILES lib/cfl/lib/xxhash/xxhash.h
+ DESTINATION ${CMT_INSTALL_INCLUDEDIR}
+ COMPONENT headers
+ PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)
+
+ install(TARGETS xxhash
+ RUNTIME DESTINATION ${CMT_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMT_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMT_INSTALL_LIBDIR}
+ COMPONENT library)
+ endif()
+endif()
+
+# fluent-otel-proto
+if (NOT CMT_HAVE_FLUENT_OTEL_PROTO)
+ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/${CMT_PATH_LIB_FLUENT_OTEL_PROTO}/include)
+ CMT_OPTION(FLUENT_PROTO_METRICS "on")
+ CMT_OPTION(FLUENT_PROTO_EXAMPLES "off")
+ add_subdirectory(lib/fluent-otel-proto)
+ CMT_DEFINITION(CMT_HAVE_FLUENT_OTEL_PROTO)
+ CMT_DEFINITION(CMT_HAVE_FLUENT_OTEL_PROTO_INTERNAL)
+ if (CMT_INSTALL_TARGETS)
+ file(GLOB bundledOTELProtoHeaders "lib/fluent-otel-proto/include/fluent-otel-proto/*.h")
+ install(FILES ${bundledOTELProtoHeaders}
+ DESTINATION ${CMT_INSTALL_INCLUDEDIR}/fluent-otel-proto
+ COMPONENT headers
+ PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)
+
+ install(TARGETS fluent-otel-proto
+ RUNTIME DESTINATION ${CMT_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMT_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMT_INSTALL_LIBDIR}
+ COMPONENT library)
+ endif()
+endif()
+
+# Source code
+add_subdirectory(include)
+add_subdirectory(src)
+
+# Tests
+if(CMT_TESTS)
+ enable_testing()
+ add_subdirectory(tests)
+endif()
+
+# Installer Generation (Cpack)
+# ============================
+
+set(CPACK_PACKAGE_VERSION ${CMT_VERSION_STR})
+set(CPACK_PACKAGE_NAME "cmetrics")
+set(CPACK_PACKAGE_RELEASE 1)
+set(CPACK_PACKAGE_CONTACT "Eduardo Silva <eduardo@calyptia.com>")
+set(CPACK_PACKAGE_VENDOR "Calyptia")
+set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")
+set(CPACK_PACKAGING_INSTALL_PREFIX "/")
+
+set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_PACKAGE_RELEASE}.${CMAKE_SYSTEM_PROCESSOR}")
+
+if(CMT_SYSTEM_WINDOWS)
+ set(CPACK_GENERATOR "ZIP")
+
+ if(CMAKE_SIZEOF_VOID_P EQUAL 8)
+ set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-win64")
+ else()
+ set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-win32")
+ endif()
+endif()
+
+
+# Enable components
+set(CPACK_DEB_COMPONENT_INSTALL ON)
+set(CPACK_RPM_COMPONENT_INSTALL ON)
+set(CPACK_productbuild_COMPONENT_INSTALL ON)
+set(CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL} binary library headers)
+set(CPACK_COMPONENTS_GROUPING "ONE_PER_GROUP")
+
+set(CPACK_COMPONENT_BINARY_GROUP "RUNTIME")
+set(CPACK_COMPONENT_LIBRARY_GROUP "RUNTIME")
+
+# Debian package setup and name sanitizer
+set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
+
+find_program(DPKG_PROGRAM dpkg DOC "dpkg program of Debian-based systems")
+if(DPKG_PROGRAM)
+ execute_process(
+ COMMAND ${DPKG_PROGRAM} --print-architecture
+ OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+
+ set(CPACK_DEBIAN_HEADERS_FILE_NAME "${CPACK_PACKAGE_NAME}_${CPACK_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}-headers.deb")
+ set(CPACK_DEBIAN_RUNTIME_PACKAGE_NAME "${CPACK_PACKAGE_NAME}")
+ set(CPACK_DEBIAN_RUNTIME_FILE_NAME "${CPACK_PACKAGE_NAME}_${CPACK_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}.deb")
+ set(CPACK_DEBIAN_RUNTIME_PACKAGE_CONTROL_EXTRA
+ ${CMAKE_CURRENT_SOURCE_DIR}/debian/conffiles
+ )
+endif()
+
+# RPM Generation information
+set(CPACK_RPM_PACKAGE_GROUP "System Environment/Daemons")
+set(CPACK_RPM_PACKAGE_LICENSE "Apache v2.0")
+set(CPACK_RPM_PACKAGE_RELEASE ${CPACK_PACKAGE_RELEASE})
+set(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/cpack/description")
+set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A standalone library to create and manipulate metrics in C")
+set(CPACK_RPM_SPEC_MORE_DEFINE "%define ignore \#")
+set(CPACK_RPM_USER_FILELIST
+ "%ignore /lib"
+ "%ignore /lib64"
+ "%ignore /lib64/pkgconfig"
+ "%ignore /usr/local"
+ "%ignore /usr/local/bin")
+
+set(CPACK_RPM_PACKAGE_AUTOREQ ON)
+set(CPACK_RPM_RUNTIME_PACKAGE_NAME "${CPACK_PACKAGE_NAME}")
+set(CPACK_RPM_HEADERS_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_PACKAGE_RELEASE}.${CMAKE_SYSTEM_PROCESSOR}-headers.rpm")
+set(CPACK_RPM_RUNTIME_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_PACKAGE_RELEASE}.${CMAKE_SYSTEM_PROCESSOR}.rpm")
+
+# CPack: DEB
+set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
+
+# CPack: Windows System
+if(CPACK_GENERATOR MATCHES "ZIP")
+ set(CPACK_MONOLITHIC_INSTALL 1)
+ set(CPACK_PACKAGE_INSTALL_DIRECTORY "cmetrics")
+endif()
+
+# CPack: macOS w/ productbuild
+if(CMT_SYSTEM_MACOS)
+ # Determine the platform suffix
+ execute_process(
+ COMMAND uname -m
+ RESULT_VARIABLE UNAME_M_RESULT
+ OUTPUT_VARIABLE UNAME_ARCH
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+ if (UNAME_M_RESULT EQUAL 0 AND UNAME_ARCH STREQUAL "arm64")
+ set(CMETRICS_PKG ${CMAKE_CURRENT_BINARY_DIR}/${CPACK_PACKAGE_NAME}-${CMT_VERSION_STR}-apple)
+ elseif(UNAME_M_RESULT EQUAL 0 AND UNAME_ARCH STREQUAL "x86_64")
+ set(CMETRICS_PKG ${CMAKE_CURRENT_BINARY_DIR}/${CPACK_PACKAGE_NAME}-${CMT_VERSION_STR}-intel)
+ else()
+ set(CMETRICS_PKG ${CMAKE_CURRENT_BINARY_DIR}/${CPACK_PACKAGE_NAME}-${CMT_VERSION_STR}-${UNAME_ARCH})
+ endif()
+
+ if (CPACK_GENERATOR MATCHES "productbuild")
+ set(CPACK_SET_DESTDIR "ON")
+ configure_file(cpack/macos/welcome.txt.cmakein ${CMAKE_CURRENT_SOURCE_DIR}/welcome.txt)
+ configure_file(LICENSE ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.txt)
+ find_program(CONVERTER textutil)
+ if (NOT CONVERTER)
+ message(FATAL_ERROR "textutil not found.")
+ endif()
+ if (CONVERTER)
+ execute_process(COMMAND ${CONVERTER} -convert html "${CMAKE_CURRENT_SOURCE_DIR}/README.md" -output "${CMAKE_CURRENT_SOURCE_DIR}/README.html")
+ endif()
+ set(CPACK_PACKAGE_FILE_NAME "${CMETRICS_PKG}")
+ set(CPACK_RESOURCE_FILE_WELCOME ${CMAKE_CURRENT_SOURCE_DIR}/welcome.txt)
+ set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.txt)
+ set(CPACK_RESOURCE_FILE_README ${CMAKE_CURRENT_SOURCE_DIR}/README.html)
+ set(CPACK_PRODUCTBUILD_IDENTIFIER "com.calyptia.${CPACK_PACKAGE_NAME}")
+ endif()
+endif()
+
+# Create tarball
+add_custom_target(tarball COMMAND "bash" "${CMAKE_CURRENT_SOURCE_DIR}/create-submoduled-tarball.sh" "cmetrics-${CMT_VERSION_STR}")
+
+include(CPack)
diff --git a/src/fluent-bit/lib/cmetrics/LICENSE b/src/fluent-bit/lib/cmetrics/LICENSE
new file mode 100644
index 000000000..f433b1a53
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/LICENSE
@@ -0,0 +1,177 @@
+
+ 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
diff --git a/src/fluent-bit/lib/cmetrics/README.md b/src/fluent-bit/lib/cmetrics/README.md
new file mode 100644
index 000000000..0ce732b3e
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/README.md
@@ -0,0 +1,22 @@
+# CMetrics
+
+> DISCLAIMER: THIS LIBRARY IS STILL IN ACTIVE DEVELOPMENT
+
+The [CMetrics](https://github.com/calyptia/cmetrics) project is a standalone C library to create and maintain a context of different sets of metrics with labels support such as:
+
+- Counters
+- Gauges
+- Histograms
+- Summaries
+
+This project is heavily based on Go Prometheus Client API design:
+
+- https://pkg.go.dev/github.com/prometheus/client_golang/prometheus#section-documentation
+
+## License
+
+This program is under the terms of the [Apache License v2.0](http://www.apache.org/licenses/LICENSE-2.0).
+
+## Authors
+
+[Calyptia Team](https://www.calyptia.com)
diff --git a/src/fluent-bit/lib/cmetrics/appveyor.yml b/src/fluent-bit/lib/cmetrics/appveyor.yml
new file mode 100644
index 000000000..1f60cfebc
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/appveyor.yml
@@ -0,0 +1,34 @@
+version: v1-winbuild-{build}
+
+image: Visual Studio 2019
+
+platform:
+ - Win32
+ - x64
+
+environment:
+ vspath: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Community'
+ winflexbison: https://github.com/lexxmark/winflexbison/releases/download/v2.5.22/win_flex_bison-2.5.22.zip
+ PATH: '%PATH%;C:\WinFlexBison'
+
+configuration:
+ - Release
+
+skip_commits:
+ message: /workflows/
+ files:
+ - '.github/**'
+
+install:
+ - ps: Invoke-WebRequest -O winflexbison.zip $env:winflexbison
+ - ps: Expand-Archive winflexbison.zip -Destination /WinFlexBison
+ - ps: Copy-Item -Path /WinFlexBison/win_bison.exe /WinFlexBison/bison.exe
+ - ps: Copy-Item -Path /WinFlexBison/win_flex.exe /WinFlexBison/flex.exe
+
+before_build:
+ - if %PLATFORM%==Win32 call "%vspath%\VC\Auxiliary\Build\vcvars32.bat"
+ - if %PLATFORM%==x64 call "%vspatH%\VC\Auxiliary\Build\vcvars64.bat"
+
+build_script:
+ - .\scripts\win_build.bat
+ - ctest -C Debug --test-dir .\tests\
diff --git a/src/fluent-bit/lib/cmetrics/cmake/headers.cmake b/src/fluent-bit/lib/cmetrics/cmake/headers.cmake
new file mode 100644
index 000000000..e18a0c28f
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/cmake/headers.cmake
@@ -0,0 +1,4 @@
+include_directories(${PROJECT_SOURCE_DIR}/lib/fluent-otel-proto/include
+ ${PROJECT_SOURCE_DIR}/lib/fluent-otel-proto/proto_c
+ ${PROJECT_SOURCE_DIR}/lib/cfl/include
+ )
diff --git a/src/fluent-bit/lib/cmetrics/cmake/libraries.cmake b/src/fluent-bit/lib/cmetrics/cmake/libraries.cmake
new file mode 100644
index 000000000..804948418
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/cmake/libraries.cmake
@@ -0,0 +1,3 @@
+# List of bundled libraries
+set(CMT_PATH_LIB_CFL "lib/cfl")
+set(CMT_PATH_LIB_FLUENT_OTEL_PROTO "lib/fluent-otel-proto")
diff --git a/src/fluent-bit/lib/cmetrics/cmake/macros.cmake b/src/fluent-bit/lib/cmetrics/cmake/macros.cmake
new file mode 100644
index 000000000..9a533d3a4
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/cmake/macros.cmake
@@ -0,0 +1,10 @@
+# Macro to set definitions
+macro(CMT_DEFINITION var)
+ add_definitions(-D${var})
+ set(CMT_BUILD_FLAGS "${CMT_BUILD_FLAGS}#ifndef ${var}\n#define ${var}\n#endif\n")
+ set(CMT_INFO_FLAGS "${CMT_INFO_FLAGS} ${var}")
+endmacro()
+
+macro(CMT_OPTION option value)
+ set(${option} ${value} CACHE INTERNAL "" FORCE)
+endmacro()
diff --git a/src/fluent-bit/lib/cmetrics/cpack/description b/src/fluent-bit/lib/cmetrics/cpack/description
new file mode 100644
index 000000000..410f22fdb
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/cpack/description
@@ -0,0 +1 @@
+A standalone library to create and manipulate metrics in C.
diff --git a/src/fluent-bit/lib/cmetrics/cpack/macos/welcome.txt.cmakein b/src/fluent-bit/lib/cmetrics/cpack/macos/welcome.txt.cmakein
new file mode 100644
index 000000000..8623042b2
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/cpack/macos/welcome.txt.cmakein
@@ -0,0 +1,7 @@
+This will install @CPACK_PACKAGE_NAME@ on your Mac.
+
+--------------------------------------------------
+
+Thank you for trying @CPACK_PACKAGE_NAME@! Have a fantastic day!
+
+You can use @CPACK_PACKAGE_NAME@ as metrics library on your system.
diff --git a/src/fluent-bit/lib/cmetrics/create-submoduled-tarball.sh b/src/fluent-bit/lib/cmetrics/create-submoduled-tarball.sh
new file mode 100644
index 000000000..455156bef
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/create-submoduled-tarball.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+if [ -z "$1" ]; then
+ echo "Specify archive name"
+ exit 1
+fi
+
+OS=$(uname -s)
+
+echo "$OS"
+if [ "$OS" == "Darwin" ]; then
+ echo "Using gtar for concatenate option"
+ TAR=gtar
+else
+ TAR=tar
+fi
+
+ROOT_ARCHIVE_NAME=$1
+
+git archive --prefix "$ROOT_ARCHIVE_NAME/" -o "$ROOT_ARCHIVE_NAME.tar" HEAD
+git submodule foreach --recursive "git archive --prefix=$ROOT_ARCHIVE_NAME/\$path/ --output=\$sha1.tar HEAD && $TAR --concatenate --file=$(pwd)/$ROOT_ARCHIVE_NAME.tar \$sha1.tar && rm \$sha1.tar"
+
+gzip "$ROOT_ARCHIVE_NAME.tar"
diff --git a/src/fluent-bit/lib/cmetrics/include/CMakeLists.txt b/src/fluent-bit/lib/cmetrics/include/CMakeLists.txt
new file mode 100644
index 000000000..060ec03e1
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/CMakeLists.txt
@@ -0,0 +1,11 @@
+file(GLOB cmetricsHeaders "cmetrics/*.h")
+install(FILES ${cmetricsHeaders}
+ DESTINATION ${CMT_INSTALL_INCLUDEDIR}/cmetrics
+ COMPONENT headers
+ PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)
+
+file(GLOB promHeaders "prometheus_remote_write/*.h")
+install(FILES ${promHeaders}
+ DESTINATION ${CMT_INSTALL_INCLUDEDIR}/prometheus_remote_write
+ COMPONENT headers
+ PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmetrics.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmetrics.h
new file mode 100644
index 000000000..75ef78d62
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmetrics.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#ifndef CMT_H
+#define CMT_H
+
+#define CMT_FALSE 0
+#define CMT_TRUE !CMT_FALSE
+
+#define CMT_COUNTER 0
+#define CMT_GAUGE 1
+#define CMT_HISTOGRAM 2
+#define CMT_SUMMARY 3
+#define CMT_UNTYPED 4
+
+#define CMT_AGGREGATION_TYPE_UNSPECIFIED 0
+#define CMT_AGGREGATION_TYPE_DELTA 1
+#define CMT_AGGREGATION_TYPE_CUMULATIVE 2
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include <cfl/cfl.h>
+
+#include <cmetrics/cmt_info.h>
+#include <cmetrics/cmt_log.h>
+#include <cmetrics/cmt_compat.h>
+#include <cmetrics/cmt_math.h>
+#include <cmetrics/cmt_time.h>
+#include <cmetrics/cmt_label.h>
+#include <cmetrics/cmt_version.h>
+
+struct cmt {
+ /* logging */
+ int log_level;
+ void (*log_cb)(void *, int, const char *, int, const char *);
+
+ /* cmetrics metadata */
+ struct cfl_kvlist *internal_metadata;
+
+ /* third party metadata (ie. otlp resource & instrumentation library) */
+ struct cfl_kvlist *external_metadata;
+
+ /* static labels */
+ struct cmt_labels *static_labels;
+
+ /* Metrics list */
+ struct cfl_list counters;
+ struct cfl_list gauges;
+ struct cfl_list histograms;
+ struct cfl_list summaries;
+ struct cfl_list untypeds;
+
+ /* Only used by the otlp decoder */
+ struct cfl_list _head;
+};
+
+void cmt_initialize();
+
+struct cmt *cmt_create();
+void cmt_destroy(struct cmt *cmt);
+int cmt_label_add(struct cmt *cmt, char *key, char *val);
+char *cmt_version();
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_atomic.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_atomic.h
new file mode 100644
index 000000000..dc2f277d1
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_atomic.h
@@ -0,0 +1,11 @@
+#ifndef CMT_ATOMIC_H
+#define CMT_ATOMIC_H
+
+#include <stdint.h>
+
+int cmt_atomic_initialize();
+int cmt_atomic_compare_exchange(uint64_t *storage, uint64_t old_value, uint64_t new_value);
+void cmt_atomic_store(uint64_t *storage, uint64_t new_value);
+uint64_t cmt_atomic_load(uint64_t *storage);
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_cat.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_cat.h
new file mode 100644
index 000000000..220dc2eeb
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_cat.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#ifndef CMT_CAT_H
+#define CMT_CAT_H
+
+#include <cmetrics/cmetrics.h>
+
+int cmt_cat(struct cmt *dst, struct cmt *src);
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_compat.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_compat.h
new file mode 100644
index 000000000..004c0aa88
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_compat.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#ifndef CMT_COMPAT_H
+#define CMT_COMPAT_H
+
+#include <time.h>
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+static inline struct tm *cmt_platform_gmtime_r(const time_t *timep, struct tm *result)
+{
+#ifdef CMT_HAVE_GMTIME_S
+ if (gmtime_s(result, timep)) {
+ return NULL;
+ }
+
+ return result;
+#else
+ /* FIXME: Need to handle gmtime_r(3) lacking platform? */
+ return gmtime_r(timep, result) ;
+#endif
+}
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_counter.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_counter.h
new file mode 100644
index 000000000..4e5c6bac3
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_counter.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#ifndef CMT_COUNTER_H
+#define CMT_COUNTER_H
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_opts.h>
+
+struct cmt_counter {
+ struct cmt_opts opts;
+ struct cmt_map *map;
+ struct cfl_list _head;
+ struct cmt *cmt;
+ int allow_reset;
+ int aggregation_type;
+};
+
+struct cmt_counter *cmt_counter_create(struct cmt *cmt,
+ char *ns, char *subsystem,
+ char *name, char *help,
+ int label_count, char **label_keys);
+void cmt_counter_allow_reset(struct cmt_counter *counter);
+int cmt_counter_destroy(struct cmt_counter *counter);
+int cmt_counter_inc(struct cmt_counter *counter, uint64_t timestamp,
+ int labels_count, char **label_vals);
+int cmt_counter_add(struct cmt_counter *counter, uint64_t timestamp,
+ double val, int labels_count, char **label_vals);
+int cmt_counter_set(struct cmt_counter *counter, uint64_t timestamp, double val,
+ int labels_count, char **label_vals);
+int cmt_counter_get_val(struct cmt_counter *counter,
+ int labels_count, char **label_vals, double *out_val);
+#endif
+
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_decode_msgpack.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_decode_msgpack.h
new file mode 100644
index 000000000..68b0eb4ba
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_decode_msgpack.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+
+#ifndef CMT_DECODE_MSGPACK_H
+#define CMT_DECODE_MSGPACK_H
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_mpack_utils_defs.h>
+
+#define CMT_DECODE_MSGPACK_SUCCESS CMT_MPACK_SUCCESS
+#define CMT_DECODE_MSGPACK_INSUFFICIENT_DATA CMT_MPACK_INSUFFICIENT_DATA
+#define CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR CMT_MPACK_INVALID_ARGUMENT_ERROR
+#define CMT_DECODE_MSGPACK_ALLOCATION_ERROR CMT_MPACK_ALLOCATION_ERROR
+#define CMT_DECODE_MSGPACK_CORRUPT_INPUT_DATA_ERROR CMT_MPACK_CORRUPT_INPUT_DATA_ERROR
+#define CMT_DECODE_MSGPACK_CONSUME_ERROR CMT_MPACK_CONSUME_ERROR
+#define CMT_DECODE_MSGPACK_ENGINE_ERROR CMT_MPACK_ENGINE_ERROR
+#define CMT_DECODE_MSGPACK_PENDING_MAP_ENTRIES CMT_MPACK_PENDING_MAP_ENTRIES
+#define CMT_DECODE_MSGPACK_PENDING_ARRAY_ENTRIES CMT_MPACK_PENDING_ARRAY_ENTRIES
+#define CMT_DECODE_MSGPACK_UNEXPECTED_KEY_ERROR CMT_MPACK_UNEXPECTED_KEY_ERROR
+#define CMT_DECODE_MSGPACK_UNEXPECTED_DATA_TYPE_ERROR CMT_MPACK_UNEXPECTED_DATA_TYPE_ERROR
+
+#define CMT_DECODE_MSGPACK_DICTIONARY_LOOKUP_ERROR CMT_MPACK_ERROR_CUTOFF + 1
+#define CMT_DECODE_MSGPACK_VERSION_ERROR CMT_MPACK_ERROR_CUTOFF + 2
+
+struct cmt_msgpack_temporary_bucket {
+ double upper_bound;
+ struct cfl_list _head;
+};
+
+struct cmt_msgpack_decode_context {
+ struct cmt *cmt;
+ struct cmt_map *map;
+ struct cmt_metric *metric;
+ double *bucket_list;
+ size_t bucket_count;
+ double *quantile_list;
+ size_t quantile_count;
+ uint64_t *summary_quantiles;
+ size_t summary_quantiles_count;
+ int aggregation_type;
+};
+
+int cmt_decode_msgpack_create(struct cmt **out_cmt, char *in_buf, size_t in_size,
+ size_t *offset);
+void cmt_decode_msgpack_destroy(struct cmt *cmt);
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_decode_opentelemetry.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_decode_opentelemetry.h
new file mode 100644
index 000000000..88327175e
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_decode_opentelemetry.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+
+#ifndef CMT_DECODE_OPENTELEMETRY_H
+#define CMT_DECODE_OPENTELEMETRY_H
+
+#include <cmetrics/cmetrics.h>
+#include <opentelemetry/proto/metrics/v1/metrics.pb-c.h>
+#include <opentelemetry/proto/collector/metrics/v1/metrics_service.pb-c.h>
+
+#define CMT_DECODE_OPENTELEMETRY_SUCCESS 0
+#define CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR 1
+#define CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR 2
+#define CMT_DECODE_OPENTELEMETRY_KVLIST_ACCESS_ERROR 3
+#define CMT_DECODE_OPENTELEMETRY_ARRAY_ACCESS_ERROR 4
+
+struct cmt_opentelemetry_decode_context {
+ struct cmt *cmt;
+ struct cmt_map *map;
+ struct cmt_metric *metric;
+ char **namespace_identifiers;
+ char **subsystem_identifiers;
+};
+
+int cmt_decode_opentelemetry_create(struct cfl_list *result_context_list,
+ char *in_buf, size_t in_size,
+ size_t *offset);
+
+void cmt_decode_opentelemetry_destroy(struct cfl_list *context_list);
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_decode_prometheus.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_decode_prometheus.h
new file mode 100644
index 000000000..7500469d2
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_decode_prometheus.h
@@ -0,0 +1,112 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#ifndef CMT_DECODE_PROMETHEUS_H
+#define CMT_DECODE_PROMETHEUS_H
+
+#include <stdbool.h>
+
+#include <cmetrics/cmetrics.h>
+#include <stdint.h>
+
+#define CMT_DECODE_PROMETHEUS_SUCCESS 0
+#define CMT_DECODE_PROMETHEUS_SYNTAX_ERROR 1
+#define CMT_DECODE_PROMETHEUS_ALLOCATION_ERROR 10
+#define CMT_DECODE_PROMETHEUS_MAX_LABEL_COUNT_EXCEEDED 30
+#define CMT_DECODE_PROMETHEUS_CMT_SET_ERROR 40
+#define CMT_DECODE_PROMETHEUS_CMT_CREATE_ERROR 50
+#define CMT_DECODE_PROMETHEUS_PARSE_VALUE_FAILED 60
+#define CMT_DECODE_PROMETHEUS_PARSE_TIMESTAMP_FAILED 70
+
+#define CMT_DECODE_PROMETHEUS_MAX_LABEL_COUNT 128
+
+enum cmt_decode_prometheus_context_sample_type {
+ CMT_DECODE_PROMETHEUS_CONTEXT_SAMPLE_TYPE_NORMAL = 0,
+ CMT_DECODE_PROMETHEUS_CONTEXT_SAMPLE_TYPE_BUCKET = 1,
+ CMT_DECODE_PROMETHEUS_CONTEXT_SAMPLE_TYPE_SUM = 2,
+ CMT_DECODE_PROMETHEUS_CONTEXT_SAMPLE_TYPE_COUNT = 3
+};
+
+struct cmt_decode_prometheus_context_sample {
+ char value1[64];
+ char value2[64];
+ int type;
+ cfl_sds_t label_values[CMT_DECODE_PROMETHEUS_MAX_LABEL_COUNT];
+
+ struct cfl_list _head;
+};
+
+struct cmt_decode_prometheus_context_metric {
+ cfl_sds_t name_orig;
+ char *ns;
+ char *subsystem;
+ char *name;
+ int type;
+ int current_sample_type;
+ cfl_sds_t docstring;
+ size_t label_count;
+ cfl_sds_t labels[CMT_DECODE_PROMETHEUS_MAX_LABEL_COUNT];
+ struct cfl_list samples;
+};
+
+struct cmt_decode_prometheus_parse_opts {
+ int start_token;
+ uint64_t default_timestamp;
+ uint64_t override_timestamp;
+ char *errbuf;
+ size_t errbuf_size;
+};
+
+struct cmt_decode_prometheus_context {
+ union {
+ struct cmt_summary *summary;
+ struct cmt_histogram *histogram;
+ } current;
+ struct cmt *cmt;
+ struct cmt_decode_prometheus_parse_opts opts;
+ int errcode;
+ cfl_sds_t strbuf;
+ struct cmt_decode_prometheus_context_metric metric;
+};
+
+#define LEX_DECL int cmt_decode_prometheus_lex \
+ (YYSTYPE * yylval_param, \
+ void* yyscanner, \
+ struct cmt_decode_prometheus_context *context)
+
+#define YY_DECL LEX_DECL
+
+#include "cmt_decode_prometheus_parser.h"
+
+#ifndef FLEX_SCANNER
+// lexer header should not be included in the generated lexer c file,
+// which defines FLEX_SCANNER
+#include "cmt_decode_prometheus_lexer.h"
+#endif
+
+LEX_DECL; /* Declear as an entity of yylex function declaration. */
+
+int cmt_decode_prometheus_create(
+ struct cmt **out_cmt,
+ const char *in_buf,
+ size_t in_size,
+ struct cmt_decode_prometheus_parse_opts *opts);
+void cmt_decode_prometheus_destroy(struct cmt *cmt);
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_encode_influx.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_encode_influx.h
new file mode 100644
index 000000000..d7d8760cf
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_encode_influx.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+
+#ifndef CMT_ENCODE_INFLUX_H
+#define CMT_ENCODE_INFLUX_H
+
+#include <cmetrics/cmetrics.h>
+
+cfl_sds_t cmt_encode_influx_create(struct cmt *cmt);
+void cmt_encode_influx_destroy(cfl_sds_t text);
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_encode_msgpack.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_encode_msgpack.h
new file mode 100644
index 000000000..f96197d0c
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_encode_msgpack.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+
+#ifndef CMT_ENCODE_MSGPACK_H
+#define CMT_ENCODE_MSGPACK_H
+
+#include <cmetrics/cmetrics.h>
+
+#define MSGPACK_ENCODER_VERSION 2
+
+int cmt_encode_msgpack_create(struct cmt *cmt, char **out_buf, size_t *out_size);
+void cmt_encode_msgpack_destroy(char *out_buf);
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_encode_opentelemetry.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_encode_opentelemetry.h
new file mode 100644
index 000000000..46beca4b2
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_encode_opentelemetry.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+
+#ifndef CMT_ENCODE_OPENTELEMETRY_H
+#define CMT_ENCODE_OPENTELEMETRY_H
+
+#include <cmetrics/cmetrics.h>
+#include <opentelemetry/proto/metrics/v1/metrics.pb-c.h>
+#include <opentelemetry/proto/collector/metrics/v1/metrics_service.pb-c.h>
+
+#define CMT_ENCODE_OPENTELEMETRY_SUCCESS 0
+#define CMT_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR 1
+#define CMT_ENCODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR 2
+#define CMT_ENCODE_OPENTELEMETRY_UNEXPECTED_METRIC_TYPE 3
+#define CMT_ENCODE_OPENTELEMETRY_DATA_POINT_INIT_ERROR 4
+
+struct cmt_opentelemetry_context
+{
+ size_t resource_index;
+ Opentelemetry__Proto__Metrics__V1__MetricsData *metrics_data;
+ struct cmt *cmt;
+};
+
+cfl_sds_t cmt_encode_opentelemetry_create(struct cmt *cmt);
+void cmt_encode_opentelemetry_destroy(cfl_sds_t text);
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_encode_prometheus.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_encode_prometheus.h
new file mode 100644
index 000000000..b16a3f378
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_encode_prometheus.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+
+#ifndef CMT_ENCODE_PROMETHEUS_H
+#define CMT_ENCODE_PROMETHEUS_H
+
+#include <cmetrics/cmetrics.h>
+
+cfl_sds_t cmt_encode_prometheus_create(struct cmt *cmt, int add_timestamp);
+void cmt_encode_prometheus_destroy(cfl_sds_t text);
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_encode_prometheus_remote_write.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_encode_prometheus_remote_write.h
new file mode 100644
index 000000000..c160c2715
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_encode_prometheus_remote_write.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+
+#ifndef CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_H
+#define CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_H
+
+#include <cmetrics/cmetrics.h>
+#include <prometheus_remote_write/remote.pb-c.h>
+
+#define CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_ADD_METADATA CMT_FALSE
+
+#define CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS 0
+#define CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_ALLOCATION_ERROR 1
+#define CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_UNEXPECTED_ERROR 2
+#define CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_INVALID_ARGUMENT_ERROR 3
+#define CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_UNEXPECTED_METRIC_TYPE 4
+
+struct cmt_prometheus_metric_metadata {
+ Prometheus__MetricMetadata data;
+ struct cfl_list _head;
+};
+
+struct cmt_prometheus_time_series {
+ uint64_t label_set_hash;
+ size_t entries_set;
+ Prometheus__TimeSeries data;
+ struct cfl_list _head;
+};
+
+struct cmt_prometheus_remote_write_context
+{
+ struct cfl_list time_series_entries;
+ struct cfl_list metadata_entries;
+ uint64_t sequence_number;
+ Prometheus__WriteRequest write_request;
+ struct cmt *cmt;
+};
+
+cfl_sds_t cmt_encode_prometheus_remote_write_create(struct cmt *cmt);
+void cmt_encode_prometheus_remote_write_destroy(cfl_sds_t text);
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_encode_splunk_hec.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_encode_splunk_hec.h
new file mode 100644
index 000000000..689529457
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_encode_splunk_hec.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+
+#ifndef CMT_ENCODE_SPLUNK_HEC_H
+#define CMT_ENCODE_SPLUNK_HEC_H
+
+#include <cmetrics/cmetrics.h>
+
+#define CMT_ENCODE_SPLUNK_HEC_SUCCESS 0
+#define CMT_ENCODE_SPLUNK_HEC_ALLOCATION_ERROR 1
+#define CMT_ENCODE_SPLUNK_HEC_INVALID_ARGUMENT_ERROR 2
+#define CMT_ENCODE_SPLUNK_HEC_UNEXPECTED_METRIC_TYPE 3
+#define CMT_ENCODE_SPLUNK_HEC_INVALID_DATA_ERROR 4
+
+struct cmt_splunk_hec_context {
+ const char *host;
+ const char *index;
+ const char *source;
+ const char *source_type;
+ struct cmt *cmt;
+};
+
+cfl_sds_t cmt_encode_splunk_hec_create(struct cmt *cmt, const char *host,
+ const char *index, const char *source, const char *source_type);
+void cmt_encode_splunk_hec_destroy(cfl_sds_t text);
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_encode_text.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_encode_text.h
new file mode 100644
index 000000000..f705f6ccc
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_encode_text.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+
+#ifndef CMT_ENCODE_TEXT_H
+#define CMT_ENCODE_TEXT_H
+
+#include <cmetrics/cmetrics.h>
+
+cfl_sds_t cmt_encode_text_create(struct cmt *cmt);
+void cmt_encode_text_destroy(cfl_sds_t text);
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_gauge.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_gauge.h
new file mode 100644
index 000000000..a75ad2e57
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_gauge.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#ifndef CMT_GAUGE_H
+#define CMT_GAUGE_H
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_opts.h>
+
+struct cmt_gauge {
+ struct cmt_opts opts; /* Metric options */
+ struct cmt_map *map;
+ struct cmt *cmt;
+ struct cfl_list _head; /* Link to list struct cmt->gauges */
+};
+
+struct cmt_gauge *cmt_gauge_create(struct cmt *cmt,
+ char *ns, char *subsystem, char *name,
+ char *help, int label_count, char **label_keys);
+int cmt_gauge_destroy(struct cmt_gauge *gauge);
+
+int cmt_gauge_set(struct cmt_gauge *gauge, uint64_t timestamp, double val,
+ int labels_count, char **label_vals);
+int cmt_gauge_inc(struct cmt_gauge *gauge, uint64_t timestamp,
+ int labels_count, char **label_vals);
+int cmt_gauge_dec(struct cmt_gauge *gauge, uint64_t timestamp,
+ int labels_count, char **label_vals);
+int cmt_gauge_add(struct cmt_gauge *gauge, uint64_t timestamp,
+ double val, int labels_count, char **label_vals);
+int cmt_gauge_sub(struct cmt_gauge *gauge, uint64_t timestamp, double val,
+ int labels_count, char **label_vals);
+int cmt_gauge_get_val(struct cmt_gauge *gauge,
+ int labels_count, char **label_vals, double *out_val);
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_histogram.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_histogram.h
new file mode 100644
index 000000000..71770163c
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_histogram.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#ifndef CMT_HISTOGRAM_H
+#define CMT_HISTOGRAM_H
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_opts.h>
+
+struct cmt_histogram_buckets {
+ size_t count;
+ double *upper_bounds;
+};
+
+struct cmt_histogram {
+ struct cmt_histogram_buckets *buckets;
+ struct cmt_opts opts;
+ struct cmt_map *map;
+ struct cfl_list _head;
+ struct cmt *cmt;
+ int aggregation_type;
+};
+
+/* Buckets */
+struct cmt_histogram_buckets *cmt_histogram_buckets_create_size(double *bkts, size_t count);
+struct cmt_histogram_buckets *cmt_histogram_buckets_create(size_t count, ...);
+void cmt_histogram_buckets_destroy(struct cmt_histogram_buckets *buckets);
+
+struct cmt_histogram_buckets *cmt_histogram_buckets_default_create();
+struct cmt_histogram_buckets *cmt_histogram_buckets_linear_create(double start,
+ double width,
+ size_t count);
+struct cmt_histogram_buckets *cmt_histogram_buckets_exponential_create(double start,
+ double factor,
+ size_t count);
+/* Histogram */
+struct cmt_histogram *cmt_histogram_create(struct cmt *cmt,
+ char *ns, char *subsystem,
+ char *name, char *help,
+ struct cmt_histogram_buckets *buckets,
+ int label_count, char **label_keys);
+
+int cmt_histogram_observe(struct cmt_histogram *histogram, uint64_t timestamp,
+ double val, int labels_count, char **label_vals);
+
+int cmt_histogram_set_default(struct cmt_histogram *histogram,
+ uint64_t timestamp,
+ uint64_t *bucket_defaults,
+ double sum,
+ uint64_t count,
+ int labels_count, char **label_vals);
+
+int cmt_histogram_destroy(struct cmt_histogram *h);
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_info.h.in b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_info.h.in
new file mode 100644
index 000000000..b35dbbcca
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_info.h.in
@@ -0,0 +1,28 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#ifndef CMT_INFO_H
+#define CMT_INFO_H
+
+#define CMT_SOURCE_DIR "@CMAKE_SOURCE_DIR@"
+
+/* General flags set by /CMakeLists.txt */
+@CMT_BUILD_FLAGS@
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_label.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_label.h
new file mode 100644
index 000000000..c201345c9
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_label.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#ifndef CMT_LABEL_H
+#define CMT_LABEL_H
+
+#include <cmetrics/cmetrics.h>
+
+struct cmt_label {
+ cfl_sds_t key; /* Label key */
+ cfl_sds_t val; /* Label value */
+ struct cfl_list _head; /* Link to list cmt_labels->list */
+};
+
+struct cmt_labels {
+ struct cfl_list list;
+};
+
+struct cmt_labels *cmt_labels_create();
+void cmt_labels_destroy(struct cmt_labels *labels);
+int cmt_labels_add_kv(struct cmt_labels *labels, char *key, char *val);
+int cmt_labels_count(struct cmt_labels *labels);
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_log.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_log.h
new file mode 100644
index 000000000..b1669caa8
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_log.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#ifndef CMT_LOG_H
+#define CMT_LOG_H
+
+#include <errno.h>
+
+/* Message types */
+#define CMT_LOG_OFF 0
+#define CMT_LOG_ERROR 1
+#define CMT_LOG_WARN 2
+#define CMT_LOG_INFO 3 /* default */
+#define CMT_LOG_DEBUG 4
+#define CMT_LOG_TRACE 5
+
+
+#define CMT_LOG_BUF_SIZE 256
+
+void cmt_log_print(void *ctx, int level, const char *file, int line,
+ const char *fmt, ...);
+int cmt_errno_print(int errnum, const char *file, int line);
+
+#define cmt_log_error(ctx, fmt, ...) \
+ cmt_log_print(ctx, CMT_LOG_ERROR, __CMT_FILENAME__, \
+ __LINE__, fmt, ##__VA_ARGS__)
+
+#define cmt_log_warn(ctx, fmt, ...) \
+ cmt_log_print(ctx, CMT_LOG_WARN, __CMT_FILENAME__, \
+ __LINE__, fmt, ##__VA_ARGS__)
+
+#define cmt_log_info(ctx, fmt, ...) \
+ cmt_log_print(ctx, CMT_LOG_INFO, __CMT_FILENAME__, \
+ __LINE__, fmt, ##__VA_ARGS__)
+
+#define cmt_log_debug(ctx, fmt, ...) \
+ cmt_log_print(ctx, CMT_LOG_DEBUG, __CMT_FILENAME__, \
+ __LINE__, fmt, ##__VA_ARGS__)
+
+#define cmt_log_trace(ctx, fmt, ...) \
+ cmt_log_print(ctx, CMT_LOG_TRACE, __CMT_FILENAME__, \
+ __LINE__, fmt, ##__VA_ARGS__)
+
+#ifdef __CMT_FILENAME__
+#define cmt_errno() cmt_errno_print(errno, __CMT_FILENAME__, __LINE__)
+#else
+#define cmt_errno() cmt_errno_print(errno, __FILE__, __LINE__)
+#endif
+
+#ifdef _WIN32
+void cmt_winapi_error_print(const char *func, int line);
+#define cmt_winapi_error() cmt_winapi_error_print(__func__, __LINE__)
+#endif
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_map.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_map.h
new file mode 100644
index 000000000..33694315c
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_map.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#ifndef CMT_MAP_H
+#define CMT_MAP_H
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_opts.h>
+#include <cmetrics/cmt_metric.h>
+
+struct cmt_map_label {
+ cfl_sds_t name; /* Label key name */
+ struct cfl_list _head; /* Link to list cmt_labels_map->labels */
+};
+
+struct cmt_map {
+ int type; /* Metric type */
+ struct cmt_opts *opts; /* Reference to parent 'opts' */
+ cfl_sds_t unit; /* Metric unit */
+
+ /* A map without label keys, uses direct access to the static metric ctx */
+ int metric_static_set; /* is the static metric set ? */
+ struct cmt_metric metric;
+
+ /* Used when labels are set */
+ struct cfl_list metrics; /* List of metrics */
+ int label_count; /* Number of labels */
+ struct cfl_list label_keys; /* Linked list of labels */
+ void *parent;
+};
+
+struct cmt_map *cmt_map_create(int type, struct cmt_opts *opts,
+ int count, char **labels, void *parent);
+void cmt_map_destroy(struct cmt_map *map);
+
+struct cmt_metric *cmt_map_metric_get(struct cmt_opts *opts, struct cmt_map *map,
+ int labels_count, char **labels_val,
+ int write_op);
+int cmt_map_metric_get_val(struct cmt_opts *opts, struct cmt_map *map,
+ int labels_count, char **labels_val,
+ double *out_val);
+
+void destroy_label_list(struct cfl_list *label_list);
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_math.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_math.h
new file mode 100644
index 000000000..6f7e3f18f
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_math.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#ifndef CMT_MATH_H
+#define CMT_MATH_H
+
+#include <inttypes.h>
+#include <string.h>
+
+union val_union {
+ uint64_t u;
+ double d;
+};
+
+/*
+ * This is not rocket-science and to make things easier we assume that operating on
+ * floating pointer numbers we will lose precision. So we just do simple casts.
+ */
+
+static inline uint64_t cmt_math_d64_to_uint64(double val)
+{
+ union val_union u;
+
+ u.d = val;
+ return u.u;
+}
+
+static inline double cmt_math_uint64_to_d64(uint64_t val)
+{
+ union val_union u;
+
+ u.u = val;
+ return u.d;
+}
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_metric.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_metric.h
new file mode 100644
index 000000000..ae066b293
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_metric.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#ifndef CMT_METRIC_H
+#define CMT_METRIC_H
+
+#include <cmetrics/cmetrics.h>
+
+struct cmt_metric {
+ /* counters and gauges */
+ uint64_t val;
+
+ /* histogram */
+ uint64_t *hist_buckets;
+ uint64_t hist_count;
+ uint64_t hist_sum;
+
+ /* summary */
+ int sum_quantiles_set; /* specify if quantive values has been set */
+ uint64_t *sum_quantiles; /* 0, 0.25, 0.5, 0.75 and 1 */
+ size_t sum_quantiles_count;
+ uint64_t sum_count;
+ uint64_t sum_sum;
+
+ /* internal */
+ uint64_t hash;
+ uint64_t timestamp;
+ struct cfl_list labels;
+ struct cfl_list _head;
+};
+
+void cmt_metric_set(struct cmt_metric *metric, uint64_t timestamp, double val);
+void cmt_metric_inc(struct cmt_metric *metric, uint64_t timestamp);
+void cmt_metric_dec(struct cmt_metric *metric, uint64_t timestamp);
+void cmt_metric_add(struct cmt_metric *metric, uint64_t timestamp, double val);
+void cmt_metric_sub(struct cmt_metric *metric, uint64_t timestamp, double val);
+double cmt_metric_get_value(struct cmt_metric *metric);
+uint64_t cmt_metric_get_timestamp(struct cmt_metric *metric);
+
+void cmt_metric_hist_inc(struct cmt_metric *metric, uint64_t timestamp,
+ int bucket_id);
+
+void cmt_metric_hist_count_inc(struct cmt_metric *metric, uint64_t timestamp);
+void cmt_metric_hist_count_set(struct cmt_metric *metric, uint64_t timestamp,
+ uint64_t count);
+
+void cmt_metric_hist_sum_add(struct cmt_metric *metric, uint64_t timestamp,
+ double val);
+void cmt_metric_hist_set(struct cmt_metric *metric, uint64_t timestamp,
+ int bucket_id, double val);
+
+uint64_t cmt_metric_hist_get_value(struct cmt_metric *metric, int bucket_id);
+
+double cmt_metric_hist_get_sum_value(struct cmt_metric *metric);
+
+uint64_t cmt_metric_hist_get_count_value(struct cmt_metric *metric);
+
+void cmt_metric_hist_sum_add(struct cmt_metric *metric,
+ uint64_t timestamp, double val);
+void cmt_metric_hist_sum_set(struct cmt_metric *metric, uint64_t timestamp,
+ double val);
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_mpack_utils.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_mpack_utils.h
new file mode 100644
index 000000000..408eb4e32
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_mpack_utils.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#ifndef CMT_MPACK_UTILS_H
+#define CMT_MPACK_UTILS_H
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_mpack_utils_defs.h>
+#include <mpack/mpack.h>
+
+typedef int (*cmt_mpack_unpacker_entry_callback_fn_t)(mpack_reader_t *reader,
+ size_t index, void *context);
+
+struct cmt_mpack_map_entry_callback_t {
+ const char *identifier;
+ cmt_mpack_unpacker_entry_callback_fn_t handler;
+};
+
+int cmt_mpack_consume_double_tag(mpack_reader_t *reader, double *output_buffer);
+int cmt_mpack_consume_uint_tag(mpack_reader_t *reader, uint64_t *output_buffer);
+int cmt_mpack_consume_string_tag(mpack_reader_t *reader, cfl_sds_t *output_buffer);
+int cmt_mpack_unpack_map(mpack_reader_t *reader,
+ struct cmt_mpack_map_entry_callback_t *callback_list,
+ void *context);
+int cmt_mpack_unpack_array(mpack_reader_t *reader,
+ cmt_mpack_unpacker_entry_callback_fn_t entry_processor_callback,
+ void *context);
+int cmt_mpack_peek_array_length(mpack_reader_t *reader);
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_mpack_utils_defs.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_mpack_utils_defs.h
new file mode 100644
index 000000000..361886c06
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_mpack_utils_defs.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#ifndef CMT_MPACK_UTILS_DEFS_H
+#define CMT_MPACK_UTILS_DEFS_H
+
+#define CMT_MPACK_SUCCESS 0
+#define CMT_MPACK_INSUFFICIENT_DATA 1
+#define CMT_MPACK_INVALID_ARGUMENT_ERROR 2
+#define CMT_MPACK_ALLOCATION_ERROR 3
+#define CMT_MPACK_CORRUPT_INPUT_DATA_ERROR 4
+#define CMT_MPACK_CONSUME_ERROR 5
+#define CMT_MPACK_ENGINE_ERROR 6
+#define CMT_MPACK_PENDING_MAP_ENTRIES 7
+#define CMT_MPACK_PENDING_ARRAY_ENTRIES 8
+#define CMT_MPACK_UNEXPECTED_KEY_ERROR 9
+#define CMT_MPACK_UNEXPECTED_DATA_TYPE_ERROR 10
+#define CMT_MPACK_ERROR_CUTOFF 20
+
+#define CMT_MPACK_MAX_ARRAY_ENTRY_COUNT 65535
+#define CMT_MPACK_MAX_MAP_ENTRY_COUNT 10
+#define CMT_MPACK_MAX_STRING_LENGTH 1024
+
+#endif \ No newline at end of file
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_opts.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_opts.h
new file mode 100644
index 000000000..b456149e1
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_opts.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#ifndef CMT_OPTS_H
+#define CMT_OPTS_H
+
+#include <cmetrics/cmetrics.h>
+
+struct cmt_opts {
+ cfl_sds_t ns; /* namespace */
+ cfl_sds_t subsystem; /* subsystem */
+ cfl_sds_t name; /* metric name */
+
+ /* Help string: what's the metric about */
+ cfl_sds_t description;
+
+ /* Formatted full qualified name: namespace_subsystem_name */
+ cfl_sds_t fqname;
+
+ /* Resource index is only used by opentelemtry */
+ int resource_index;
+};
+
+int cmt_opts_init(struct cmt_opts *opts,
+ char *ns, char *subsystem, char *name, char *help);
+void cmt_opts_exit(struct cmt_opts *opts);
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_summary.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_summary.h
new file mode 100644
index 000000000..641b837af
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_summary.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#ifndef CMT_SUMMARY_H
+#define CMT_SUMMARY_H
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_opts.h>
+#include <cmetrics/cmt_metric.h>
+
+/*
+ * The structure only is aware about final 'quantile' values, not percentiles or
+ * any other involved variable. We won't do calculations.
+ */
+struct cmt_summary {
+ /* summary specific */
+ double *quantiles;
+ size_t quantiles_count;
+
+ /* metrics common */
+ struct cmt_opts opts;
+ struct cmt_map *map;
+ struct cfl_list _head;
+ struct cmt *cmt;
+
+};
+
+struct cmt_summary *cmt_summary_create(struct cmt *cmt,
+ char *ns, char *subsystem,
+ char *name, char *help,
+ size_t quantiles_count,
+ double *quantiles,
+ int label_count, char **label_keys);
+
+int cmt_summary_destroy(struct cmt_summary *summary);
+
+int cmt_summary_set_default(struct cmt_summary *summary,
+ uint64_t timestamp,
+ double *quantile_values,
+ double sum,
+ uint64_t count,
+ int labels_count, char **label_vars);
+
+/* quantiles */
+double cmt_summary_quantile_get_value(struct cmt_metric *metric, int quantile_id);
+
+double cmt_summary_get_sum_value(struct cmt_metric *metric);
+uint64_t cmt_summary_get_count_value(struct cmt_metric *metric);
+
+void cmt_summary_quantile_set(struct cmt_metric *metric, uint64_t timestamp,
+ int quantile_id, double val);
+
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_time.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_time.h
new file mode 100644
index 000000000..fbd29a348
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_time.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#ifndef CMT_TIME_H
+#define CMT_TIME_H
+
+#include <inttypes.h>
+#include <time.h>
+
+void cmt_time_from_ns(struct timespec *tm, uint64_t ns);
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_untyped.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_untyped.h
new file mode 100644
index 000000000..72a9c2c53
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_untyped.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#ifndef CMT_UNTYPED_H
+#define CMT_UNTYPED_H
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_opts.h>
+
+struct cmt_untyped {
+ struct cmt_opts opts;
+ struct cmt_map *map;
+ struct cmt *cmt;
+ struct cfl_list _head;
+};
+
+struct cmt_untyped *cmt_untyped_create(struct cmt *cmt,
+ char *ns, char *subsystem,
+ char *name, char *help,
+ int label_count, char **label_keys);
+
+int cmt_untyped_destroy(struct cmt_untyped *counter);
+
+int cmt_untyped_set(struct cmt_untyped *counter, uint64_t timestamp, double val,
+ int labels_count, char **label_vals);
+
+int cmt_untyped_get_val(struct cmt_untyped *counter,
+ int labels_count, char **label_vals, double *out_val);
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_variant_utils.h b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_variant_utils.h
new file mode 100644
index 000000000..0e5c58ad3
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_variant_utils.h
@@ -0,0 +1,626 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#ifndef CMT_VARIANT_UTILS_H
+#define CMT_VARIANT_UTILS_H
+
+#include <mpack/mpack.h>
+
+/* These are the only functions meant for general use,
+ * the reason why the kvlist packing and unpacking
+ * functions are exposed is the internal and external
+ * metadata kvlists in the cmetrics context are not
+ * contained by a variant instance.
+ *
+ * Result :
+ * Upon success all of these return 0, otherwise they will
+ * raise the innermost error code which should be treated
+ * as an opaque value.
+ *
+ * Notes :
+ * When decoding -1 means the check after mpack_read_tag
+ * failed and -2 means the type was not the one expected
+ */
+
+static inline int pack_cfl_variant(mpack_writer_t *writer,
+ struct cfl_variant *value);
+
+static inline int pack_cfl_variant_kvlist(mpack_writer_t *writer,
+ struct cfl_kvlist *kvlist);
+
+static inline int unpack_cfl_variant(mpack_reader_t *reader,
+ struct cfl_variant **value);
+
+static inline int unpack_cfl_kvlist(mpack_reader_t *reader,
+ struct cfl_kvlist **result_kvlist);
+
+/* Packers */
+static inline int pack_cfl_variant_string(mpack_writer_t *writer,
+ char *value)
+{
+ mpack_write_cstr(writer, value);
+
+ return 0;
+}
+
+static inline int pack_cfl_variant_binary(mpack_writer_t *writer,
+ char *value,
+ size_t length)
+{
+ mpack_write_bin(writer, value, length);
+
+ return 0;
+}
+
+static inline int pack_cfl_variant_boolean(mpack_writer_t *writer,
+ unsigned int value)
+{
+ mpack_write_bool(writer, value);
+
+ return 0;
+}
+
+static inline int pack_cfl_variant_int64(mpack_writer_t *writer,
+ int64_t value)
+{
+ mpack_write_int(writer, value);
+
+ return 0;
+}
+
+static inline int pack_cfl_variant_double(mpack_writer_t *writer,
+ double value)
+{
+ mpack_write_double(writer, value);
+
+ return 0;
+}
+
+static inline int pack_cfl_variant_array(mpack_writer_t *writer,
+ struct cfl_array *array)
+{
+ size_t entry_count;
+ struct cfl_variant *entry_value;
+ int result;
+ size_t index;
+
+ entry_count = array->entry_count;
+
+ mpack_start_array(writer, entry_count);
+
+ for (index = 0 ; index < entry_count ; index++) {
+ entry_value = cfl_array_fetch_by_index(array, index);
+
+ if (entry_value == NULL) {
+ return -1;
+ }
+
+ result = pack_cfl_variant(writer, entry_value);
+
+ if (result != 0) {
+ return result;
+ }
+ }
+
+ mpack_finish_array(writer);
+
+ return 0;
+}
+
+static inline int pack_cfl_variant_kvlist(mpack_writer_t *writer,
+ struct cfl_kvlist *kvlist) {
+ size_t entry_count;
+ struct cfl_list *iterator;
+ struct cfl_kvpair *kvpair;
+ int result;
+
+ entry_count = cfl_kvlist_count(kvlist);
+
+ mpack_start_map(writer, entry_count);
+
+ cfl_list_foreach(iterator, &kvlist->list) {
+ kvpair = cfl_list_entry(iterator, struct cfl_kvpair, _head);
+
+ mpack_write_cstr(writer, kvpair->key);
+
+ result = pack_cfl_variant(writer, kvpair->val);
+
+ if (result != 0) {
+ return result;
+ }
+ }
+
+ mpack_finish_map(writer);
+
+ return 0;
+}
+
+static inline int pack_cfl_variant(mpack_writer_t *writer,
+ struct cfl_variant *value)
+{
+ int result;
+
+ if (value->type == CFL_VARIANT_STRING) {
+ result = pack_cfl_variant_string(writer, value->data.as_string);
+ }
+ else if (value->type == CFL_VARIANT_BOOL) {
+ result = pack_cfl_variant_boolean(writer, value->data.as_bool);
+ }
+ else if (value->type == CFL_VARIANT_INT) {
+ result = pack_cfl_variant_int64(writer, value->data.as_int64);
+ }
+ else if (value->type == CFL_VARIANT_DOUBLE) {
+ result = pack_cfl_variant_double(writer, value->data.as_double);
+ }
+ else if (value->type == CFL_VARIANT_ARRAY) {
+ result = pack_cfl_variant_array(writer, value->data.as_array);
+ }
+ else if (value->type == CFL_VARIANT_KVLIST) {
+ result = pack_cfl_variant_kvlist(writer, value->data.as_kvlist);
+ }
+ else if (value->type == CFL_VARIANT_BYTES) {
+ result = pack_cfl_variant_binary(writer,
+ value->data.as_bytes,
+ cfl_sds_len(value->data.as_bytes));
+ }
+ else if (value->type == CFL_VARIANT_REFERENCE) {
+ result = pack_cfl_variant_string(writer, value->data.as_string);
+ }
+ else {
+ result = -1;
+ }
+
+ return result;
+}
+
+/* Unpackers */
+
+static inline int unpack_cfl_variant_read_tag(mpack_reader_t *reader,
+ mpack_tag_t *tag,
+ mpack_type_t expected_type)
+{
+ *tag = mpack_read_tag(reader);
+
+ if (mpack_ok != mpack_reader_error(reader)) {
+ return -1;
+ }
+
+ if (mpack_tag_type(tag) != expected_type) {
+ return -2;
+ }
+
+ return 0;
+}
+
+static inline int unpack_cfl_array(mpack_reader_t *reader,
+ struct cfl_array **result_array)
+{
+ struct cfl_array *internal_array;
+ size_t entry_count;
+ struct cfl_variant *entry_value;
+ int result;
+ size_t index;
+ mpack_tag_t tag;
+
+ result = unpack_cfl_variant_read_tag(reader, &tag, mpack_type_array);
+
+ if (result != 0) {
+ return result;
+ }
+
+ entry_count = mpack_tag_array_count(&tag);
+
+ internal_array = cfl_array_create(entry_count);
+
+ if (internal_array == NULL) {
+ return -3;
+ }
+
+ for (index = 0 ; index < entry_count ; index++) {
+ result = unpack_cfl_variant(reader, &entry_value);
+
+ if (result != 0) {
+ cfl_array_destroy(internal_array);
+
+ return -4;
+ }
+
+ result = cfl_array_append(internal_array, entry_value);
+
+ if (result != 0) {
+ cfl_array_destroy(internal_array);
+
+ return -5;
+ }
+ }
+
+ mpack_done_array(reader);
+
+ if (mpack_reader_error(reader) != mpack_ok) {
+ cfl_array_destroy(internal_array);
+
+ return -6;
+ }
+
+ *result_array = internal_array;
+
+ return 0;
+}
+
+static inline int unpack_cfl_kvlist(mpack_reader_t *reader,
+ struct cfl_kvlist **result_kvlist)
+{
+ struct cfl_kvlist *internal_kvlist;
+ char key_name[256];
+ size_t entry_count;
+ size_t key_length;
+ struct cfl_variant *key_value;
+ mpack_tag_t key_tag;
+ int result;
+ size_t index;
+ mpack_tag_t tag;
+
+ result = unpack_cfl_variant_read_tag(reader, &tag, mpack_type_map);
+
+ if (result != 0) {
+ return result;
+ }
+
+ entry_count = mpack_tag_map_count(&tag);
+
+ internal_kvlist = cfl_kvlist_create();
+
+ if (internal_kvlist == NULL) {
+ return -3;
+ }
+
+ result = 0;
+ key_value = NULL;
+
+ for (index = 0 ; index < entry_count ; index++) {
+ result = unpack_cfl_variant_read_tag(reader, &key_tag, mpack_type_str);
+
+ if (result != 0) {
+ result = -4;
+
+ break;
+ }
+
+ key_length = mpack_tag_str_length(&key_tag);
+
+ if (key_length >= sizeof(key_name)) {
+ result = -5;
+
+ break;
+ }
+
+ mpack_read_cstr(reader, key_name, sizeof(key_name), key_length);
+
+ key_name[key_length] = '\0';
+
+ mpack_done_str(reader);
+
+ if (mpack_ok != mpack_reader_error(reader)) {
+ result = -6;
+
+ break;
+ }
+
+ result = unpack_cfl_variant(reader, &key_value);
+
+ if (result != 0) {
+ result = -7;
+
+ break;
+ }
+
+ result = cfl_kvlist_insert(internal_kvlist, key_name, key_value);
+
+ if (result != 0) {
+ result = -8;
+
+ break;
+ }
+
+ key_value = NULL;
+ }
+
+ mpack_done_map(reader);
+
+ if (mpack_reader_error(reader) != mpack_ok) {
+ result = -9;
+ }
+
+ if (result != 0) {
+ cfl_kvlist_destroy(internal_kvlist);
+
+ if (key_value != NULL) {
+ cfl_variant_destroy(key_value);
+ }
+ }
+ else {
+ *result_kvlist = internal_kvlist;
+ }
+
+ return result;
+}
+
+static inline int unpack_cfl_variant_string(mpack_reader_t *reader,
+ struct cfl_variant **value)
+{
+ size_t value_length;
+ char *value_data;
+ int result;
+ mpack_tag_t tag;
+
+ result = unpack_cfl_variant_read_tag(reader, &tag, mpack_type_str);
+
+ if (result != 0) {
+ return result;
+ }
+
+ value_length = mpack_tag_str_length(&tag);
+
+ value_data = cfl_sds_create_size(value_length + 1);
+
+ if (value_data == NULL) {
+ return -3;
+ }
+
+ cfl_sds_set_len(value_data, value_length);
+
+ mpack_read_cstr(reader, value_data, value_length + 1, value_length);
+
+ mpack_done_str(reader);
+
+ if (mpack_reader_error(reader) != mpack_ok) {
+ cfl_sds_destroy(value_data);
+
+ return -4;
+ }
+
+ *value = cfl_variant_create_from_reference(value_data);
+
+ if (*value == NULL) {
+ return -5;
+ }
+
+ (*value)->type = CFL_VARIANT_STRING;
+
+ return 0;
+}
+
+static inline int unpack_cfl_variant_binary(mpack_reader_t *reader,
+ struct cfl_variant **value)
+{
+ size_t value_length;
+ char *value_data;
+ int result;
+ mpack_tag_t tag;
+
+ result = unpack_cfl_variant_read_tag(reader, &tag, mpack_type_bin);
+
+ if (result != 0) {
+ return result;
+ }
+
+ value_length = mpack_tag_bin_length(&tag);
+
+ value_data = cfl_sds_create_size(value_length);
+
+ if (value_data == NULL) {
+ return -3;
+ }
+
+ cfl_sds_set_len(value_data, value_length);
+
+ mpack_read_bytes(reader, value_data, value_length);
+
+ mpack_done_bin(reader);
+
+ if (mpack_reader_error(reader) != mpack_ok) {
+ cfl_sds_destroy(value_data);
+
+ return -4;
+ }
+
+ *value = cfl_variant_create_from_reference(value_data);
+
+ if (*value == NULL) {
+ return -5;
+ }
+
+ (*value)->type = CFL_VARIANT_BYTES;
+
+ return 0;
+}
+
+static inline int unpack_cfl_variant_boolean(mpack_reader_t *reader,
+ struct cfl_variant **value)
+{
+ int result;
+ mpack_tag_t tag;
+
+ result = unpack_cfl_variant_read_tag(reader, &tag, mpack_type_bool);
+
+ if (result != 0) {
+ return result;
+ }
+
+ *value = cfl_variant_create_from_bool((unsigned int) mpack_tag_bool_value(&tag));
+
+ if (*value == NULL) {
+ return -3;
+ }
+
+ return 0;
+}
+
+static inline int unpack_cfl_variant_uint64(mpack_reader_t *reader,
+ struct cfl_variant **value)
+{
+ int result;
+ mpack_tag_t tag;
+
+ result = unpack_cfl_variant_read_tag(reader, &tag, mpack_type_uint);
+
+ if (result != 0) {
+ return result;
+ }
+
+ *value = cfl_variant_create_from_int64((int64_t) mpack_tag_uint_value(&tag));
+
+ if (*value == NULL) {
+ return -3;
+ }
+
+ return 0;
+}
+
+static inline int unpack_cfl_variant_int64(mpack_reader_t *reader,
+ struct cfl_variant **value)
+{
+ int result;
+ mpack_tag_t tag;
+
+ result = unpack_cfl_variant_read_tag(reader, &tag, mpack_type_int);
+
+ if (result != 0) {
+ return result;
+ }
+
+ *value = cfl_variant_create_from_int64((int64_t) mpack_tag_int_value(&tag));
+
+ if (*value == NULL) {
+ return -3;
+ }
+
+ return 0;
+}
+
+static inline int unpack_cfl_variant_double(mpack_reader_t *reader,
+ struct cfl_variant **value)
+{
+ int result;
+ mpack_tag_t tag;
+
+ result = unpack_cfl_variant_read_tag(reader, &tag, mpack_type_double);
+
+ if (result != 0) {
+ return result;
+ }
+
+ *value = cfl_variant_create_from_double(mpack_tag_double_value(&tag));
+
+ if (*value == NULL) {
+ return -3;
+ }
+
+ return 0;
+}
+
+static inline int unpack_cfl_variant_array(mpack_reader_t *reader,
+ struct cfl_variant **value)
+{
+ struct cfl_array *unpacked_array;
+ int result;
+
+ result = unpack_cfl_array(reader, &unpacked_array);
+
+ if (result != 0) {
+ return result;
+ }
+
+ *value = cfl_variant_create_from_array(unpacked_array);
+
+ if (*value == NULL) {
+ return -3;
+ }
+
+ return 0;
+}
+
+static inline int unpack_cfl_variant_kvlist(mpack_reader_t *reader,
+ struct cfl_variant **value)
+{
+ struct cfl_kvlist *unpacked_kvlist;
+ int result;
+
+ result = unpack_cfl_kvlist(reader, &unpacked_kvlist);
+
+ if (result != 0) {
+ return result;
+ }
+
+ *value = cfl_variant_create_from_kvlist(unpacked_kvlist);
+
+ if (*value == NULL) {
+ return -3;
+ }
+
+ return 0;
+}
+
+static inline int unpack_cfl_variant(mpack_reader_t *reader,
+ struct cfl_variant **value)
+{
+ mpack_type_t value_type;
+ int result;
+ mpack_tag_t tag;
+
+ tag = mpack_peek_tag(reader);
+
+ if (mpack_ok != mpack_reader_error(reader)) {
+ return -1;
+ }
+
+ value_type = mpack_tag_type(&tag);
+
+ if (value_type == mpack_type_str) {
+ result = unpack_cfl_variant_string(reader, value);
+ }
+ else if (value_type == mpack_type_str) {
+ result = unpack_cfl_variant_boolean(reader, value);
+ }
+ else if (value_type == mpack_type_int) {
+ result = unpack_cfl_variant_int64(reader, value);
+ }
+ else if (value_type == mpack_type_uint) {
+ result = unpack_cfl_variant_uint64(reader, value);
+ }
+ else if (value_type == mpack_type_double) {
+ result = unpack_cfl_variant_double(reader, value);
+ }
+ else if (value_type == mpack_type_array) {
+ result = unpack_cfl_variant_array(reader, value);
+ }
+ else if (value_type == mpack_type_map) {
+ result = unpack_cfl_variant_kvlist(reader, value);
+ }
+ else if (value_type == mpack_type_bin) {
+ result = unpack_cfl_variant_binary(reader, value);
+ }
+ else {
+ result = -1;
+ }
+
+ return result;
+}
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_version.h.in b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_version.h.in
new file mode 100644
index 000000000..a1b43a1c2
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/cmetrics/cmt_version.h.in
@@ -0,0 +1,36 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Chunk I/O
+ * =========
+ * Copyright 2018 Eduardo Silva <eduardo@monkey.io>
+ *
+ * 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.
+ */
+
+#ifndef CMT_VERSION_H
+#define CMT_VERSION_H
+
+/* Helpers to convert/format version string */
+#define STR_HELPER(s) #s
+#define STR(s) STR_HELPER(s)
+
+/* Chunk I/O Version */
+#define CMT_VERSION_MAJOR @CMT_VERSION_MAJOR@
+#define CMT_VERSION_MINOR @CMT_VERSION_MINOR@
+#define CMT_VERSION_PATCH @CMT_VERSION_PATCH@
+#define CMT_VERSION (CMT_VERSION_MAJOR * 10000 \
+ CMT_VERSION_MINOR * 100 \
+ CMT_VERSION_PATCH)
+#define CMT_VERSION_STR "@CMT_VERSION_STR@"
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/include/prometheus_remote_write/protobuf-c.h b/src/fluent-bit/lib/cmetrics/include/prometheus_remote_write/protobuf-c.h
new file mode 100644
index 000000000..b633722ed
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/prometheus_remote_write/protobuf-c.h
@@ -0,0 +1,1110 @@
+/*
+ * Copyright (c) 2008-2018, Dave Benson and the protobuf-c authors.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+/*! \file
+ * \mainpage Introduction
+ *
+ * This is [protobuf-c], a C implementation of [Protocol Buffers].
+ *
+ * This file defines the public API for the `libprotobuf-c` support library.
+ * This API includes interfaces that can be used directly by client code as well
+ * as the interfaces used by the code generated by the `protoc-c` compiler.
+ *
+ * The `libprotobuf-c` support library performs the actual serialization and
+ * deserialization of Protocol Buffers messages. It interacts with structures,
+ * definitions, and metadata generated by the `protoc-c` compiler from .proto
+ * files.
+ *
+ * \authors Dave Benson and the `protobuf-c` authors.
+ *
+ * \copyright 2008-2014. Licensed under the terms of the [BSD-2-Clause] license.
+ *
+ * [protobuf-c]: https://github.com/protobuf-c/protobuf-c
+ * [Protocol Buffers]: https://developers.google.com/protocol-buffers/
+ * [BSD-2-Clause]: http://opensource.org/licenses/BSD-2-Clause
+ *
+ * \page gencode Generated Code
+ *
+ * For each enum, we generate a C enum. For each message, we generate a C
+ * structure which can be cast to a `ProtobufCMessage`.
+ *
+ * For each enum and message, we generate a descriptor object that allows us to
+ * implement a kind of reflection on the structures.
+ *
+ * First, some naming conventions:
+ *
+ * - The name of the type for enums and messages and services is camel case
+ * (meaning WordsAreCrammedTogether) except that double underscores are used
+ * to delimit scopes. For example, the following `.proto` file:
+ *
+~~~{.proto}
+ package foo.bar;
+ message BazBah {
+ optional int32 val = 1;
+ }
+~~~
+ *
+ * would generate a C type `Foo__Bar__BazBah`.
+ *
+ * - Identifiers for functions and globals are all lowercase, with camel case
+ * words separated by single underscores. For example, one of the function
+ * prototypes generated by `protoc-c` for the above example:
+ *
+~~~{.c}
+Foo__Bar__BazBah *
+ foo__bar__baz_bah__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+~~~
+ *
+ * - Identifiers for enum values contain an uppercase prefix which embeds the
+ * package name and the enum type name.
+ *
+ * - A double underscore is used to separate further components of identifier
+ * names.
+ *
+ * For example, in the name of the unpack function above, the package name
+ * `foo.bar` has become `foo__bar`, the message name BazBah has become
+ * `baz_bah`, and the method name is `unpack`. These are all joined with double
+ * underscores to form the C identifier `foo__bar__baz_bah__unpack`.
+ *
+ * We also generate descriptor objects for messages and enums. These are
+ * declared in the `.pb-c.h` files:
+ *
+~~~{.c}
+extern const ProtobufCMessageDescriptor foo__bar__baz_bah__descriptor;
+~~~
+ *
+ * The message structures all begin with `ProtobufCMessageDescriptor *` which is
+ * sufficient to allow them to be cast to `ProtobufCMessage`.
+ *
+ * For each message defined in a `.proto` file, we generate a number of
+ * functions and macros. Each function name contains a prefix based on the
+ * package name and message name in order to make it a unique C identifier.
+ *
+ * - `INIT`. Statically initializes a message object, initializing its
+ * descriptor and setting its fields to default values. Uninitialized
+ * messages cannot be processed by the protobuf-c library.
+ *
+~~~{.c}
+#define FOO__BAR__BAZ_BAH__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&foo__bar__baz_bah__descriptor), 0 }
+~~~
+ * - `init()`. Initializes a message object, initializing its descriptor and
+ * setting its fields to default values. Uninitialized messages cannot be
+ * processed by the protobuf-c library.
+ *
+~~~{.c}
+void foo__bar__baz_bah__init
+ (Foo__Bar__BazBah *message);
+~~~
+ * - `unpack()`. Unpacks data for a particular message format. Note that the
+ * `allocator` parameter is usually `NULL` to indicate that the system's
+ * `malloc()` and `free()` functions should be used for dynamically allocating
+ * memory.
+ *
+~~~{.c}
+Foo__Bar__BazBah *
+ foo__bar__baz_bah__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+~~~
+ *
+ * - `free_unpacked()`. Frees a message object obtained with the `unpack()`
+ * method. Freeing `NULL` is allowed (the same as with `free()`).
+ *
+~~~{.c}
+void foo__bar__baz_bah__free_unpacked
+ (Foo__Bar__BazBah *message,
+ ProtobufCAllocator *allocator);
+~~~
+ *
+ * - `get_packed_size()`. Calculates the length in bytes of the serialized
+ * representation of the message object.
+ *
+~~~{.c}
+size_t foo__bar__baz_bah__get_packed_size
+ (const Foo__Bar__BazBah *message);
+~~~
+ *
+ * - `pack()`. Pack a message object into a preallocated buffer. Assumes that
+ * the buffer is large enough. (Use `get_packed_size()` first.)
+ *
+~~~{.c}
+size_t foo__bar__baz_bah__pack
+ (const Foo__Bar__BazBah *message,
+ uint8_t *out);
+~~~
+ *
+ * - `pack_to_buffer()`. Packs a message into a "virtual buffer". This is an
+ * object which defines an "append bytes" callback to consume data as it is
+ * serialized.
+ *
+~~~{.c}
+size_t foo__bar__baz_bah__pack_to_buffer
+ (const Foo__Bar__BazBah *message,
+ ProtobufCBuffer *buffer);
+~~~
+ *
+ * \page pack Packing and unpacking messages
+ *
+ * To pack a message, first compute the packed size of the message with
+ * protobuf_c_message_get_packed_size(), then allocate a buffer of at least
+ * that size, then call protobuf_c_message_pack().
+ *
+ * Alternatively, a message can be serialized without calculating the final size
+ * first. Use the protobuf_c_message_pack_to_buffer() function and provide a
+ * ProtobufCBuffer object which implements an "append" method that consumes
+ * data.
+ *
+ * To unpack a message, call the protobuf_c_message_unpack() function. The
+ * result can be cast to an object of the type that matches the descriptor for
+ * the message.
+ *
+ * The result of unpacking a message should be freed with
+ * protobuf_c_message_free_unpacked().
+ */
+
+#ifndef PROTOBUF_C_H
+#define PROTOBUF_C_H
+
+#include <assert.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+# define PROTOBUF_C__BEGIN_DECLS extern "C" {
+# define PROTOBUF_C__END_DECLS }
+#else
+# define PROTOBUF_C__BEGIN_DECLS
+# define PROTOBUF_C__END_DECLS
+#endif
+
+PROTOBUF_C__BEGIN_DECLS
+
+#if defined(_WIN32) && defined(PROTOBUF_C_USE_SHARED_LIB)
+# ifdef PROTOBUF_C_EXPORT
+# define PROTOBUF_C__API __declspec(dllexport)
+# else
+# define PROTOBUF_C__API __declspec(dllimport)
+# endif
+#else
+# define PROTOBUF_C__API
+#endif
+
+#if !defined(PROTOBUF_C__NO_DEPRECATED) && \
+ ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
+# define PROTOBUF_C__DEPRECATED __attribute__((__deprecated__))
+#else
+# define PROTOBUF_C__DEPRECATED
+#endif
+
+#ifndef PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE
+ #define PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(enum_name) \
+ , _##enum_name##_IS_INT_SIZE = INT_MAX
+#endif
+
+#define PROTOBUF_C__SERVICE_DESCRIPTOR_MAGIC 0x14159bc3
+#define PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC 0x28aaeef9
+#define PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC 0x114315af
+
+/* Empty string used for initializers */
+#if defined(_WIN32) && defined(PROTOBUF_C_USE_SHARED_LIB)
+static const char protobuf_c_empty_string[] = "";
+#else
+extern const char protobuf_c_empty_string[];
+#endif
+
+/**
+ * \defgroup api Public API
+ *
+ * This is the public API for `libprotobuf-c`. These interfaces are stable and
+ * subject to Semantic Versioning guarantees.
+ *
+ * @{
+ */
+
+/**
+ * Values for the `flags` word in `ProtobufCFieldDescriptor`.
+ */
+typedef enum {
+ /** Set if the field is repeated and marked with the `packed` option. */
+ PROTOBUF_C_FIELD_FLAG_PACKED = (1 << 0),
+
+ /** Set if the field is marked with the `deprecated` option. */
+ PROTOBUF_C_FIELD_FLAG_DEPRECATED = (1 << 1),
+
+ /** Set if the field is a member of a oneof (union). */
+ PROTOBUF_C_FIELD_FLAG_ONEOF = (1 << 2),
+} ProtobufCFieldFlag;
+
+/**
+ * Message field rules.
+ *
+ * \see [Defining A Message Type] in the Protocol Buffers documentation.
+ *
+ * [Defining A Message Type]:
+ * https://developers.google.com/protocol-buffers/docs/proto#simple
+ */
+typedef enum {
+ /** A well-formed message must have exactly one of this field. */
+ PROTOBUF_C_LABEL_REQUIRED,
+
+ /**
+ * A well-formed message can have zero or one of this field (but not
+ * more than one).
+ */
+ PROTOBUF_C_LABEL_OPTIONAL,
+
+ /**
+ * This field can be repeated any number of times (including zero) in a
+ * well-formed message. The order of the repeated values will be
+ * preserved.
+ */
+ PROTOBUF_C_LABEL_REPEATED,
+
+ /**
+ * This field has no label. This is valid only in proto3 and is
+ * equivalent to OPTIONAL but no "has" quantifier will be consulted.
+ */
+ PROTOBUF_C_LABEL_NONE,
+} ProtobufCLabel;
+
+/**
+ * Field value types.
+ *
+ * \see [Scalar Value Types] in the Protocol Buffers documentation.
+ *
+ * [Scalar Value Types]:
+ * https://developers.google.com/protocol-buffers/docs/proto#scalar
+ */
+typedef enum {
+ PROTOBUF_C_TYPE_INT32, /**< int32 */
+ PROTOBUF_C_TYPE_SINT32, /**< signed int32 */
+ PROTOBUF_C_TYPE_SFIXED32, /**< signed int32 (4 bytes) */
+ PROTOBUF_C_TYPE_INT64, /**< int64 */
+ PROTOBUF_C_TYPE_SINT64, /**< signed int64 */
+ PROTOBUF_C_TYPE_SFIXED64, /**< signed int64 (8 bytes) */
+ PROTOBUF_C_TYPE_UINT32, /**< unsigned int32 */
+ PROTOBUF_C_TYPE_FIXED32, /**< unsigned int32 (4 bytes) */
+ PROTOBUF_C_TYPE_UINT64, /**< unsigned int64 */
+ PROTOBUF_C_TYPE_FIXED64, /**< unsigned int64 (8 bytes) */
+ PROTOBUF_C_TYPE_FLOAT, /**< float */
+ PROTOBUF_C_TYPE_DOUBLE, /**< double */
+ PROTOBUF_C_TYPE_BOOL, /**< boolean */
+ PROTOBUF_C_TYPE_ENUM, /**< enumerated type */
+ PROTOBUF_C_TYPE_STRING, /**< UTF-8 or ASCII string */
+ PROTOBUF_C_TYPE_BYTES, /**< arbitrary byte sequence */
+ PROTOBUF_C_TYPE_MESSAGE, /**< nested message */
+} ProtobufCType;
+
+/**
+ * Field wire types.
+ *
+ * \see [Message Structure] in the Protocol Buffers documentation.
+ *
+ * [Message Structure]:
+ * https://developers.google.com/protocol-buffers/docs/encoding#structure
+ */
+typedef enum {
+ PROTOBUF_C_WIRE_TYPE_VARINT = 0,
+ PROTOBUF_C_WIRE_TYPE_64BIT = 1,
+ PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED = 2,
+ /* "Start group" and "end group" wire types are unsupported. */
+ PROTOBUF_C_WIRE_TYPE_32BIT = 5,
+} ProtobufCWireType;
+
+struct ProtobufCAllocator;
+struct ProtobufCBinaryData;
+struct ProtobufCBuffer;
+struct ProtobufCBufferSimple;
+struct ProtobufCEnumDescriptor;
+struct ProtobufCEnumValue;
+struct ProtobufCEnumValueIndex;
+struct ProtobufCFieldDescriptor;
+struct ProtobufCIntRange;
+struct ProtobufCMessage;
+struct ProtobufCMessageDescriptor;
+struct ProtobufCMessageUnknownField;
+struct ProtobufCMethodDescriptor;
+struct ProtobufCService;
+struct ProtobufCServiceDescriptor;
+
+typedef struct ProtobufCAllocator ProtobufCAllocator;
+typedef struct ProtobufCBinaryData ProtobufCBinaryData;
+typedef struct ProtobufCBuffer ProtobufCBuffer;
+typedef struct ProtobufCBufferSimple ProtobufCBufferSimple;
+typedef struct ProtobufCEnumDescriptor ProtobufCEnumDescriptor;
+typedef struct ProtobufCEnumValue ProtobufCEnumValue;
+typedef struct ProtobufCEnumValueIndex ProtobufCEnumValueIndex;
+typedef struct ProtobufCFieldDescriptor ProtobufCFieldDescriptor;
+typedef struct ProtobufCIntRange ProtobufCIntRange;
+typedef struct ProtobufCMessage ProtobufCMessage;
+typedef struct ProtobufCMessageDescriptor ProtobufCMessageDescriptor;
+typedef struct ProtobufCMessageUnknownField ProtobufCMessageUnknownField;
+typedef struct ProtobufCMethodDescriptor ProtobufCMethodDescriptor;
+typedef struct ProtobufCService ProtobufCService;
+typedef struct ProtobufCServiceDescriptor ProtobufCServiceDescriptor;
+
+/** Boolean type. */
+typedef int protobuf_c_boolean;
+
+typedef void (*ProtobufCClosure)(const ProtobufCMessage *, void *closure_data);
+typedef void (*ProtobufCMessageInit)(ProtobufCMessage *);
+typedef void (*ProtobufCServiceDestroy)(ProtobufCService *);
+
+/**
+ * Structure for defining a custom memory allocator.
+ */
+struct ProtobufCAllocator {
+ /** Function to allocate memory. */
+ void *(*alloc)(void *allocator_data, size_t size);
+
+ /** Function to free memory. */
+ void (*free)(void *allocator_data, void *pointer);
+
+ /** Opaque pointer passed to `alloc` and `free` functions. */
+ void *allocator_data;
+};
+
+/**
+ * Structure for the protobuf `bytes` scalar type.
+ *
+ * The data contained in a `ProtobufCBinaryData` is an arbitrary sequence of
+ * bytes. It may contain embedded `NUL` characters and is not required to be
+ * `NUL`-terminated.
+ */
+struct ProtobufCBinaryData {
+ size_t len; /**< Number of bytes in the `data` field. */
+ uint8_t *data; /**< Data bytes. */
+};
+
+/**
+ * Structure for defining a virtual append-only buffer. Used by
+ * protobuf_c_message_pack_to_buffer() to abstract the consumption of serialized
+ * bytes.
+ *
+ * `ProtobufCBuffer` "subclasses" may be defined on the stack. For example, to
+ * write to a `FILE` object:
+ *
+~~~{.c}
+typedef struct {
+ ProtobufCBuffer base;
+ FILE *fp;
+} BufferAppendToFile;
+
+static void
+my_buffer_file_append(ProtobufCBuffer *buffer,
+ size_t len,
+ const uint8_t *data)
+{
+ BufferAppendToFile *file_buf = (BufferAppendToFile *) buffer;
+ fwrite(data, len, 1, file_buf->fp); // XXX: No error handling!
+}
+~~~
+ *
+ * To use this new type of ProtobufCBuffer, it could be called as follows:
+ *
+~~~{.c}
+...
+BufferAppendToFile tmp = {0};
+tmp.base.append = my_buffer_file_append;
+tmp.fp = fp;
+protobuf_c_message_pack_to_buffer(&message, &tmp);
+...
+~~~
+ */
+struct ProtobufCBuffer {
+ /** Append function. Consumes the `len` bytes stored at `data`. */
+ void (*append)(ProtobufCBuffer *buffer,
+ size_t len,
+ const uint8_t *data);
+};
+
+/**
+ * Simple buffer "subclass" of `ProtobufCBuffer`.
+ *
+ * A `ProtobufCBufferSimple` object is declared on the stack and uses a
+ * scratch buffer provided by the user for the initial allocation. It performs
+ * exponential resizing, using dynamically allocated memory. A
+ * `ProtobufCBufferSimple` object can be created and used as follows:
+ *
+~~~{.c}
+uint8_t pad[128];
+ProtobufCBufferSimple simple = PROTOBUF_C_BUFFER_SIMPLE_INIT(pad);
+ProtobufCBuffer *buffer = (ProtobufCBuffer *) &simple;
+~~~
+ *
+ * `buffer` can now be used with `protobuf_c_message_pack_to_buffer()`. Once a
+ * message has been serialized to a `ProtobufCBufferSimple` object, the
+ * serialized data bytes can be accessed from the `.data` field.
+ *
+ * To free the memory allocated by a `ProtobufCBufferSimple` object, if any,
+ * call PROTOBUF_C_BUFFER_SIMPLE_CLEAR() on the object, for example:
+ *
+~~~{.c}
+PROTOBUF_C_BUFFER_SIMPLE_CLEAR(&simple);
+~~~
+ *
+ * \see PROTOBUF_C_BUFFER_SIMPLE_INIT
+ * \see PROTOBUF_C_BUFFER_SIMPLE_CLEAR
+ */
+struct ProtobufCBufferSimple {
+ /** "Base class". */
+ ProtobufCBuffer base;
+ /** Number of bytes allocated in `data`. */
+ size_t alloced;
+ /** Number of bytes currently stored in `data`. */
+ size_t len;
+ /** Data bytes. */
+ uint8_t *data;
+ /** Whether `data` must be freed. */
+ protobuf_c_boolean must_free_data;
+ /** Allocator to use. May be NULL to indicate the system allocator. */
+ ProtobufCAllocator *allocator;
+};
+
+/**
+ * Describes an enumeration as a whole, with all of its values.
+ */
+struct ProtobufCEnumDescriptor {
+ /** Magic value checked to ensure that the API is used correctly. */
+ uint32_t magic;
+
+ /** The qualified name (e.g., "namespace.Type"). */
+ const char *name;
+ /** The unqualified name as given in the .proto file (e.g., "Type"). */
+ const char *short_name;
+ /** Identifier used in generated C code. */
+ const char *c_name;
+ /** The dot-separated namespace. */
+ const char *package_name;
+
+ /** Number elements in `values`. */
+ unsigned n_values;
+ /** Array of distinct values, sorted by numeric value. */
+ const ProtobufCEnumValue *values;
+
+ /** Number of elements in `values_by_name`. */
+ unsigned n_value_names;
+ /** Array of named values, including aliases, sorted by name. */
+ const ProtobufCEnumValueIndex *values_by_name;
+
+ /** Number of elements in `value_ranges`. */
+ unsigned n_value_ranges;
+ /** Value ranges, for faster lookups by numeric value. */
+ const ProtobufCIntRange *value_ranges;
+
+ /** Reserved for future use. */
+ void *reserved1;
+ /** Reserved for future use. */
+ void *reserved2;
+ /** Reserved for future use. */
+ void *reserved3;
+ /** Reserved for future use. */
+ void *reserved4;
+};
+
+/**
+ * Represents a single value of an enumeration.
+ */
+struct ProtobufCEnumValue {
+ /** The string identifying this value in the .proto file. */
+ const char *name;
+
+ /** The string identifying this value in generated C code. */
+ const char *c_name;
+
+ /** The numeric value assigned in the .proto file. */
+ int value;
+};
+
+/**
+ * Used by `ProtobufCEnumDescriptor` to look up enum values.
+ */
+struct ProtobufCEnumValueIndex {
+ /** Name of the enum value. */
+ const char *name;
+ /** Index into values[] array. */
+ unsigned index;
+};
+
+/**
+ * Describes a single field in a message.
+ */
+struct ProtobufCFieldDescriptor {
+ /** Name of the field as given in the .proto file. */
+ const char *name;
+
+ /** Tag value of the field as given in the .proto file. */
+ uint32_t id;
+
+ /** Whether the field is `REQUIRED`, `OPTIONAL`, or `REPEATED`. */
+ ProtobufCLabel label;
+
+ /** The type of the field. */
+ ProtobufCType type;
+
+ /**
+ * The offset in bytes of the message's C structure's quantifier field
+ * (the `has_MEMBER` field for optional members or the `n_MEMBER` field
+ * for repeated members or the case enum for oneofs).
+ */
+ unsigned quantifier_offset;
+
+ /**
+ * The offset in bytes into the message's C structure for the member
+ * itself.
+ */
+ unsigned offset;
+
+ /**
+ * A type-specific descriptor.
+ *
+ * If `type` is `PROTOBUF_C_TYPE_ENUM`, then `descriptor` points to the
+ * corresponding `ProtobufCEnumDescriptor`.
+ *
+ * If `type` is `PROTOBUF_C_TYPE_MESSAGE`, then `descriptor` points to
+ * the corresponding `ProtobufCMessageDescriptor`.
+ *
+ * Otherwise this field is NULL.
+ */
+ const void *descriptor; /* for MESSAGE and ENUM types */
+
+ /** The default value for this field, if defined. May be NULL. */
+ const void *default_value;
+
+ /**
+ * A flag word. Zero or more of the bits defined in the
+ * `ProtobufCFieldFlag` enum may be set.
+ */
+ uint32_t flags;
+
+ /** Reserved for future use. */
+ unsigned reserved_flags;
+ /** Reserved for future use. */
+ void *reserved2;
+ /** Reserved for future use. */
+ void *reserved3;
+};
+
+/**
+ * Helper structure for optimizing int => index lookups in the case
+ * where the keys are mostly consecutive values, as they presumably are for
+ * enums and fields.
+ *
+ * The data structures requires that the values in the original array are
+ * sorted.
+ */
+struct ProtobufCIntRange {
+ int start_value;
+ unsigned orig_index;
+ /*
+ * NOTE: the number of values in the range can be inferred by looking
+ * at the next element's orig_index. A dummy element is added to make
+ * this simple.
+ */
+};
+
+/**
+ * An instance of a message.
+ *
+ * `ProtobufCMessage` is a light-weight "base class" for all messages.
+ *
+ * In particular, `ProtobufCMessage` doesn't have any allocation policy
+ * associated with it. That's because it's common to create `ProtobufCMessage`
+ * objects on the stack. In fact, that's what we recommend for sending messages.
+ * If the object is allocated from the stack, you can't really have a memory
+ * leak.
+ *
+ * This means that calls to functions like protobuf_c_message_unpack() which
+ * return a `ProtobufCMessage` must be paired with a call to a free function,
+ * like protobuf_c_message_free_unpacked().
+ */
+struct ProtobufCMessage {
+ /** The descriptor for this message type. */
+ const ProtobufCMessageDescriptor *descriptor;
+ /** The number of elements in `unknown_fields`. */
+ unsigned n_unknown_fields;
+ /** The fields that weren't recognized by the parser. */
+ ProtobufCMessageUnknownField *unknown_fields;
+};
+
+/**
+ * Describes a message.
+ */
+struct ProtobufCMessageDescriptor {
+ /** Magic value checked to ensure that the API is used correctly. */
+ uint32_t magic;
+
+ /** The qualified name (e.g., "namespace.Type"). */
+ const char *name;
+ /** The unqualified name as given in the .proto file (e.g., "Type"). */
+ const char *short_name;
+ /** Identifier used in generated C code. */
+ const char *c_name;
+ /** The dot-separated namespace. */
+ const char *package_name;
+
+ /**
+ * Size in bytes of the C structure representing an instance of this
+ * type of message.
+ */
+ size_t sizeof_message;
+
+ /** Number of elements in `fields`. */
+ unsigned n_fields;
+ /** Field descriptors, sorted by tag number. */
+ const ProtobufCFieldDescriptor *fields;
+ /** Used for looking up fields by name. */
+ const unsigned *fields_sorted_by_name;
+
+ /** Number of elements in `field_ranges`. */
+ unsigned n_field_ranges;
+ /** Used for looking up fields by id. */
+ const ProtobufCIntRange *field_ranges;
+
+ /** Message initialisation function. */
+ ProtobufCMessageInit message_init;
+
+ /** Reserved for future use. */
+ void *reserved1;
+ /** Reserved for future use. */
+ void *reserved2;
+ /** Reserved for future use. */
+ void *reserved3;
+};
+
+/**
+ * An unknown message field.
+ */
+struct ProtobufCMessageUnknownField {
+ /** The tag number. */
+ uint32_t tag;
+ /** The wire type of the field. */
+ ProtobufCWireType wire_type;
+ /** Number of bytes in `data`. */
+ size_t len;
+ /** Field data. */
+ uint8_t *data;
+};
+
+/**
+ * Method descriptor.
+ */
+struct ProtobufCMethodDescriptor {
+ /** Method name. */
+ const char *name;
+ /** Input message descriptor. */
+ const ProtobufCMessageDescriptor *input;
+ /** Output message descriptor. */
+ const ProtobufCMessageDescriptor *output;
+};
+
+/**
+ * Service.
+ */
+struct ProtobufCService {
+ /** Service descriptor. */
+ const ProtobufCServiceDescriptor *descriptor;
+ /** Function to invoke the service. */
+ void (*invoke)(ProtobufCService *service,
+ unsigned method_index,
+ const ProtobufCMessage *input,
+ ProtobufCClosure closure,
+ void *closure_data);
+ /** Function to destroy the service. */
+ void (*destroy)(ProtobufCService *service);
+};
+
+/**
+ * Service descriptor.
+ */
+struct ProtobufCServiceDescriptor {
+ /** Magic value checked to ensure that the API is used correctly. */
+ uint32_t magic;
+
+ /** Service name. */
+ const char *name;
+ /** Short version of service name. */
+ const char *short_name;
+ /** C identifier for the service name. */
+ const char *c_name;
+ /** Package name. */
+ const char *package;
+ /** Number of elements in `methods`. */
+ unsigned n_methods;
+ /** Method descriptors, in the order defined in the .proto file. */
+ const ProtobufCMethodDescriptor *methods;
+ /** Sort index of methods. */
+ const unsigned *method_indices_by_name;
+};
+
+/**
+ * Get the version of the protobuf-c library. Note that this is the version of
+ * the library linked against, not the version of the headers compiled against.
+ *
+ * \return A string containing the version number of protobuf-c.
+ */
+PROTOBUF_C__API
+const char *
+protobuf_c_version(void);
+
+/**
+ * Get the version of the protobuf-c library. Note that this is the version of
+ * the library linked against, not the version of the headers compiled against.
+ *
+ * \return A 32 bit unsigned integer containing the version number of
+ * protobuf-c, represented in base-10 as (MAJOR*1E6) + (MINOR*1E3) + PATCH.
+ */
+PROTOBUF_C__API
+uint32_t
+protobuf_c_version_number(void);
+
+/**
+ * The version of the protobuf-c headers, represented as a string using the same
+ * format as protobuf_c_version().
+ */
+#define PROTOBUF_C_VERSION "1.4.0"
+
+/**
+ * The version of the protobuf-c headers, represented as an integer using the
+ * same format as protobuf_c_version_number().
+ */
+#define PROTOBUF_C_VERSION_NUMBER 1004000
+
+/**
+ * The minimum protoc-c version which works with the current version of the
+ * protobuf-c headers.
+ */
+#define PROTOBUF_C_MIN_COMPILER_VERSION 1000000
+
+/**
+ * Look up a `ProtobufCEnumValue` from a `ProtobufCEnumDescriptor` by name.
+ *
+ * \param desc
+ * The `ProtobufCEnumDescriptor` object.
+ * \param name
+ * The `name` field from the corresponding `ProtobufCEnumValue` object to
+ * match.
+ * \return
+ * A `ProtobufCEnumValue` object.
+ * \retval NULL
+ * If not found or if the optimize_for = CODE_SIZE option was set.
+ */
+PROTOBUF_C__API
+const ProtobufCEnumValue *
+protobuf_c_enum_descriptor_get_value_by_name(
+ const ProtobufCEnumDescriptor *desc,
+ const char *name);
+
+/**
+ * Look up a `ProtobufCEnumValue` from a `ProtobufCEnumDescriptor` by numeric
+ * value.
+ *
+ * \param desc
+ * The `ProtobufCEnumDescriptor` object.
+ * \param value
+ * The `value` field from the corresponding `ProtobufCEnumValue` object to
+ * match.
+ *
+ * \return
+ * A `ProtobufCEnumValue` object.
+ * \retval NULL
+ * If not found.
+ */
+PROTOBUF_C__API
+const ProtobufCEnumValue *
+protobuf_c_enum_descriptor_get_value(
+ const ProtobufCEnumDescriptor *desc,
+ int value);
+
+/**
+ * Look up a `ProtobufCFieldDescriptor` from a `ProtobufCMessageDescriptor` by
+ * the name of the field.
+ *
+ * \param desc
+ * The `ProtobufCMessageDescriptor` object.
+ * \param name
+ * The name of the field.
+ * \return
+ * A `ProtobufCFieldDescriptor` object.
+ * \retval NULL
+ * If not found or if the optimize_for = CODE_SIZE option was set.
+ */
+PROTOBUF_C__API
+const ProtobufCFieldDescriptor *
+protobuf_c_message_descriptor_get_field_by_name(
+ const ProtobufCMessageDescriptor *desc,
+ const char *name);
+
+/**
+ * Look up a `ProtobufCFieldDescriptor` from a `ProtobufCMessageDescriptor` by
+ * the tag value of the field.
+ *
+ * \param desc
+ * The `ProtobufCMessageDescriptor` object.
+ * \param value
+ * The tag value of the field.
+ * \return
+ * A `ProtobufCFieldDescriptor` object.
+ * \retval NULL
+ * If not found.
+ */
+PROTOBUF_C__API
+const ProtobufCFieldDescriptor *
+protobuf_c_message_descriptor_get_field(
+ const ProtobufCMessageDescriptor *desc,
+ unsigned value);
+
+/**
+ * Determine the number of bytes required to store the serialised message.
+ *
+ * \param message
+ * The message object to serialise.
+ * \return
+ * Number of bytes.
+ */
+PROTOBUF_C__API
+size_t
+protobuf_c_message_get_packed_size(const ProtobufCMessage *message);
+
+/**
+ * Serialise a message from its in-memory representation.
+ *
+ * This function stores the serialised bytes of the message in a pre-allocated
+ * buffer.
+ *
+ * \param message
+ * The message object to serialise.
+ * \param[out] out
+ * Buffer to store the bytes of the serialised message. This buffer must
+ * have enough space to store the packed message. Use
+ * protobuf_c_message_get_packed_size() to determine the number of bytes
+ * required.
+ * \return
+ * Number of bytes stored in `out`.
+ */
+PROTOBUF_C__API
+size_t
+protobuf_c_message_pack(const ProtobufCMessage *message, uint8_t *out);
+
+/**
+ * Serialise a message from its in-memory representation to a virtual buffer.
+ *
+ * This function calls the `append` method of a `ProtobufCBuffer` object to
+ * consume the bytes generated by the serialiser.
+ *
+ * \param message
+ * The message object to serialise.
+ * \param buffer
+ * The virtual buffer object.
+ * \return
+ * Number of bytes passed to the virtual buffer.
+ */
+PROTOBUF_C__API
+size_t
+protobuf_c_message_pack_to_buffer(
+ const ProtobufCMessage *message,
+ ProtobufCBuffer *buffer);
+
+/**
+ * Unpack a serialised message into an in-memory representation.
+ *
+ * \param descriptor
+ * The message descriptor.
+ * \param allocator
+ * `ProtobufCAllocator` to use for memory allocation. May be NULL to
+ * specify the default allocator.
+ * \param len
+ * Length in bytes of the serialised message.
+ * \param data
+ * Pointer to the serialised message.
+ * \return
+ * An unpacked message object.
+ * \retval NULL
+ * If an error occurred during unpacking.
+ */
+PROTOBUF_C__API
+ProtobufCMessage *
+protobuf_c_message_unpack(
+ const ProtobufCMessageDescriptor *descriptor,
+ ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+
+/**
+ * Free an unpacked message object.
+ *
+ * This function should be used to deallocate the memory used by a call to
+ * protobuf_c_message_unpack().
+ *
+ * \param message
+ * The message object to free. May be NULL.
+ * \param allocator
+ * `ProtobufCAllocator` to use for memory deallocation. May be NULL to
+ * specify the default allocator.
+ */
+PROTOBUF_C__API
+void
+protobuf_c_message_free_unpacked(
+ ProtobufCMessage *message,
+ ProtobufCAllocator *allocator);
+
+/**
+ * Check the validity of a message object.
+ *
+ * Makes sure all required fields (`PROTOBUF_C_LABEL_REQUIRED`) are present.
+ * Recursively checks nested messages.
+ *
+ * \retval TRUE
+ * Message is valid.
+ * \retval FALSE
+ * Message is invalid.
+ */
+PROTOBUF_C__API
+protobuf_c_boolean
+protobuf_c_message_check(const ProtobufCMessage *);
+
+/** Message initialiser. */
+#define PROTOBUF_C_MESSAGE_INIT(descriptor) { descriptor, 0, NULL }
+
+/**
+ * Initialise a message object from a message descriptor.
+ *
+ * \param descriptor
+ * Message descriptor.
+ * \param message
+ * Allocated block of memory of size `descriptor->sizeof_message`.
+ */
+PROTOBUF_C__API
+void
+protobuf_c_message_init(
+ const ProtobufCMessageDescriptor *descriptor,
+ void *message);
+
+/**
+ * Free a service.
+ *
+ * \param service
+ * The service object to free.
+ */
+PROTOBUF_C__API
+void
+protobuf_c_service_destroy(ProtobufCService *service);
+
+/**
+ * Look up a `ProtobufCMethodDescriptor` by name.
+ *
+ * \param desc
+ * Service descriptor.
+ * \param name
+ * Name of the method.
+ *
+ * \return
+ * A `ProtobufCMethodDescriptor` object.
+ * \retval NULL
+ * If not found or if the optimize_for = CODE_SIZE option was set.
+ */
+PROTOBUF_C__API
+const ProtobufCMethodDescriptor *
+protobuf_c_service_descriptor_get_method_by_name(
+ const ProtobufCServiceDescriptor *desc,
+ const char *name);
+
+/**
+ * Initialise a `ProtobufCBufferSimple` object.
+ */
+#define PROTOBUF_C_BUFFER_SIMPLE_INIT(array_of_bytes) \
+{ \
+ { protobuf_c_buffer_simple_append }, \
+ sizeof(array_of_bytes), \
+ 0, \
+ (array_of_bytes), \
+ 0, \
+ NULL \
+}
+
+/**
+ * Clear a `ProtobufCBufferSimple` object, freeing any allocated memory.
+ */
+#define PROTOBUF_C_BUFFER_SIMPLE_CLEAR(simp_buf) \
+do { \
+ if ((simp_buf)->must_free_data) { \
+ if ((simp_buf)->allocator != NULL) \
+ (simp_buf)->allocator->free( \
+ (simp_buf)->allocator, \
+ (simp_buf)->data); \
+ else \
+ free((simp_buf)->data); \
+ } \
+} while (0)
+
+/**
+ * The `append` method for `ProtobufCBufferSimple`.
+ *
+ * \param buffer
+ * The buffer object to append to. Must actually be a
+ * `ProtobufCBufferSimple` object.
+ * \param len
+ * Number of bytes in `data`.
+ * \param data
+ * Data to append.
+ */
+PROTOBUF_C__API
+void
+protobuf_c_buffer_simple_append(
+ ProtobufCBuffer *buffer,
+ size_t len,
+ const unsigned char *data);
+
+PROTOBUF_C__API
+void
+protobuf_c_service_generated_init(
+ ProtobufCService *service,
+ const ProtobufCServiceDescriptor *descriptor,
+ ProtobufCServiceDestroy destroy);
+
+PROTOBUF_C__API
+void
+protobuf_c_service_invoke_internal(
+ ProtobufCService *service,
+ unsigned method_index,
+ const ProtobufCMessage *input,
+ ProtobufCClosure closure,
+ void *closure_data);
+
+/**@}*/
+
+PROTOBUF_C__END_DECLS
+
+#endif /* PROTOBUF_C_H */
diff --git a/src/fluent-bit/lib/cmetrics/include/prometheus_remote_write/remote.pb-c.h b/src/fluent-bit/lib/cmetrics/include/prometheus_remote_write/remote.pb-c.h
new file mode 100644
index 000000000..a816a4bbd
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/prometheus_remote_write/remote.pb-c.h
@@ -0,0 +1,304 @@
+/* Generated by the protocol buffer compiler. DO NOT EDIT! */
+/* Generated from: remote.proto */
+
+#ifndef PROTOBUF_C_remote_2eproto__INCLUDED
+#define PROTOBUF_C_remote_2eproto__INCLUDED
+
+#include <prometheus_remote_write/protobuf-c.h>
+
+PROTOBUF_C__BEGIN_DECLS
+
+#if PROTOBUF_C_VERSION_NUMBER < 1003000
+# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers.
+#elif 1003003 < PROTOBUF_C_MIN_COMPILER_VERSION
+# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c.
+#endif
+
+#include "types.pb-c.h"
+// #include "gogoproto/gogo.pb-c.h"
+
+typedef struct _Prometheus__WriteRequest Prometheus__WriteRequest;
+typedef struct _Prometheus__ReadRequest Prometheus__ReadRequest;
+typedef struct _Prometheus__ReadResponse Prometheus__ReadResponse;
+typedef struct _Prometheus__Query Prometheus__Query;
+typedef struct _Prometheus__QueryResult Prometheus__QueryResult;
+typedef struct _Prometheus__ChunkedReadResponse Prometheus__ChunkedReadResponse;
+
+
+/* --- enums --- */
+
+typedef enum _Prometheus__ReadRequest__ResponseType {
+ /*
+ * Server will return a single ReadResponse message with matched series that includes list of raw samples.
+ * It's recommended to use streamed response types instead.
+ * Response headers:
+ * Content-Type: "application/x-protobuf"
+ * Content-Encoding: "snappy"
+ */
+ PROMETHEUS__READ_REQUEST__RESPONSE_TYPE__SAMPLES = 0,
+ /*
+ * Server will stream a delimited ChunkedReadResponse message that contains XOR encoded chunks for a single series.
+ * Each message is following varint size and fixed size bigendian uint32 for CRC32 Castagnoli checksum.
+ * Response headers:
+ * Content-Type: "application/x-streamed-protobuf; proto=prometheus.ChunkedReadResponse"
+ * Content-Encoding: ""
+ */
+ PROMETHEUS__READ_REQUEST__RESPONSE_TYPE__STREAMED_XOR_CHUNKS = 1
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(PROMETHEUS__READ_REQUEST__RESPONSE_TYPE)
+} Prometheus__ReadRequest__ResponseType;
+
+/* --- messages --- */
+
+struct _Prometheus__WriteRequest
+{
+ ProtobufCMessage base;
+ size_t n_timeseries;
+ Prometheus__TimeSeries **timeseries;
+ size_t n_metadata;
+ Prometheus__MetricMetadata **metadata;
+};
+#define PROMETHEUS__WRITE_REQUEST__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&prometheus__write_request__descriptor) \
+ , 0,NULL, 0,NULL }
+
+
+/*
+ * ReadRequest represents a remote read request.
+ */
+struct _Prometheus__ReadRequest
+{
+ ProtobufCMessage base;
+ size_t n_queries;
+ Prometheus__Query **queries;
+ /*
+ * accepted_response_types allows negotiating the content type of the response.
+ * Response types are taken from the list in the FIFO order. If no response type in `accepted_response_types` is
+ * implemented by server, error is returned.
+ * For request that do not contain `accepted_response_types` field the SAMPLES response type will be used.
+ */
+ size_t n_accepted_response_types;
+ Prometheus__ReadRequest__ResponseType *accepted_response_types;
+};
+#define PROMETHEUS__READ_REQUEST__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&prometheus__read_request__descriptor) \
+ , 0,NULL, 0,NULL }
+
+
+/*
+ * ReadResponse is a response when response_type equals SAMPLES.
+ */
+struct _Prometheus__ReadResponse
+{
+ ProtobufCMessage base;
+ /*
+ * In same order as the request's queries.
+ */
+ size_t n_results;
+ Prometheus__QueryResult **results;
+};
+#define PROMETHEUS__READ_RESPONSE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&prometheus__read_response__descriptor) \
+ , 0,NULL }
+
+
+struct _Prometheus__Query
+{
+ ProtobufCMessage base;
+ int64_t start_timestamp_ms;
+ int64_t end_timestamp_ms;
+ size_t n_matchers;
+ Prometheus__LabelMatcher **matchers;
+ Prometheus__ReadHints *hints;
+};
+#define PROMETHEUS__QUERY__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&prometheus__query__descriptor) \
+ , 0, 0, 0,NULL, NULL }
+
+
+struct _Prometheus__QueryResult
+{
+ ProtobufCMessage base;
+ /*
+ * Samples within a time series must be ordered by time.
+ */
+ size_t n_timeseries;
+ Prometheus__TimeSeries **timeseries;
+};
+#define PROMETHEUS__QUERY_RESULT__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&prometheus__query_result__descriptor) \
+ , 0,NULL }
+
+
+/*
+ * ChunkedReadResponse is a response when response_type equals STREAMED_XOR_CHUNKS.
+ * We strictly stream full series after series, optionally split by time. This means that a single frame can contain
+ * partition of the single series, but once a new series is started to be streamed it means that no more chunks will
+ * be sent for previous one. Series are returned sorted in the same way TSDB block are internally.
+ */
+struct _Prometheus__ChunkedReadResponse
+{
+ ProtobufCMessage base;
+ size_t n_chunked_series;
+ Prometheus__ChunkedSeries **chunked_series;
+ /*
+ * query_index represents an index of the query from ReadRequest.queries these chunks relates to.
+ */
+ int64_t query_index;
+};
+#define PROMETHEUS__CHUNKED_READ_RESPONSE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&prometheus__chunked_read_response__descriptor) \
+ , 0,NULL, 0 }
+
+
+/* Prometheus__WriteRequest methods */
+void prometheus__write_request__init
+ (Prometheus__WriteRequest *message);
+size_t prometheus__write_request__get_packed_size
+ (const Prometheus__WriteRequest *message);
+size_t prometheus__write_request__pack
+ (const Prometheus__WriteRequest *message,
+ uint8_t *out);
+size_t prometheus__write_request__pack_to_buffer
+ (const Prometheus__WriteRequest *message,
+ ProtobufCBuffer *buffer);
+Prometheus__WriteRequest *
+ prometheus__write_request__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void prometheus__write_request__free_unpacked
+ (Prometheus__WriteRequest *message,
+ ProtobufCAllocator *allocator);
+/* Prometheus__ReadRequest methods */
+void prometheus__read_request__init
+ (Prometheus__ReadRequest *message);
+size_t prometheus__read_request__get_packed_size
+ (const Prometheus__ReadRequest *message);
+size_t prometheus__read_request__pack
+ (const Prometheus__ReadRequest *message,
+ uint8_t *out);
+size_t prometheus__read_request__pack_to_buffer
+ (const Prometheus__ReadRequest *message,
+ ProtobufCBuffer *buffer);
+Prometheus__ReadRequest *
+ prometheus__read_request__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void prometheus__read_request__free_unpacked
+ (Prometheus__ReadRequest *message,
+ ProtobufCAllocator *allocator);
+/* Prometheus__ReadResponse methods */
+void prometheus__read_response__init
+ (Prometheus__ReadResponse *message);
+size_t prometheus__read_response__get_packed_size
+ (const Prometheus__ReadResponse *message);
+size_t prometheus__read_response__pack
+ (const Prometheus__ReadResponse *message,
+ uint8_t *out);
+size_t prometheus__read_response__pack_to_buffer
+ (const Prometheus__ReadResponse *message,
+ ProtobufCBuffer *buffer);
+Prometheus__ReadResponse *
+ prometheus__read_response__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void prometheus__read_response__free_unpacked
+ (Prometheus__ReadResponse *message,
+ ProtobufCAllocator *allocator);
+/* Prometheus__Query methods */
+void prometheus__query__init
+ (Prometheus__Query *message);
+size_t prometheus__query__get_packed_size
+ (const Prometheus__Query *message);
+size_t prometheus__query__pack
+ (const Prometheus__Query *message,
+ uint8_t *out);
+size_t prometheus__query__pack_to_buffer
+ (const Prometheus__Query *message,
+ ProtobufCBuffer *buffer);
+Prometheus__Query *
+ prometheus__query__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void prometheus__query__free_unpacked
+ (Prometheus__Query *message,
+ ProtobufCAllocator *allocator);
+/* Prometheus__QueryResult methods */
+void prometheus__query_result__init
+ (Prometheus__QueryResult *message);
+size_t prometheus__query_result__get_packed_size
+ (const Prometheus__QueryResult *message);
+size_t prometheus__query_result__pack
+ (const Prometheus__QueryResult *message,
+ uint8_t *out);
+size_t prometheus__query_result__pack_to_buffer
+ (const Prometheus__QueryResult *message,
+ ProtobufCBuffer *buffer);
+Prometheus__QueryResult *
+ prometheus__query_result__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void prometheus__query_result__free_unpacked
+ (Prometheus__QueryResult *message,
+ ProtobufCAllocator *allocator);
+/* Prometheus__ChunkedReadResponse methods */
+void prometheus__chunked_read_response__init
+ (Prometheus__ChunkedReadResponse *message);
+size_t prometheus__chunked_read_response__get_packed_size
+ (const Prometheus__ChunkedReadResponse *message);
+size_t prometheus__chunked_read_response__pack
+ (const Prometheus__ChunkedReadResponse *message,
+ uint8_t *out);
+size_t prometheus__chunked_read_response__pack_to_buffer
+ (const Prometheus__ChunkedReadResponse *message,
+ ProtobufCBuffer *buffer);
+Prometheus__ChunkedReadResponse *
+ prometheus__chunked_read_response__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void prometheus__chunked_read_response__free_unpacked
+ (Prometheus__ChunkedReadResponse *message,
+ ProtobufCAllocator *allocator);
+/* --- per-message closures --- */
+
+typedef void (*Prometheus__WriteRequest_Closure)
+ (const Prometheus__WriteRequest *message,
+ void *closure_data);
+typedef void (*Prometheus__ReadRequest_Closure)
+ (const Prometheus__ReadRequest *message,
+ void *closure_data);
+typedef void (*Prometheus__ReadResponse_Closure)
+ (const Prometheus__ReadResponse *message,
+ void *closure_data);
+typedef void (*Prometheus__Query_Closure)
+ (const Prometheus__Query *message,
+ void *closure_data);
+typedef void (*Prometheus__QueryResult_Closure)
+ (const Prometheus__QueryResult *message,
+ void *closure_data);
+typedef void (*Prometheus__ChunkedReadResponse_Closure)
+ (const Prometheus__ChunkedReadResponse *message,
+ void *closure_data);
+
+/* --- services --- */
+
+
+/* --- descriptors --- */
+
+extern const ProtobufCMessageDescriptor prometheus__write_request__descriptor;
+extern const ProtobufCMessageDescriptor prometheus__read_request__descriptor;
+extern const ProtobufCEnumDescriptor prometheus__read_request__response_type__descriptor;
+extern const ProtobufCMessageDescriptor prometheus__read_response__descriptor;
+extern const ProtobufCMessageDescriptor prometheus__query__descriptor;
+extern const ProtobufCMessageDescriptor prometheus__query_result__descriptor;
+extern const ProtobufCMessageDescriptor prometheus__chunked_read_response__descriptor;
+
+PROTOBUF_C__END_DECLS
+
+
+#endif /* PROTOBUF_C_remote_2eproto__INCLUDED */
diff --git a/src/fluent-bit/lib/cmetrics/include/prometheus_remote_write/types.pb-c.h b/src/fluent-bit/lib/cmetrics/include/prometheus_remote_write/types.pb-c.h
new file mode 100644
index 000000000..25291c92e
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/include/prometheus_remote_write/types.pb-c.h
@@ -0,0 +1,441 @@
+/* Generated by the protocol buffer compiler. DO NOT EDIT! */
+/* Generated from: types.proto */
+
+#ifndef PROTOBUF_C_types_2eproto__INCLUDED
+#define PROTOBUF_C_types_2eproto__INCLUDED
+
+#include <prometheus_remote_write/protobuf-c.h>
+
+PROTOBUF_C__BEGIN_DECLS
+
+#if PROTOBUF_C_VERSION_NUMBER < 1003000
+# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers.
+#elif 1003003 < PROTOBUF_C_MIN_COMPILER_VERSION
+# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c.
+#endif
+
+// #include "gogoproto/gogo.pb-c.h"
+
+typedef struct _Prometheus__MetricMetadata Prometheus__MetricMetadata;
+typedef struct _Prometheus__Sample Prometheus__Sample;
+typedef struct _Prometheus__TimeSeries Prometheus__TimeSeries;
+typedef struct _Prometheus__Label Prometheus__Label;
+typedef struct _Prometheus__Labels Prometheus__Labels;
+typedef struct _Prometheus__LabelMatcher Prometheus__LabelMatcher;
+typedef struct _Prometheus__ReadHints Prometheus__ReadHints;
+typedef struct _Prometheus__Chunk Prometheus__Chunk;
+typedef struct _Prometheus__ChunkedSeries Prometheus__ChunkedSeries;
+
+
+/* --- enums --- */
+
+typedef enum _Prometheus__MetricMetadata__MetricType {
+ PROMETHEUS__METRIC_METADATA__METRIC_TYPE__UNKNOWN = 0,
+ PROMETHEUS__METRIC_METADATA__METRIC_TYPE__COUNTER = 1,
+ PROMETHEUS__METRIC_METADATA__METRIC_TYPE__GAUGE = 2,
+ PROMETHEUS__METRIC_METADATA__METRIC_TYPE__HISTOGRAM = 3,
+ PROMETHEUS__METRIC_METADATA__METRIC_TYPE__GAUGEHISTOGRAM = 4,
+ PROMETHEUS__METRIC_METADATA__METRIC_TYPE__SUMMARY = 5,
+ PROMETHEUS__METRIC_METADATA__METRIC_TYPE__INFO = 6,
+ PROMETHEUS__METRIC_METADATA__METRIC_TYPE__STATESET = 7
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(PROMETHEUS__METRIC_METADATA__METRIC_TYPE)
+} Prometheus__MetricMetadata__MetricType;
+typedef enum _Prometheus__LabelMatcher__Type {
+ PROMETHEUS__LABEL_MATCHER__TYPE__EQ = 0,
+ PROMETHEUS__LABEL_MATCHER__TYPE__NEQ = 1,
+ PROMETHEUS__LABEL_MATCHER__TYPE__RE = 2,
+ PROMETHEUS__LABEL_MATCHER__TYPE__NRE = 3
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(PROMETHEUS__LABEL_MATCHER__TYPE)
+} Prometheus__LabelMatcher__Type;
+/*
+ * We require this to match chunkenc.Encoding.
+ */
+typedef enum _Prometheus__Chunk__Encoding {
+ PROMETHEUS__CHUNK__ENCODING__UNKNOWN = 0,
+ PROMETHEUS__CHUNK__ENCODING__XOR = 1
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(PROMETHEUS__CHUNK__ENCODING)
+} Prometheus__Chunk__Encoding;
+
+/* --- messages --- */
+
+struct _Prometheus__MetricMetadata
+{
+ ProtobufCMessage base;
+ /*
+ * Represents the metric type, these match the set from Prometheus.
+ * Refer to pkg/textparse/interface.go for details.
+ */
+ Prometheus__MetricMetadata__MetricType type;
+ char *metric_family_name;
+ char *help;
+ char *unit;
+};
+#define PROMETHEUS__METRIC_METADATA__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&prometheus__metric_metadata__descriptor) \
+ , PROMETHEUS__METRIC_METADATA__METRIC_TYPE__UNKNOWN, (char *)protobuf_c_empty_string, (char *)protobuf_c_empty_string, (char *)protobuf_c_empty_string }
+
+
+struct _Prometheus__Sample
+{
+ ProtobufCMessage base;
+ double value;
+ int64_t timestamp;
+};
+#define PROMETHEUS__SAMPLE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&prometheus__sample__descriptor) \
+ , 0, 0 }
+
+
+/*
+ * TimeSeries represents samples and labels for a single time series.
+ */
+struct _Prometheus__TimeSeries
+{
+ ProtobufCMessage base;
+ size_t n_labels;
+ Prometheus__Label **labels;
+ size_t n_samples;
+ Prometheus__Sample **samples;
+};
+#define PROMETHEUS__TIME_SERIES__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&prometheus__time_series__descriptor) \
+ , 0,NULL, 0,NULL }
+
+
+struct _Prometheus__Label
+{
+ ProtobufCMessage base;
+ char *name;
+ char *value;
+};
+#define PROMETHEUS__LABEL__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&prometheus__label__descriptor) \
+ , (char *)protobuf_c_empty_string, (char *)protobuf_c_empty_string }
+
+
+struct _Prometheus__Labels
+{
+ ProtobufCMessage base;
+ size_t n_labels;
+ Prometheus__Label **labels;
+};
+#define PROMETHEUS__LABELS__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&prometheus__labels__descriptor) \
+ , 0,NULL }
+
+
+/*
+ * Matcher specifies a rule, which can match or set of labels or not.
+ */
+struct _Prometheus__LabelMatcher
+{
+ ProtobufCMessage base;
+ Prometheus__LabelMatcher__Type type;
+ char *name;
+ char *value;
+};
+#define PROMETHEUS__LABEL_MATCHER__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&prometheus__label_matcher__descriptor) \
+ , PROMETHEUS__LABEL_MATCHER__TYPE__EQ, (char *)protobuf_c_empty_string, (char *)protobuf_c_empty_string }
+
+
+struct _Prometheus__ReadHints
+{
+ ProtobufCMessage base;
+ /*
+ * Query step size in milliseconds.
+ */
+ int64_t step_ms;
+ /*
+ * String representation of surrounding function or aggregation.
+ */
+ char *func;
+ /*
+ * Start time in milliseconds.
+ */
+ int64_t start_ms;
+ /*
+ * End time in milliseconds.
+ */
+ int64_t end_ms;
+ /*
+ * List of label names used in aggregation.
+ */
+ size_t n_grouping;
+ char **grouping;
+ /*
+ * Indicate whether it is without or by.
+ */
+ protobuf_c_boolean by;
+ /*
+ * Range vector selector range in milliseconds.
+ */
+ int64_t range_ms;
+};
+#define PROMETHEUS__READ_HINTS__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&prometheus__read_hints__descriptor) \
+ , 0, (char *)protobuf_c_empty_string, 0, 0, 0,NULL, 0, 0 }
+
+
+/*
+ * Chunk represents a TSDB chunk.
+ * Time range [min, max] is inclusive.
+ */
+struct _Prometheus__Chunk
+{
+ ProtobufCMessage base;
+ int64_t min_time_ms;
+ int64_t max_time_ms;
+ Prometheus__Chunk__Encoding type;
+ ProtobufCBinaryData data;
+};
+#define PROMETHEUS__CHUNK__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&prometheus__chunk__descriptor) \
+ , 0, 0, PROMETHEUS__CHUNK__ENCODING__UNKNOWN, {0,NULL} }
+
+
+/*
+ * ChunkedSeries represents single, encoded time series.
+ */
+struct _Prometheus__ChunkedSeries
+{
+ ProtobufCMessage base;
+ /*
+ * Labels should be sorted.
+ */
+ size_t n_labels;
+ Prometheus__Label **labels;
+ /*
+ * Chunks will be in start time order and may overlap.
+ */
+ size_t n_chunks;
+ Prometheus__Chunk **chunks;
+};
+#define PROMETHEUS__CHUNKED_SERIES__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&prometheus__chunked_series__descriptor) \
+ , 0,NULL, 0,NULL }
+
+
+/* Prometheus__MetricMetadata methods */
+void prometheus__metric_metadata__init
+ (Prometheus__MetricMetadata *message);
+size_t prometheus__metric_metadata__get_packed_size
+ (const Prometheus__MetricMetadata *message);
+size_t prometheus__metric_metadata__pack
+ (const Prometheus__MetricMetadata *message,
+ uint8_t *out);
+size_t prometheus__metric_metadata__pack_to_buffer
+ (const Prometheus__MetricMetadata *message,
+ ProtobufCBuffer *buffer);
+Prometheus__MetricMetadata *
+ prometheus__metric_metadata__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void prometheus__metric_metadata__free_unpacked
+ (Prometheus__MetricMetadata *message,
+ ProtobufCAllocator *allocator);
+/* Prometheus__Sample methods */
+void prometheus__sample__init
+ (Prometheus__Sample *message);
+size_t prometheus__sample__get_packed_size
+ (const Prometheus__Sample *message);
+size_t prometheus__sample__pack
+ (const Prometheus__Sample *message,
+ uint8_t *out);
+size_t prometheus__sample__pack_to_buffer
+ (const Prometheus__Sample *message,
+ ProtobufCBuffer *buffer);
+Prometheus__Sample *
+ prometheus__sample__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void prometheus__sample__free_unpacked
+ (Prometheus__Sample *message,
+ ProtobufCAllocator *allocator);
+/* Prometheus__TimeSeries methods */
+void prometheus__time_series__init
+ (Prometheus__TimeSeries *message);
+size_t prometheus__time_series__get_packed_size
+ (const Prometheus__TimeSeries *message);
+size_t prometheus__time_series__pack
+ (const Prometheus__TimeSeries *message,
+ uint8_t *out);
+size_t prometheus__time_series__pack_to_buffer
+ (const Prometheus__TimeSeries *message,
+ ProtobufCBuffer *buffer);
+Prometheus__TimeSeries *
+ prometheus__time_series__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void prometheus__time_series__free_unpacked
+ (Prometheus__TimeSeries *message,
+ ProtobufCAllocator *allocator);
+/* Prometheus__Label methods */
+void prometheus__label__init
+ (Prometheus__Label *message);
+size_t prometheus__label__get_packed_size
+ (const Prometheus__Label *message);
+size_t prometheus__label__pack
+ (const Prometheus__Label *message,
+ uint8_t *out);
+size_t prometheus__label__pack_to_buffer
+ (const Prometheus__Label *message,
+ ProtobufCBuffer *buffer);
+Prometheus__Label *
+ prometheus__label__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void prometheus__label__free_unpacked
+ (Prometheus__Label *message,
+ ProtobufCAllocator *allocator);
+/* Prometheus__Labels methods */
+void prometheus__labels__init
+ (Prometheus__Labels *message);
+size_t prometheus__labels__get_packed_size
+ (const Prometheus__Labels *message);
+size_t prometheus__labels__pack
+ (const Prometheus__Labels *message,
+ uint8_t *out);
+size_t prometheus__labels__pack_to_buffer
+ (const Prometheus__Labels *message,
+ ProtobufCBuffer *buffer);
+Prometheus__Labels *
+ prometheus__labels__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void prometheus__labels__free_unpacked
+ (Prometheus__Labels *message,
+ ProtobufCAllocator *allocator);
+/* Prometheus__LabelMatcher methods */
+void prometheus__label_matcher__init
+ (Prometheus__LabelMatcher *message);
+size_t prometheus__label_matcher__get_packed_size
+ (const Prometheus__LabelMatcher *message);
+size_t prometheus__label_matcher__pack
+ (const Prometheus__LabelMatcher *message,
+ uint8_t *out);
+size_t prometheus__label_matcher__pack_to_buffer
+ (const Prometheus__LabelMatcher *message,
+ ProtobufCBuffer *buffer);
+Prometheus__LabelMatcher *
+ prometheus__label_matcher__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void prometheus__label_matcher__free_unpacked
+ (Prometheus__LabelMatcher *message,
+ ProtobufCAllocator *allocator);
+/* Prometheus__ReadHints methods */
+void prometheus__read_hints__init
+ (Prometheus__ReadHints *message);
+size_t prometheus__read_hints__get_packed_size
+ (const Prometheus__ReadHints *message);
+size_t prometheus__read_hints__pack
+ (const Prometheus__ReadHints *message,
+ uint8_t *out);
+size_t prometheus__read_hints__pack_to_buffer
+ (const Prometheus__ReadHints *message,
+ ProtobufCBuffer *buffer);
+Prometheus__ReadHints *
+ prometheus__read_hints__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void prometheus__read_hints__free_unpacked
+ (Prometheus__ReadHints *message,
+ ProtobufCAllocator *allocator);
+/* Prometheus__Chunk methods */
+void prometheus__chunk__init
+ (Prometheus__Chunk *message);
+size_t prometheus__chunk__get_packed_size
+ (const Prometheus__Chunk *message);
+size_t prometheus__chunk__pack
+ (const Prometheus__Chunk *message,
+ uint8_t *out);
+size_t prometheus__chunk__pack_to_buffer
+ (const Prometheus__Chunk *message,
+ ProtobufCBuffer *buffer);
+Prometheus__Chunk *
+ prometheus__chunk__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void prometheus__chunk__free_unpacked
+ (Prometheus__Chunk *message,
+ ProtobufCAllocator *allocator);
+/* Prometheus__ChunkedSeries methods */
+void prometheus__chunked_series__init
+ (Prometheus__ChunkedSeries *message);
+size_t prometheus__chunked_series__get_packed_size
+ (const Prometheus__ChunkedSeries *message);
+size_t prometheus__chunked_series__pack
+ (const Prometheus__ChunkedSeries *message,
+ uint8_t *out);
+size_t prometheus__chunked_series__pack_to_buffer
+ (const Prometheus__ChunkedSeries *message,
+ ProtobufCBuffer *buffer);
+Prometheus__ChunkedSeries *
+ prometheus__chunked_series__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void prometheus__chunked_series__free_unpacked
+ (Prometheus__ChunkedSeries *message,
+ ProtobufCAllocator *allocator);
+/* --- per-message closures --- */
+
+typedef void (*Prometheus__MetricMetadata_Closure)
+ (const Prometheus__MetricMetadata *message,
+ void *closure_data);
+typedef void (*Prometheus__Sample_Closure)
+ (const Prometheus__Sample *message,
+ void *closure_data);
+typedef void (*Prometheus__TimeSeries_Closure)
+ (const Prometheus__TimeSeries *message,
+ void *closure_data);
+typedef void (*Prometheus__Label_Closure)
+ (const Prometheus__Label *message,
+ void *closure_data);
+typedef void (*Prometheus__Labels_Closure)
+ (const Prometheus__Labels *message,
+ void *closure_data);
+typedef void (*Prometheus__LabelMatcher_Closure)
+ (const Prometheus__LabelMatcher *message,
+ void *closure_data);
+typedef void (*Prometheus__ReadHints_Closure)
+ (const Prometheus__ReadHints *message,
+ void *closure_data);
+typedef void (*Prometheus__Chunk_Closure)
+ (const Prometheus__Chunk *message,
+ void *closure_data);
+typedef void (*Prometheus__ChunkedSeries_Closure)
+ (const Prometheus__ChunkedSeries *message,
+ void *closure_data);
+
+/* --- services --- */
+
+
+/* --- descriptors --- */
+
+extern const ProtobufCMessageDescriptor prometheus__metric_metadata__descriptor;
+extern const ProtobufCEnumDescriptor prometheus__metric_metadata__metric_type__descriptor;
+extern const ProtobufCMessageDescriptor prometheus__sample__descriptor;
+extern const ProtobufCMessageDescriptor prometheus__time_series__descriptor;
+extern const ProtobufCMessageDescriptor prometheus__label__descriptor;
+extern const ProtobufCMessageDescriptor prometheus__labels__descriptor;
+extern const ProtobufCMessageDescriptor prometheus__label_matcher__descriptor;
+extern const ProtobufCEnumDescriptor prometheus__label_matcher__type__descriptor;
+extern const ProtobufCMessageDescriptor prometheus__read_hints__descriptor;
+extern const ProtobufCMessageDescriptor prometheus__chunk__descriptor;
+extern const ProtobufCEnumDescriptor prometheus__chunk__encoding__descriptor;
+extern const ProtobufCMessageDescriptor prometheus__chunked_series__descriptor;
+
+PROTOBUF_C__END_DECLS
+
+
+#endif /* PROTOBUF_C_types_2eproto__INCLUDED */
diff --git a/src/fluent-bit/lib/cmetrics/lib/mpack/CMakeLists.txt b/src/fluent-bit/lib/cmetrics/lib/mpack/CMakeLists.txt
new file mode 100644
index 000000000..bc6e73157
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/lib/mpack/CMakeLists.txt
@@ -0,0 +1,31 @@
+set(src
+ src/mpack/mpack.c
+ )
+
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99")
+add_definitions(-DMPACK_EXTENSIONS=1)
+add_library(mpack-static STATIC ${src})
+
+# Install Library
+if(MSVC)
+ # Rename the output for Windows environment to avoid naming issues
+ set_target_properties(mpack-static PROPERTIES OUTPUT_NAME libmpack)
+else()
+ set_target_properties(mpack-static PROPERTIES OUTPUT_NAME mpack)
+endif(MSVC)
+
+# Installation Directories
+# ========================
+if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
+ set(MPACK_INSTALL_LIBDIR "lib")
+ set(MPACK_INSTALL_INCLUDEDIR "include")
+else()
+ set(MPACK_INSTALL_LIBDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
+ set(MPACK_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_PREFIX}/include")
+endif()
+
+install(TARGETS mpack-static
+ RUNTIME DESTINATION ${MPACK_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${MPACK_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${MPACK_INSTALL_LIBDIR}
+ COMPONENT library)
diff --git a/src/fluent-bit/lib/cmetrics/lib/mpack/src/mpack/mpack.c b/src/fluent-bit/lib/cmetrics/lib/mpack/src/mpack/mpack.c
new file mode 100644
index 000000000..337ab0955
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/lib/mpack/src/mpack/mpack.c
@@ -0,0 +1,6472 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015-2018 Nicholas Fraser
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+/*
+ * This is the MPack 1.0 amalgamation package.
+ *
+ * http://github.com/ludocode/mpack
+ */
+
+#define MPACK_INTERNAL 1
+#define MPACK_EMIT_INLINE_DEFS 1
+
+#include "mpack.h"
+
+
+/* mpack/mpack-platform.c.c */
+
+
+// We define MPACK_EMIT_INLINE_DEFS and include mpack.h to emit
+// standalone definitions of all (non-static) inline functions in MPack.
+
+#define MPACK_INTERNAL 1
+#define MPACK_EMIT_INLINE_DEFS 1
+
+/* #include "mpack-platform.h" */
+/* #include "mpack.h" */
+
+
+#if MPACK_DEBUG && MPACK_STDIO
+#include <stdarg.h>
+#endif
+
+
+
+#if MPACK_DEBUG
+
+#if MPACK_STDIO
+void mpack_assert_fail_format(const char* format, ...) {
+ char buffer[512];
+ va_list args;
+ va_start(args, format);
+ vsnprintf(buffer, sizeof(buffer), format, args);
+ va_end(args);
+ buffer[sizeof(buffer) - 1] = 0;
+ mpack_assert_fail_wrapper(buffer);
+}
+
+void mpack_break_hit_format(const char* format, ...) {
+ char buffer[512];
+ va_list args;
+ va_start(args, format);
+ vsnprintf(buffer, sizeof(buffer), format, args);
+ va_end(args);
+ buffer[sizeof(buffer) - 1] = 0;
+ mpack_break_hit(buffer);
+}
+#endif
+
+#if !MPACK_CUSTOM_ASSERT
+void mpack_assert_fail(const char* message) {
+ MPACK_UNUSED(message);
+
+ #if MPACK_STDIO
+ fprintf(stderr, "%s\n", message);
+ #endif
+}
+#endif
+
+// We split the assert failure from the wrapper so that a
+// custom assert function can return.
+void mpack_assert_fail_wrapper(const char* message) {
+
+ #ifdef MPACK_GCOV
+ // gcov marks even __builtin_unreachable() as an uncovered line. this
+ // silences it.
+ (mpack_assert_fail(message), __builtin_unreachable());
+
+ #else
+ mpack_assert_fail(message);
+
+ // mpack_assert_fail() is not supposed to return. in case it does, we
+ // abort.
+
+ #if !MPACK_NO_BUILTINS
+ #if defined(__GNUC__) || defined(__clang__)
+ __builtin_trap();
+ #elif defined(WIN32)
+ __debugbreak();
+ #endif
+ #endif
+
+ #if (defined(__GNUC__) || defined(__clang__)) && !MPACK_NO_BUILTINS
+ __builtin_abort();
+ #elif MPACK_STDLIB
+ abort();
+ #endif
+
+ MPACK_UNREACHABLE;
+ #endif
+}
+
+#if !MPACK_CUSTOM_BREAK
+
+// If we have a custom assert handler, break wraps it by default.
+// This allows users of MPack to only implement mpack_assert_fail() without
+// having to worry about the difference between assert and break.
+//
+// MPACK_CUSTOM_BREAK is available to define a separate break handler
+// (which is needed by the unit test suite), but this is not offered in
+// mpack-config.h for simplicity.
+
+#if MPACK_CUSTOM_ASSERT
+void mpack_break_hit(const char* message) {
+ mpack_assert_fail_wrapper(message);
+}
+#else
+void mpack_break_hit(const char* message) {
+ MPACK_UNUSED(message);
+
+ #if MPACK_STDIO
+ fprintf(stderr, "%s\n", message);
+ #endif
+
+ #if defined(__GNUC__) || defined(__clang__) && !MPACK_NO_BUILTINS
+ __builtin_trap();
+ #elif defined(WIN32) && !MPACK_NO_BUILTINS
+ __debugbreak();
+ #elif MPACK_STDLIB
+ abort();
+ #endif
+}
+#endif
+
+#endif
+
+#endif
+
+
+
+// The below are adapted from the C wikibook:
+// https://en.wikibooks.org/wiki/C_Programming/Strings
+
+#ifndef mpack_memcmp
+int mpack_memcmp(const void* s1, const void* s2, size_t n) {
+ const unsigned char *us1 = (const unsigned char *) s1;
+ const unsigned char *us2 = (const unsigned char *) s2;
+ while (n-- != 0) {
+ if (*us1 != *us2)
+ return (*us1 < *us2) ? -1 : +1;
+ us1++;
+ us2++;
+ }
+ return 0;
+}
+#endif
+
+#ifndef mpack_memcpy
+void* mpack_memcpy(void* MPACK_RESTRICT s1, const void* MPACK_RESTRICT s2, size_t n) {
+ char* MPACK_RESTRICT dst = (char *)s1;
+ const char* MPACK_RESTRICT src = (const char *)s2;
+ while (n-- != 0)
+ *dst++ = *src++;
+ return s1;
+}
+#endif
+
+#ifndef mpack_memmove
+void* mpack_memmove(void* s1, const void* s2, size_t n) {
+ char *p1 = (char *)s1;
+ const char *p2 = (const char *)s2;
+ if (p2 < p1 && p1 < p2 + n) {
+ p2 += n;
+ p1 += n;
+ while (n-- != 0)
+ *--p1 = *--p2;
+ } else
+ while (n-- != 0)
+ *p1++ = *p2++;
+ return s1;
+}
+#endif
+
+#ifndef mpack_memset
+void* mpack_memset(void* s, int c, size_t n) {
+ unsigned char *us = (unsigned char *)s;
+ unsigned char uc = (unsigned char)c;
+ while (n-- != 0)
+ *us++ = uc;
+ return s;
+}
+#endif
+
+#ifndef mpack_strlen
+size_t mpack_strlen(const char* s) {
+ const char* p = s;
+ while (*p != '\0')
+ p++;
+ return (size_t)(p - s);
+}
+#endif
+
+
+
+#if defined(MPACK_MALLOC) && !defined(MPACK_REALLOC)
+void* mpack_realloc(void* old_ptr, size_t used_size, size_t new_size) {
+ if (new_size == 0) {
+ if (old_ptr)
+ MPACK_FREE(old_ptr);
+ return NULL;
+ }
+
+ void* new_ptr = MPACK_MALLOC(new_size);
+ if (new_ptr == NULL)
+ return NULL;
+
+ mpack_memcpy(new_ptr, old_ptr, used_size);
+ MPACK_FREE(old_ptr);
+ return new_ptr;
+}
+#endif
+
+/* mpack/mpack-common.c.c */
+
+#define MPACK_INTERNAL 1
+
+/* #include "mpack-common.h" */
+
+#if MPACK_DEBUG && MPACK_STDIO
+#include <stdarg.h>
+#endif
+
+const char* mpack_error_to_string(mpack_error_t error) {
+ #if MPACK_STRINGS
+ switch (error) {
+ #define MPACK_ERROR_STRING_CASE(e) case e: return #e
+ MPACK_ERROR_STRING_CASE(mpack_ok);
+ MPACK_ERROR_STRING_CASE(mpack_error_io);
+ MPACK_ERROR_STRING_CASE(mpack_error_invalid);
+ MPACK_ERROR_STRING_CASE(mpack_error_unsupported);
+ MPACK_ERROR_STRING_CASE(mpack_error_type);
+ MPACK_ERROR_STRING_CASE(mpack_error_too_big);
+ MPACK_ERROR_STRING_CASE(mpack_error_memory);
+ MPACK_ERROR_STRING_CASE(mpack_error_bug);
+ MPACK_ERROR_STRING_CASE(mpack_error_data);
+ MPACK_ERROR_STRING_CASE(mpack_error_eof);
+ #undef MPACK_ERROR_STRING_CASE
+ }
+ mpack_assert(0, "unrecognized error %i", (int)error);
+ return "(unknown mpack_error_t)";
+ #else
+ MPACK_UNUSED(error);
+ return "";
+ #endif
+}
+
+const char* mpack_type_to_string(mpack_type_t type) {
+ #if MPACK_STRINGS
+ switch (type) {
+ #define MPACK_TYPE_STRING_CASE(e) case e: return #e
+ MPACK_TYPE_STRING_CASE(mpack_type_missing);
+ MPACK_TYPE_STRING_CASE(mpack_type_nil);
+ MPACK_TYPE_STRING_CASE(mpack_type_bool);
+ MPACK_TYPE_STRING_CASE(mpack_type_float);
+ MPACK_TYPE_STRING_CASE(mpack_type_double);
+ MPACK_TYPE_STRING_CASE(mpack_type_int);
+ MPACK_TYPE_STRING_CASE(mpack_type_uint);
+ MPACK_TYPE_STRING_CASE(mpack_type_str);
+ MPACK_TYPE_STRING_CASE(mpack_type_bin);
+ MPACK_TYPE_STRING_CASE(mpack_type_array);
+ MPACK_TYPE_STRING_CASE(mpack_type_map);
+ #if MPACK_EXTENSIONS
+ MPACK_TYPE_STRING_CASE(mpack_type_ext);
+ #endif
+ #undef MPACK_TYPE_STRING_CASE
+ }
+ mpack_assert(0, "unrecognized type %i", (int)type);
+ return "(unknown mpack_type_t)";
+ #else
+ MPACK_UNUSED(type);
+ return "";
+ #endif
+}
+
+int mpack_tag_cmp(mpack_tag_t left, mpack_tag_t right) {
+
+ // positive numbers may be stored as int; convert to uint
+ if (left.type == mpack_type_int && left.v.i >= 0) {
+ left.type = mpack_type_uint;
+ left.v.u = (uint64_t)left.v.i;
+ }
+ if (right.type == mpack_type_int && right.v.i >= 0) {
+ right.type = mpack_type_uint;
+ right.v.u = (uint64_t)right.v.i;
+ }
+
+ if (left.type != right.type)
+ return ((int)left.type < (int)right.type) ? -1 : 1;
+
+ switch (left.type) {
+ case mpack_type_missing: // fallthrough
+ case mpack_type_nil:
+ return 0;
+
+ case mpack_type_bool:
+ return (int)left.v.b - (int)right.v.b;
+
+ case mpack_type_int:
+ if (left.v.i == right.v.i)
+ return 0;
+ return (left.v.i < right.v.i) ? -1 : 1;
+
+ case mpack_type_uint:
+ if (left.v.u == right.v.u)
+ return 0;
+ return (left.v.u < right.v.u) ? -1 : 1;
+
+ case mpack_type_array:
+ case mpack_type_map:
+ if (left.v.n == right.v.n)
+ return 0;
+ return (left.v.n < right.v.n) ? -1 : 1;
+
+ case mpack_type_str:
+ case mpack_type_bin:
+ if (left.v.l == right.v.l)
+ return 0;
+ return (left.v.l < right.v.l) ? -1 : 1;
+
+ #if MPACK_EXTENSIONS
+ case mpack_type_ext:
+ if (left.exttype == right.exttype) {
+ if (left.v.l == right.v.l)
+ return 0;
+ return (left.v.l < right.v.l) ? -1 : 1;
+ }
+ return (int)left.exttype - (int)right.exttype;
+ #endif
+
+ // floats should not normally be compared for equality. we compare
+ // with memcmp() to silence compiler warnings, but this will return
+ // equal if both are NaNs with the same representation (though we may
+ // want this, for instance if you are for some bizarre reason using
+ // floats as map keys.) i'm not sure what the right thing to
+ // do is here. check for NaN first? always return false if the type
+ // is float? use operator== and pragmas to silence compiler warning?
+ // please send me your suggestions.
+ // note also that we don't convert floats to doubles, so when this is
+ // used for ordering purposes, all floats are ordered before all
+ // doubles.
+ case mpack_type_float:
+ return mpack_memcmp(&left.v.f, &right.v.f, sizeof(left.v.f));
+ case mpack_type_double:
+ return mpack_memcmp(&left.v.d, &right.v.d, sizeof(left.v.d));
+ }
+
+ mpack_assert(0, "unrecognized type %i", (int)left.type);
+ return false;
+}
+
+#if MPACK_DEBUG && MPACK_STDIO
+static char mpack_hex_char(uint8_t hex_value) {
+ // Older compilers (e.g. GCC 4.4.7) promote the result of this ternary to
+ // int and warn under -Wconversion, so we have to cast it back to char.
+ return (char)((hex_value < 10) ? (char)('0' + hex_value) : (char)('a' + (hex_value - 10)));
+}
+
+static void mpack_tag_debug_complete_bin_ext(mpack_tag_t tag, size_t string_length, char* buffer, size_t buffer_size,
+ const char* prefix, size_t prefix_size)
+{
+ // If at any point in this function we run out of space in the buffer, we
+ // bail out. The outer tag print wrapper will make sure we have a
+ // null-terminator.
+
+ if (string_length == 0 || string_length >= buffer_size)
+ return;
+ buffer += string_length;
+ buffer_size -= string_length;
+
+ size_t total = mpack_tag_bytes(&tag);
+ if (total == 0) {
+ strncpy(buffer, ">", buffer_size);
+ return;
+ }
+
+ strncpy(buffer, ": ", buffer_size);
+ if (buffer_size < 2)
+ return;
+ buffer += 2;
+ buffer_size -= 2;
+
+ size_t hex_bytes = 0;
+ for (size_t i = 0; i < MPACK_PRINT_BYTE_COUNT && i < prefix_size && buffer_size > 2; ++i) {
+ uint8_t byte = (uint8_t)prefix[i];
+ buffer[0] = mpack_hex_char((uint8_t)(byte >> 4));
+ buffer[1] = mpack_hex_char((uint8_t)(byte & 0xfu));
+ buffer += 2;
+ buffer_size -= 2;
+ ++hex_bytes;
+ }
+
+ if (buffer_size != 0)
+ mpack_snprintf(buffer, buffer_size, "%s>", (total > hex_bytes) ? "..." : "");
+}
+
+static void mpack_tag_debug_pseudo_json_bin(mpack_tag_t tag, char* buffer, size_t buffer_size,
+ const char* prefix, size_t prefix_size)
+{
+ mpack_assert(mpack_tag_type(&tag) == mpack_type_bin);
+ size_t length = (size_t)mpack_snprintf(buffer, buffer_size, "<binary data of length %u", tag.v.l);
+ mpack_tag_debug_complete_bin_ext(tag, length, buffer, buffer_size, prefix, prefix_size);
+}
+
+#if MPACK_EXTENSIONS
+static void mpack_tag_debug_pseudo_json_ext(mpack_tag_t tag, char* buffer, size_t buffer_size,
+ const char* prefix, size_t prefix_size)
+{
+ mpack_assert(mpack_tag_type(&tag) == mpack_type_ext);
+ size_t length = (size_t)mpack_snprintf(buffer, buffer_size, "<ext data of type %i and length %u",
+ mpack_tag_ext_exttype(&tag), mpack_tag_ext_length(&tag));
+ mpack_tag_debug_complete_bin_ext(tag, length, buffer, buffer_size, prefix, prefix_size);
+}
+#endif
+
+static void mpack_tag_debug_pseudo_json_impl(mpack_tag_t tag, char* buffer, size_t buffer_size,
+ const char* prefix, size_t prefix_size)
+{
+ switch (tag.type) {
+ case mpack_type_missing:
+ mpack_snprintf(buffer, buffer_size, "<missing!>");
+ return;
+ case mpack_type_nil:
+ mpack_snprintf(buffer, buffer_size, "null");
+ return;
+ case mpack_type_bool:
+ mpack_snprintf(buffer, buffer_size, tag.v.b ? "true" : "false");
+ return;
+ case mpack_type_int:
+ mpack_snprintf(buffer, buffer_size, "%" PRIi64, tag.v.i);
+ return;
+ case mpack_type_uint:
+ mpack_snprintf(buffer, buffer_size, "%" PRIu64, tag.v.u);
+ return;
+ case mpack_type_float:
+ mpack_snprintf(buffer, buffer_size, "%f", tag.v.f);
+ return;
+ case mpack_type_double:
+ mpack_snprintf(buffer, buffer_size, "%f", tag.v.d);
+ return;
+
+ case mpack_type_str:
+ mpack_snprintf(buffer, buffer_size, "<string of %u bytes>", tag.v.l);
+ return;
+ case mpack_type_bin:
+ mpack_tag_debug_pseudo_json_bin(tag, buffer, buffer_size, prefix, prefix_size);
+ return;
+ #if MPACK_EXTENSIONS
+ case mpack_type_ext:
+ mpack_tag_debug_pseudo_json_ext(tag, buffer, buffer_size, prefix, prefix_size);
+ return;
+ #endif
+
+ case mpack_type_array:
+ mpack_snprintf(buffer, buffer_size, "<array of %u elements>", tag.v.n);
+ return;
+ case mpack_type_map:
+ mpack_snprintf(buffer, buffer_size, "<map of %u key-value pairs>", tag.v.n);
+ return;
+ }
+
+ mpack_snprintf(buffer, buffer_size, "<unknown!>");
+}
+
+void mpack_tag_debug_pseudo_json(mpack_tag_t tag, char* buffer, size_t buffer_size,
+ const char* prefix, size_t prefix_size)
+{
+ mpack_assert(buffer_size > 0, "buffer size cannot be zero!");
+ buffer[0] = 0;
+
+ mpack_tag_debug_pseudo_json_impl(tag, buffer, buffer_size, prefix, prefix_size);
+
+ // We always null-terminate the buffer manually just in case the snprintf()
+ // function doesn't null-terminate when the string doesn't fit.
+ buffer[buffer_size - 1] = 0;
+}
+
+static void mpack_tag_debug_describe_impl(mpack_tag_t tag, char* buffer, size_t buffer_size) {
+ switch (tag.type) {
+ case mpack_type_missing:
+ mpack_snprintf(buffer, buffer_size, "missing");
+ return;
+ case mpack_type_nil:
+ mpack_snprintf(buffer, buffer_size, "nil");
+ return;
+ case mpack_type_bool:
+ mpack_snprintf(buffer, buffer_size, tag.v.b ? "true" : "false");
+ return;
+ case mpack_type_int:
+ mpack_snprintf(buffer, buffer_size, "int %" PRIi64, tag.v.i);
+ return;
+ case mpack_type_uint:
+ mpack_snprintf(buffer, buffer_size, "uint %" PRIu64, tag.v.u);
+ return;
+ case mpack_type_float:
+ mpack_snprintf(buffer, buffer_size, "float %f", tag.v.f);
+ return;
+ case mpack_type_double:
+ mpack_snprintf(buffer, buffer_size, "double %f", tag.v.d);
+ return;
+ case mpack_type_str:
+ mpack_snprintf(buffer, buffer_size, "str of %u bytes", tag.v.l);
+ return;
+ case mpack_type_bin:
+ mpack_snprintf(buffer, buffer_size, "bin of %u bytes", tag.v.l);
+ return;
+ #if MPACK_EXTENSIONS
+ case mpack_type_ext:
+ mpack_snprintf(buffer, buffer_size, "ext of type %i, %u bytes",
+ mpack_tag_ext_exttype(&tag), mpack_tag_ext_length(&tag));
+ return;
+ #endif
+ case mpack_type_array:
+ mpack_snprintf(buffer, buffer_size, "array of %u elements", tag.v.n);
+ return;
+ case mpack_type_map:
+ mpack_snprintf(buffer, buffer_size, "map of %u key-value pairs", tag.v.n);
+ return;
+ }
+
+ mpack_snprintf(buffer, buffer_size, "unknown!");
+}
+
+void mpack_tag_debug_describe(mpack_tag_t tag, char* buffer, size_t buffer_size) {
+ mpack_assert(buffer_size > 0, "buffer size cannot be zero!");
+ buffer[0] = 0;
+
+ mpack_tag_debug_describe_impl(tag, buffer, buffer_size);
+
+ // We always null-terminate the buffer manually just in case the snprintf()
+ // function doesn't null-terminate when the string doesn't fit.
+ buffer[buffer_size - 1] = 0;
+}
+#endif
+
+
+
+#if MPACK_READ_TRACKING || MPACK_WRITE_TRACKING
+
+#ifndef MPACK_TRACKING_INITIAL_CAPACITY
+// seems like a reasonable number. we grow by doubling, and it only
+// needs to be as long as the maximum depth of the message.
+#define MPACK_TRACKING_INITIAL_CAPACITY 8
+#endif
+
+mpack_error_t mpack_track_init(mpack_track_t* track) {
+ track->count = 0;
+ track->capacity = MPACK_TRACKING_INITIAL_CAPACITY;
+ track->elements = (mpack_track_element_t*)MPACK_MALLOC(sizeof(mpack_track_element_t) * track->capacity);
+ if (track->elements == NULL)
+ return mpack_error_memory;
+ return mpack_ok;
+}
+
+mpack_error_t mpack_track_grow(mpack_track_t* track) {
+ mpack_assert(track->elements, "null track elements!");
+ mpack_assert(track->count == track->capacity, "incorrect growing?");
+
+ size_t new_capacity = track->capacity * 2;
+
+ mpack_track_element_t* new_elements = (mpack_track_element_t*)mpack_realloc(track->elements,
+ sizeof(mpack_track_element_t) * track->count, sizeof(mpack_track_element_t) * new_capacity);
+ if (new_elements == NULL)
+ return mpack_error_memory;
+
+ track->elements = new_elements;
+ track->capacity = new_capacity;
+ return mpack_ok;
+}
+
+mpack_error_t mpack_track_push(mpack_track_t* track, mpack_type_t type, uint32_t count) {
+ mpack_assert(track->elements, "null track elements!");
+ mpack_log("track pushing %s count %i\n", mpack_type_to_string(type), (int)count);
+
+ // grow if needed
+ if (track->count == track->capacity) {
+ mpack_error_t error = mpack_track_grow(track);
+ if (error != mpack_ok)
+ return error;
+ }
+
+ // insert new track
+ track->elements[track->count].type = type;
+ track->elements[track->count].left = count;
+ track->elements[track->count].key_needs_value = false;
+ ++track->count;
+ return mpack_ok;
+}
+
+mpack_error_t mpack_track_pop(mpack_track_t* track, mpack_type_t type) {
+ mpack_assert(track->elements, "null track elements!");
+ mpack_log("track popping %s\n", mpack_type_to_string(type));
+
+ if (track->count == 0) {
+ mpack_break("attempting to close a %s but nothing was opened!", mpack_type_to_string(type));
+ return mpack_error_bug;
+ }
+
+ mpack_track_element_t* element = &track->elements[track->count - 1];
+
+ if (element->type != type) {
+ mpack_break("attempting to close a %s but the open element is a %s!",
+ mpack_type_to_string(type), mpack_type_to_string(element->type));
+ return mpack_error_bug;
+ }
+
+ if (element->key_needs_value) {
+ mpack_assert(type == mpack_type_map, "key_needs_value can only be true for maps!");
+ mpack_break("attempting to close a %s but an odd number of elements were written",
+ mpack_type_to_string(type));
+ return mpack_error_bug;
+ }
+
+ if (element->left != 0) {
+ mpack_break("attempting to close a %s but there are %i %s left",
+ mpack_type_to_string(type), element->left,
+ (type == mpack_type_map || type == mpack_type_array) ? "elements" : "bytes");
+ return mpack_error_bug;
+ }
+
+ --track->count;
+ return mpack_ok;
+}
+
+mpack_error_t mpack_track_peek_element(mpack_track_t* track, bool read) {
+ MPACK_UNUSED(read);
+ mpack_assert(track->elements, "null track elements!");
+
+ // if there are no open elements, that's fine, we can read/write elements at will
+ if (track->count == 0)
+ return mpack_ok;
+
+ mpack_track_element_t* element = &track->elements[track->count - 1];
+
+ if (element->type != mpack_type_map && element->type != mpack_type_array) {
+ mpack_break("elements cannot be %s within an %s", read ? "read" : "written",
+ mpack_type_to_string(element->type));
+ return mpack_error_bug;
+ }
+
+ if (element->left == 0 && !element->key_needs_value) {
+ mpack_break("too many elements %s for %s", read ? "read" : "written",
+ mpack_type_to_string(element->type));
+ return mpack_error_bug;
+ }
+
+ return mpack_ok;
+}
+
+mpack_error_t mpack_track_element(mpack_track_t* track, bool read) {
+ mpack_error_t error = mpack_track_peek_element(track, read);
+ if (track->count == 0 || error != mpack_ok)
+ return error;
+
+ mpack_track_element_t* element = &track->elements[track->count - 1];
+
+ if (element->type == mpack_type_map) {
+ if (!element->key_needs_value) {
+ element->key_needs_value = true;
+ return mpack_ok; // don't decrement
+ }
+ element->key_needs_value = false;
+ }
+
+ --element->left;
+ return mpack_ok;
+}
+
+mpack_error_t mpack_track_bytes(mpack_track_t* track, bool read, size_t count) {
+ MPACK_UNUSED(read);
+ mpack_assert(track->elements, "null track elements!");
+
+ if (count > UINT32_MAX) {
+ mpack_break("%s more bytes than could possibly fit in a str/bin/ext!",
+ read ? "reading" : "writing");
+ return mpack_error_bug;
+ }
+
+ if (track->count == 0) {
+ mpack_break("bytes cannot be %s with no open bin, str or ext", read ? "read" : "written");
+ return mpack_error_bug;
+ }
+
+ mpack_track_element_t* element = &track->elements[track->count - 1];
+
+ if (element->type == mpack_type_map || element->type == mpack_type_array) {
+ mpack_break("bytes cannot be %s within an %s", read ? "read" : "written",
+ mpack_type_to_string(element->type));
+ return mpack_error_bug;
+ }
+
+ if (element->left < count) {
+ mpack_break("too many bytes %s for %s", read ? "read" : "written",
+ mpack_type_to_string(element->type));
+ return mpack_error_bug;
+ }
+
+ element->left -= (uint32_t)count;
+ return mpack_ok;
+}
+
+mpack_error_t mpack_track_str_bytes_all(mpack_track_t* track, bool read, size_t count) {
+ mpack_error_t error = mpack_track_bytes(track, read, count);
+ if (error != mpack_ok)
+ return error;
+
+ mpack_track_element_t* element = &track->elements[track->count - 1];
+
+ if (element->type != mpack_type_str) {
+ mpack_break("the open type must be a string, not a %s", mpack_type_to_string(element->type));
+ return mpack_error_bug;
+ }
+
+ if (element->left != 0) {
+ mpack_break("not all bytes were read; the wrong byte count was requested for a string read.");
+ return mpack_error_bug;
+ }
+
+ return mpack_ok;
+}
+
+mpack_error_t mpack_track_check_empty(mpack_track_t* track) {
+ if (track->count != 0) {
+ mpack_break("unclosed %s", mpack_type_to_string(track->elements[0].type));
+ return mpack_error_bug;
+ }
+ return mpack_ok;
+}
+
+mpack_error_t mpack_track_destroy(mpack_track_t* track, bool cancel) {
+ mpack_error_t error = cancel ? mpack_ok : mpack_track_check_empty(track);
+ if (track->elements) {
+ MPACK_FREE(track->elements);
+ track->elements = NULL;
+ }
+ return error;
+}
+#endif
+
+
+
+static bool mpack_utf8_check_impl(const uint8_t* str, size_t count, bool allow_null) {
+ while (count > 0) {
+ uint8_t lead = str[0];
+
+ // NUL
+ if (!allow_null && lead == '\0') // we don't allow NUL bytes in MPack C-strings
+ return false;
+
+ // ASCII
+ if (lead <= 0x7F) {
+ ++str;
+ --count;
+
+ // 2-byte sequence
+ } else if ((lead & 0xE0) == 0xC0) {
+ if (count < 2) // truncated sequence
+ return false;
+
+ uint8_t cont = str[1];
+ if ((cont & 0xC0) != 0x80) // not a continuation byte
+ return false;
+
+ str += 2;
+ count -= 2;
+
+ uint32_t z = ((uint32_t)(lead & ~0xE0) << 6) |
+ (uint32_t)(cont & ~0xC0);
+
+ if (z < 0x80) // overlong sequence
+ return false;
+
+ // 3-byte sequence
+ } else if ((lead & 0xF0) == 0xE0) {
+ if (count < 3) // truncated sequence
+ return false;
+
+ uint8_t cont1 = str[1];
+ if ((cont1 & 0xC0) != 0x80) // not a continuation byte
+ return false;
+ uint8_t cont2 = str[2];
+ if ((cont2 & 0xC0) != 0x80) // not a continuation byte
+ return false;
+
+ str += 3;
+ count -= 3;
+
+ uint32_t z = ((uint32_t)(lead & ~0xF0) << 12) |
+ ((uint32_t)(cont1 & ~0xC0) << 6) |
+ (uint32_t)(cont2 & ~0xC0);
+
+ if (z < 0x800) // overlong sequence
+ return false;
+ if (z >= 0xD800 && z <= 0xDFFF) // surrogate
+ return false;
+
+ // 4-byte sequence
+ } else if ((lead & 0xF8) == 0xF0) {
+ if (count < 4) // truncated sequence
+ return false;
+
+ uint8_t cont1 = str[1];
+ if ((cont1 & 0xC0) != 0x80) // not a continuation byte
+ return false;
+ uint8_t cont2 = str[2];
+ if ((cont2 & 0xC0) != 0x80) // not a continuation byte
+ return false;
+ uint8_t cont3 = str[3];
+ if ((cont3 & 0xC0) != 0x80) // not a continuation byte
+ return false;
+
+ str += 4;
+ count -= 4;
+
+ uint32_t z = ((uint32_t)(lead & ~0xF8) << 18) |
+ ((uint32_t)(cont1 & ~0xC0) << 12) |
+ ((uint32_t)(cont2 & ~0xC0) << 6) |
+ (uint32_t)(cont3 & ~0xC0);
+
+ if (z < 0x10000) // overlong sequence
+ return false;
+ if (z > 0x10FFFF) // codepoint limit
+ return false;
+
+ } else {
+ return false; // continuation byte without a lead, or lead for a 5-byte sequence or longer
+ }
+ }
+ return true;
+}
+
+bool mpack_utf8_check(const char* str, size_t bytes) {
+ return mpack_utf8_check_impl((const uint8_t*)str, bytes, true);
+}
+
+bool mpack_utf8_check_no_null(const char* str, size_t bytes) {
+ return mpack_utf8_check_impl((const uint8_t*)str, bytes, false);
+}
+
+bool mpack_str_check_no_null(const char* str, size_t bytes) {
+ for (size_t i = 0; i < bytes; ++i)
+ if (str[i] == '\0')
+ return false;
+ return true;
+}
+
+#if MPACK_DEBUG && MPACK_STDIO
+void mpack_print_append(mpack_print_t* print, const char* data, size_t count) {
+
+ // copy whatever fits into the buffer
+ size_t copy = print->size - print->count;
+ if (copy > count)
+ copy = count;
+ mpack_memcpy(print->buffer + print->count, data, copy);
+ print->count += copy;
+ data += copy;
+ count -= copy;
+
+ // if we don't need to flush or can't flush there's nothing else to do
+ if (count == 0 || print->callback == NULL)
+ return;
+
+ // flush the buffer
+ print->callback(print->context, print->buffer, print->count);
+
+ if (count > print->size / 2) {
+ // flush the rest of the data
+ print->count = 0;
+ print->callback(print->context, data, count);
+ } else {
+ // copy the rest of the data into the buffer
+ mpack_memcpy(print->buffer, data, count);
+ print->count = count;
+ }
+
+}
+
+void mpack_print_flush(mpack_print_t* print) {
+ if (print->count > 0 && print->callback != NULL) {
+ print->callback(print->context, print->buffer, print->count);
+ print->count = 0;
+ }
+}
+
+void mpack_print_file_callback(void* context, const char* data, size_t count) {
+ FILE* file = (FILE*)context;
+ fwrite(data, 1, count, file);
+}
+#endif
+
+/* mpack/mpack-writer.c.c */
+
+#define MPACK_INTERNAL 1
+
+/* #include "mpack-writer.h" */
+
+#if MPACK_WRITER
+
+#if MPACK_WRITE_TRACKING
+static void mpack_writer_flag_if_error(mpack_writer_t* writer, mpack_error_t error) {
+ if (error != mpack_ok)
+ mpack_writer_flag_error(writer, error);
+}
+
+void mpack_writer_track_push(mpack_writer_t* writer, mpack_type_t type, uint32_t count) {
+ if (writer->error == mpack_ok)
+ mpack_writer_flag_if_error(writer, mpack_track_push(&writer->track, type, count));
+}
+
+void mpack_writer_track_pop(mpack_writer_t* writer, mpack_type_t type) {
+ if (writer->error == mpack_ok)
+ mpack_writer_flag_if_error(writer, mpack_track_pop(&writer->track, type));
+}
+
+void mpack_writer_track_element(mpack_writer_t* writer) {
+ if (writer->error == mpack_ok)
+ mpack_writer_flag_if_error(writer, mpack_track_element(&writer->track, false));
+}
+
+void mpack_writer_track_bytes(mpack_writer_t* writer, size_t count) {
+ if (writer->error == mpack_ok)
+ mpack_writer_flag_if_error(writer, mpack_track_bytes(&writer->track, false, count));
+}
+#endif
+
+static void mpack_writer_clear(mpack_writer_t* writer) {
+ #if MPACK_COMPATIBILITY
+ writer->version = mpack_version_current;
+ #endif
+ writer->flush = NULL;
+ writer->error_fn = NULL;
+ writer->teardown = NULL;
+ writer->context = NULL;
+
+ writer->buffer = NULL;
+ writer->current = NULL;
+ writer->end = NULL;
+ writer->error = mpack_ok;
+
+ #if MPACK_WRITE_TRACKING
+ mpack_memset(&writer->track, 0, sizeof(writer->track));
+ #endif
+}
+
+void mpack_writer_init(mpack_writer_t* writer, char* buffer, size_t size) {
+ mpack_assert(buffer != NULL, "cannot initialize writer with empty buffer");
+ mpack_writer_clear(writer);
+ writer->buffer = buffer;
+ writer->current = buffer;
+ writer->end = writer->buffer + size;
+
+ #if MPACK_WRITE_TRACKING
+ mpack_writer_flag_if_error(writer, mpack_track_init(&writer->track));
+ #endif
+
+ mpack_log("===========================\n");
+ mpack_log("initializing writer with buffer size %i\n", (int)size);
+}
+
+void mpack_writer_init_error(mpack_writer_t* writer, mpack_error_t error) {
+ mpack_writer_clear(writer);
+ writer->error = error;
+
+ mpack_log("===========================\n");
+ mpack_log("initializing writer in error state %i\n", (int)error);
+}
+
+void mpack_writer_set_flush(mpack_writer_t* writer, mpack_writer_flush_t flush) {
+ MPACK_STATIC_ASSERT(MPACK_WRITER_MINIMUM_BUFFER_SIZE >= MPACK_MAXIMUM_TAG_SIZE,
+ "minimum buffer size must fit any tag!");
+ MPACK_STATIC_ASSERT(31 + MPACK_TAG_SIZE_FIXSTR >= MPACK_WRITER_MINIMUM_BUFFER_SIZE,
+ "minimum buffer size must fit the largest possible fixstr!");
+
+ if (mpack_writer_buffer_size(writer) < MPACK_WRITER_MINIMUM_BUFFER_SIZE) {
+ mpack_break("buffer size is %i, but minimum buffer size for flush is %i",
+ (int)mpack_writer_buffer_size(writer), MPACK_WRITER_MINIMUM_BUFFER_SIZE);
+ mpack_writer_flag_error(writer, mpack_error_bug);
+ return;
+ }
+
+ writer->flush = flush;
+}
+
+#ifdef MPACK_MALLOC
+typedef struct mpack_growable_writer_t {
+ char** target_data;
+ size_t* target_size;
+} mpack_growable_writer_t;
+
+static char* mpack_writer_get_reserved(mpack_writer_t* writer) {
+ // This is in a separate function in order to avoid false strict aliasing
+ // warnings. We aren't actually violating strict aliasing (the reserved
+ // space is only ever dereferenced as an mpack_growable_writer_t.)
+ return (char*)writer->reserved;
+}
+
+static void mpack_growable_writer_flush(mpack_writer_t* writer, const char* data, size_t count) {
+
+ // This is an intrusive flush function which modifies the writer's buffer
+ // in response to a flush instead of emptying it in order to add more
+ // capacity for data. This removes the need to copy data from a fixed buffer
+ // into a growable one, improving performance.
+ //
+ // There are three ways flush can be called:
+ // - flushing the buffer during writing (used is zero, count is all data, data is buffer)
+ // - flushing extra data during writing (used is all flushed data, count is extra data, data is not buffer)
+ // - flushing during teardown (used and count are both all flushed data, data is buffer)
+ //
+ // In the first two cases, we grow the buffer by at least double, enough
+ // to ensure that new data will fit. We ignore the teardown flush.
+
+ if (data == writer->buffer) {
+
+ // teardown, do nothing
+ if (mpack_writer_buffer_used(writer) == count)
+ return;
+
+ // otherwise leave the data in the buffer and just grow
+ writer->current = writer->buffer + count;
+ count = 0;
+ }
+
+ size_t used = mpack_writer_buffer_used(writer);
+ size_t size = mpack_writer_buffer_size(writer);
+
+ mpack_log("flush size %i used %i data %p buffer %p\n",
+ (int)count, (int)used, data, writer->buffer);
+
+ mpack_assert(data == writer->buffer || used + count > size,
+ "extra flush for %i but there is %i space left in the buffer! (%i/%i)",
+ (int)count, (int)mpack_writer_buffer_left(writer), (int)used, (int)size);
+
+ // grow to fit the data
+ // TODO: this really needs to correctly test for overflow
+ size_t new_size = size * 2;
+ while (new_size < used + count)
+ new_size *= 2;
+
+ mpack_log("flush growing buffer size from %i to %i\n", (int)size, (int)new_size);
+
+ // grow the buffer
+ char* new_buffer = (char*)mpack_realloc(writer->buffer, used, new_size);
+ if (new_buffer == NULL) {
+ mpack_writer_flag_error(writer, mpack_error_memory);
+ return;
+ }
+ writer->current = new_buffer + used;
+ writer->buffer = new_buffer;
+ writer->end = writer->buffer + new_size;
+
+ // append the extra data
+ if (count > 0) {
+ mpack_memcpy(writer->current, data, count);
+ writer->current += count;
+ }
+
+ mpack_log("new buffer %p, used %i\n", new_buffer, (int)mpack_writer_buffer_used(writer));
+}
+
+static void mpack_growable_writer_teardown(mpack_writer_t* writer) {
+ mpack_growable_writer_t* growable_writer = (mpack_growable_writer_t*)mpack_writer_get_reserved(writer);
+
+ if (mpack_writer_error(writer) == mpack_ok) {
+
+ // shrink the buffer to an appropriate size if the data is
+ // much smaller than the buffer
+ if (mpack_writer_buffer_used(writer) < mpack_writer_buffer_size(writer) / 2) {
+ size_t used = mpack_writer_buffer_used(writer);
+
+ // We always return a non-null pointer that must be freed, even if
+ // nothing was written. malloc() and realloc() do not necessarily
+ // do this so we enforce it ourselves.
+ size_t size = (used != 0) ? used : 1;
+
+ char* buffer = (char*)mpack_realloc(writer->buffer, used, size);
+ if (!buffer) {
+ MPACK_FREE(writer->buffer);
+ mpack_writer_flag_error(writer, mpack_error_memory);
+ return;
+ }
+ writer->buffer = buffer;
+ writer->end = (writer->current = writer->buffer + used);
+ }
+
+ *growable_writer->target_data = writer->buffer;
+ *growable_writer->target_size = mpack_writer_buffer_used(writer);
+ writer->buffer = NULL;
+
+ } else if (writer->buffer) {
+ MPACK_FREE(writer->buffer);
+ writer->buffer = NULL;
+ }
+
+ writer->context = NULL;
+}
+
+void mpack_writer_init_growable(mpack_writer_t* writer, char** target_data, size_t* target_size) {
+ mpack_assert(target_data != NULL, "cannot initialize writer without a destination for the data");
+ mpack_assert(target_size != NULL, "cannot initialize writer without a destination for the size");
+
+ *target_data = NULL;
+ *target_size = 0;
+
+ MPACK_STATIC_ASSERT(sizeof(mpack_growable_writer_t) <= sizeof(writer->reserved),
+ "not enough reserved space for growable writer!");
+ mpack_growable_writer_t* growable_writer = (mpack_growable_writer_t*)mpack_writer_get_reserved(writer);
+
+ growable_writer->target_data = target_data;
+ growable_writer->target_size = target_size;
+
+ size_t capacity = MPACK_BUFFER_SIZE;
+ char* buffer = (char*)MPACK_MALLOC(capacity);
+ if (buffer == NULL) {
+ mpack_writer_init_error(writer, mpack_error_memory);
+ return;
+ }
+
+ mpack_writer_init(writer, buffer, capacity);
+ mpack_writer_set_flush(writer, mpack_growable_writer_flush);
+ mpack_writer_set_teardown(writer, mpack_growable_writer_teardown);
+}
+#endif
+
+#if MPACK_STDIO
+static void mpack_file_writer_flush(mpack_writer_t* writer, const char* buffer, size_t count) {
+ FILE* file = (FILE*)writer->context;
+ size_t written = fwrite((const void*)buffer, 1, count, file);
+ if (written != count)
+ mpack_writer_flag_error(writer, mpack_error_io);
+}
+
+static void mpack_file_writer_teardown(mpack_writer_t* writer) {
+ MPACK_FREE(writer->buffer);
+ writer->buffer = NULL;
+ writer->context = NULL;
+}
+
+static void mpack_file_writer_teardown_close(mpack_writer_t* writer) {
+ FILE* file = (FILE*)writer->context;
+
+ if (file) {
+ int ret = fclose(file);
+ if (ret != 0)
+ mpack_writer_flag_error(writer, mpack_error_io);
+ }
+
+ mpack_file_writer_teardown(writer);
+}
+
+void mpack_writer_init_stdfile(mpack_writer_t* writer, FILE* file, bool close_when_done) {
+ mpack_assert(file != NULL, "file is NULL");
+
+ size_t capacity = MPACK_BUFFER_SIZE;
+ char* buffer = (char*)MPACK_MALLOC(capacity);
+ if (buffer == NULL) {
+ mpack_writer_init_error(writer, mpack_error_memory);
+ if (close_when_done) {
+ fclose(file);
+ }
+ return;
+ }
+
+ mpack_writer_init(writer, buffer, capacity);
+ mpack_writer_set_context(writer, file);
+ mpack_writer_set_flush(writer, mpack_file_writer_flush);
+ mpack_writer_set_teardown(writer, close_when_done ?
+ mpack_file_writer_teardown_close :
+ mpack_file_writer_teardown);
+}
+
+void mpack_writer_init_filename(mpack_writer_t* writer, const char* filename) {
+ mpack_assert(filename != NULL, "filename is NULL");
+
+ FILE* file = fopen(filename, "wb");
+ if (file == NULL) {
+ mpack_writer_init_error(writer, mpack_error_io);
+ return;
+ }
+
+ mpack_writer_init_stdfile(writer, file, true);
+}
+#endif
+
+void mpack_writer_flag_error(mpack_writer_t* writer, mpack_error_t error) {
+ mpack_log("writer %p setting error %i: %s\n", (void*)writer, (int)error, mpack_error_to_string(error));
+
+ if (writer->error == mpack_ok) {
+ writer->error = error;
+ if (writer->error_fn)
+ writer->error_fn(writer, writer->error);
+ }
+}
+
+MPACK_STATIC_INLINE void mpack_writer_flush_unchecked(mpack_writer_t* writer) {
+ // This is a bit ugly; we reset used before calling flush so that
+ // a flush function can distinguish between flushing the buffer
+ // versus flushing external data. see mpack_growable_writer_flush()
+ size_t used = mpack_writer_buffer_used(writer);
+ writer->current = writer->buffer;
+ writer->flush(writer, writer->buffer, used);
+}
+
+void mpack_writer_flush_message(mpack_writer_t* writer) {
+ if (writer->error != mpack_ok)
+ return;
+
+ #if MPACK_WRITE_TRACKING
+ mpack_writer_flag_if_error(writer, mpack_track_check_empty(&writer->track));
+ if (writer->error != mpack_ok)
+ return;
+ #endif
+
+ if (writer->flush == NULL) {
+ mpack_break("cannot call mpack_writer_flush_message() without a flush function!");
+ mpack_writer_flag_error(writer, mpack_error_bug);
+ return;
+ }
+
+ if (mpack_writer_buffer_used(writer) > 0)
+ mpack_writer_flush_unchecked(writer);
+}
+
+// Ensures there are at least count bytes free in the buffer. This
+// will flag an error if the flush function fails to make enough
+// room in the buffer.
+MPACK_NOINLINE static bool mpack_writer_ensure(mpack_writer_t* writer, size_t count) {
+ mpack_assert(count != 0, "cannot ensure zero bytes!");
+ mpack_assert(count <= MPACK_WRITER_MINIMUM_BUFFER_SIZE,
+ "cannot ensure %i bytes, this is more than the minimum buffer size %i!",
+ (int)count, (int)MPACK_WRITER_MINIMUM_BUFFER_SIZE);
+ mpack_assert(count > mpack_writer_buffer_left(writer),
+ "request to ensure %i bytes but there are already %i left in the buffer!",
+ (int)count, (int)mpack_writer_buffer_left(writer));
+
+ mpack_log("ensuring %i bytes, %i left\n", (int)count, (int)mpack_writer_buffer_left(writer));
+
+ if (mpack_writer_error(writer) != mpack_ok)
+ return false;
+
+ if (writer->flush == NULL) {
+ mpack_writer_flag_error(writer, mpack_error_too_big);
+ return false;
+ }
+
+ mpack_writer_flush_unchecked(writer);
+ if (mpack_writer_error(writer) != mpack_ok)
+ return false;
+
+ if (mpack_writer_buffer_left(writer) >= count)
+ return true;
+
+ mpack_writer_flag_error(writer, mpack_error_io);
+ return false;
+}
+
+// Writes encoded bytes to the buffer when we already know the data
+// does not fit in the buffer (i.e. it straddles the edge of the
+// buffer.) If there is a flush function, it is guaranteed to be
+// called; otherwise mpack_error_too_big is raised.
+MPACK_NOINLINE static void mpack_write_native_straddle(mpack_writer_t* writer, const char* p, size_t count) {
+ mpack_assert(count == 0 || p != NULL, "data pointer for %i bytes is NULL", (int)count);
+
+ if (mpack_writer_error(writer) != mpack_ok)
+ return;
+ mpack_log("big write for %i bytes from %p, %i space left in buffer\n",
+ (int)count, p, (int)mpack_writer_buffer_left(writer));
+ mpack_assert(count > mpack_writer_buffer_left(writer),
+ "big write requested for %i bytes, but there is %i available "
+ "space in buffer. should have called mpack_write_native() instead",
+ (int)count, (int)(mpack_writer_buffer_left(writer)));
+
+ // we'll need a flush function
+ if (!writer->flush) {
+ mpack_writer_flag_error(writer, mpack_error_too_big);
+ return;
+ }
+
+ // flush the buffer
+ mpack_writer_flush_unchecked(writer);
+ if (mpack_writer_error(writer) != mpack_ok)
+ return;
+
+ // note that an intrusive flush function (such as mpack_growable_writer_flush())
+ // may have changed size and/or reset used to a non-zero value. we treat both as
+ // though they may have changed, and there may still be data in the buffer.
+
+ // flush the extra data directly if it doesn't fit in the buffer
+ if (count > mpack_writer_buffer_left(writer)) {
+ writer->flush(writer, p, count);
+ if (mpack_writer_error(writer) != mpack_ok)
+ return;
+ } else {
+ mpack_memcpy(writer->current, p, count);
+ writer->current += count;
+ }
+}
+
+// Writes encoded bytes to the buffer, flushing if necessary.
+MPACK_STATIC_INLINE void mpack_write_native(mpack_writer_t* writer, const char* p, size_t count) {
+ mpack_assert(count == 0 || p != NULL, "data pointer for %i bytes is NULL", (int)count);
+
+ if (mpack_writer_buffer_left(writer) < count) {
+ mpack_write_native_straddle(writer, p, count);
+ } else {
+ mpack_memcpy(writer->current, p, count);
+ writer->current += count;
+ }
+}
+
+mpack_error_t mpack_writer_destroy(mpack_writer_t* writer) {
+
+ // clean up tracking, asserting if we're not already in an error state
+ #if MPACK_WRITE_TRACKING
+ mpack_track_destroy(&writer->track, writer->error != mpack_ok);
+ #endif
+
+ // flush any outstanding data
+ if (mpack_writer_error(writer) == mpack_ok && mpack_writer_buffer_used(writer) != 0 && writer->flush != NULL) {
+ writer->flush(writer, writer->buffer, mpack_writer_buffer_used(writer));
+ writer->flush = NULL;
+ }
+
+ if (writer->teardown) {
+ writer->teardown(writer);
+ writer->teardown = NULL;
+ }
+
+ return writer->error;
+}
+
+void mpack_write_tag(mpack_writer_t* writer, mpack_tag_t value) {
+ switch (value.type) {
+ case mpack_type_missing:
+ mpack_break("cannot write a missing value!");
+ mpack_writer_flag_error(writer, mpack_error_bug);
+ return;
+
+ case mpack_type_nil: mpack_write_nil (writer); return;
+ case mpack_type_bool: mpack_write_bool (writer, value.v.b); return;
+ case mpack_type_float: mpack_write_float (writer, value.v.f); return;
+ case mpack_type_double: mpack_write_double(writer, value.v.d); return;
+ case mpack_type_int: mpack_write_int (writer, value.v.i); return;
+ case mpack_type_uint: mpack_write_uint (writer, value.v.u); return;
+
+ case mpack_type_str: mpack_start_str(writer, value.v.l); return;
+ case mpack_type_bin: mpack_start_bin(writer, value.v.l); return;
+
+ #if MPACK_EXTENSIONS
+ case mpack_type_ext:
+ mpack_start_ext(writer, mpack_tag_ext_exttype(&value), mpack_tag_ext_length(&value));
+ return;
+ #endif
+
+ case mpack_type_array: mpack_start_array(writer, value.v.n); return;
+ case mpack_type_map: mpack_start_map(writer, value.v.n); return;
+ }
+
+ mpack_break("unrecognized type %i", (int)value.type);
+ mpack_writer_flag_error(writer, mpack_error_bug);
+}
+
+MPACK_STATIC_INLINE void mpack_write_byte_element(mpack_writer_t* writer, char value) {
+ mpack_writer_track_element(writer);
+ if (MPACK_LIKELY(mpack_writer_buffer_left(writer) >= 1) || mpack_writer_ensure(writer, 1))
+ *(writer->current++) = value;
+}
+
+void mpack_write_nil(mpack_writer_t* writer) {
+ mpack_write_byte_element(writer, (char)0xc0);
+}
+
+void mpack_write_bool(mpack_writer_t* writer, bool value) {
+ mpack_write_byte_element(writer, (char)(0xc2 | (value ? 1 : 0)));
+}
+
+void mpack_write_true(mpack_writer_t* writer) {
+ mpack_write_byte_element(writer, (char)0xc3);
+}
+
+void mpack_write_false(mpack_writer_t* writer) {
+ mpack_write_byte_element(writer, (char)0xc2);
+}
+
+void mpack_write_object_bytes(mpack_writer_t* writer, const char* data, size_t bytes) {
+ mpack_writer_track_element(writer);
+ mpack_write_native(writer, data, bytes);
+}
+
+/*
+ * Encode functions
+ */
+
+MPACK_STATIC_INLINE void mpack_encode_fixuint(char* p, uint8_t value) {
+ mpack_assert(value <= 127);
+ mpack_store_u8(p, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_u8(char* p, uint8_t value) {
+ mpack_assert(value > 127);
+ mpack_store_u8(p, 0xcc);
+ mpack_store_u8(p + 1, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_u16(char* p, uint16_t value) {
+ mpack_assert(value > UINT8_MAX);
+ mpack_store_u8(p, 0xcd);
+ mpack_store_u16(p + 1, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_u32(char* p, uint32_t value) {
+ mpack_assert(value > UINT16_MAX);
+ mpack_store_u8(p, 0xce);
+ mpack_store_u32(p + 1, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_u64(char* p, uint64_t value) {
+ mpack_assert(value > UINT32_MAX);
+ mpack_store_u8(p, 0xcf);
+ mpack_store_u64(p + 1, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_fixint(char* p, int8_t value) {
+ // this can encode positive or negative fixints
+ mpack_assert(value >= -32);
+ mpack_store_i8(p, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_i8(char* p, int8_t value) {
+ mpack_assert(value < -32);
+ mpack_store_u8(p, 0xd0);
+ mpack_store_i8(p + 1, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_i16(char* p, int16_t value) {
+ mpack_assert(value < INT8_MIN);
+ mpack_store_u8(p, 0xd1);
+ mpack_store_i16(p + 1, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_i32(char* p, int32_t value) {
+ mpack_assert(value < INT16_MIN);
+ mpack_store_u8(p, 0xd2);
+ mpack_store_i32(p + 1, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_i64(char* p, int64_t value) {
+ mpack_assert(value < INT32_MIN);
+ mpack_store_u8(p, 0xd3);
+ mpack_store_i64(p + 1, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_float(char* p, float value) {
+ mpack_store_u8(p, 0xca);
+ mpack_store_float(p + 1, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_double(char* p, double value) {
+ mpack_store_u8(p, 0xcb);
+ mpack_store_double(p + 1, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_fixarray(char* p, uint8_t count) {
+ mpack_assert(count <= 15);
+ mpack_store_u8(p, (uint8_t)(0x90 | count));
+}
+
+MPACK_STATIC_INLINE void mpack_encode_array16(char* p, uint16_t count) {
+ mpack_assert(count > 15);
+ mpack_store_u8(p, 0xdc);
+ mpack_store_u16(p + 1, count);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_array32(char* p, uint32_t count) {
+ mpack_assert(count > UINT16_MAX);
+ mpack_store_u8(p, 0xdd);
+ mpack_store_u32(p + 1, count);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_fixmap(char* p, uint8_t count) {
+ mpack_assert(count <= 15);
+ mpack_store_u8(p, (uint8_t)(0x80 | count));
+}
+
+MPACK_STATIC_INLINE void mpack_encode_map16(char* p, uint16_t count) {
+ mpack_assert(count > 15);
+ mpack_store_u8(p, 0xde);
+ mpack_store_u16(p + 1, count);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_map32(char* p, uint32_t count) {
+ mpack_assert(count > UINT16_MAX);
+ mpack_store_u8(p, 0xdf);
+ mpack_store_u32(p + 1, count);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_fixstr(char* p, uint8_t count) {
+ mpack_assert(count <= 31);
+ mpack_store_u8(p, (uint8_t)(0xa0 | count));
+}
+
+MPACK_STATIC_INLINE void mpack_encode_str8(char* p, uint8_t count) {
+ mpack_assert(count > 31);
+ mpack_store_u8(p, 0xd9);
+ mpack_store_u8(p + 1, count);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_str16(char* p, uint16_t count) {
+ // we might be encoding a raw in compatibility mode, so we
+ // allow count to be in the range [32, UINT8_MAX].
+ mpack_assert(count > 31);
+ mpack_store_u8(p, 0xda);
+ mpack_store_u16(p + 1, count);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_str32(char* p, uint32_t count) {
+ mpack_assert(count > UINT16_MAX);
+ mpack_store_u8(p, 0xdb);
+ mpack_store_u32(p + 1, count);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_bin8(char* p, uint8_t count) {
+ mpack_store_u8(p, 0xc4);
+ mpack_store_u8(p + 1, count);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_bin16(char* p, uint16_t count) {
+ mpack_assert(count > UINT8_MAX);
+ mpack_store_u8(p, 0xc5);
+ mpack_store_u16(p + 1, count);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_bin32(char* p, uint32_t count) {
+ mpack_assert(count > UINT16_MAX);
+ mpack_store_u8(p, 0xc6);
+ mpack_store_u32(p + 1, count);
+}
+
+#if MPACK_EXTENSIONS
+MPACK_STATIC_INLINE void mpack_encode_fixext1(char* p, int8_t exttype) {
+ mpack_store_u8(p, 0xd4);
+ mpack_store_i8(p + 1, exttype);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_fixext2(char* p, int8_t exttype) {
+ mpack_store_u8(p, 0xd5);
+ mpack_store_i8(p + 1, exttype);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_fixext4(char* p, int8_t exttype) {
+ mpack_store_u8(p, 0xd6);
+ mpack_store_i8(p + 1, exttype);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_fixext8(char* p, int8_t exttype) {
+ mpack_store_u8(p, 0xd7);
+ mpack_store_i8(p + 1, exttype);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_fixext16(char* p, int8_t exttype) {
+ mpack_store_u8(p, 0xd8);
+ mpack_store_i8(p + 1, exttype);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_ext8(char* p, int8_t exttype, uint8_t count) {
+ mpack_assert(count != 1 && count != 2 && count != 4 && count != 8 && count != 16);
+ mpack_store_u8(p, 0xc7);
+ mpack_store_u8(p + 1, count);
+ mpack_store_i8(p + 2, exttype);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_ext16(char* p, int8_t exttype, uint16_t count) {
+ mpack_assert(count > UINT8_MAX);
+ mpack_store_u8(p, 0xc8);
+ mpack_store_u16(p + 1, count);
+ mpack_store_i8(p + 3, exttype);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_ext32(char* p, int8_t exttype, uint32_t count) {
+ mpack_assert(count > UINT16_MAX);
+ mpack_store_u8(p, 0xc9);
+ mpack_store_u32(p + 1, count);
+ mpack_store_i8(p + 5, exttype);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_timestamp_4(char* p, uint32_t seconds) {
+ mpack_encode_fixext4(p, MPACK_EXTTYPE_TIMESTAMP);
+ mpack_store_u32(p + MPACK_TAG_SIZE_FIXEXT4, seconds);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_timestamp_8(char* p, int64_t seconds, uint32_t nanoseconds) {
+ mpack_assert(nanoseconds <= MPACK_TIMESTAMP_NANOSECONDS_MAX);
+ mpack_encode_fixext8(p, MPACK_EXTTYPE_TIMESTAMP);
+ uint64_t encoded = ((uint64_t)nanoseconds << 34) | (uint64_t)seconds;
+ mpack_store_u64(p + MPACK_TAG_SIZE_FIXEXT8, encoded);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_timestamp_12(char* p, int64_t seconds, uint32_t nanoseconds) {
+ mpack_assert(nanoseconds <= MPACK_TIMESTAMP_NANOSECONDS_MAX);
+ mpack_encode_ext8(p, MPACK_EXTTYPE_TIMESTAMP, 12);
+ mpack_store_u32(p + MPACK_TAG_SIZE_EXT8, nanoseconds);
+ mpack_store_i64(p + MPACK_TAG_SIZE_EXT8 + 4, seconds);
+}
+#endif
+
+
+
+/*
+ * Write functions
+ */
+
+// This is a macro wrapper to the encode functions to encode
+// directly into the buffer. If mpack_writer_ensure() fails
+// it will flag an error so we don't have to do anything.
+#define MPACK_WRITE_ENCODED(encode_fn, size, ...) do { \
+ if (MPACK_LIKELY(mpack_writer_buffer_left(writer) >= size) || mpack_writer_ensure(writer, size)) { \
+ MPACK_EXPAND(encode_fn(writer->current, __VA_ARGS__)); \
+ writer->current += size; \
+ } \
+} while (0)
+
+void mpack_write_u8(mpack_writer_t* writer, uint8_t value) {
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ mpack_write_u64(writer, value);
+ #else
+ mpack_writer_track_element(writer);
+ if (value <= 127) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixuint, MPACK_TAG_SIZE_FIXUINT, value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_u8, MPACK_TAG_SIZE_U8, value);
+ }
+ #endif
+}
+
+void mpack_write_u16(mpack_writer_t* writer, uint16_t value) {
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ mpack_write_u64(writer, value);
+ #else
+ mpack_writer_track_element(writer);
+ if (value <= 127) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixuint, MPACK_TAG_SIZE_FIXUINT, (uint8_t)value);
+ } else if (value <= UINT8_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u8, MPACK_TAG_SIZE_U8, (uint8_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_u16, MPACK_TAG_SIZE_U16, value);
+ }
+ #endif
+}
+
+void mpack_write_u32(mpack_writer_t* writer, uint32_t value) {
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ mpack_write_u64(writer, value);
+ #else
+ mpack_writer_track_element(writer);
+ if (value <= 127) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixuint, MPACK_TAG_SIZE_FIXUINT, (uint8_t)value);
+ } else if (value <= UINT8_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u8, MPACK_TAG_SIZE_U8, (uint8_t)value);
+ } else if (value <= UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u16, MPACK_TAG_SIZE_U16, (uint16_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_u32, MPACK_TAG_SIZE_U32, value);
+ }
+ #endif
+}
+
+void mpack_write_u64(mpack_writer_t* writer, uint64_t value) {
+ mpack_writer_track_element(writer);
+
+ if (value <= 127) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixuint, MPACK_TAG_SIZE_FIXUINT, (uint8_t)value);
+ } else if (value <= UINT8_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u8, MPACK_TAG_SIZE_U8, (uint8_t)value);
+ } else if (value <= UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u16, MPACK_TAG_SIZE_U16, (uint16_t)value);
+ } else if (value <= UINT32_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u32, MPACK_TAG_SIZE_U32, (uint32_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_u64, MPACK_TAG_SIZE_U64, value);
+ }
+}
+
+void mpack_write_i8(mpack_writer_t* writer, int8_t value) {
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ mpack_write_i64(writer, value);
+ #else
+ mpack_writer_track_element(writer);
+ if (value >= -32) {
+ // we encode positive and negative fixints together
+ MPACK_WRITE_ENCODED(mpack_encode_fixint, MPACK_TAG_SIZE_FIXINT, (int8_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_i8, MPACK_TAG_SIZE_I8, (int8_t)value);
+ }
+ #endif
+}
+
+void mpack_write_i16(mpack_writer_t* writer, int16_t value) {
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ mpack_write_i64(writer, value);
+ #else
+ mpack_writer_track_element(writer);
+ if (value >= -32) {
+ if (value <= 127) {
+ // we encode positive and negative fixints together
+ MPACK_WRITE_ENCODED(mpack_encode_fixint, MPACK_TAG_SIZE_FIXINT, (int8_t)value);
+ } else if (value <= UINT8_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u8, MPACK_TAG_SIZE_U8, (uint8_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_u16, MPACK_TAG_SIZE_U16, (uint16_t)value);
+ }
+ } else if (value >= INT8_MIN) {
+ MPACK_WRITE_ENCODED(mpack_encode_i8, MPACK_TAG_SIZE_I8, (int8_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_i16, MPACK_TAG_SIZE_I16, (int16_t)value);
+ }
+ #endif
+}
+
+void mpack_write_i32(mpack_writer_t* writer, int32_t value) {
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ mpack_write_i64(writer, value);
+ #else
+ mpack_writer_track_element(writer);
+ if (value >= -32) {
+ if (value <= 127) {
+ // we encode positive and negative fixints together
+ MPACK_WRITE_ENCODED(mpack_encode_fixint, MPACK_TAG_SIZE_FIXINT, (int8_t)value);
+ } else if (value <= UINT8_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u8, MPACK_TAG_SIZE_U8, (uint8_t)value);
+ } else if (value <= UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u16, MPACK_TAG_SIZE_U16, (uint16_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_u32, MPACK_TAG_SIZE_U32, (uint32_t)value);
+ }
+ } else if (value >= INT8_MIN) {
+ MPACK_WRITE_ENCODED(mpack_encode_i8, MPACK_TAG_SIZE_I8, (int8_t)value);
+ } else if (value >= INT16_MIN) {
+ MPACK_WRITE_ENCODED(mpack_encode_i16, MPACK_TAG_SIZE_I16, (int16_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_i32, MPACK_TAG_SIZE_I32, value);
+ }
+ #endif
+}
+
+void mpack_write_i64(mpack_writer_t* writer, int64_t value) {
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ if (value > 127) {
+ // for non-fix positive ints we call the u64 writer to save space
+ mpack_write_u64(writer, (uint64_t)value);
+ return;
+ }
+ #endif
+
+ mpack_writer_track_element(writer);
+ if (value >= -32) {
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ MPACK_WRITE_ENCODED(mpack_encode_fixint, MPACK_TAG_SIZE_FIXINT, (int8_t)value);
+ #else
+ if (value <= 127) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixint, MPACK_TAG_SIZE_FIXINT, (int8_t)value);
+ } else if (value <= UINT8_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u8, MPACK_TAG_SIZE_U8, (uint8_t)value);
+ } else if (value <= UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u16, MPACK_TAG_SIZE_U16, (uint16_t)value);
+ } else if (value <= UINT32_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u32, MPACK_TAG_SIZE_U32, (uint32_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_u64, MPACK_TAG_SIZE_U64, (uint64_t)value);
+ }
+ #endif
+ } else if (value >= INT8_MIN) {
+ MPACK_WRITE_ENCODED(mpack_encode_i8, MPACK_TAG_SIZE_I8, (int8_t)value);
+ } else if (value >= INT16_MIN) {
+ MPACK_WRITE_ENCODED(mpack_encode_i16, MPACK_TAG_SIZE_I16, (int16_t)value);
+ } else if (value >= INT32_MIN) {
+ MPACK_WRITE_ENCODED(mpack_encode_i32, MPACK_TAG_SIZE_I32, (int32_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_i64, MPACK_TAG_SIZE_I64, value);
+ }
+}
+
+void mpack_write_float(mpack_writer_t* writer, float value) {
+ mpack_writer_track_element(writer);
+ MPACK_WRITE_ENCODED(mpack_encode_float, MPACK_TAG_SIZE_FLOAT, value);
+}
+
+void mpack_write_double(mpack_writer_t* writer, double value) {
+ mpack_writer_track_element(writer);
+ MPACK_WRITE_ENCODED(mpack_encode_double, MPACK_TAG_SIZE_DOUBLE, value);
+}
+
+#if MPACK_EXTENSIONS
+void mpack_write_timestamp(mpack_writer_t* writer, int64_t seconds, uint32_t nanoseconds) {
+ #if MPACK_COMPATIBILITY
+ if (writer->version <= mpack_version_v4) {
+ mpack_break("Timestamps require spec version v5 or later. This writer is in v%i mode.", (int)writer->version);
+ mpack_writer_flag_error(writer, mpack_error_bug);
+ return;
+ }
+ #endif
+
+ if (nanoseconds > MPACK_TIMESTAMP_NANOSECONDS_MAX) {
+ mpack_break("timestamp nanoseconds out of bounds: %u", nanoseconds);
+ mpack_writer_flag_error(writer, mpack_error_bug);
+ return;
+ }
+
+ mpack_writer_track_element(writer);
+
+ if (seconds < 0 || seconds >= (INT64_C(1) << 34)) {
+ MPACK_WRITE_ENCODED(mpack_encode_timestamp_12, MPACK_EXT_SIZE_TIMESTAMP12, seconds, nanoseconds);
+ } else if (seconds > UINT32_MAX || nanoseconds > 0) {
+ MPACK_WRITE_ENCODED(mpack_encode_timestamp_8, MPACK_EXT_SIZE_TIMESTAMP8, seconds, nanoseconds);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_timestamp_4, MPACK_EXT_SIZE_TIMESTAMP4, (uint32_t)seconds);
+ }
+}
+#endif
+
+void mpack_start_array(mpack_writer_t* writer, uint32_t count) {
+ mpack_writer_track_element(writer);
+
+ if (count <= 15) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixarray, MPACK_TAG_SIZE_FIXARRAY, (uint8_t)count);
+ } else if (count <= UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_array16, MPACK_TAG_SIZE_ARRAY16, (uint16_t)count);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_array32, MPACK_TAG_SIZE_ARRAY32, (uint32_t)count);
+ }
+
+ mpack_writer_track_push(writer, mpack_type_array, count);
+}
+
+void mpack_start_map(mpack_writer_t* writer, uint32_t count) {
+ mpack_writer_track_element(writer);
+
+ if (count <= 15) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixmap, MPACK_TAG_SIZE_FIXMAP, (uint8_t)count);
+ } else if (count <= UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_map16, MPACK_TAG_SIZE_MAP16, (uint16_t)count);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_map32, MPACK_TAG_SIZE_MAP32, (uint32_t)count);
+ }
+
+ mpack_writer_track_push(writer, mpack_type_map, count);
+}
+
+static void mpack_start_str_notrack(mpack_writer_t* writer, uint32_t count) {
+ if (count <= 31) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixstr, MPACK_TAG_SIZE_FIXSTR, (uint8_t)count);
+
+ // str8 is only supported in v5 or later.
+ } else if (count <= UINT8_MAX
+ #if MPACK_COMPATIBILITY
+ && writer->version >= mpack_version_v5
+ #endif
+ ) {
+ MPACK_WRITE_ENCODED(mpack_encode_str8, MPACK_TAG_SIZE_STR8, (uint8_t)count);
+
+ } else if (count <= UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_str16, MPACK_TAG_SIZE_STR16, (uint16_t)count);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_str32, MPACK_TAG_SIZE_STR32, (uint32_t)count);
+ }
+}
+
+static void mpack_start_bin_notrack(mpack_writer_t* writer, uint32_t count) {
+ #if MPACK_COMPATIBILITY
+ // In the v4 spec, there was only the raw type for any kind of
+ // variable-length data. In v4 mode, we support the bin functions,
+ // but we produce an old-style raw.
+ if (writer->version <= mpack_version_v4) {
+ mpack_start_str_notrack(writer, count);
+ return;
+ }
+ #endif
+
+ if (count <= UINT8_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_bin8, MPACK_TAG_SIZE_BIN8, (uint8_t)count);
+ } else if (count <= UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_bin16, MPACK_TAG_SIZE_BIN16, (uint16_t)count);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_bin32, MPACK_TAG_SIZE_BIN32, (uint32_t)count);
+ }
+}
+
+void mpack_start_str(mpack_writer_t* writer, uint32_t count) {
+ mpack_writer_track_element(writer);
+ mpack_start_str_notrack(writer, count);
+ mpack_writer_track_push(writer, mpack_type_str, count);
+}
+
+void mpack_start_bin(mpack_writer_t* writer, uint32_t count) {
+ mpack_writer_track_element(writer);
+ mpack_start_bin_notrack(writer, count);
+ mpack_writer_track_push(writer, mpack_type_bin, count);
+}
+
+#if MPACK_EXTENSIONS
+void mpack_start_ext(mpack_writer_t* writer, int8_t exttype, uint32_t count) {
+ #if MPACK_COMPATIBILITY
+ if (writer->version <= mpack_version_v4) {
+ mpack_break("Ext types require spec version v5 or later. This writer is in v%i mode.", (int)writer->version);
+ mpack_writer_flag_error(writer, mpack_error_bug);
+ return;
+ }
+ #endif
+
+ mpack_writer_track_element(writer);
+
+ if (count == 1) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixext1, MPACK_TAG_SIZE_FIXEXT1, exttype);
+ } else if (count == 2) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixext2, MPACK_TAG_SIZE_FIXEXT2, exttype);
+ } else if (count == 4) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixext4, MPACK_TAG_SIZE_FIXEXT4, exttype);
+ } else if (count == 8) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixext8, MPACK_TAG_SIZE_FIXEXT8, exttype);
+ } else if (count == 16) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixext16, MPACK_TAG_SIZE_FIXEXT16, exttype);
+ } else if (count <= UINT8_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_ext8, MPACK_TAG_SIZE_EXT8, exttype, (uint8_t)count);
+ } else if (count <= UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_ext16, MPACK_TAG_SIZE_EXT16, exttype, (uint16_t)count);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_ext32, MPACK_TAG_SIZE_EXT32, exttype, (uint32_t)count);
+ }
+
+ mpack_writer_track_push(writer, mpack_type_ext, count);
+}
+#endif
+
+
+
+/*
+ * Compound helpers and other functions
+ */
+
+void mpack_write_str(mpack_writer_t* writer, const char* data, uint32_t count) {
+ mpack_assert(data != NULL, "data for string of length %i is NULL", (int)count);
+
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ mpack_writer_track_element(writer);
+ mpack_start_str_notrack(writer, count);
+ mpack_write_native(writer, data, count);
+ #else
+
+ mpack_writer_track_element(writer);
+
+ if (count <= 31) {
+ // The minimum buffer size when using a flush function is guaranteed to
+ // fit the largest possible fixstr.
+ size_t size = count + MPACK_TAG_SIZE_FIXSTR;
+ if (MPACK_LIKELY(mpack_writer_buffer_left(writer) >= size) || mpack_writer_ensure(writer, size)) {
+ char* MPACK_RESTRICT p = writer->current;
+ mpack_encode_fixstr(p, (uint8_t)count);
+ mpack_memcpy(p + MPACK_TAG_SIZE_FIXSTR, data, count);
+ writer->current += count + MPACK_TAG_SIZE_FIXSTR;
+ }
+ return;
+ }
+
+ if (count <= UINT8_MAX
+ #if MPACK_COMPATIBILITY
+ && writer->version >= mpack_version_v5
+ #endif
+ ) {
+ if (count + MPACK_TAG_SIZE_STR8 <= mpack_writer_buffer_left(writer)) {
+ char* MPACK_RESTRICT p = writer->current;
+ mpack_encode_str8(p, (uint8_t)count);
+ mpack_memcpy(p + MPACK_TAG_SIZE_STR8, data, count);
+ writer->current += count + MPACK_TAG_SIZE_STR8;
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_str8, MPACK_TAG_SIZE_STR8, (uint8_t)count);
+ mpack_write_native(writer, data, count);
+ }
+ return;
+ }
+
+ // str16 and str32 are likely to be a significant fraction of the buffer
+ // size, so we don't bother with a combined space check in order to
+ // minimize code size.
+ if (count <= UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_str16, MPACK_TAG_SIZE_STR16, (uint16_t)count);
+ mpack_write_native(writer, data, count);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_str32, MPACK_TAG_SIZE_STR32, (uint32_t)count);
+ mpack_write_native(writer, data, count);
+ }
+
+ #endif
+}
+
+void mpack_write_bin(mpack_writer_t* writer, const char* data, uint32_t count) {
+ mpack_assert(data != NULL, "data pointer for bin of %i bytes is NULL", (int)count);
+ mpack_start_bin(writer, count);
+ mpack_write_bytes(writer, data, count);
+ mpack_finish_bin(writer);
+}
+
+#if MPACK_EXTENSIONS
+void mpack_write_ext(mpack_writer_t* writer, int8_t exttype, const char* data, uint32_t count) {
+ mpack_assert(data != NULL, "data pointer for ext of type %i and %i bytes is NULL", exttype, (int)count);
+ mpack_start_ext(writer, exttype, count);
+ mpack_write_bytes(writer, data, count);
+ mpack_finish_ext(writer);
+}
+#endif
+
+void mpack_write_bytes(mpack_writer_t* writer, const char* data, size_t count) {
+ mpack_assert(data != NULL, "data pointer for %i bytes is NULL", (int)count);
+ mpack_writer_track_bytes(writer, count);
+ mpack_write_native(writer, data, count);
+}
+
+void mpack_write_cstr(mpack_writer_t* writer, const char* cstr) {
+ mpack_assert(cstr != NULL, "cstr pointer is NULL");
+ size_t length = mpack_strlen(cstr);
+ if (length > UINT32_MAX)
+ mpack_writer_flag_error(writer, mpack_error_invalid);
+ mpack_write_str(writer, cstr, (uint32_t)length);
+}
+
+void mpack_write_cstr_or_nil(mpack_writer_t* writer, const char* cstr) {
+ if (cstr)
+ mpack_write_cstr(writer, cstr);
+ else
+ mpack_write_nil(writer);
+}
+
+void mpack_write_utf8(mpack_writer_t* writer, const char* str, uint32_t length) {
+ mpack_assert(str != NULL, "data for string of length %i is NULL", (int)length);
+ if (!mpack_utf8_check(str, length)) {
+ mpack_writer_flag_error(writer, mpack_error_invalid);
+ return;
+ }
+ mpack_write_str(writer, str, length);
+}
+
+void mpack_write_utf8_cstr(mpack_writer_t* writer, const char* cstr) {
+ mpack_assert(cstr != NULL, "cstr pointer is NULL");
+ size_t length = mpack_strlen(cstr);
+ if (length > UINT32_MAX) {
+ mpack_writer_flag_error(writer, mpack_error_invalid);
+ return;
+ }
+ mpack_write_utf8(writer, cstr, (uint32_t)length);
+}
+
+void mpack_write_utf8_cstr_or_nil(mpack_writer_t* writer, const char* cstr) {
+ if (cstr)
+ mpack_write_utf8_cstr(writer, cstr);
+ else
+ mpack_write_nil(writer);
+}
+
+#endif
+
+
+/* mpack/mpack-reader.c.c */
+
+#define MPACK_INTERNAL 1
+
+/* #include "mpack-reader.h" */
+
+#if MPACK_READER
+
+static void mpack_reader_skip_using_fill(mpack_reader_t* reader, size_t count);
+
+void mpack_reader_init(mpack_reader_t* reader, char* buffer, size_t size, size_t count) {
+ mpack_assert(buffer != NULL, "buffer is NULL");
+
+ mpack_memset(reader, 0, sizeof(*reader));
+ reader->buffer = buffer;
+ reader->size = size;
+ reader->data = buffer;
+ reader->end = buffer + count;
+
+ #if MPACK_READ_TRACKING
+ mpack_reader_flag_if_error(reader, mpack_track_init(&reader->track));
+ #endif
+
+ mpack_log("===========================\n");
+ mpack_log("initializing reader with buffer size %i\n", (int)size);
+}
+
+void mpack_reader_init_error(mpack_reader_t* reader, mpack_error_t error) {
+ mpack_memset(reader, 0, sizeof(*reader));
+ reader->error = error;
+
+ mpack_log("===========================\n");
+ mpack_log("initializing reader error state %i\n", (int)error);
+}
+
+void mpack_reader_init_data(mpack_reader_t* reader, const char* data, size_t count) {
+ mpack_assert(data != NULL, "data is NULL");
+
+ mpack_memset(reader, 0, sizeof(*reader));
+ reader->data = data;
+ reader->end = data + count;
+
+ #if MPACK_READ_TRACKING
+ mpack_reader_flag_if_error(reader, mpack_track_init(&reader->track));
+ #endif
+
+ mpack_log("===========================\n");
+ mpack_log("initializing reader with data size %i\n", (int)count);
+}
+
+void mpack_reader_set_fill(mpack_reader_t* reader, mpack_reader_fill_t fill) {
+ MPACK_STATIC_ASSERT(MPACK_READER_MINIMUM_BUFFER_SIZE >= MPACK_MAXIMUM_TAG_SIZE,
+ "minimum buffer size must fit any tag!");
+
+ if (reader->size == 0) {
+ mpack_break("cannot use fill function without a writeable buffer!");
+ mpack_reader_flag_error(reader, mpack_error_bug);
+ return;
+ }
+
+ if (reader->size < MPACK_READER_MINIMUM_BUFFER_SIZE) {
+ mpack_break("buffer size is %i, but minimum buffer size for fill is %i",
+ (int)reader->size, MPACK_READER_MINIMUM_BUFFER_SIZE);
+ mpack_reader_flag_error(reader, mpack_error_bug);
+ return;
+ }
+
+ reader->fill = fill;
+}
+
+void mpack_reader_set_skip(mpack_reader_t* reader, mpack_reader_skip_t skip) {
+ mpack_assert(reader->size != 0, "cannot use skip function without a writeable buffer!");
+ reader->skip = skip;
+}
+
+#if MPACK_STDIO
+static size_t mpack_file_reader_fill(mpack_reader_t* reader, char* buffer, size_t count) {
+ if (feof((FILE *)reader->context)) {
+ mpack_reader_flag_error(reader, mpack_error_eof);
+ return 0;
+ }
+ return fread((void*)buffer, 1, count, (FILE*)reader->context);
+}
+
+static void mpack_file_reader_skip(mpack_reader_t* reader, size_t count) {
+ if (mpack_reader_error(reader) != mpack_ok)
+ return;
+ FILE* file = (FILE*)reader->context;
+
+ // We call ftell() to test whether the stream is seekable
+ // without causing a file error.
+ if (ftell(file) >= 0) {
+ mpack_log("seeking forward %i bytes\n", (int)count);
+ if (fseek(file, (long int)count, SEEK_CUR) == 0)
+ return;
+ mpack_log("fseek() didn't return zero!\n");
+ if (ferror(file)) {
+ mpack_reader_flag_error(reader, mpack_error_io);
+ return;
+ }
+ }
+
+ // If the stream is not seekable, fall back to the fill function.
+ mpack_reader_skip_using_fill(reader, count);
+}
+
+static void mpack_file_reader_teardown(mpack_reader_t* reader) {
+ MPACK_FREE(reader->buffer);
+ reader->buffer = NULL;
+ reader->context = NULL;
+ reader->size = 0;
+ reader->fill = NULL;
+ reader->skip = NULL;
+ reader->teardown = NULL;
+}
+
+static void mpack_file_reader_teardown_close(mpack_reader_t* reader) {
+ FILE* file = (FILE*)reader->context;
+
+ if (file) {
+ int ret = fclose(file);
+ if (ret != 0)
+ mpack_reader_flag_error(reader, mpack_error_io);
+ }
+
+ mpack_file_reader_teardown(reader);
+}
+
+void mpack_reader_init_stdfile(mpack_reader_t* reader, FILE* file, bool close_when_done) {
+ mpack_assert(file != NULL, "file is NULL");
+
+ size_t capacity = MPACK_BUFFER_SIZE;
+ char* buffer = (char*)MPACK_MALLOC(capacity);
+ if (buffer == NULL) {
+ mpack_reader_init_error(reader, mpack_error_memory);
+ if (close_when_done) {
+ fclose(file);
+ }
+ return;
+ }
+
+ mpack_reader_init(reader, buffer, capacity, 0);
+ mpack_reader_set_context(reader, file);
+ mpack_reader_set_fill(reader, mpack_file_reader_fill);
+ mpack_reader_set_skip(reader, mpack_file_reader_skip);
+ mpack_reader_set_teardown(reader, close_when_done ?
+ mpack_file_reader_teardown_close :
+ mpack_file_reader_teardown);
+}
+
+void mpack_reader_init_filename(mpack_reader_t* reader, const char* filename) {
+ mpack_assert(filename != NULL, "filename is NULL");
+
+ FILE* file = fopen(filename, "rb");
+ if (file == NULL) {
+ mpack_reader_init_error(reader, mpack_error_io);
+ return;
+ }
+
+ mpack_reader_init_stdfile(reader, file, true);
+}
+#endif
+
+mpack_error_t mpack_reader_destroy(mpack_reader_t* reader) {
+
+ // clean up tracking, asserting if we're not already in an error state
+ #if MPACK_READ_TRACKING
+ mpack_reader_flag_if_error(reader, mpack_track_destroy(&reader->track, mpack_reader_error(reader) != mpack_ok));
+ #endif
+
+ if (reader->teardown)
+ reader->teardown(reader);
+ reader->teardown = NULL;
+
+ return reader->error;
+}
+
+size_t mpack_reader_remaining(mpack_reader_t* reader, const char** data) {
+ if (mpack_reader_error(reader) != mpack_ok)
+ return 0;
+
+ #if MPACK_READ_TRACKING
+ if (mpack_reader_flag_if_error(reader, mpack_track_check_empty(&reader->track)) != mpack_ok)
+ return 0;
+ #endif
+
+ if (data)
+ *data = reader->data;
+ return (size_t)(reader->end - reader->data);
+}
+
+void mpack_reader_flag_error(mpack_reader_t* reader, mpack_error_t error) {
+ mpack_log("reader %p setting error %i: %s\n", (void*)reader, (int)error, mpack_error_to_string(error));
+
+ if (reader->error == mpack_ok) {
+ reader->error = error;
+ reader->end = reader->data;
+ if (reader->error_fn)
+ reader->error_fn(reader, error);
+ }
+}
+
+// Loops on the fill function, reading between the minimum and
+// maximum number of bytes and flagging an error if it fails.
+MPACK_NOINLINE static size_t mpack_fill_range(mpack_reader_t* reader, char* p, size_t min_bytes, size_t max_bytes) {
+ mpack_assert(reader->fill != NULL, "mpack_fill_range() called with no fill function?");
+ mpack_assert(min_bytes > 0, "cannot fill zero bytes!");
+ mpack_assert(max_bytes >= min_bytes, "min_bytes %i cannot be larger than max_bytes %i!",
+ (int)min_bytes, (int)max_bytes);
+
+ size_t count = 0;
+ while (count < min_bytes) {
+ size_t read = reader->fill(reader, p + count, max_bytes - count);
+
+ // Reader fill functions can flag an error or return 0 on failure. We
+ // also guard against functions that return -1 just in case.
+ if (mpack_reader_error(reader) != mpack_ok)
+ return 0;
+ if (read == 0 || read == ((size_t)(-1))) {
+ mpack_reader_flag_error(reader, mpack_error_io);
+ return 0;
+ }
+
+ count += read;
+ }
+ return count;
+}
+
+MPACK_NOINLINE bool mpack_reader_ensure_straddle(mpack_reader_t* reader, size_t count) {
+ mpack_assert(count != 0, "cannot ensure zero bytes!");
+ mpack_assert(reader->error == mpack_ok, "reader cannot be in an error state!");
+
+ mpack_assert(count > (size_t)(reader->end - reader->data),
+ "straddling ensure requested for %i bytes, but there are %i bytes "
+ "left in buffer. call mpack_reader_ensure() instead",
+ (int)count, (int)(reader->end - reader->data));
+
+ // we'll need a fill function to get more data. if there's no
+ // fill function, the buffer should contain an entire MessagePack
+ // object, so we raise mpack_error_invalid instead of mpack_error_io
+ // on truncated data.
+ if (reader->fill == NULL) {
+ mpack_reader_flag_error(reader, mpack_error_invalid);
+ return false;
+ }
+
+ // we need enough space in the buffer. if the buffer is not
+ // big enough, we return mpack_error_too_big (since this is
+ // for an in-place read larger than the buffer size.)
+ if (count > reader->size) {
+ mpack_reader_flag_error(reader, mpack_error_too_big);
+ return false;
+ }
+
+ // move the existing data to the start of the buffer
+ size_t left = (size_t)(reader->end - reader->data);
+ mpack_memmove(reader->buffer, reader->data, left);
+ reader->end -= reader->data - reader->buffer;
+ reader->data = reader->buffer;
+
+ // read at least the necessary number of bytes, accepting up to the
+ // buffer size
+ size_t read = mpack_fill_range(reader, reader->buffer + left,
+ count - left, reader->size - left);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return false;
+ reader->end += read;
+ return true;
+}
+
+// Reads count bytes into p. Used when there are not enough bytes
+// left in the buffer to satisfy a read.
+MPACK_NOINLINE void mpack_read_native_straddle(mpack_reader_t* reader, char* p, size_t count) {
+ mpack_assert(count == 0 || p != NULL, "data pointer for %i bytes is NULL", (int)count);
+
+ if (mpack_reader_error(reader) != mpack_ok) {
+ mpack_memset(p, 0, count);
+ return;
+ }
+
+ size_t left = (size_t)(reader->end - reader->data);
+ mpack_log("big read for %i bytes into %p, %i left in buffer, buffer size %i\n",
+ (int)count, p, (int)left, (int)reader->size);
+
+ if (count <= left) {
+ mpack_assert(0,
+ "big read requested for %i bytes, but there are %i bytes "
+ "left in buffer. call mpack_read_native() instead",
+ (int)count, (int)left);
+ mpack_reader_flag_error(reader, mpack_error_bug);
+ mpack_memset(p, 0, count);
+ return;
+ }
+
+ // we'll need a fill function to get more data. if there's no
+ // fill function, the buffer should contain an entire MessagePack
+ // object, so we raise mpack_error_invalid instead of mpack_error_io
+ // on truncated data.
+ if (reader->fill == NULL) {
+ mpack_reader_flag_error(reader, mpack_error_invalid);
+ mpack_memset(p, 0, count);
+ return;
+ }
+
+ if (reader->size == 0) {
+ // somewhat debatable what error should be returned here. when
+ // initializing a reader with an in-memory buffer it's not
+ // necessarily a bug if the data is blank; it might just have
+ // been truncated to zero. for this reason we return the same
+ // error as if the data was truncated.
+ mpack_reader_flag_error(reader, mpack_error_io);
+ mpack_memset(p, 0, count);
+ return;
+ }
+
+ // flush what's left of the buffer
+ if (left > 0) {
+ mpack_log("flushing %i bytes remaining in buffer\n", (int)left);
+ mpack_memcpy(p, reader->data, left);
+ count -= left;
+ p += left;
+ reader->data += left;
+ }
+
+ // if the remaining data needed is some small fraction of the
+ // buffer size, we'll try to fill the buffer as much as possible
+ // and copy the needed data out.
+ if (count <= reader->size / MPACK_READER_SMALL_FRACTION_DENOMINATOR) {
+ size_t read = mpack_fill_range(reader, reader->buffer, count, reader->size);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return;
+ mpack_memcpy(p, reader->buffer, count);
+ reader->data = reader->buffer + count;
+ reader->end = reader->buffer + read;
+
+ // otherwise we read the remaining data directly into the target.
+ } else {
+ mpack_log("reading %i additional bytes\n", (int)count);
+ mpack_fill_range(reader, p, count, count);
+ }
+}
+
+MPACK_NOINLINE static void mpack_skip_bytes_straddle(mpack_reader_t* reader, size_t count) {
+
+ // we'll need at least a fill function to skip more data. if there's
+ // no fill function, the buffer should contain an entire MessagePack
+ // object, so we raise mpack_error_invalid instead of mpack_error_io
+ // on truncated data. (see mpack_read_native_straddle())
+ if (reader->fill == NULL) {
+ mpack_log("reader has no fill function!\n");
+ mpack_reader_flag_error(reader, mpack_error_invalid);
+ return;
+ }
+
+ // discard whatever's left in the buffer
+ size_t left = (size_t)(reader->end - reader->data);
+ mpack_log("discarding %i bytes still in buffer\n", (int)left);
+ count -= left;
+ reader->data = reader->end;
+
+ // use the skip function if we've got one, and if we're trying
+ // to skip a lot of data. if we only need to skip some tiny
+ // fraction of the buffer size, it's probably better to just
+ // fill the buffer and skip from it instead of trying to seek.
+ if (reader->skip && count > reader->size / 16) {
+ mpack_log("calling skip function for %i bytes\n", (int)count);
+ reader->skip(reader, count);
+ return;
+ }
+
+ mpack_reader_skip_using_fill(reader, count);
+}
+
+void mpack_skip_bytes(mpack_reader_t* reader, size_t count) {
+ if (mpack_reader_error(reader) != mpack_ok)
+ return;
+ mpack_log("skip requested for %i bytes\n", (int)count);
+
+ mpack_reader_track_bytes(reader, count);
+
+ // check if we have enough in the buffer already
+ size_t left = (size_t)(reader->end - reader->data);
+ if (left >= count) {
+ mpack_log("skipping %u bytes still in buffer\n", (uint32_t)count);
+ reader->data += count;
+ return;
+ }
+
+ mpack_skip_bytes_straddle(reader, count);
+}
+
+MPACK_NOINLINE static void mpack_reader_skip_using_fill(mpack_reader_t* reader, size_t count) {
+ mpack_assert(reader->fill != NULL, "missing fill function!");
+ mpack_assert(reader->data == reader->end, "there are bytes left in the buffer!");
+ mpack_assert(reader->error == mpack_ok, "should not have called this in an error state (%i)", reader->error);
+ mpack_log("skip using fill for %i bytes\n", (int)count);
+
+ // fill and discard multiples of the buffer size
+ while (count > reader->size) {
+ mpack_log("filling and discarding buffer of %i bytes\n", (int)reader->size);
+ if (mpack_fill_range(reader, reader->buffer, reader->size, reader->size) < reader->size) {
+ mpack_reader_flag_error(reader, mpack_error_io);
+ return;
+ }
+ count -= reader->size;
+ }
+
+ // fill the buffer as much as possible
+ reader->data = reader->buffer;
+ size_t read = mpack_fill_range(reader, reader->buffer, count, reader->size);
+ if (read < count) {
+ mpack_reader_flag_error(reader, mpack_error_io);
+ return;
+ }
+ reader->end = reader->data + read;
+ mpack_log("filled %i bytes into buffer; discarding %i bytes\n", (int)read, (int)count);
+ reader->data += count;
+}
+
+void mpack_read_bytes(mpack_reader_t* reader, char* p, size_t count) {
+ mpack_assert(p != NULL, "destination for read of %i bytes is NULL", (int)count);
+ mpack_reader_track_bytes(reader, count);
+ mpack_read_native(reader, p, count);
+}
+
+void mpack_read_utf8(mpack_reader_t* reader, char* p, size_t byte_count) {
+ mpack_assert(p != NULL, "destination for read of %i bytes is NULL", (int)byte_count);
+ mpack_reader_track_str_bytes_all(reader, byte_count);
+ mpack_read_native(reader, p, byte_count);
+
+ if (mpack_reader_error(reader) == mpack_ok && !mpack_utf8_check(p, byte_count))
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+static void mpack_read_cstr_unchecked(mpack_reader_t* reader, char* buf, size_t buffer_size, size_t byte_count) {
+ mpack_assert(buf != NULL, "destination for read of %i bytes is NULL", (int)byte_count);
+ mpack_assert(buffer_size >= 1, "buffer size is zero; you must have room for at least a null-terminator");
+
+ if (mpack_reader_error(reader)) {
+ buf[0] = 0;
+ return;
+ }
+
+ if (byte_count > buffer_size - 1) {
+ mpack_reader_flag_error(reader, mpack_error_too_big);
+ buf[0] = 0;
+ return;
+ }
+
+ mpack_reader_track_str_bytes_all(reader, byte_count);
+ mpack_read_native(reader, buf, byte_count);
+ buf[byte_count] = 0;
+}
+
+void mpack_read_cstr(mpack_reader_t* reader, char* buf, size_t buffer_size, size_t byte_count) {
+ mpack_read_cstr_unchecked(reader, buf, buffer_size, byte_count);
+
+ // check for null bytes
+ if (mpack_reader_error(reader) == mpack_ok && !mpack_str_check_no_null(buf, byte_count)) {
+ buf[0] = 0;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ }
+}
+
+void mpack_read_utf8_cstr(mpack_reader_t* reader, char* buf, size_t buffer_size, size_t byte_count) {
+ mpack_read_cstr_unchecked(reader, buf, buffer_size, byte_count);
+
+ // check encoding
+ if (mpack_reader_error(reader) == mpack_ok && !mpack_utf8_check_no_null(buf, byte_count)) {
+ buf[0] = 0;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ }
+}
+
+#ifdef MPACK_MALLOC
+// Reads native bytes with error callback disabled. This allows MPack reader functions
+// to hold an allocated buffer and read native data into it without leaking it in
+// case of a non-local jump (longjmp, throw) out of an error handler.
+static void mpack_read_native_noerrorfn(mpack_reader_t* reader, char* p, size_t count) {
+ mpack_assert(reader->error == mpack_ok, "cannot call if an error is already flagged!");
+ mpack_reader_error_t error_fn = reader->error_fn;
+ reader->error_fn = NULL;
+ mpack_read_native(reader, p, count);
+ reader->error_fn = error_fn;
+}
+
+char* mpack_read_bytes_alloc_impl(mpack_reader_t* reader, size_t count, bool null_terminated) {
+
+ // track the bytes first in case it jumps
+ mpack_reader_track_bytes(reader, count);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return NULL;
+
+ // cannot allocate zero bytes. this is not an error.
+ if (count == 0 && null_terminated == false)
+ return NULL;
+
+ // allocate data
+ char* data = (char*)MPACK_MALLOC(count + (null_terminated ? 1 : 0)); // TODO: can this overflow?
+ if (data == NULL) {
+ mpack_reader_flag_error(reader, mpack_error_memory);
+ return NULL;
+ }
+
+ // read with error callback disabled so we don't leak our buffer
+ mpack_read_native_noerrorfn(reader, data, count);
+
+ // report flagged errors
+ if (mpack_reader_error(reader) != mpack_ok) {
+ MPACK_FREE(data);
+ if (reader->error_fn)
+ reader->error_fn(reader, mpack_reader_error(reader));
+ return NULL;
+ }
+
+ if (null_terminated)
+ data[count] = '\0';
+ return data;
+}
+#endif
+
+// read inplace without tracking (since there are different
+// tracking modes for different inplace readers)
+static const char* mpack_read_bytes_inplace_notrack(mpack_reader_t* reader, size_t count) {
+ if (mpack_reader_error(reader) != mpack_ok)
+ return NULL;
+
+ // if we have enough bytes already in the buffer, we can return it directly.
+ if ((size_t)(reader->end - reader->data) >= count) {
+ const char* bytes = reader->data;
+ reader->data += count;
+ return bytes;
+ }
+
+ if (!mpack_reader_ensure(reader, count))
+ return NULL;
+
+ const char* bytes = reader->data;
+ reader->data += count;
+ return bytes;
+}
+
+const char* mpack_read_bytes_inplace(mpack_reader_t* reader, size_t count) {
+ mpack_reader_track_bytes(reader, count);
+ return mpack_read_bytes_inplace_notrack(reader, count);
+}
+
+const char* mpack_read_utf8_inplace(mpack_reader_t* reader, size_t count) {
+ mpack_reader_track_str_bytes_all(reader, count);
+ const char* str = mpack_read_bytes_inplace_notrack(reader, count);
+
+ if (mpack_reader_error(reader) == mpack_ok && !mpack_utf8_check(str, count)) {
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return NULL;
+ }
+
+ return str;
+}
+
+static size_t mpack_parse_tag(mpack_reader_t* reader, mpack_tag_t* tag) {
+ mpack_assert(reader->error == mpack_ok, "reader cannot be in an error state!");
+
+ if (!mpack_reader_ensure(reader, 1))
+ return 0;
+ uint8_t type = mpack_load_u8(reader->data);
+
+ // unfortunately, by far the fastest way to parse a tag is to switch
+ // on the first byte, and to explicitly list every possible byte. so for
+ // infix types, the list of cases is quite large.
+ //
+ // in size-optimized builds, we switch on the top four bits first to
+ // handle most infix types with a smaller jump table to save space.
+
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ switch (type >> 4) {
+
+ // positive fixnum
+ case 0x0: case 0x1: case 0x2: case 0x3:
+ case 0x4: case 0x5: case 0x6: case 0x7:
+ *tag = mpack_tag_make_uint(type);
+ return 1;
+
+ // negative fixnum
+ case 0xe: case 0xf:
+ *tag = mpack_tag_make_int((int8_t)type);
+ return 1;
+
+ // fixmap
+ case 0x8:
+ *tag = mpack_tag_make_map(type & ~0xf0u);
+ return 1;
+
+ // fixarray
+ case 0x9:
+ *tag = mpack_tag_make_array(type & ~0xf0u);
+ return 1;
+
+ // fixstr
+ case 0xa: case 0xb:
+ *tag = mpack_tag_make_str(type & ~0xe0u);
+ return 1;
+
+ // not one of the common infix types
+ default:
+ break;
+
+ }
+ #endif
+
+ // handle individual type tags
+ switch (type) {
+
+ #if !MPACK_OPTIMIZE_FOR_SIZE
+ // positive fixnum
+ case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07:
+ case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f:
+ case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17:
+ case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f:
+ case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27:
+ case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: case 0x2e: case 0x2f:
+ case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37:
+ case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f:
+ case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47:
+ case 0x48: case 0x49: case 0x4a: case 0x4b: case 0x4c: case 0x4d: case 0x4e: case 0x4f:
+ case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57:
+ case 0x58: case 0x59: case 0x5a: case 0x5b: case 0x5c: case 0x5d: case 0x5e: case 0x5f:
+ case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67:
+ case 0x68: case 0x69: case 0x6a: case 0x6b: case 0x6c: case 0x6d: case 0x6e: case 0x6f:
+ case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77:
+ case 0x78: case 0x79: case 0x7a: case 0x7b: case 0x7c: case 0x7d: case 0x7e: case 0x7f:
+ *tag = mpack_tag_make_uint(type);
+ return 1;
+
+ // negative fixnum
+ case 0xe0: case 0xe1: case 0xe2: case 0xe3: case 0xe4: case 0xe5: case 0xe6: case 0xe7:
+ case 0xe8: case 0xe9: case 0xea: case 0xeb: case 0xec: case 0xed: case 0xee: case 0xef:
+ case 0xf0: case 0xf1: case 0xf2: case 0xf3: case 0xf4: case 0xf5: case 0xf6: case 0xf7:
+ case 0xf8: case 0xf9: case 0xfa: case 0xfb: case 0xfc: case 0xfd: case 0xfe: case 0xff:
+ *tag = mpack_tag_make_int((int8_t)type);
+ return 1;
+
+ // fixmap
+ case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87:
+ case 0x88: case 0x89: case 0x8a: case 0x8b: case 0x8c: case 0x8d: case 0x8e: case 0x8f:
+ *tag = mpack_tag_make_map(type & ~0xf0u);
+ return 1;
+
+ // fixarray
+ case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97:
+ case 0x98: case 0x99: case 0x9a: case 0x9b: case 0x9c: case 0x9d: case 0x9e: case 0x9f:
+ *tag = mpack_tag_make_array(type & ~0xf0u);
+ return 1;
+
+ // fixstr
+ case 0xa0: case 0xa1: case 0xa2: case 0xa3: case 0xa4: case 0xa5: case 0xa6: case 0xa7:
+ case 0xa8: case 0xa9: case 0xaa: case 0xab: case 0xac: case 0xad: case 0xae: case 0xaf:
+ case 0xb0: case 0xb1: case 0xb2: case 0xb3: case 0xb4: case 0xb5: case 0xb6: case 0xb7:
+ case 0xb8: case 0xb9: case 0xba: case 0xbb: case 0xbc: case 0xbd: case 0xbe: case 0xbf:
+ *tag = mpack_tag_make_str(type & ~0xe0u);
+ return 1;
+ #endif
+
+ // nil
+ case 0xc0:
+ *tag = mpack_tag_make_nil();
+ return 1;
+
+ // bool
+ case 0xc2: case 0xc3:
+ *tag = mpack_tag_make_bool((bool)(type & 1));
+ return 1;
+
+ // bin8
+ case 0xc4:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_BIN8))
+ return 0;
+ *tag = mpack_tag_make_bin(mpack_load_u8(reader->data + 1));
+ return MPACK_TAG_SIZE_BIN8;
+
+ // bin16
+ case 0xc5:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_BIN16))
+ return 0;
+ *tag = mpack_tag_make_bin(mpack_load_u16(reader->data + 1));
+ return MPACK_TAG_SIZE_BIN16;
+
+ // bin32
+ case 0xc6:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_BIN32))
+ return 0;
+ *tag = mpack_tag_make_bin(mpack_load_u32(reader->data + 1));
+ return MPACK_TAG_SIZE_BIN32;
+
+ #if MPACK_EXTENSIONS
+ // ext8
+ case 0xc7:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_EXT8))
+ return 0;
+ *tag = mpack_tag_make_ext(mpack_load_i8(reader->data + 2), mpack_load_u8(reader->data + 1));
+ return MPACK_TAG_SIZE_EXT8;
+
+ // ext16
+ case 0xc8:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_EXT16))
+ return 0;
+ *tag = mpack_tag_make_ext(mpack_load_i8(reader->data + 3), mpack_load_u16(reader->data + 1));
+ return MPACK_TAG_SIZE_EXT16;
+
+ // ext32
+ case 0xc9:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_EXT32))
+ return 0;
+ *tag = mpack_tag_make_ext(mpack_load_i8(reader->data + 5), mpack_load_u32(reader->data + 1));
+ return MPACK_TAG_SIZE_EXT32;
+ #endif
+
+ // float
+ case 0xca:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_FLOAT))
+ return 0;
+ *tag = mpack_tag_make_float(mpack_load_float(reader->data + 1));
+ return MPACK_TAG_SIZE_FLOAT;
+
+ // double
+ case 0xcb:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_DOUBLE))
+ return 0;
+ *tag = mpack_tag_make_double(mpack_load_double(reader->data + 1));
+ return MPACK_TAG_SIZE_DOUBLE;
+
+ // uint8
+ case 0xcc:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_U8))
+ return 0;
+ *tag = mpack_tag_make_uint(mpack_load_u8(reader->data + 1));
+ return MPACK_TAG_SIZE_U8;
+
+ // uint16
+ case 0xcd:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_U16))
+ return 0;
+ *tag = mpack_tag_make_uint(mpack_load_u16(reader->data + 1));
+ return MPACK_TAG_SIZE_U16;
+
+ // uint32
+ case 0xce:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_U32))
+ return 0;
+ *tag = mpack_tag_make_uint(mpack_load_u32(reader->data + 1));
+ return MPACK_TAG_SIZE_U32;
+
+ // uint64
+ case 0xcf:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_U64))
+ return 0;
+ *tag = mpack_tag_make_uint(mpack_load_u64(reader->data + 1));
+ return MPACK_TAG_SIZE_U64;
+
+ // int8
+ case 0xd0:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_I8))
+ return 0;
+ *tag = mpack_tag_make_int(mpack_load_i8(reader->data + 1));
+ return MPACK_TAG_SIZE_I8;
+
+ // int16
+ case 0xd1:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_I16))
+ return 0;
+ *tag = mpack_tag_make_int(mpack_load_i16(reader->data + 1));
+ return MPACK_TAG_SIZE_I16;
+
+ // int32
+ case 0xd2:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_I32))
+ return 0;
+ *tag = mpack_tag_make_int(mpack_load_i32(reader->data + 1));
+ return MPACK_TAG_SIZE_I32;
+
+ // int64
+ case 0xd3:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_I64))
+ return 0;
+ *tag = mpack_tag_make_int(mpack_load_i64(reader->data + 1));
+ return MPACK_TAG_SIZE_I64;
+
+ #if MPACK_EXTENSIONS
+ // fixext1
+ case 0xd4:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_FIXEXT1))
+ return 0;
+ *tag = mpack_tag_make_ext(mpack_load_i8(reader->data + 1), 1);
+ return MPACK_TAG_SIZE_FIXEXT1;
+
+ // fixext2
+ case 0xd5:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_FIXEXT2))
+ return 0;
+ *tag = mpack_tag_make_ext(mpack_load_i8(reader->data + 1), 2);
+ return MPACK_TAG_SIZE_FIXEXT2;
+
+ // fixext4
+ case 0xd6:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_FIXEXT4))
+ return 0;
+ *tag = mpack_tag_make_ext(mpack_load_i8(reader->data + 1), 4);
+ return 2;
+
+ // fixext8
+ case 0xd7:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_FIXEXT8))
+ return 0;
+ *tag = mpack_tag_make_ext(mpack_load_i8(reader->data + 1), 8);
+ return MPACK_TAG_SIZE_FIXEXT8;
+
+ // fixext16
+ case 0xd8:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_FIXEXT16))
+ return 0;
+ *tag = mpack_tag_make_ext(mpack_load_i8(reader->data + 1), 16);
+ return MPACK_TAG_SIZE_FIXEXT16;
+ #endif
+
+ // str8
+ case 0xd9:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_STR8))
+ return 0;
+ *tag = mpack_tag_make_str(mpack_load_u8(reader->data + 1));
+ return MPACK_TAG_SIZE_STR8;
+
+ // str16
+ case 0xda:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_STR16))
+ return 0;
+ *tag = mpack_tag_make_str(mpack_load_u16(reader->data + 1));
+ return MPACK_TAG_SIZE_STR16;
+
+ // str32
+ case 0xdb:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_STR32))
+ return 0;
+ *tag = mpack_tag_make_str(mpack_load_u32(reader->data + 1));
+ return MPACK_TAG_SIZE_STR32;
+
+ // array16
+ case 0xdc:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_ARRAY16))
+ return 0;
+ *tag = mpack_tag_make_array(mpack_load_u16(reader->data + 1));
+ return MPACK_TAG_SIZE_ARRAY16;
+
+ // array32
+ case 0xdd:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_ARRAY32))
+ return 0;
+ *tag = mpack_tag_make_array(mpack_load_u32(reader->data + 1));
+ return MPACK_TAG_SIZE_ARRAY32;
+
+ // map16
+ case 0xde:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_MAP16))
+ return 0;
+ *tag = mpack_tag_make_map(mpack_load_u16(reader->data + 1));
+ return MPACK_TAG_SIZE_MAP16;
+
+ // map32
+ case 0xdf:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_MAP32))
+ return 0;
+ *tag = mpack_tag_make_map(mpack_load_u32(reader->data + 1));
+ return MPACK_TAG_SIZE_MAP32;
+
+ // reserved
+ case 0xc1:
+ mpack_reader_flag_error(reader, mpack_error_invalid);
+ return 0;
+
+ #if !MPACK_EXTENSIONS
+ // ext
+ case 0xc7: // fallthrough
+ case 0xc8: // fallthrough
+ case 0xc9: // fallthrough
+ // fixext
+ case 0xd4: // fallthrough
+ case 0xd5: // fallthrough
+ case 0xd6: // fallthrough
+ case 0xd7: // fallthrough
+ case 0xd8:
+ mpack_reader_flag_error(reader, mpack_error_unsupported);
+ return 0;
+ #endif
+
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ // any other bytes should have been handled by the infix switch
+ default:
+ break;
+ #endif
+ }
+
+ mpack_assert(0, "unreachable");
+ return 0;
+}
+
+mpack_tag_t mpack_read_tag(mpack_reader_t* reader) {
+ mpack_log("reading tag\n");
+
+ // make sure we can read a tag
+ if (mpack_reader_error(reader) != mpack_ok)
+ return mpack_tag_nil();
+ if (mpack_reader_track_element(reader) != mpack_ok)
+ return mpack_tag_nil();
+
+ mpack_tag_t tag = MPACK_TAG_ZERO;
+ size_t count = mpack_parse_tag(reader, &tag);
+ if (count == 0)
+ return mpack_tag_nil();
+
+ #if MPACK_READ_TRACKING
+ mpack_error_t track_error = mpack_ok;
+
+ switch (tag.type) {
+ case mpack_type_map:
+ case mpack_type_array:
+ track_error = mpack_track_push(&reader->track, tag.type, tag.v.n);
+ break;
+ #if MPACK_EXTENSIONS
+ case mpack_type_ext:
+ #endif
+ case mpack_type_str:
+ case mpack_type_bin:
+ track_error = mpack_track_push(&reader->track, tag.type, tag.v.l);
+ break;
+ default:
+ break;
+ }
+
+ if (track_error != mpack_ok) {
+ mpack_reader_flag_error(reader, track_error);
+ return mpack_tag_nil();
+ }
+ #endif
+
+ reader->data += count;
+ return tag;
+}
+
+mpack_tag_t mpack_peek_tag(mpack_reader_t* reader) {
+ mpack_log("peeking tag\n");
+
+ // make sure we can peek a tag
+ if (mpack_reader_error(reader) != mpack_ok)
+ return mpack_tag_nil();
+ if (mpack_reader_track_peek_element(reader) != mpack_ok)
+ return mpack_tag_nil();
+
+ mpack_tag_t tag = MPACK_TAG_ZERO;
+ if (mpack_parse_tag(reader, &tag) == 0)
+ return mpack_tag_nil();
+ return tag;
+}
+
+void mpack_discard(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (mpack_reader_error(reader))
+ return;
+ switch (var.type) {
+ case mpack_type_str:
+ mpack_skip_bytes(reader, var.v.l);
+ mpack_done_str(reader);
+ break;
+ case mpack_type_bin:
+ mpack_skip_bytes(reader, var.v.l);
+ mpack_done_bin(reader);
+ break;
+ #if MPACK_EXTENSIONS
+ case mpack_type_ext:
+ mpack_skip_bytes(reader, var.v.l);
+ mpack_done_ext(reader);
+ break;
+ #endif
+ case mpack_type_array: {
+ for (; var.v.n > 0; --var.v.n) {
+ mpack_discard(reader);
+ if (mpack_reader_error(reader))
+ break;
+ }
+ mpack_done_array(reader);
+ break;
+ }
+ case mpack_type_map: {
+ for (; var.v.n > 0; --var.v.n) {
+ mpack_discard(reader);
+ mpack_discard(reader);
+ if (mpack_reader_error(reader))
+ break;
+ }
+ mpack_done_map(reader);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+#if MPACK_EXTENSIONS
+mpack_timestamp_t mpack_read_timestamp(mpack_reader_t* reader, size_t size) {
+ mpack_timestamp_t timestamp = {0, 0};
+
+ if (size != 4 && size != 8 && size != 12) {
+ mpack_reader_flag_error(reader, mpack_error_invalid);
+ return timestamp;
+ }
+
+ char buf[12];
+ mpack_read_bytes(reader, buf, size);
+ mpack_done_ext(reader);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return timestamp;
+
+ switch (size) {
+ case 4:
+ timestamp.seconds = (int64_t)(uint64_t)mpack_load_u32(buf);
+ break;
+
+ case 8: {
+ uint64_t packed = mpack_load_u64(buf);
+ timestamp.seconds = (int64_t)(packed & ((UINT64_C(1) << 34) - 1));
+ timestamp.nanoseconds = (uint32_t)(packed >> 34);
+ break;
+ }
+
+ case 12:
+ timestamp.nanoseconds = mpack_load_u32(buf);
+ timestamp.seconds = mpack_load_i64(buf + 4);
+ break;
+
+ default:
+ mpack_assert(false, "unreachable");
+ break;
+ }
+
+ if (timestamp.nanoseconds > MPACK_TIMESTAMP_NANOSECONDS_MAX) {
+ mpack_reader_flag_error(reader, mpack_error_invalid);
+ mpack_timestamp_t zero = {0, 0};
+ return zero;
+ }
+
+ return timestamp;
+}
+#endif
+
+#if MPACK_READ_TRACKING
+void mpack_done_type(mpack_reader_t* reader, mpack_type_t type) {
+ if (mpack_reader_error(reader) == mpack_ok)
+ mpack_reader_flag_if_error(reader, mpack_track_pop(&reader->track, type));
+}
+#endif
+
+#if MPACK_DEBUG && MPACK_STDIO
+static size_t mpack_print_read_prefix(mpack_reader_t* reader, size_t length, char* buffer, size_t buffer_size) {
+ if (length == 0)
+ return 0;
+
+ size_t read = (length < buffer_size) ? length : buffer_size;
+ mpack_read_bytes(reader, buffer, read);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return 0;
+
+ mpack_skip_bytes(reader, length - read);
+ return read;
+}
+
+static void mpack_print_element(mpack_reader_t* reader, mpack_print_t* print, size_t depth) {
+ mpack_tag_t val = mpack_read_tag(reader);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return;
+
+ // We read some bytes from bin and ext so we can print its prefix in hex.
+ char buffer[MPACK_PRINT_BYTE_COUNT];
+ size_t count = 0;
+
+ switch (val.type) {
+ case mpack_type_str:
+ mpack_print_append_cstr(print, "\"");
+ for (size_t i = 0; i < val.v.l; ++i) {
+ char c;
+ mpack_read_bytes(reader, &c, 1);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return;
+ switch (c) {
+ case '\n': mpack_print_append_cstr(print, "\\n"); break;
+ case '\\': mpack_print_append_cstr(print, "\\\\"); break;
+ case '"': mpack_print_append_cstr(print, "\\\""); break;
+ default: mpack_print_append(print, &c, 1); break;
+ }
+ }
+ mpack_print_append_cstr(print, "\"");
+ mpack_done_str(reader);
+ return;
+
+ case mpack_type_array:
+ mpack_print_append_cstr(print, "[\n");
+ for (size_t i = 0; i < val.v.n; ++i) {
+ for (size_t j = 0; j < depth + 1; ++j)
+ mpack_print_append_cstr(print, " ");
+ mpack_print_element(reader, print, depth + 1);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return;
+ if (i != val.v.n - 1)
+ mpack_print_append_cstr(print, ",");
+ mpack_print_append_cstr(print, "\n");
+ }
+ for (size_t i = 0; i < depth; ++i)
+ mpack_print_append_cstr(print, " ");
+ mpack_print_append_cstr(print, "]");
+ mpack_done_array(reader);
+ return;
+
+ case mpack_type_map:
+ mpack_print_append_cstr(print, "{\n");
+ for (size_t i = 0; i < val.v.n; ++i) {
+ for (size_t j = 0; j < depth + 1; ++j)
+ mpack_print_append_cstr(print, " ");
+ mpack_print_element(reader, print, depth + 1);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return;
+ mpack_print_append_cstr(print, ": ");
+ mpack_print_element(reader, print, depth + 1);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return;
+ if (i != val.v.n - 1)
+ mpack_print_append_cstr(print, ",");
+ mpack_print_append_cstr(print, "\n");
+ }
+ for (size_t i = 0; i < depth; ++i)
+ mpack_print_append_cstr(print, " ");
+ mpack_print_append_cstr(print, "}");
+ mpack_done_map(reader);
+ return;
+
+ // The above cases return so as not to print a pseudo-json value. The
+ // below cases break and print pseudo-json.
+
+ case mpack_type_bin:
+ count = mpack_print_read_prefix(reader, mpack_tag_bin_length(&val), buffer, sizeof(buffer));
+ mpack_done_bin(reader);
+ break;
+
+ #if MPACK_EXTENSIONS
+ case mpack_type_ext:
+ count = mpack_print_read_prefix(reader, mpack_tag_ext_length(&val), buffer, sizeof(buffer));
+ mpack_done_ext(reader);
+ break;
+ #endif
+
+ default:
+ break;
+ }
+
+ char buf[256];
+ mpack_tag_debug_pseudo_json(val, buf, sizeof(buf), buffer, count);
+ mpack_print_append_cstr(print, buf);
+}
+
+static void mpack_print_and_destroy(mpack_reader_t* reader, mpack_print_t* print, size_t depth) {
+ for (size_t i = 0; i < depth; ++i)
+ mpack_print_append_cstr(print, " ");
+ mpack_print_element(reader, print, depth);
+
+ size_t remaining = mpack_reader_remaining(reader, NULL);
+
+ char buf[256];
+ if (mpack_reader_destroy(reader) != mpack_ok) {
+ mpack_snprintf(buf, sizeof(buf), "\n<mpack parsing error %s>", mpack_error_to_string(mpack_reader_error(reader)));
+ buf[sizeof(buf) - 1] = '\0';
+ mpack_print_append_cstr(print, buf);
+ } else if (remaining > 0) {
+ mpack_snprintf(buf, sizeof(buf), "\n<%i extra bytes at end of message>", (int)remaining);
+ buf[sizeof(buf) - 1] = '\0';
+ mpack_print_append_cstr(print, buf);
+ }
+}
+
+static void mpack_print_data(const char* data, size_t len, mpack_print_t* print, size_t depth) {
+ mpack_reader_t reader;
+ mpack_reader_init_data(&reader, data, len);
+ mpack_print_and_destroy(&reader, print, depth);
+}
+
+void mpack_print_data_to_buffer(const char* data, size_t data_size, char* buffer, size_t buffer_size) {
+ if (buffer_size == 0) {
+ mpack_assert(false, "buffer size is zero!");
+ return;
+ }
+
+ mpack_print_t print;
+ mpack_memset(&print, 0, sizeof(print));
+ print.buffer = buffer;
+ print.size = buffer_size;
+ mpack_print_data(data, data_size, &print, 0);
+ mpack_print_append(&print, "", 1); // null-terminator
+ mpack_print_flush(&print);
+
+ // we always make sure there's a null-terminator at the end of the buffer
+ // in case we ran out of space.
+ print.buffer[print.size - 1] = '\0';
+}
+
+void mpack_print_data_to_callback(const char* data, size_t size, mpack_print_callback_t callback, void* context) {
+ char buffer[1024];
+ mpack_print_t print;
+ mpack_memset(&print, 0, sizeof(print));
+ print.buffer = buffer;
+ print.size = sizeof(buffer);
+ print.callback = callback;
+ print.context = context;
+ mpack_print_data(data, size, &print, 0);
+ mpack_print_flush(&print);
+}
+
+void mpack_print_data_to_file(const char* data, size_t len, FILE* file) {
+ mpack_assert(data != NULL, "data is NULL");
+ mpack_assert(file != NULL, "file is NULL");
+
+ char buffer[1024];
+ mpack_print_t print;
+ mpack_memset(&print, 0, sizeof(print));
+ print.buffer = buffer;
+ print.size = sizeof(buffer);
+ print.callback = &mpack_print_file_callback;
+ print.context = file;
+
+ mpack_print_data(data, len, &print, 2);
+ mpack_print_append_cstr(&print, "\n");
+ mpack_print_flush(&print);
+}
+
+void mpack_print_stdfile_to_callback(FILE* file, mpack_print_callback_t callback, void* context) {
+ char buffer[1024];
+ mpack_print_t print;
+ mpack_memset(&print, 0, sizeof(print));
+ print.buffer = buffer;
+ print.size = sizeof(buffer);
+ print.callback = callback;
+ print.context = context;
+
+ mpack_reader_t reader;
+ mpack_reader_init_stdfile(&reader, file, false);
+ mpack_print_and_destroy(&reader, &print, 0);
+ mpack_print_flush(&print);
+}
+#endif
+
+#endif
+
+/* mpack/mpack-expect.c.c */
+
+#define MPACK_INTERNAL 1
+
+/* #include "mpack-expect.h" */
+
+#if MPACK_EXPECT
+
+
+// Helpers
+
+MPACK_STATIC_INLINE uint8_t mpack_expect_native_u8(mpack_reader_t* reader) {
+ if (mpack_reader_error(reader) != mpack_ok)
+ return 0;
+ uint8_t type;
+ if (!mpack_reader_ensure(reader, sizeof(type)))
+ return 0;
+ type = mpack_load_u8(reader->data);
+ reader->data += sizeof(type);
+ return type;
+}
+
+#if !MPACK_OPTIMIZE_FOR_SIZE
+MPACK_STATIC_INLINE uint16_t mpack_expect_native_u16(mpack_reader_t* reader) {
+ if (mpack_reader_error(reader) != mpack_ok)
+ return 0;
+ uint16_t type;
+ if (!mpack_reader_ensure(reader, sizeof(type)))
+ return 0;
+ type = mpack_load_u16(reader->data);
+ reader->data += sizeof(type);
+ return type;
+}
+
+MPACK_STATIC_INLINE uint32_t mpack_expect_native_u32(mpack_reader_t* reader) {
+ if (mpack_reader_error(reader) != mpack_ok)
+ return 0;
+ uint32_t type;
+ if (!mpack_reader_ensure(reader, sizeof(type)))
+ return 0;
+ type = mpack_load_u32(reader->data);
+ reader->data += sizeof(type);
+ return type;
+}
+#endif
+
+MPACK_STATIC_INLINE uint8_t mpack_expect_type_byte(mpack_reader_t* reader) {
+ mpack_reader_track_element(reader);
+ return mpack_expect_native_u8(reader);
+}
+
+
+// Basic Number Functions
+
+uint8_t mpack_expect_u8(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint) {
+ if (var.v.u <= UINT8_MAX)
+ return (uint8_t)var.v.u;
+ } else if (var.type == mpack_type_int) {
+ if (var.v.i >= 0 && var.v.i <= UINT8_MAX)
+ return (uint8_t)var.v.i;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+uint16_t mpack_expect_u16(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint) {
+ if (var.v.u <= UINT16_MAX)
+ return (uint16_t)var.v.u;
+ } else if (var.type == mpack_type_int) {
+ if (var.v.i >= 0 && var.v.i <= UINT16_MAX)
+ return (uint16_t)var.v.i;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+uint32_t mpack_expect_u32(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint) {
+ if (var.v.u <= UINT32_MAX)
+ return (uint32_t)var.v.u;
+ } else if (var.type == mpack_type_int) {
+ if (var.v.i >= 0 && var.v.i <= UINT32_MAX)
+ return (uint32_t)var.v.i;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+uint64_t mpack_expect_u64(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint) {
+ return var.v.u;
+ } else if (var.type == mpack_type_int) {
+ if (var.v.i >= 0)
+ return (uint64_t)var.v.i;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+int8_t mpack_expect_i8(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint) {
+ if (var.v.u <= INT8_MAX)
+ return (int8_t)var.v.u;
+ } else if (var.type == mpack_type_int) {
+ if (var.v.i >= INT8_MIN && var.v.i <= INT8_MAX)
+ return (int8_t)var.v.i;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+int16_t mpack_expect_i16(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint) {
+ if (var.v.u <= INT16_MAX)
+ return (int16_t)var.v.u;
+ } else if (var.type == mpack_type_int) {
+ if (var.v.i >= INT16_MIN && var.v.i <= INT16_MAX)
+ return (int16_t)var.v.i;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+int32_t mpack_expect_i32(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint) {
+ if (var.v.u <= INT32_MAX)
+ return (int32_t)var.v.u;
+ } else if (var.type == mpack_type_int) {
+ if (var.v.i >= INT32_MIN && var.v.i <= INT32_MAX)
+ return (int32_t)var.v.i;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+int64_t mpack_expect_i64(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint) {
+ if (var.v.u <= INT64_MAX)
+ return (int64_t)var.v.u;
+ } else if (var.type == mpack_type_int) {
+ return var.v.i;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+float mpack_expect_float(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint)
+ return (float)var.v.u;
+ else if (var.type == mpack_type_int)
+ return (float)var.v.i;
+ else if (var.type == mpack_type_float)
+ return var.v.f;
+ else if (var.type == mpack_type_double)
+ return (float)var.v.d;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0.0f;
+}
+
+double mpack_expect_double(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint)
+ return (double)var.v.u;
+ else if (var.type == mpack_type_int)
+ return (double)var.v.i;
+ else if (var.type == mpack_type_float)
+ return (double)var.v.f;
+ else if (var.type == mpack_type_double)
+ return var.v.d;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0.0;
+}
+
+float mpack_expect_float_strict(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_float)
+ return var.v.f;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0.0f;
+}
+
+double mpack_expect_double_strict(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_float)
+ return (double)var.v.f;
+ else if (var.type == mpack_type_double)
+ return var.v.d;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0.0;
+}
+
+
+// Ranged Number Functions
+//
+// All ranged functions are identical other than the type, so we
+// define their content with a macro. The prototypes are still written
+// out in full to support ctags/IDE tools.
+
+#define MPACK_EXPECT_RANGE_IMPL(name, type_t) \
+ \
+ /* make sure the range is sensible */ \
+ mpack_assert(min_value <= max_value, \
+ "min_value %i must be less than or equal to max_value %i", \
+ min_value, max_value); \
+ \
+ /* read the value */ \
+ type_t val = mpack_expect_##name(reader); \
+ if (mpack_reader_error(reader) != mpack_ok) \
+ return min_value; \
+ \
+ /* make sure it fits */ \
+ if (val < min_value || val > max_value) { \
+ mpack_reader_flag_error(reader, mpack_error_type); \
+ return min_value; \
+ } \
+ \
+ return val;
+
+uint8_t mpack_expect_u8_range(mpack_reader_t* reader, uint8_t min_value, uint8_t max_value) {MPACK_EXPECT_RANGE_IMPL(u8, uint8_t)}
+uint16_t mpack_expect_u16_range(mpack_reader_t* reader, uint16_t min_value, uint16_t max_value) {MPACK_EXPECT_RANGE_IMPL(u16, uint16_t)}
+uint32_t mpack_expect_u32_range(mpack_reader_t* reader, uint32_t min_value, uint32_t max_value) {MPACK_EXPECT_RANGE_IMPL(u32, uint32_t)}
+uint64_t mpack_expect_u64_range(mpack_reader_t* reader, uint64_t min_value, uint64_t max_value) {MPACK_EXPECT_RANGE_IMPL(u64, uint64_t)}
+
+int8_t mpack_expect_i8_range(mpack_reader_t* reader, int8_t min_value, int8_t max_value) {MPACK_EXPECT_RANGE_IMPL(i8, int8_t)}
+int16_t mpack_expect_i16_range(mpack_reader_t* reader, int16_t min_value, int16_t max_value) {MPACK_EXPECT_RANGE_IMPL(i16, int16_t)}
+int32_t mpack_expect_i32_range(mpack_reader_t* reader, int32_t min_value, int32_t max_value) {MPACK_EXPECT_RANGE_IMPL(i32, int32_t)}
+int64_t mpack_expect_i64_range(mpack_reader_t* reader, int64_t min_value, int64_t max_value) {MPACK_EXPECT_RANGE_IMPL(i64, int64_t)}
+
+float mpack_expect_float_range(mpack_reader_t* reader, float min_value, float max_value) {MPACK_EXPECT_RANGE_IMPL(float, float)}
+double mpack_expect_double_range(mpack_reader_t* reader, double min_value, double max_value) {MPACK_EXPECT_RANGE_IMPL(double, double)}
+
+uint32_t mpack_expect_map_range(mpack_reader_t* reader, uint32_t min_value, uint32_t max_value) {MPACK_EXPECT_RANGE_IMPL(map, uint32_t)}
+uint32_t mpack_expect_array_range(mpack_reader_t* reader, uint32_t min_value, uint32_t max_value) {MPACK_EXPECT_RANGE_IMPL(array, uint32_t)}
+
+
+// Matching Number Functions
+
+void mpack_expect_uint_match(mpack_reader_t* reader, uint64_t value) {
+ if (mpack_expect_u64(reader) != value)
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+void mpack_expect_int_match(mpack_reader_t* reader, int64_t value) {
+ if (mpack_expect_i64(reader) != value)
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+
+// Other Basic Types
+
+void mpack_expect_nil(mpack_reader_t* reader) {
+ if (mpack_expect_type_byte(reader) != 0xc0)
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+bool mpack_expect_bool(mpack_reader_t* reader) {
+ uint8_t type = mpack_expect_type_byte(reader);
+ if ((type & ~1) != 0xc2)
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return (bool)(type & 1);
+}
+
+void mpack_expect_true(mpack_reader_t* reader) {
+ if (mpack_expect_bool(reader) != true)
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+void mpack_expect_false(mpack_reader_t* reader) {
+ if (mpack_expect_bool(reader) != false)
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+#if MPACK_EXTENSIONS
+mpack_timestamp_t mpack_expect_timestamp(mpack_reader_t* reader) {
+ mpack_timestamp_t zero = {0, 0};
+
+ mpack_tag_t tag = mpack_read_tag(reader);
+ if (tag.type != mpack_type_ext) {
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return zero;
+ }
+ if (mpack_tag_ext_exttype(&tag) != MPACK_EXTTYPE_TIMESTAMP) {
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return zero;
+ }
+
+ return mpack_read_timestamp(reader, mpack_tag_ext_length(&tag));
+}
+
+int64_t mpack_expect_timestamp_truncate(mpack_reader_t* reader) {
+ return mpack_expect_timestamp(reader).seconds;
+}
+#endif
+
+
+// Compound Types
+
+uint32_t mpack_expect_map(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_map)
+ return var.v.n;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+void mpack_expect_map_match(mpack_reader_t* reader, uint32_t count) {
+ if (mpack_expect_map(reader) != count)
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+bool mpack_expect_map_or_nil(mpack_reader_t* reader, uint32_t* count) {
+ mpack_assert(count != NULL, "count cannot be NULL");
+
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_nil) {
+ *count = 0;
+ return false;
+ }
+ if (var.type == mpack_type_map) {
+ *count = var.v.n;
+ return true;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ *count = 0;
+ return false;
+}
+
+bool mpack_expect_map_max_or_nil(mpack_reader_t* reader, uint32_t max_count, uint32_t* count) {
+ mpack_assert(count != NULL, "count cannot be NULL");
+
+ bool has_map = mpack_expect_map_or_nil(reader, count);
+ if (has_map && *count > max_count) {
+ *count = 0;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return false;
+ }
+ return has_map;
+}
+
+uint32_t mpack_expect_array(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_array)
+ return var.v.n;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+void mpack_expect_array_match(mpack_reader_t* reader, uint32_t count) {
+ if (mpack_expect_array(reader) != count)
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+bool mpack_expect_array_or_nil(mpack_reader_t* reader, uint32_t* count) {
+ mpack_assert(count != NULL, "count cannot be NULL");
+
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_nil) {
+ *count = 0;
+ return false;
+ }
+ if (var.type == mpack_type_array) {
+ *count = var.v.n;
+ return true;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ *count = 0;
+ return false;
+}
+
+bool mpack_expect_array_max_or_nil(mpack_reader_t* reader, uint32_t max_count, uint32_t* count) {
+ mpack_assert(count != NULL, "count cannot be NULL");
+
+ bool has_array = mpack_expect_array_or_nil(reader, count);
+ if (has_array && *count > max_count) {
+ *count = 0;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return false;
+ }
+ return has_array;
+}
+
+#ifdef MPACK_MALLOC
+void* mpack_expect_array_alloc_impl(mpack_reader_t* reader, size_t element_size, uint32_t max_count, uint32_t* out_count, bool allow_nil) {
+ mpack_assert(out_count != NULL, "out_count cannot be NULL");
+ *out_count = 0;
+
+ uint32_t count;
+ bool has_array = true;
+ if (allow_nil)
+ has_array = mpack_expect_array_max_or_nil(reader, max_count, &count);
+ else
+ count = mpack_expect_array_max(reader, max_count);
+ if (mpack_reader_error(reader))
+ return NULL;
+
+ // size 0 is not an error; we return NULL for no elements.
+ if (count == 0) {
+ // we call mpack_done_array() automatically ONLY if we are using
+ // the _or_nil variant. this is the only way to allow nil and empty
+ // to work the same way.
+ if (allow_nil && has_array)
+ mpack_done_array(reader);
+ return NULL;
+ }
+
+ void* p = MPACK_MALLOC(element_size * count);
+ if (p == NULL) {
+ mpack_reader_flag_error(reader, mpack_error_memory);
+ return NULL;
+ }
+
+ *out_count = count;
+ return p;
+}
+#endif
+
+
+// Str, Bin and Ext Functions
+
+uint32_t mpack_expect_str(mpack_reader_t* reader) {
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_str)
+ return var.v.l;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+ #else
+ uint8_t type = mpack_expect_type_byte(reader);
+ uint32_t count;
+
+ if ((type >> 5) == 5) {
+ count = type & (uint8_t)~0xe0;
+ } else if (type == 0xd9) {
+ count = mpack_expect_native_u8(reader);
+ } else if (type == 0xda) {
+ count = mpack_expect_native_u16(reader);
+ } else if (type == 0xdb) {
+ count = mpack_expect_native_u32(reader);
+ } else {
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+ }
+
+ #if MPACK_READ_TRACKING
+ mpack_reader_flag_if_error(reader, mpack_track_push(&reader->track, mpack_type_str, count));
+ #endif
+ return count;
+ #endif
+}
+
+size_t mpack_expect_str_buf(mpack_reader_t* reader, char* buf, size_t bufsize) {
+ mpack_assert(buf != NULL, "buf cannot be NULL");
+
+ size_t length = mpack_expect_str(reader);
+ if (mpack_reader_error(reader))
+ return 0;
+
+ if (length > bufsize) {
+ mpack_reader_flag_error(reader, mpack_error_too_big);
+ return 0;
+ }
+
+ mpack_read_bytes(reader, buf, length);
+ if (mpack_reader_error(reader))
+ return 0;
+
+ mpack_done_str(reader);
+ return length;
+}
+
+size_t mpack_expect_utf8(mpack_reader_t* reader, char* buf, size_t size) {
+ mpack_assert(buf != NULL, "buf cannot be NULL");
+
+ size_t length = mpack_expect_str_buf(reader, buf, size);
+
+ if (!mpack_utf8_check(buf, length)) {
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+ }
+
+ return length;
+}
+
+uint32_t mpack_expect_bin(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_bin)
+ return var.v.l;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+size_t mpack_expect_bin_buf(mpack_reader_t* reader, char* buf, size_t bufsize) {
+ mpack_assert(buf != NULL, "buf cannot be NULL");
+
+ size_t binsize = mpack_expect_bin(reader);
+ if (mpack_reader_error(reader))
+ return 0;
+ if (binsize > bufsize) {
+ mpack_reader_flag_error(reader, mpack_error_too_big);
+ return 0;
+ }
+ mpack_read_bytes(reader, buf, binsize);
+ if (mpack_reader_error(reader))
+ return 0;
+ mpack_done_bin(reader);
+ return binsize;
+}
+
+void mpack_expect_bin_size_buf(mpack_reader_t* reader, char* buf, uint32_t size) {
+ mpack_assert(buf != NULL, "buf cannot be NULL");
+ mpack_expect_bin_size(reader, size);
+ mpack_read_bytes(reader, buf, size);
+ mpack_done_bin(reader);
+}
+
+#if MPACK_EXTENSIONS
+uint32_t mpack_expect_ext(mpack_reader_t* reader, int8_t* type) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_ext) {
+ *type = mpack_tag_ext_exttype(&var);
+ return mpack_tag_ext_length(&var);
+ }
+ *type = 0;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+size_t mpack_expect_ext_buf(mpack_reader_t* reader, int8_t* type, char* buf, size_t bufsize) {
+ mpack_assert(buf != NULL, "buf cannot be NULL");
+
+ size_t extsize = mpack_expect_ext(reader, type);
+ if (mpack_reader_error(reader))
+ return 0;
+ if (extsize > bufsize) {
+ *type = 0;
+ mpack_reader_flag_error(reader, mpack_error_too_big);
+ return 0;
+ }
+ mpack_read_bytes(reader, buf, extsize);
+ if (mpack_reader_error(reader)) {
+ *type = 0;
+ return 0;
+ }
+ mpack_done_ext(reader);
+ return extsize;
+}
+#endif
+
+void mpack_expect_cstr(mpack_reader_t* reader, char* buf, size_t bufsize) {
+ uint32_t length = mpack_expect_str(reader);
+ mpack_read_cstr(reader, buf, bufsize, length);
+ mpack_done_str(reader);
+}
+
+void mpack_expect_utf8_cstr(mpack_reader_t* reader, char* buf, size_t bufsize) {
+ uint32_t length = mpack_expect_str(reader);
+ mpack_read_utf8_cstr(reader, buf, bufsize, length);
+ mpack_done_str(reader);
+}
+
+#ifdef MPACK_MALLOC
+static char* mpack_expect_cstr_alloc_unchecked(mpack_reader_t* reader, size_t maxsize, size_t* out_length) {
+ mpack_assert(out_length != NULL, "out_length cannot be NULL");
+ *out_length = 0;
+
+ // make sure argument makes sense
+ if (maxsize < 1) {
+ mpack_break("maxsize is zero; you must have room for at least a null-terminator");
+ mpack_reader_flag_error(reader, mpack_error_bug);
+ return NULL;
+ }
+
+ if (maxsize > UINT32_MAX)
+ maxsize = UINT32_MAX;
+
+ size_t length = mpack_expect_str_max(reader, (uint32_t)maxsize - 1);
+ char* str = mpack_read_bytes_alloc_impl(reader, length, true);
+ mpack_done_str(reader);
+
+ if (str)
+ *out_length = length;
+ return str;
+}
+
+char* mpack_expect_cstr_alloc(mpack_reader_t* reader, size_t maxsize) {
+ size_t length;
+ char* str = mpack_expect_cstr_alloc_unchecked(reader, maxsize, &length);
+
+ if (str && !mpack_str_check_no_null(str, length)) {
+ MPACK_FREE(str);
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return NULL;
+ }
+
+ return str;
+}
+
+char* mpack_expect_utf8_cstr_alloc(mpack_reader_t* reader, size_t maxsize) {
+ size_t length;
+ char* str = mpack_expect_cstr_alloc_unchecked(reader, maxsize, &length);
+
+ if (str && !mpack_utf8_check_no_null(str, length)) {
+ MPACK_FREE(str);
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return NULL;
+ }
+
+ return str;
+}
+#endif
+
+void mpack_expect_str_match(mpack_reader_t* reader, const char* str, size_t len) {
+ mpack_assert(str != NULL, "str cannot be NULL");
+
+ // expect a str the correct length
+ if (len > UINT32_MAX)
+ mpack_reader_flag_error(reader, mpack_error_type);
+ mpack_expect_str_length(reader, (uint32_t)len);
+ if (mpack_reader_error(reader))
+ return;
+ mpack_reader_track_bytes(reader, (uint32_t)len);
+
+ // check each byte one by one (matched strings are likely to be very small)
+ for (; len > 0; --len) {
+ if (mpack_expect_native_u8(reader) != *str++) {
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return;
+ }
+ }
+
+ mpack_done_str(reader);
+}
+
+void mpack_expect_tag(mpack_reader_t* reader, mpack_tag_t expected) {
+ mpack_tag_t actual = mpack_read_tag(reader);
+ if (!mpack_tag_equal(actual, expected))
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+#ifdef MPACK_MALLOC
+char* mpack_expect_bin_alloc(mpack_reader_t* reader, size_t maxsize, size_t* size) {
+ mpack_assert(size != NULL, "size cannot be NULL");
+ *size = 0;
+
+ if (maxsize > UINT32_MAX)
+ maxsize = UINT32_MAX;
+
+ size_t length = mpack_expect_bin_max(reader, (uint32_t)maxsize);
+ if (mpack_reader_error(reader))
+ return NULL;
+
+ char* data = mpack_read_bytes_alloc(reader, length);
+ mpack_done_bin(reader);
+
+ if (data)
+ *size = length;
+ return data;
+}
+#endif
+
+#if MPACK_EXTENSIONS && defined(MPACK_MALLOC)
+char* mpack_expect_ext_alloc(mpack_reader_t* reader, int8_t* type, size_t maxsize, size_t* size) {
+ mpack_assert(size != NULL, "size cannot be NULL");
+ *size = 0;
+
+ if (maxsize > UINT32_MAX)
+ maxsize = UINT32_MAX;
+
+ size_t length = mpack_expect_ext_max(reader, type, (uint32_t)maxsize);
+ if (mpack_reader_error(reader))
+ return NULL;
+
+ char* data = mpack_read_bytes_alloc(reader, length);
+ mpack_done_ext(reader);
+
+ if (data) {
+ *size = length;
+ } else {
+ *type = 0;
+ }
+ return data;
+}
+#endif
+
+size_t mpack_expect_enum(mpack_reader_t* reader, const char* strings[], size_t count) {
+
+ // read the string in-place
+ size_t keylen = mpack_expect_str(reader);
+ const char* key = mpack_read_bytes_inplace(reader, keylen);
+ mpack_done_str(reader);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return count;
+
+ // find what key it matches
+ for (size_t i = 0; i < count; ++i) {
+ const char* other = strings[i];
+ size_t otherlen = mpack_strlen(other);
+ if (keylen == otherlen && mpack_memcmp(key, other, keylen) == 0)
+ return i;
+ }
+
+ // no matches
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return count;
+}
+
+size_t mpack_expect_enum_optional(mpack_reader_t* reader, const char* strings[], size_t count) {
+ if (mpack_reader_error(reader) != mpack_ok)
+ return count;
+
+ mpack_assert(count != 0, "count cannot be zero; no strings are valid!");
+ mpack_assert(strings != NULL, "strings cannot be NULL");
+
+ // the key is only recognized if it is a string
+ if (mpack_peek_tag(reader).type != mpack_type_str) {
+ mpack_discard(reader);
+ return count;
+ }
+
+ // read the string in-place
+ size_t keylen = mpack_expect_str(reader);
+ const char* key = mpack_read_bytes_inplace(reader, keylen);
+ mpack_done_str(reader);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return count;
+
+ // find what key it matches
+ for (size_t i = 0; i < count; ++i) {
+ const char* other = strings[i];
+ size_t otherlen = mpack_strlen(other);
+ if (keylen == otherlen && mpack_memcmp(key, other, keylen) == 0)
+ return i;
+ }
+
+ // no matches
+ return count;
+}
+
+size_t mpack_expect_key_uint(mpack_reader_t* reader, bool found[], size_t count) {
+ if (mpack_reader_error(reader) != mpack_ok)
+ return count;
+
+ if (count == 0) {
+ mpack_break("count cannot be zero; no keys are valid!");
+ mpack_reader_flag_error(reader, mpack_error_bug);
+ return count;
+ }
+ mpack_assert(found != NULL, "found cannot be NULL");
+
+ // the key is only recognized if it is an unsigned int
+ if (mpack_peek_tag(reader).type != mpack_type_uint) {
+ mpack_discard(reader);
+ return count;
+ }
+
+ // read the key
+ uint64_t value = mpack_expect_u64(reader);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return count;
+
+ // unrecognized keys are fine, we just return count
+ if (value >= count)
+ return count;
+
+ // check if this key is a duplicate
+ if (found[value]) {
+ mpack_reader_flag_error(reader, mpack_error_invalid);
+ return count;
+ }
+
+ found[value] = true;
+ return (size_t)value;
+}
+
+size_t mpack_expect_key_cstr(mpack_reader_t* reader, const char* keys[], bool found[], size_t count) {
+ size_t i = mpack_expect_enum_optional(reader, keys, count);
+
+ // unrecognized keys are fine, we just return count
+ if (i == count)
+ return count;
+
+ // check if this key is a duplicate
+ mpack_assert(found != NULL, "found cannot be NULL");
+ if (found[i]) {
+ mpack_reader_flag_error(reader, mpack_error_invalid);
+ return count;
+ }
+
+ found[i] = true;
+ return i;
+}
+
+#endif
+
+
+/* mpack/mpack-node.c.c */
+
+#define MPACK_INTERNAL 1
+
+/* #include "mpack-node.h" */
+
+#if MPACK_NODE
+
+MPACK_STATIC_INLINE const char* mpack_node_data_unchecked(mpack_node_t node) {
+ mpack_assert(mpack_node_error(node) == mpack_ok, "tree is in an error state!");
+
+ mpack_type_t type = node.data->type;
+ MPACK_UNUSED(type);
+ #if MPACK_EXTENSIONS
+ mpack_assert(type == mpack_type_str || type == mpack_type_bin || type == mpack_type_ext,
+ "node of type %i (%s) is not a data type!", type, mpack_type_to_string(type));
+ #else
+ mpack_assert(type == mpack_type_str || type == mpack_type_bin,
+ "node of type %i (%s) is not a data type!", type, mpack_type_to_string(type));
+ #endif
+
+ return node.tree->data + node.data->value.offset;
+}
+
+#if MPACK_EXTENSIONS
+MPACK_STATIC_INLINE int8_t mpack_node_exttype_unchecked(mpack_node_t node) {
+ mpack_assert(mpack_node_error(node) == mpack_ok, "tree is in an error state!");
+
+ mpack_type_t type = node.data->type;
+ MPACK_UNUSED(type);
+ mpack_assert(type == mpack_type_ext, "node of type %i (%s) is not an ext type!",
+ type, mpack_type_to_string(type));
+
+ // the exttype of an ext node is stored in the byte preceding the data
+ return mpack_load_i8(mpack_node_data_unchecked(node) - 1);
+}
+#endif
+
+
+
+/*
+ * Tree Parsing
+ */
+
+#ifdef MPACK_MALLOC
+
+// fix up the alloc size to make sure it exactly fits the
+// maximum number of nodes it can contain (the allocator will
+// waste it back anyway, but we round it down just in case)
+
+#define MPACK_NODES_PER_PAGE \
+ ((MPACK_NODE_PAGE_SIZE - sizeof(mpack_tree_page_t)) / sizeof(mpack_node_data_t) + 1)
+
+#define MPACK_PAGE_ALLOC_SIZE \
+ (sizeof(mpack_tree_page_t) + sizeof(mpack_node_data_t) * (MPACK_NODES_PER_PAGE - 1))
+
+#endif
+
+#ifdef MPACK_MALLOC
+/*
+ * Fills the tree until we have at least enough bytes for the current node.
+ */
+static bool mpack_tree_reserve_fill(mpack_tree_t* tree) {
+ mpack_assert(tree->parser.state == mpack_tree_parse_state_in_progress);
+
+ size_t bytes = tree->parser.current_node_reserved;
+ mpack_assert(bytes > tree->parser.possible_nodes_left,
+ "there are already enough bytes! call mpack_tree_ensure() instead.");
+ mpack_log("filling to reserve %i bytes\n", (int)bytes);
+
+ // if the necessary bytes would put us over the maximum tree
+ // size, fail right away.
+ // TODO: check for overflow?
+ if (tree->data_length + bytes > tree->max_size) {
+ mpack_tree_flag_error(tree, mpack_error_too_big);
+ return false;
+ }
+
+ // we'll need a read function to fetch more data. if there's
+ // no read function, the data should contain an entire message
+ // (or messages), so we flag it as invalid.
+ if (tree->read_fn == NULL) {
+ mpack_log("tree has no read function!\n");
+ mpack_tree_flag_error(tree, mpack_error_invalid);
+ return false;
+ }
+
+ // expand the buffer if needed
+ if (tree->data_length + bytes > tree->buffer_capacity) {
+
+ // TODO: check for overflow?
+ size_t new_capacity = (tree->buffer_capacity == 0) ? MPACK_BUFFER_SIZE : tree->buffer_capacity;
+ while (new_capacity < tree->data_length + bytes)
+ new_capacity *= 2;
+ if (new_capacity > tree->max_size)
+ new_capacity = tree->max_size;
+
+ mpack_log("expanding buffer from %i to %i\n", (int)tree->buffer_capacity, (int)new_capacity);
+
+ char* new_buffer;
+ if (tree->buffer == NULL)
+ new_buffer = (char*)MPACK_MALLOC(new_capacity);
+ else
+ new_buffer = (char*)mpack_realloc(tree->buffer, tree->data_length, new_capacity);
+
+ if (new_buffer == NULL) {
+ mpack_tree_flag_error(tree, mpack_error_memory);
+ return false;
+ }
+
+ tree->data = new_buffer;
+ tree->buffer = new_buffer;
+ tree->buffer_capacity = new_capacity;
+ }
+
+ // request as much data as possible, looping until we have
+ // all the data we need
+ do {
+ size_t read = tree->read_fn(tree, tree->buffer + tree->data_length, tree->buffer_capacity - tree->data_length);
+
+ // If the fill function encounters an error, it should flag an error on
+ // the tree.
+ if (mpack_tree_error(tree) != mpack_ok)
+ return false;
+
+ // We guard against fill functions that return -1 just in case.
+ if (read == (size_t)(-1)) {
+ mpack_tree_flag_error(tree, mpack_error_io);
+ return false;
+ }
+
+ // If the fill function returns 0, the data is not available yet. We
+ // return false to stop parsing the current node.
+ if (read == 0) {
+ mpack_log("not enough data.\n");
+ return false;
+ }
+
+ mpack_log("read %u more bytes\n", (uint32_t)read);
+ tree->data_length += read;
+ tree->parser.possible_nodes_left += read;
+ } while (tree->parser.possible_nodes_left < bytes);
+
+ return true;
+}
+#endif
+
+/*
+ * Ensures there are enough additional bytes in the tree for the current node
+ * (including reserved bytes for the children of this node, and in addition to
+ * the reserved bytes for children of previous compound nodes), reading more
+ * data if needed.
+ *
+ * extra_bytes is the number of additional bytes to reserve for the current
+ * node beyond the type byte (since one byte is already reserved for each node
+ * by its parent array or map.)
+ *
+ * This may reallocate the tree, which means the tree->data pointer may change!
+ *
+ * Returns false if not enough bytes could be read.
+ */
+MPACK_STATIC_INLINE bool mpack_tree_reserve_bytes(mpack_tree_t* tree, size_t extra_bytes) {
+ mpack_assert(tree->parser.state == mpack_tree_parse_state_in_progress);
+
+ // We guard against overflow here. A compound type could declare more than
+ // UINT32_MAX contents which overflows SIZE_MAX on 32-bit platforms. We
+ // flag mpack_error_invalid instead of mpack_error_too_big since it's far
+ // more likely that the message is corrupt than that the data is valid but
+ // not parseable on this architecture (see test_read_node_possible() in
+ // test-node.c .)
+ if ((uint64_t)tree->parser.current_node_reserved + (uint64_t)extra_bytes > SIZE_MAX) {
+ mpack_tree_flag_error(tree, mpack_error_invalid);
+ return false;
+ }
+
+ tree->parser.current_node_reserved += extra_bytes;
+
+ // Note that possible_nodes_left already accounts for reserved bytes for
+ // children of previous compound nodes. So even if there are hundreds of
+ // bytes left in the buffer, we might need to read anyway.
+ if (tree->parser.current_node_reserved <= tree->parser.possible_nodes_left)
+ return true;
+
+ #ifdef MPACK_MALLOC
+ return mpack_tree_reserve_fill(tree);
+ #else
+ return false;
+ #endif
+}
+
+MPACK_STATIC_INLINE size_t mpack_tree_parser_stack_capacity(mpack_tree_t* tree) {
+ #ifdef MPACK_MALLOC
+ return tree->parser.stack_capacity;
+ #else
+ return sizeof(tree->parser.stack) / sizeof(tree->parser.stack[0]);
+ #endif
+}
+
+static bool mpack_tree_push_stack(mpack_tree_t* tree, mpack_node_data_t* first_child, size_t total) {
+ mpack_tree_parser_t* parser = &tree->parser;
+ mpack_assert(parser->state == mpack_tree_parse_state_in_progress);
+
+ // No need to push empty containers
+ if (total == 0)
+ return true;
+
+ // Make sure we have enough room in the stack
+ if (parser->level + 1 == mpack_tree_parser_stack_capacity(tree)) {
+ #ifdef MPACK_MALLOC
+ size_t new_capacity = parser->stack_capacity * 2;
+ mpack_log("growing parse stack to capacity %i\n", (int)new_capacity);
+
+ // Replace the stack-allocated parsing stack
+ if (!parser->stack_owned) {
+ mpack_level_t* new_stack = (mpack_level_t*)MPACK_MALLOC(sizeof(mpack_level_t) * new_capacity);
+ if (!new_stack) {
+ mpack_tree_flag_error(tree, mpack_error_memory);
+ return false;
+ }
+ mpack_memcpy(new_stack, parser->stack, sizeof(mpack_level_t) * parser->stack_capacity);
+ parser->stack = new_stack;
+ parser->stack_owned = true;
+
+ // Realloc the allocated parsing stack
+ } else {
+ mpack_level_t* new_stack = (mpack_level_t*)mpack_realloc(parser->stack,
+ sizeof(mpack_level_t) * parser->stack_capacity, sizeof(mpack_level_t) * new_capacity);
+ if (!new_stack) {
+ mpack_tree_flag_error(tree, mpack_error_memory);
+ return false;
+ }
+ parser->stack = new_stack;
+ }
+ parser->stack_capacity = new_capacity;
+ #else
+ mpack_tree_flag_error(tree, mpack_error_too_big);
+ return false;
+ #endif
+ }
+
+ // Push the contents of this node onto the parsing stack
+ ++parser->level;
+ parser->stack[parser->level].child = first_child;
+ parser->stack[parser->level].left = total;
+ return true;
+}
+
+static bool mpack_tree_parse_children(mpack_tree_t* tree, mpack_node_data_t* node) {
+ mpack_tree_parser_t* parser = &tree->parser;
+ mpack_assert(parser->state == mpack_tree_parse_state_in_progress);
+
+ mpack_type_t type = node->type;
+ size_t total = node->len;
+
+ // Calculate total elements to read
+ if (type == mpack_type_map) {
+ if ((uint64_t)total * 2 > SIZE_MAX) {
+ mpack_tree_flag_error(tree, mpack_error_too_big);
+ return false;
+ }
+ total *= 2;
+ }
+
+ // Make sure we are under our total node limit (TODO can this overflow?)
+ tree->node_count += total;
+ if (tree->node_count > tree->max_nodes) {
+ mpack_tree_flag_error(tree, mpack_error_too_big);
+ return false;
+ }
+
+ // Each node is at least one byte. Count these bytes now to make
+ // sure there is enough data left.
+ if (!mpack_tree_reserve_bytes(tree, total))
+ return false;
+
+ // If there are enough nodes left in the current page, no need to grow
+ if (total <= parser->nodes_left) {
+ node->value.children = parser->nodes;
+ parser->nodes += total;
+ parser->nodes_left -= total;
+
+ } else {
+
+ #ifdef MPACK_MALLOC
+
+ // We can't grow if we're using a fixed pool (i.e. we didn't start with a page)
+ if (!tree->next) {
+ mpack_tree_flag_error(tree, mpack_error_too_big);
+ return false;
+ }
+
+ // Otherwise we need to grow, and the node's children need to be contiguous.
+ // This is a heuristic to decide whether we should waste the remaining space
+ // in the current page and start a new one, or give the children their
+ // own page. With a fraction of 1/8, this causes at most 12% additional
+ // waste. Note that reducing this too much causes less cache coherence and
+ // more malloc() overhead due to smaller allocations, so there's a tradeoff
+ // here. This heuristic could use some improvement, especially with custom
+ // page sizes.
+
+ mpack_tree_page_t* page;
+
+ if (total > MPACK_NODES_PER_PAGE || parser->nodes_left > MPACK_NODES_PER_PAGE / 8) {
+ // TODO: this should check for overflow
+ page = (mpack_tree_page_t*)MPACK_MALLOC(
+ sizeof(mpack_tree_page_t) + sizeof(mpack_node_data_t) * (total - 1));
+ if (page == NULL) {
+ mpack_tree_flag_error(tree, mpack_error_memory);
+ return false;
+ }
+ mpack_log("allocated seperate page %p for %i children, %i left in page of %i total\n",
+ (void*)page, (int)total, (int)parser->nodes_left, (int)MPACK_NODES_PER_PAGE);
+
+ node->value.children = page->nodes;
+
+ } else {
+ page = (mpack_tree_page_t*)MPACK_MALLOC(MPACK_PAGE_ALLOC_SIZE);
+ if (page == NULL) {
+ mpack_tree_flag_error(tree, mpack_error_memory);
+ return false;
+ }
+ mpack_log("allocated new page %p for %i children, wasting %i in page of %i total\n",
+ (void*)page, (int)total, (int)parser->nodes_left, (int)MPACK_NODES_PER_PAGE);
+
+ node->value.children = page->nodes;
+ parser->nodes = page->nodes + total;
+ parser->nodes_left = MPACK_NODES_PER_PAGE - total;
+ }
+
+ page->next = tree->next;
+ tree->next = page;
+
+ #else
+ // We can't grow if we don't have an allocator
+ mpack_tree_flag_error(tree, mpack_error_too_big);
+ return false;
+ #endif
+ }
+
+ return mpack_tree_push_stack(tree, node->value.children, total);
+}
+
+static bool mpack_tree_parse_bytes(mpack_tree_t* tree, mpack_node_data_t* node) {
+ node->value.offset = tree->size + tree->parser.current_node_reserved + 1;
+ return mpack_tree_reserve_bytes(tree, node->len);
+}
+
+#if MPACK_EXTENSIONS
+static bool mpack_tree_parse_ext(mpack_tree_t* tree, mpack_node_data_t* node) {
+ // reserve space for exttype
+ tree->parser.current_node_reserved += sizeof(int8_t);
+ node->type = mpack_type_ext;
+ return mpack_tree_parse_bytes(tree, node);
+}
+#endif
+
+static bool mpack_tree_parse_node_contents(mpack_tree_t* tree, mpack_node_data_t* node) {
+ mpack_assert(tree->parser.state == mpack_tree_parse_state_in_progress);
+ mpack_assert(node != NULL, "null node?");
+
+ // read the type. we've already accounted for this byte in
+ // possible_nodes_left, so we already know it is in bounds, and we don't
+ // need to reserve it for this node.
+ mpack_assert(tree->data_length > tree->size);
+ uint8_t type = mpack_load_u8(tree->data + tree->size);
+ mpack_log("node type %x\n", type);
+ tree->parser.current_node_reserved = 0;
+
+ // as with mpack_read_tag(), the fastest way to parse a node is to switch
+ // on the first byte, and to explicitly list every possible byte. we switch
+ // on the first four bits in size-optimized builds.
+
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ switch (type >> 4) {
+
+ // positive fixnum
+ case 0x0: case 0x1: case 0x2: case 0x3:
+ case 0x4: case 0x5: case 0x6: case 0x7:
+ node->type = mpack_type_uint;
+ node->value.u = type;
+ return true;
+
+ // negative fixnum
+ case 0xe: case 0xf:
+ node->type = mpack_type_int;
+ node->value.i = (int8_t)type;
+ return true;
+
+ // fixmap
+ case 0x8:
+ node->type = mpack_type_map;
+ node->len = (uint32_t)(type & ~0xf0);
+ return mpack_tree_parse_children(tree, node);
+
+ // fixarray
+ case 0x9:
+ node->type = mpack_type_array;
+ node->len = (uint32_t)(type & ~0xf0);
+ return mpack_tree_parse_children(tree, node);
+
+ // fixstr
+ case 0xa: case 0xb:
+ node->type = mpack_type_str;
+ node->len = (uint32_t)(type & ~0xe0);
+ return mpack_tree_parse_bytes(tree, node);
+
+ // not one of the common infix types
+ default:
+ break;
+ }
+ #endif
+
+ switch (type) {
+
+ #if !MPACK_OPTIMIZE_FOR_SIZE
+ // positive fixnum
+ case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07:
+ case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f:
+ case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17:
+ case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f:
+ case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27:
+ case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: case 0x2e: case 0x2f:
+ case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37:
+ case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f:
+ case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47:
+ case 0x48: case 0x49: case 0x4a: case 0x4b: case 0x4c: case 0x4d: case 0x4e: case 0x4f:
+ case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57:
+ case 0x58: case 0x59: case 0x5a: case 0x5b: case 0x5c: case 0x5d: case 0x5e: case 0x5f:
+ case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67:
+ case 0x68: case 0x69: case 0x6a: case 0x6b: case 0x6c: case 0x6d: case 0x6e: case 0x6f:
+ case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77:
+ case 0x78: case 0x79: case 0x7a: case 0x7b: case 0x7c: case 0x7d: case 0x7e: case 0x7f:
+ node->type = mpack_type_uint;
+ node->value.u = type;
+ return true;
+
+ // negative fixnum
+ case 0xe0: case 0xe1: case 0xe2: case 0xe3: case 0xe4: case 0xe5: case 0xe6: case 0xe7:
+ case 0xe8: case 0xe9: case 0xea: case 0xeb: case 0xec: case 0xed: case 0xee: case 0xef:
+ case 0xf0: case 0xf1: case 0xf2: case 0xf3: case 0xf4: case 0xf5: case 0xf6: case 0xf7:
+ case 0xf8: case 0xf9: case 0xfa: case 0xfb: case 0xfc: case 0xfd: case 0xfe: case 0xff:
+ node->type = mpack_type_int;
+ node->value.i = (int8_t)type;
+ return true;
+
+ // fixmap
+ case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87:
+ case 0x88: case 0x89: case 0x8a: case 0x8b: case 0x8c: case 0x8d: case 0x8e: case 0x8f:
+ node->type = mpack_type_map;
+ node->len = (uint32_t)(type & ~0xf0);
+ return mpack_tree_parse_children(tree, node);
+
+ // fixarray
+ case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97:
+ case 0x98: case 0x99: case 0x9a: case 0x9b: case 0x9c: case 0x9d: case 0x9e: case 0x9f:
+ node->type = mpack_type_array;
+ node->len = (uint32_t)(type & ~0xf0);
+ return mpack_tree_parse_children(tree, node);
+
+ // fixstr
+ case 0xa0: case 0xa1: case 0xa2: case 0xa3: case 0xa4: case 0xa5: case 0xa6: case 0xa7:
+ case 0xa8: case 0xa9: case 0xaa: case 0xab: case 0xac: case 0xad: case 0xae: case 0xaf:
+ case 0xb0: case 0xb1: case 0xb2: case 0xb3: case 0xb4: case 0xb5: case 0xb6: case 0xb7:
+ case 0xb8: case 0xb9: case 0xba: case 0xbb: case 0xbc: case 0xbd: case 0xbe: case 0xbf:
+ node->type = mpack_type_str;
+ node->len = (uint32_t)(type & ~0xe0);
+ return mpack_tree_parse_bytes(tree, node);
+ #endif
+
+ // nil
+ case 0xc0:
+ node->type = mpack_type_nil;
+ return true;
+
+ // bool
+ case 0xc2: case 0xc3:
+ node->type = mpack_type_bool;
+ node->value.b = type & 1;
+ return true;
+
+ // bin8
+ case 0xc4:
+ node->type = mpack_type_bin;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint8_t)))
+ return false;
+ node->len = mpack_load_u8(tree->data + tree->size + 1);
+ return mpack_tree_parse_bytes(tree, node);
+
+ // bin16
+ case 0xc5:
+ node->type = mpack_type_bin;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint16_t)))
+ return false;
+ node->len = mpack_load_u16(tree->data + tree->size + 1);
+ return mpack_tree_parse_bytes(tree, node);
+
+ // bin32
+ case 0xc6:
+ node->type = mpack_type_bin;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint32_t)))
+ return false;
+ node->len = mpack_load_u32(tree->data + tree->size + 1);
+ return mpack_tree_parse_bytes(tree, node);
+
+ #if MPACK_EXTENSIONS
+ // ext8
+ case 0xc7:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint8_t)))
+ return false;
+ node->len = mpack_load_u8(tree->data + tree->size + 1);
+ return mpack_tree_parse_ext(tree, node);
+
+ // ext16
+ case 0xc8:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint16_t)))
+ return false;
+ node->len = mpack_load_u16(tree->data + tree->size + 1);
+ return mpack_tree_parse_ext(tree, node);
+
+ // ext32
+ case 0xc9:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint32_t)))
+ return false;
+ node->len = mpack_load_u32(tree->data + tree->size + 1);
+ return mpack_tree_parse_ext(tree, node);
+ #endif
+
+ // float
+ case 0xca:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(float)))
+ return false;
+ node->value.f = mpack_load_float(tree->data + tree->size + 1);
+ node->type = mpack_type_float;
+ return true;
+
+ // double
+ case 0xcb:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(double)))
+ return false;
+ node->value.d = mpack_load_double(tree->data + tree->size + 1);
+ node->type = mpack_type_double;
+ return true;
+
+ // uint8
+ case 0xcc:
+ node->type = mpack_type_uint;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint8_t)))
+ return false;
+ node->value.u = mpack_load_u8(tree->data + tree->size + 1);
+ return true;
+
+ // uint16
+ case 0xcd:
+ node->type = mpack_type_uint;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint16_t)))
+ return false;
+ node->value.u = mpack_load_u16(tree->data + tree->size + 1);
+ return true;
+
+ // uint32
+ case 0xce:
+ node->type = mpack_type_uint;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint32_t)))
+ return false;
+ node->value.u = mpack_load_u32(tree->data + tree->size + 1);
+ return true;
+
+ // uint64
+ case 0xcf:
+ node->type = mpack_type_uint;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint64_t)))
+ return false;
+ node->value.u = mpack_load_u64(tree->data + tree->size + 1);
+ return true;
+
+ // int8
+ case 0xd0:
+ node->type = mpack_type_int;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(int8_t)))
+ return false;
+ node->value.i = mpack_load_i8(tree->data + tree->size + 1);
+ return true;
+
+ // int16
+ case 0xd1:
+ node->type = mpack_type_int;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(int16_t)))
+ return false;
+ node->value.i = mpack_load_i16(tree->data + tree->size + 1);
+ return true;
+
+ // int32
+ case 0xd2:
+ node->type = mpack_type_int;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(int32_t)))
+ return false;
+ node->value.i = mpack_load_i32(tree->data + tree->size + 1);
+ return true;
+
+ // int64
+ case 0xd3:
+ node->type = mpack_type_int;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(int64_t)))
+ return false;
+ node->value.i = mpack_load_i64(tree->data + tree->size + 1);
+ return true;
+
+ #if MPACK_EXTENSIONS
+ // fixext1
+ case 0xd4:
+ node->len = 1;
+ return mpack_tree_parse_ext(tree, node);
+
+ // fixext2
+ case 0xd5:
+ node->len = 2;
+ return mpack_tree_parse_ext(tree, node);
+
+ // fixext4
+ case 0xd6:
+ node->len = 4;
+ return mpack_tree_parse_ext(tree, node);
+
+ // fixext8
+ case 0xd7:
+ node->len = 8;
+ return mpack_tree_parse_ext(tree, node);
+
+ // fixext16
+ case 0xd8:
+ node->len = 16;
+ return mpack_tree_parse_ext(tree, node);
+ #endif
+
+ // str8
+ case 0xd9:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint8_t)))
+ return false;
+ node->len = mpack_load_u8(tree->data + tree->size + 1);
+ node->type = mpack_type_str;
+ return mpack_tree_parse_bytes(tree, node);
+
+ // str16
+ case 0xda:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint16_t)))
+ return false;
+ node->len = mpack_load_u16(tree->data + tree->size + 1);
+ node->type = mpack_type_str;
+ return mpack_tree_parse_bytes(tree, node);
+
+ // str32
+ case 0xdb:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint32_t)))
+ return false;
+ node->len = mpack_load_u32(tree->data + tree->size + 1);
+ node->type = mpack_type_str;
+ return mpack_tree_parse_bytes(tree, node);
+
+ // array16
+ case 0xdc:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint16_t)))
+ return false;
+ node->len = mpack_load_u16(tree->data + tree->size + 1);
+ node->type = mpack_type_array;
+ return mpack_tree_parse_children(tree, node);
+
+ // array32
+ case 0xdd:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint32_t)))
+ return false;
+ node->len = mpack_load_u32(tree->data + tree->size + 1);
+ node->type = mpack_type_array;
+ return mpack_tree_parse_children(tree, node);
+
+ // map16
+ case 0xde:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint16_t)))
+ return false;
+ node->len = mpack_load_u16(tree->data + tree->size + 1);
+ node->type = mpack_type_map;
+ return mpack_tree_parse_children(tree, node);
+
+ // map32
+ case 0xdf:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint32_t)))
+ return false;
+ node->len = mpack_load_u32(tree->data + tree->size + 1);
+ node->type = mpack_type_map;
+ return mpack_tree_parse_children(tree, node);
+
+ // reserved
+ case 0xc1:
+ mpack_tree_flag_error(tree, mpack_error_invalid);
+ return false;
+
+ #if !MPACK_EXTENSIONS
+ // ext
+ case 0xc7: // fallthrough
+ case 0xc8: // fallthrough
+ case 0xc9: // fallthrough
+ // fixext
+ case 0xd4: // fallthrough
+ case 0xd5: // fallthrough
+ case 0xd6: // fallthrough
+ case 0xd7: // fallthrough
+ case 0xd8:
+ mpack_tree_flag_error(tree, mpack_error_unsupported);
+ return false;
+ #endif
+
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ // any other bytes should have been handled by the infix switch
+ default:
+ break;
+ #endif
+ }
+
+ mpack_assert(0, "unreachable");
+ return false;
+}
+
+static bool mpack_tree_parse_node(mpack_tree_t* tree, mpack_node_data_t* node) {
+ mpack_log("parsing a node at position %i in level %i\n",
+ (int)tree->size, (int)tree->parser.level);
+
+ if (!mpack_tree_parse_node_contents(tree, node)) {
+ mpack_log("node parsing returned false\n");
+ return false;
+ }
+
+ tree->parser.possible_nodes_left -= tree->parser.current_node_reserved;
+
+ // The reserve for the current node does not include the initial byte
+ // previously reserved as part of its parent.
+ size_t node_size = tree->parser.current_node_reserved + 1;
+
+ // If the parsed type is a map or array, the reserve includes one byte for
+ // each child. We want to subtract these out of possible_nodes_left, but
+ // not out of the current size of the tree.
+ if (node->type == mpack_type_array)
+ node_size -= node->len;
+ else if (node->type == mpack_type_map)
+ node_size -= node->len * 2;
+ tree->size += node_size;
+
+ mpack_log("parsed a node of type %s of %i bytes and "
+ "%i additional bytes reserved for children.\n",
+ mpack_type_to_string(node->type), (int)node_size,
+ (int)tree->parser.current_node_reserved + 1 - (int)node_size);
+
+ return true;
+}
+
+/*
+ * We read nodes in a loop instead of recursively for maximum performance. The
+ * stack holds the amount of children left to read in each level of the tree.
+ * Parsing can pause and resume when more data becomes available.
+ */
+static bool mpack_tree_continue_parsing(mpack_tree_t* tree) {
+ if (mpack_tree_error(tree) != mpack_ok)
+ return false;
+
+ mpack_tree_parser_t* parser = &tree->parser;
+ mpack_assert(parser->state == mpack_tree_parse_state_in_progress);
+ mpack_log("parsing tree elements, %i bytes in buffer\n", (int)tree->data_length);
+
+ // we loop parsing nodes until the parse stack is empty. we break
+ // by returning out of the function.
+ while (true) {
+ mpack_node_data_t* node = parser->stack[parser->level].child;
+ size_t level = parser->level;
+ if (!mpack_tree_parse_node(tree, node))
+ return false;
+ --parser->stack[level].left;
+ ++parser->stack[level].child;
+
+ mpack_assert(mpack_tree_error(tree) == mpack_ok,
+ "mpack_tree_parse_node() should have returned false due to error!");
+
+ // pop empty stack levels, exiting the outer loop when the stack is empty.
+ // (we could tail-optimize containers by pre-emptively popping empty
+ // stack levels before reading the new element, this way we wouldn't
+ // have to loop. but we eventually want to use the parse stack to give
+ // better error messages that contain the location of the error, so
+ // it needs to be complete.)
+ while (parser->stack[parser->level].left == 0) {
+ if (parser->level == 0)
+ return true;
+ --parser->level;
+ }
+ }
+}
+
+static void mpack_tree_cleanup(mpack_tree_t* tree) {
+ MPACK_UNUSED(tree);
+
+ #ifdef MPACK_MALLOC
+ if (tree->parser.stack_owned) {
+ MPACK_FREE(tree->parser.stack);
+ tree->parser.stack = NULL;
+ tree->parser.stack_owned = false;
+ }
+
+ mpack_tree_page_t* page = tree->next;
+ while (page != NULL) {
+ mpack_tree_page_t* next = page->next;
+ mpack_log("freeing page %p\n", (void*)page);
+ MPACK_FREE(page);
+ page = next;
+ }
+ tree->next = NULL;
+ #endif
+}
+
+static bool mpack_tree_parse_start(mpack_tree_t* tree) {
+ if (mpack_tree_error(tree) != mpack_ok)
+ return false;
+
+ mpack_tree_parser_t* parser = &tree->parser;
+ mpack_assert(parser->state != mpack_tree_parse_state_in_progress,
+ "previous parsing was not finished!");
+
+ if (parser->state == mpack_tree_parse_state_parsed)
+ mpack_tree_cleanup(tree);
+
+ mpack_log("starting parse\n");
+ tree->parser.state = mpack_tree_parse_state_in_progress;
+ tree->parser.current_node_reserved = 0;
+
+ // check if we previously parsed a tree
+ if (tree->size > 0) {
+ #ifdef MPACK_MALLOC
+ // if we're buffered, move the remaining data back to the
+ // start of the buffer
+ // TODO: This is not ideal performance-wise. We should only move data
+ // when we need to call the fill function.
+ // TODO: We could consider shrinking the buffer here, especially if we
+ // determine that the fill function is providing less than a quarter of
+ // the buffer size or if messages take up less than a quarter of the
+ // buffer size. Maybe this should be configurable.
+ if (tree->buffer != NULL) {
+ mpack_memmove(tree->buffer, tree->buffer + tree->size, tree->data_length - tree->size);
+ }
+ else
+ #endif
+ // otherwise advance past the parsed data
+ {
+ tree->data += tree->size;
+ }
+ tree->data_length -= tree->size;
+ tree->size = 0;
+ tree->node_count = 0;
+ }
+
+ // make sure we have at least one byte available before allocating anything
+ parser->possible_nodes_left = tree->data_length;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint8_t))) {
+ tree->parser.state = mpack_tree_parse_state_not_started;
+ return false;
+ }
+ mpack_log("parsing tree at %p starting with byte %x\n", tree->data, (uint8_t)tree->data[0]);
+ parser->possible_nodes_left -= 1;
+ tree->node_count = 1;
+
+ #ifdef MPACK_MALLOC
+ parser->stack = parser->stack_local;
+ parser->stack_owned = false;
+ parser->stack_capacity = sizeof(parser->stack_local) / sizeof(*parser->stack_local);
+
+ if (tree->pool == NULL) {
+
+ // allocate first page
+ mpack_tree_page_t* page = (mpack_tree_page_t*)MPACK_MALLOC(MPACK_PAGE_ALLOC_SIZE);
+ mpack_log("allocated initial page %p of size %i count %i\n",
+ (void*)page, (int)MPACK_PAGE_ALLOC_SIZE, (int)MPACK_NODES_PER_PAGE);
+ if (page == NULL) {
+ tree->error = mpack_error_memory;
+ return false;
+ }
+ page->next = NULL;
+ tree->next = page;
+
+ parser->nodes = page->nodes;
+ parser->nodes_left = MPACK_NODES_PER_PAGE;
+ }
+ else
+ #endif
+ {
+ // otherwise use the provided pool
+ mpack_assert(tree->pool != NULL, "no pool provided?");
+ parser->nodes = tree->pool;
+ parser->nodes_left = tree->pool_count;
+ }
+
+ tree->root = parser->nodes;
+ ++parser->nodes;
+ --parser->nodes_left;
+
+ parser->level = 0;
+ parser->stack[0].child = tree->root;
+ parser->stack[0].left = 1;
+
+ return true;
+}
+
+void mpack_tree_parse(mpack_tree_t* tree) {
+ if (mpack_tree_error(tree) != mpack_ok)
+ return;
+
+ if (tree->parser.state != mpack_tree_parse_state_in_progress) {
+ if (!mpack_tree_parse_start(tree)) {
+ mpack_tree_flag_error(tree, (tree->read_fn == NULL) ?
+ mpack_error_invalid : mpack_error_io);
+ return;
+ }
+ }
+
+ if (!mpack_tree_continue_parsing(tree)) {
+ if (mpack_tree_error(tree) != mpack_ok)
+ return;
+
+ // We're parsing synchronously on a blocking fill function. If we
+ // didn't completely finish parsing the tree, it's an error.
+ mpack_log("tree parsing incomplete. flagging error.\n");
+ mpack_tree_flag_error(tree, (tree->read_fn == NULL) ?
+ mpack_error_invalid : mpack_error_io);
+ return;
+ }
+
+ mpack_assert(mpack_tree_error(tree) == mpack_ok);
+ mpack_assert(tree->parser.level == 0);
+ tree->parser.state = mpack_tree_parse_state_parsed;
+ mpack_log("parsed tree of %i bytes, %i bytes left\n", (int)tree->size, (int)tree->parser.possible_nodes_left);
+ mpack_log("%i nodes in final page\n", (int)tree->parser.nodes_left);
+}
+
+bool mpack_tree_try_parse(mpack_tree_t* tree) {
+ if (mpack_tree_error(tree) != mpack_ok)
+ return false;
+
+ if (tree->parser.state != mpack_tree_parse_state_in_progress)
+ if (!mpack_tree_parse_start(tree))
+ return false;
+
+ if (!mpack_tree_continue_parsing(tree))
+ return false;
+
+ mpack_assert(mpack_tree_error(tree) == mpack_ok);
+ mpack_assert(tree->parser.level == 0);
+ tree->parser.state = mpack_tree_parse_state_parsed;
+ return true;
+}
+
+
+
+/*
+ * Tree functions
+ */
+
+mpack_node_t mpack_tree_root(mpack_tree_t* tree) {
+ if (mpack_tree_error(tree) != mpack_ok)
+ return mpack_tree_nil_node(tree);
+
+ // We check that a tree was parsed successfully and assert if not. You must
+ // call mpack_tree_parse() (or mpack_tree_try_parse() with a success
+ // result) in order to access the root node.
+ if (tree->parser.state != mpack_tree_parse_state_parsed) {
+ mpack_break("Tree has not been parsed! "
+ "Did you call mpack_tree_parse() or mpack_tree_try_parse()?");
+ mpack_tree_flag_error(tree, mpack_error_bug);
+ return mpack_tree_nil_node(tree);
+ }
+
+ return mpack_node(tree, tree->root);
+}
+
+static void mpack_tree_init_clear(mpack_tree_t* tree) {
+ mpack_memset(tree, 0, sizeof(*tree));
+ tree->nil_node.type = mpack_type_nil;
+ tree->missing_node.type = mpack_type_missing;
+ tree->max_size = SIZE_MAX;
+ tree->max_nodes = SIZE_MAX;
+}
+
+#ifdef MPACK_MALLOC
+void mpack_tree_init_data(mpack_tree_t* tree, const char* data, size_t length) {
+ mpack_tree_init_clear(tree);
+
+ MPACK_STATIC_ASSERT(MPACK_NODE_PAGE_SIZE >= sizeof(mpack_tree_page_t),
+ "MPACK_NODE_PAGE_SIZE is too small");
+
+ MPACK_STATIC_ASSERT(MPACK_PAGE_ALLOC_SIZE <= MPACK_NODE_PAGE_SIZE,
+ "incorrect page rounding?");
+
+ tree->data = data;
+ tree->data_length = length;
+ tree->pool = NULL;
+ tree->pool_count = 0;
+ tree->next = NULL;
+
+ mpack_log("===========================\n");
+ mpack_log("initializing tree with data of size %i\n", (int)length);
+}
+#endif
+
+void mpack_tree_init_pool(mpack_tree_t* tree, const char* data, size_t length,
+ mpack_node_data_t* node_pool, size_t node_pool_count)
+{
+ mpack_tree_init_clear(tree);
+ #ifdef MPACK_MALLOC
+ tree->next = NULL;
+ #endif
+
+ if (node_pool_count == 0) {
+ mpack_break("initial page has no nodes!");
+ mpack_tree_flag_error(tree, mpack_error_bug);
+ return;
+ }
+
+ tree->data = data;
+ tree->data_length = length;
+ tree->pool = node_pool;
+ tree->pool_count = node_pool_count;
+
+ mpack_log("===========================\n");
+ mpack_log("initializing tree with data of size %i and pool of count %i\n",
+ (int)length, (int)node_pool_count);
+}
+
+void mpack_tree_init_error(mpack_tree_t* tree, mpack_error_t error) {
+ mpack_tree_init_clear(tree);
+ tree->error = error;
+
+ mpack_log("===========================\n");
+ mpack_log("initializing tree error state %i\n", (int)error);
+}
+
+#ifdef MPACK_MALLOC
+void mpack_tree_init_stream(mpack_tree_t* tree, mpack_tree_read_t read_fn, void* context,
+ size_t max_message_size, size_t max_message_nodes) {
+ mpack_tree_init_clear(tree);
+
+ tree->read_fn = read_fn;
+ tree->context = context;
+
+ mpack_tree_set_limits(tree, max_message_size, max_message_nodes);
+ tree->max_size = max_message_size;
+ tree->max_nodes = max_message_nodes;
+
+ mpack_log("===========================\n");
+ mpack_log("initializing tree with stream, max size %i max nodes %i\n",
+ (int)max_message_size, (int)max_message_nodes);
+}
+#endif
+
+void mpack_tree_set_limits(mpack_tree_t* tree, size_t max_message_size, size_t max_message_nodes) {
+ mpack_assert(max_message_size > 0);
+ mpack_assert(max_message_nodes > 0);
+ tree->max_size = max_message_size;
+ tree->max_nodes = max_message_nodes;
+}
+
+#if MPACK_STDIO
+typedef struct mpack_file_tree_t {
+ char* data;
+ size_t size;
+ char buffer[MPACK_BUFFER_SIZE];
+} mpack_file_tree_t;
+
+static void mpack_file_tree_teardown(mpack_tree_t* tree) {
+ mpack_file_tree_t* file_tree = (mpack_file_tree_t*)tree->context;
+ MPACK_FREE(file_tree->data);
+ MPACK_FREE(file_tree);
+}
+
+static bool mpack_file_tree_read(mpack_tree_t* tree, mpack_file_tree_t* file_tree, FILE* file, size_t max_bytes) {
+
+ // get the file size
+ errno = 0;
+ int error = 0;
+ fseek(file, 0, SEEK_END);
+ error |= errno;
+ long size = ftell(file);
+ error |= errno;
+ fseek(file, 0, SEEK_SET);
+ error |= errno;
+
+ // check for errors
+ if (error != 0 || size < 0) {
+ mpack_tree_init_error(tree, mpack_error_io);
+ return false;
+ }
+ if (size == 0) {
+ mpack_tree_init_error(tree, mpack_error_invalid);
+ return false;
+ }
+
+ // make sure the size is less than max_bytes
+ // (this mess exists to safely convert between long and size_t regardless of their widths)
+ if (max_bytes != 0 && (((uint64_t)LONG_MAX > (uint64_t)SIZE_MAX && size > (long)SIZE_MAX) || (size_t)size > max_bytes)) {
+ mpack_tree_init_error(tree, mpack_error_too_big);
+ return false;
+ }
+
+ // allocate data
+ file_tree->data = (char*)MPACK_MALLOC((size_t)size);
+ if (file_tree->data == NULL) {
+ mpack_tree_init_error(tree, mpack_error_memory);
+ return false;
+ }
+
+ // read the file
+ long total = 0;
+ while (total < size) {
+ size_t read = fread(file_tree->data + total, 1, (size_t)(size - total), file);
+ if (read <= 0) {
+ mpack_tree_init_error(tree, mpack_error_io);
+ MPACK_FREE(file_tree->data);
+ return false;
+ }
+ total += (long)read;
+ }
+
+ file_tree->size = (size_t)size;
+ return true;
+}
+
+static bool mpack_tree_file_check_max_bytes(mpack_tree_t* tree, size_t max_bytes) {
+
+ // the C STDIO family of file functions use long (e.g. ftell)
+ if (max_bytes > LONG_MAX) {
+ mpack_break("max_bytes of %" PRIu64 " is invalid, maximum is LONG_MAX", (uint64_t)max_bytes);
+ mpack_tree_init_error(tree, mpack_error_bug);
+ return false;
+ }
+
+ return true;
+}
+
+static void mpack_tree_init_stdfile_noclose(mpack_tree_t* tree, FILE* stdfile, size_t max_bytes) {
+
+ // allocate file tree
+ mpack_file_tree_t* file_tree = (mpack_file_tree_t*) MPACK_MALLOC(sizeof(mpack_file_tree_t));
+ if (file_tree == NULL) {
+ mpack_tree_init_error(tree, mpack_error_memory);
+ return;
+ }
+
+ // read all data
+ if (!mpack_file_tree_read(tree, file_tree, stdfile, max_bytes)) {
+ MPACK_FREE(file_tree);
+ return;
+ }
+
+ mpack_tree_init_data(tree, file_tree->data, file_tree->size);
+ mpack_tree_set_context(tree, file_tree);
+ mpack_tree_set_teardown(tree, mpack_file_tree_teardown);
+}
+
+void mpack_tree_init_stdfile(mpack_tree_t* tree, FILE* stdfile, size_t max_bytes, bool close_when_done) {
+ if (!mpack_tree_file_check_max_bytes(tree, max_bytes))
+ return;
+
+ mpack_tree_init_stdfile_noclose(tree, stdfile, max_bytes);
+
+ if (close_when_done)
+ fclose(stdfile);
+}
+
+void mpack_tree_init_filename(mpack_tree_t* tree, const char* filename, size_t max_bytes) {
+ if (!mpack_tree_file_check_max_bytes(tree, max_bytes))
+ return;
+
+ // open the file
+ FILE* file = fopen(filename, "rb");
+ if (!file) {
+ mpack_tree_init_error(tree, mpack_error_io);
+ return;
+ }
+
+ mpack_tree_init_stdfile(tree, file, max_bytes, true);
+}
+#endif
+
+mpack_error_t mpack_tree_destroy(mpack_tree_t* tree) {
+ mpack_tree_cleanup(tree);
+
+ #ifdef MPACK_MALLOC
+ if (tree->buffer)
+ MPACK_FREE(tree->buffer);
+ #endif
+
+ if (tree->teardown)
+ tree->teardown(tree);
+ tree->teardown = NULL;
+
+ return tree->error;
+}
+
+void mpack_tree_flag_error(mpack_tree_t* tree, mpack_error_t error) {
+ if (tree->error == mpack_ok) {
+ mpack_log("tree %p setting error %i: %s\n", (void*)tree, (int)error, mpack_error_to_string(error));
+ tree->error = error;
+ if (tree->error_fn)
+ tree->error_fn(tree, error);
+ }
+
+}
+
+
+
+/*
+ * Node misc functions
+ */
+
+void mpack_node_flag_error(mpack_node_t node, mpack_error_t error) {
+ mpack_tree_flag_error(node.tree, error);
+}
+
+mpack_tag_t mpack_node_tag(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return mpack_tag_nil();
+
+ mpack_tag_t tag = MPACK_TAG_ZERO;
+
+ tag.type = node.data->type;
+ switch (node.data->type) {
+ case mpack_type_missing:
+ // If a node is missing, I don't know if it makes sense to ask for
+ // a tag for it. We'll return a missing tag to match the missing
+ // node I guess, but attempting to use the tag for anything (like
+ // writing it for example) will flag mpack_error_bug.
+ break;
+ case mpack_type_nil: break;
+ case mpack_type_bool: tag.v.b = node.data->value.b; break;
+ case mpack_type_float: tag.v.f = node.data->value.f; break;
+ case mpack_type_double: tag.v.d = node.data->value.d; break;
+ case mpack_type_int: tag.v.i = node.data->value.i; break;
+ case mpack_type_uint: tag.v.u = node.data->value.u; break;
+
+ case mpack_type_str: tag.v.l = node.data->len; break;
+ case mpack_type_bin: tag.v.l = node.data->len; break;
+
+ #if MPACK_EXTENSIONS
+ case mpack_type_ext:
+ tag.v.l = node.data->len;
+ tag.exttype = mpack_node_exttype_unchecked(node);
+ break;
+ #endif
+
+ case mpack_type_array: tag.v.n = node.data->len; break;
+ case mpack_type_map: tag.v.n = node.data->len; break;
+
+ default:
+ mpack_assert(0, "unrecognized type %i", (int)node.data->type);
+ break;
+ }
+ return tag;
+}
+
+#if MPACK_DEBUG && MPACK_STDIO
+static void mpack_node_print_element(mpack_node_t node, mpack_print_t* print, size_t depth) {
+ mpack_node_data_t* data = node.data;
+ switch (data->type) {
+ case mpack_type_str:
+ {
+ mpack_print_append_cstr(print, "\"");
+ const char* bytes = mpack_node_data_unchecked(node);
+ for (size_t i = 0; i < data->len; ++i) {
+ char c = bytes[i];
+ switch (c) {
+ case '\n': mpack_print_append_cstr(print, "\\n"); break;
+ case '\\': mpack_print_append_cstr(print, "\\\\"); break;
+ case '"': mpack_print_append_cstr(print, "\\\""); break;
+ default: mpack_print_append(print, &c, 1); break;
+ }
+ }
+ mpack_print_append_cstr(print, "\"");
+ }
+ break;
+
+ case mpack_type_array:
+ mpack_print_append_cstr(print, "[\n");
+ for (size_t i = 0; i < data->len; ++i) {
+ for (size_t j = 0; j < depth + 1; ++j)
+ mpack_print_append_cstr(print, " ");
+ mpack_node_print_element(mpack_node_array_at(node, i), print, depth + 1);
+ if (i != data->len - 1)
+ mpack_print_append_cstr(print, ",");
+ mpack_print_append_cstr(print, "\n");
+ }
+ for (size_t i = 0; i < depth; ++i)
+ mpack_print_append_cstr(print, " ");
+ mpack_print_append_cstr(print, "]");
+ break;
+
+ case mpack_type_map:
+ mpack_print_append_cstr(print, "{\n");
+ for (size_t i = 0; i < data->len; ++i) {
+ for (size_t j = 0; j < depth + 1; ++j)
+ mpack_print_append_cstr(print, " ");
+ mpack_node_print_element(mpack_node_map_key_at(node, i), print, depth + 1);
+ mpack_print_append_cstr(print, ": ");
+ mpack_node_print_element(mpack_node_map_value_at(node, i), print, depth + 1);
+ if (i != data->len - 1)
+ mpack_print_append_cstr(print, ",");
+ mpack_print_append_cstr(print, "\n");
+ }
+ for (size_t i = 0; i < depth; ++i)
+ mpack_print_append_cstr(print, " ");
+ mpack_print_append_cstr(print, "}");
+ break;
+
+ default:
+ {
+ const char* prefix = NULL;
+ size_t prefix_length = 0;
+ if (mpack_node_type(node) == mpack_type_bin
+ #if MPACK_EXTENSIONS
+ || mpack_node_type(node) == mpack_type_ext
+ #endif
+ ) {
+ prefix = mpack_node_data(node);
+ prefix_length = mpack_node_data_len(node);
+ }
+
+ char buf[256];
+ mpack_tag_t tag = mpack_node_tag(node);
+ mpack_tag_debug_pseudo_json(tag, buf, sizeof(buf), prefix, prefix_length);
+ mpack_print_append_cstr(print, buf);
+ }
+ break;
+ }
+}
+
+void mpack_node_print_to_buffer(mpack_node_t node, char* buffer, size_t buffer_size) {
+ if (buffer_size == 0) {
+ mpack_assert(false, "buffer size is zero!");
+ return;
+ }
+
+ mpack_print_t print;
+ mpack_memset(&print, 0, sizeof(print));
+ print.buffer = buffer;
+ print.size = buffer_size;
+ mpack_node_print_element(node, &print, 0);
+ mpack_print_append(&print, "", 1); // null-terminator
+ mpack_print_flush(&print);
+
+ // we always make sure there's a null-terminator at the end of the buffer
+ // in case we ran out of space.
+ print.buffer[print.size - 1] = '\0';
+}
+
+void mpack_node_print_to_callback(mpack_node_t node, mpack_print_callback_t callback, void* context) {
+ char buffer[1024];
+ mpack_print_t print;
+ mpack_memset(&print, 0, sizeof(print));
+ print.buffer = buffer;
+ print.size = sizeof(buffer);
+ print.callback = callback;
+ print.context = context;
+ mpack_node_print_element(node, &print, 0);
+ mpack_print_flush(&print);
+}
+
+void mpack_node_print_to_file(mpack_node_t node, FILE* file) {
+ mpack_assert(file != NULL, "file is NULL");
+
+ char buffer[1024];
+ mpack_print_t print;
+ mpack_memset(&print, 0, sizeof(print));
+ print.buffer = buffer;
+ print.size = sizeof(buffer);
+ print.callback = &mpack_print_file_callback;
+ print.context = file;
+
+ size_t depth = 2;
+ for (size_t i = 0; i < depth; ++i)
+ mpack_print_append_cstr(&print, " ");
+ mpack_node_print_element(node, &print, depth);
+ mpack_print_append_cstr(&print, "\n");
+ mpack_print_flush(&print);
+}
+#endif
+
+
+
+/*
+ * Node Value Functions
+ */
+
+#if MPACK_EXTENSIONS
+mpack_timestamp_t mpack_node_timestamp(mpack_node_t node) {
+ mpack_timestamp_t timestamp = {0, 0};
+
+ // we'll let mpack_node_exttype() do most checks
+ if (mpack_node_exttype(node) != MPACK_EXTTYPE_TIMESTAMP) {
+ mpack_log("exttype %i\n", mpack_node_exttype(node));
+ mpack_node_flag_error(node, mpack_error_type);
+ return timestamp;
+ }
+
+ const char* p = mpack_node_data_unchecked(node);
+
+ switch (node.data->len) {
+ case 4:
+ timestamp.nanoseconds = 0;
+ timestamp.seconds = mpack_load_u32(p);
+ break;
+
+ case 8: {
+ uint64_t value = mpack_load_u64(p);
+ timestamp.nanoseconds = (uint32_t)(value >> 34);
+ timestamp.seconds = value & ((UINT64_C(1) << 34) - 1);
+ break;
+ }
+
+ case 12:
+ timestamp.nanoseconds = mpack_load_u32(p);
+ timestamp.seconds = mpack_load_i64(p + 4);
+ break;
+
+ default:
+ mpack_tree_flag_error(node.tree, mpack_error_invalid);
+ return timestamp;
+ }
+
+ if (timestamp.nanoseconds > MPACK_TIMESTAMP_NANOSECONDS_MAX) {
+ mpack_tree_flag_error(node.tree, mpack_error_invalid);
+ mpack_timestamp_t zero = {0, 0};
+ return zero;
+ }
+
+ return timestamp;
+}
+
+int64_t mpack_node_timestamp_seconds(mpack_node_t node) {
+ return mpack_node_timestamp(node).seconds;
+}
+
+uint32_t mpack_node_timestamp_nanoseconds(mpack_node_t node) {
+ return mpack_node_timestamp(node).nanoseconds;
+}
+#endif
+
+
+
+/*
+ * Node Data Functions
+ */
+
+void mpack_node_check_utf8(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return;
+ mpack_node_data_t* data = node.data;
+ if (data->type != mpack_type_str || !mpack_utf8_check(mpack_node_data_unchecked(node), data->len))
+ mpack_node_flag_error(node, mpack_error_type);
+}
+
+void mpack_node_check_utf8_cstr(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return;
+ mpack_node_data_t* data = node.data;
+ if (data->type != mpack_type_str || !mpack_utf8_check_no_null(mpack_node_data_unchecked(node), data->len))
+ mpack_node_flag_error(node, mpack_error_type);
+}
+
+size_t mpack_node_copy_data(mpack_node_t node, char* buffer, size_t bufsize) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ mpack_assert(bufsize == 0 || buffer != NULL, "buffer is NULL for maximum of %i bytes", (int)bufsize);
+
+ mpack_type_t type = node.data->type;
+ if (type != mpack_type_str && type != mpack_type_bin
+ #if MPACK_EXTENSIONS
+ && type != mpack_type_ext
+ #endif
+ ) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+ }
+
+ if (node.data->len > bufsize) {
+ mpack_node_flag_error(node, mpack_error_too_big);
+ return 0;
+ }
+
+ mpack_memcpy(buffer, mpack_node_data_unchecked(node), node.data->len);
+ return (size_t)node.data->len;
+}
+
+size_t mpack_node_copy_utf8(mpack_node_t node, char* buffer, size_t bufsize) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ mpack_assert(bufsize == 0 || buffer != NULL, "buffer is NULL for maximum of %i bytes", (int)bufsize);
+
+ mpack_type_t type = node.data->type;
+ if (type != mpack_type_str) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+ }
+
+ if (node.data->len > bufsize) {
+ mpack_node_flag_error(node, mpack_error_too_big);
+ return 0;
+ }
+
+ if (!mpack_utf8_check(mpack_node_data_unchecked(node), node.data->len)) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+ }
+
+ mpack_memcpy(buffer, mpack_node_data_unchecked(node), node.data->len);
+ return (size_t)node.data->len;
+}
+
+void mpack_node_copy_cstr(mpack_node_t node, char* buffer, size_t bufsize) {
+
+ // we can't break here because the error isn't recoverable; we
+ // have to add a null-terminator.
+ mpack_assert(buffer != NULL, "buffer is NULL");
+ mpack_assert(bufsize >= 1, "buffer size is zero; you must have room for at least a null-terminator");
+
+ if (mpack_node_error(node) != mpack_ok) {
+ buffer[0] = '\0';
+ return;
+ }
+
+ if (node.data->type != mpack_type_str) {
+ buffer[0] = '\0';
+ mpack_node_flag_error(node, mpack_error_type);
+ return;
+ }
+
+ if (node.data->len > bufsize - 1) {
+ buffer[0] = '\0';
+ mpack_node_flag_error(node, mpack_error_too_big);
+ return;
+ }
+
+ if (!mpack_str_check_no_null(mpack_node_data_unchecked(node), node.data->len)) {
+ buffer[0] = '\0';
+ mpack_node_flag_error(node, mpack_error_type);
+ return;
+ }
+
+ mpack_memcpy(buffer, mpack_node_data_unchecked(node), node.data->len);
+ buffer[node.data->len] = '\0';
+}
+
+void mpack_node_copy_utf8_cstr(mpack_node_t node, char* buffer, size_t bufsize) {
+
+ // we can't break here because the error isn't recoverable; we
+ // have to add a null-terminator.
+ mpack_assert(buffer != NULL, "buffer is NULL");
+ mpack_assert(bufsize >= 1, "buffer size is zero; you must have room for at least a null-terminator");
+
+ if (mpack_node_error(node) != mpack_ok) {
+ buffer[0] = '\0';
+ return;
+ }
+
+ if (node.data->type != mpack_type_str) {
+ buffer[0] = '\0';
+ mpack_node_flag_error(node, mpack_error_type);
+ return;
+ }
+
+ if (node.data->len > bufsize - 1) {
+ buffer[0] = '\0';
+ mpack_node_flag_error(node, mpack_error_too_big);
+ return;
+ }
+
+ if (!mpack_utf8_check_no_null(mpack_node_data_unchecked(node), node.data->len)) {
+ buffer[0] = '\0';
+ mpack_node_flag_error(node, mpack_error_type);
+ return;
+ }
+
+ mpack_memcpy(buffer, mpack_node_data_unchecked(node), node.data->len);
+ buffer[node.data->len] = '\0';
+}
+
+#ifdef MPACK_MALLOC
+char* mpack_node_data_alloc(mpack_node_t node, size_t maxlen) {
+ if (mpack_node_error(node) != mpack_ok)
+ return NULL;
+
+ // make sure this is a valid data type
+ mpack_type_t type = node.data->type;
+ if (type != mpack_type_str && type != mpack_type_bin
+ #if MPACK_EXTENSIONS
+ && type != mpack_type_ext
+ #endif
+ ) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+ }
+
+ if (node.data->len > maxlen) {
+ mpack_node_flag_error(node, mpack_error_too_big);
+ return NULL;
+ }
+
+ char* ret = (char*) MPACK_MALLOC((size_t)node.data->len);
+ if (ret == NULL) {
+ mpack_node_flag_error(node, mpack_error_memory);
+ return NULL;
+ }
+
+ mpack_memcpy(ret, mpack_node_data_unchecked(node), node.data->len);
+ return ret;
+}
+
+char* mpack_node_cstr_alloc(mpack_node_t node, size_t maxlen) {
+ if (mpack_node_error(node) != mpack_ok)
+ return NULL;
+
+ // make sure maxlen makes sense
+ if (maxlen < 1) {
+ mpack_break("maxlen is zero; you must have room for at least a null-terminator");
+ mpack_node_flag_error(node, mpack_error_bug);
+ return NULL;
+ }
+
+ if (node.data->type != mpack_type_str) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+ }
+
+ if (node.data->len > maxlen - 1) {
+ mpack_node_flag_error(node, mpack_error_too_big);
+ return NULL;
+ }
+
+ if (!mpack_str_check_no_null(mpack_node_data_unchecked(node), node.data->len)) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+ }
+
+ char* ret = (char*) MPACK_MALLOC((size_t)(node.data->len + 1));
+ if (ret == NULL) {
+ mpack_node_flag_error(node, mpack_error_memory);
+ return NULL;
+ }
+
+ mpack_memcpy(ret, mpack_node_data_unchecked(node), node.data->len);
+ ret[node.data->len] = '\0';
+ return ret;
+}
+
+char* mpack_node_utf8_cstr_alloc(mpack_node_t node, size_t maxlen) {
+ if (mpack_node_error(node) != mpack_ok)
+ return NULL;
+
+ // make sure maxlen makes sense
+ if (maxlen < 1) {
+ mpack_break("maxlen is zero; you must have room for at least a null-terminator");
+ mpack_node_flag_error(node, mpack_error_bug);
+ return NULL;
+ }
+
+ if (node.data->type != mpack_type_str) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+ }
+
+ if (node.data->len > maxlen - 1) {
+ mpack_node_flag_error(node, mpack_error_too_big);
+ return NULL;
+ }
+
+ if (!mpack_utf8_check_no_null(mpack_node_data_unchecked(node), node.data->len)) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+ }
+
+ char* ret = (char*) MPACK_MALLOC((size_t)(node.data->len + 1));
+ if (ret == NULL) {
+ mpack_node_flag_error(node, mpack_error_memory);
+ return NULL;
+ }
+
+ mpack_memcpy(ret, mpack_node_data_unchecked(node), node.data->len);
+ ret[node.data->len] = '\0';
+ return ret;
+}
+#endif
+
+
+/*
+ * Compound Node Functions
+ */
+
+static mpack_node_data_t* mpack_node_map_int_impl(mpack_node_t node, int64_t num) {
+ if (mpack_node_error(node) != mpack_ok)
+ return NULL;
+
+ if (node.data->type != mpack_type_map) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+ }
+
+ mpack_node_data_t* found = NULL;
+
+ for (size_t i = 0; i < node.data->len; ++i) {
+ mpack_node_data_t* key = mpack_node_child(node, i * 2);
+
+ if ((key->type == mpack_type_int && key->value.i == num) ||
+ (key->type == mpack_type_uint && num >= 0 && key->value.u == (uint64_t)num))
+ {
+ if (found) {
+ mpack_node_flag_error(node, mpack_error_data);
+ return NULL;
+ }
+ found = mpack_node_child(node, i * 2 + 1);
+ }
+ }
+
+ if (found)
+ return found;
+
+ return NULL;
+}
+
+static mpack_node_data_t* mpack_node_map_uint_impl(mpack_node_t node, uint64_t num) {
+ if (mpack_node_error(node) != mpack_ok)
+ return NULL;
+
+ if (node.data->type != mpack_type_map) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+ }
+
+ mpack_node_data_t* found = NULL;
+
+ for (size_t i = 0; i < node.data->len; ++i) {
+ mpack_node_data_t* key = mpack_node_child(node, i * 2);
+
+ if ((key->type == mpack_type_uint && key->value.u == num) ||
+ (key->type == mpack_type_int && key->value.i >= 0 && (uint64_t)key->value.i == num))
+ {
+ if (found) {
+ mpack_node_flag_error(node, mpack_error_data);
+ return NULL;
+ }
+ found = mpack_node_child(node, i * 2 + 1);
+ }
+ }
+
+ if (found)
+ return found;
+
+ return NULL;
+}
+
+static mpack_node_data_t* mpack_node_map_str_impl(mpack_node_t node, const char* str, size_t length) {
+ if (mpack_node_error(node) != mpack_ok)
+ return NULL;
+
+ mpack_assert(length == 0 || str != NULL, "str of length %i is NULL", (int)length);
+
+ if (node.data->type != mpack_type_map) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+ }
+
+ mpack_tree_t* tree = node.tree;
+ mpack_node_data_t* found = NULL;
+
+ for (size_t i = 0; i < node.data->len; ++i) {
+ mpack_node_data_t* key = mpack_node_child(node, i * 2);
+
+ if (key->type == mpack_type_str && key->len == length &&
+ mpack_memcmp(str, mpack_node_data_unchecked(mpack_node(tree, key)), length) == 0) {
+ if (found) {
+ mpack_node_flag_error(node, mpack_error_data);
+ return NULL;
+ }
+ found = mpack_node_child(node, i * 2 + 1);
+ }
+ }
+
+ if (found)
+ return found;
+
+ return NULL;
+}
+
+static mpack_node_t mpack_node_wrap_lookup(mpack_tree_t* tree, mpack_node_data_t* data) {
+ if (!data) {
+ if (tree->error == mpack_ok)
+ mpack_tree_flag_error(tree, mpack_error_data);
+ return mpack_tree_nil_node(tree);
+ }
+ return mpack_node(tree, data);
+}
+
+static mpack_node_t mpack_node_wrap_lookup_optional(mpack_tree_t* tree, mpack_node_data_t* data) {
+ if (!data) {
+ if (tree->error == mpack_ok)
+ return mpack_tree_missing_node(tree);
+ return mpack_tree_nil_node(tree);
+ }
+ return mpack_node(tree, data);
+}
+
+mpack_node_t mpack_node_map_int(mpack_node_t node, int64_t num) {
+ return mpack_node_wrap_lookup(node.tree, mpack_node_map_int_impl(node, num));
+}
+
+mpack_node_t mpack_node_map_int_optional(mpack_node_t node, int64_t num) {
+ return mpack_node_wrap_lookup_optional(node.tree, mpack_node_map_int_impl(node, num));
+}
+
+mpack_node_t mpack_node_map_uint(mpack_node_t node, uint64_t num) {
+ return mpack_node_wrap_lookup(node.tree, mpack_node_map_uint_impl(node, num));
+}
+
+mpack_node_t mpack_node_map_uint_optional(mpack_node_t node, uint64_t num) {
+ return mpack_node_wrap_lookup_optional(node.tree, mpack_node_map_uint_impl(node, num));
+}
+
+mpack_node_t mpack_node_map_str(mpack_node_t node, const char* str, size_t length) {
+ return mpack_node_wrap_lookup(node.tree, mpack_node_map_str_impl(node, str, length));
+}
+
+mpack_node_t mpack_node_map_str_optional(mpack_node_t node, const char* str, size_t length) {
+ return mpack_node_wrap_lookup_optional(node.tree, mpack_node_map_str_impl(node, str, length));
+}
+
+mpack_node_t mpack_node_map_cstr(mpack_node_t node, const char* cstr) {
+ mpack_assert(cstr != NULL, "cstr is NULL");
+ return mpack_node_map_str(node, cstr, mpack_strlen(cstr));
+}
+
+mpack_node_t mpack_node_map_cstr_optional(mpack_node_t node, const char* cstr) {
+ mpack_assert(cstr != NULL, "cstr is NULL");
+ return mpack_node_map_str_optional(node, cstr, mpack_strlen(cstr));
+}
+
+bool mpack_node_map_contains_int(mpack_node_t node, int64_t num) {
+ return mpack_node_map_int_impl(node, num) != NULL;
+}
+
+bool mpack_node_map_contains_uint(mpack_node_t node, uint64_t num) {
+ return mpack_node_map_uint_impl(node, num) != NULL;
+}
+
+bool mpack_node_map_contains_str(mpack_node_t node, const char* str, size_t length) {
+ return mpack_node_map_str_impl(node, str, length) != NULL;
+}
+
+bool mpack_node_map_contains_cstr(mpack_node_t node, const char* cstr) {
+ mpack_assert(cstr != NULL, "cstr is NULL");
+ return mpack_node_map_contains_str(node, cstr, mpack_strlen(cstr));
+}
+
+size_t mpack_node_enum_optional(mpack_node_t node, const char* strings[], size_t count) {
+ if (mpack_node_error(node) != mpack_ok)
+ return count;
+
+ // the value is only recognized if it is a string
+ if (mpack_node_type(node) != mpack_type_str)
+ return count;
+
+ // fetch the string
+ const char* key = mpack_node_str(node);
+ size_t keylen = mpack_node_strlen(node);
+ mpack_assert(mpack_node_error(node) == mpack_ok, "these should not fail");
+
+ // find what key it matches
+ for (size_t i = 0; i < count; ++i) {
+ const char* other = strings[i];
+ size_t otherlen = mpack_strlen(other);
+ if (keylen == otherlen && mpack_memcmp(key, other, keylen) == 0)
+ return i;
+ }
+
+ // no matches
+ return count;
+}
+
+size_t mpack_node_enum(mpack_node_t node, const char* strings[], size_t count) {
+ size_t value = mpack_node_enum_optional(node, strings, count);
+ if (value == count)
+ mpack_node_flag_error(node, mpack_error_type);
+ return value;
+}
+
+mpack_type_t mpack_node_type(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return mpack_type_nil;
+ return node.data->type;
+}
+
+bool mpack_node_is_nil(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok) {
+ // All nodes are treated as nil nodes when we are in error.
+ return true;
+ }
+ return node.data->type == mpack_type_nil;
+}
+
+bool mpack_node_is_missing(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok) {
+ // errors still return nil nodes, not missing nodes.
+ return false;
+ }
+ return node.data->type == mpack_type_missing;
+}
+
+void mpack_node_nil(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return;
+ if (node.data->type != mpack_type_nil)
+ mpack_node_flag_error(node, mpack_error_type);
+}
+
+void mpack_node_missing(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return;
+ if (node.data->type != mpack_type_missing)
+ mpack_node_flag_error(node, mpack_error_type);
+}
+
+bool mpack_node_bool(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return false;
+
+ if (node.data->type == mpack_type_bool)
+ return node.data->value.b;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return false;
+}
+
+void mpack_node_true(mpack_node_t node) {
+ if (mpack_node_bool(node) != true)
+ mpack_node_flag_error(node, mpack_error_type);
+}
+
+void mpack_node_false(mpack_node_t node) {
+ if (mpack_node_bool(node) != false)
+ mpack_node_flag_error(node, mpack_error_type);
+}
+
+uint8_t mpack_node_u8(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_uint) {
+ if (node.data->value.u <= UINT8_MAX)
+ return (uint8_t)node.data->value.u;
+ } else if (node.data->type == mpack_type_int) {
+ if (node.data->value.i >= 0 && node.data->value.i <= UINT8_MAX)
+ return (uint8_t)node.data->value.i;
+ }
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+int8_t mpack_node_i8(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_uint) {
+ if (node.data->value.u <= INT8_MAX)
+ return (int8_t)node.data->value.u;
+ } else if (node.data->type == mpack_type_int) {
+ if (node.data->value.i >= INT8_MIN && node.data->value.i <= INT8_MAX)
+ return (int8_t)node.data->value.i;
+ }
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+uint16_t mpack_node_u16(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_uint) {
+ if (node.data->value.u <= UINT16_MAX)
+ return (uint16_t)node.data->value.u;
+ } else if (node.data->type == mpack_type_int) {
+ if (node.data->value.i >= 0 && node.data->value.i <= UINT16_MAX)
+ return (uint16_t)node.data->value.i;
+ }
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+int16_t mpack_node_i16(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_uint) {
+ if (node.data->value.u <= INT16_MAX)
+ return (int16_t)node.data->value.u;
+ } else if (node.data->type == mpack_type_int) {
+ if (node.data->value.i >= INT16_MIN && node.data->value.i <= INT16_MAX)
+ return (int16_t)node.data->value.i;
+ }
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+uint32_t mpack_node_u32(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_uint) {
+ if (node.data->value.u <= UINT32_MAX)
+ return (uint32_t)node.data->value.u;
+ } else if (node.data->type == mpack_type_int) {
+ if (node.data->value.i >= 0 && node.data->value.i <= UINT32_MAX)
+ return (uint32_t)node.data->value.i;
+ }
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+int32_t mpack_node_i32(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_uint) {
+ if (node.data->value.u <= INT32_MAX)
+ return (int32_t)node.data->value.u;
+ } else if (node.data->type == mpack_type_int) {
+ if (node.data->value.i >= INT32_MIN && node.data->value.i <= INT32_MAX)
+ return (int32_t)node.data->value.i;
+ }
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+uint64_t mpack_node_u64(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_uint) {
+ return node.data->value.u;
+ } else if (node.data->type == mpack_type_int) {
+ if (node.data->value.i >= 0)
+ return (uint64_t)node.data->value.i;
+ }
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+int64_t mpack_node_i64(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_uint) {
+ if (node.data->value.u <= (uint64_t)INT64_MAX)
+ return (int64_t)node.data->value.u;
+ } else if (node.data->type == mpack_type_int) {
+ return node.data->value.i;
+ }
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+unsigned int mpack_node_uint(mpack_node_t node) {
+
+ // This should be true at compile-time, so this just wraps the 32-bit function.
+ if (sizeof(unsigned int) == 4)
+ return (unsigned int)mpack_node_u32(node);
+
+ // Otherwise we use u64 and check the range.
+ uint64_t val = mpack_node_u64(node);
+ if (val <= UINT_MAX)
+ return (unsigned int)val;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+int mpack_node_int(mpack_node_t node) {
+
+ // This should be true at compile-time, so this just wraps the 32-bit function.
+ if (sizeof(int) == 4)
+ return (int)mpack_node_i32(node);
+
+ // Otherwise we use i64 and check the range.
+ int64_t val = mpack_node_i64(node);
+ if (val >= INT_MIN && val <= INT_MAX)
+ return (int)val;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+float mpack_node_float(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0.0f;
+
+ if (node.data->type == mpack_type_uint)
+ return (float)node.data->value.u;
+ else if (node.data->type == mpack_type_int)
+ return (float)node.data->value.i;
+ else if (node.data->type == mpack_type_float)
+ return node.data->value.f;
+ else if (node.data->type == mpack_type_double)
+ return (float)node.data->value.d;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0.0f;
+}
+
+double mpack_node_double(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0.0;
+
+ if (node.data->type == mpack_type_uint)
+ return (double)node.data->value.u;
+ else if (node.data->type == mpack_type_int)
+ return (double)node.data->value.i;
+ else if (node.data->type == mpack_type_float)
+ return (double)node.data->value.f;
+ else if (node.data->type == mpack_type_double)
+ return node.data->value.d;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0.0;
+}
+
+float mpack_node_float_strict(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0.0f;
+
+ if (node.data->type == mpack_type_float)
+ return node.data->value.f;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0.0f;
+}
+
+double mpack_node_double_strict(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0.0;
+
+ if (node.data->type == mpack_type_float)
+ return (double)node.data->value.f;
+ else if (node.data->type == mpack_type_double)
+ return node.data->value.d;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0.0;
+}
+
+#if MPACK_EXTENSIONS
+int8_t mpack_node_exttype(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_ext)
+ return mpack_node_exttype_unchecked(node);
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+#endif
+
+uint32_t mpack_node_data_len(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ mpack_type_t type = node.data->type;
+ if (type == mpack_type_str || type == mpack_type_bin
+ #if MPACK_EXTENSIONS
+ || type == mpack_type_ext
+ #endif
+ )
+ return (uint32_t)node.data->len;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+size_t mpack_node_strlen(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_str)
+ return (size_t)node.data->len;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+const char* mpack_node_str(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return NULL;
+
+ mpack_type_t type = node.data->type;
+ if (type == mpack_type_str)
+ return mpack_node_data_unchecked(node);
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+}
+
+const char* mpack_node_data(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return NULL;
+
+ mpack_type_t type = node.data->type;
+ if (type == mpack_type_str || type == mpack_type_bin
+ #if MPACK_EXTENSIONS
+ || type == mpack_type_ext
+ #endif
+ )
+ return mpack_node_data_unchecked(node);
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+}
+
+const char* mpack_node_bin_data(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return NULL;
+
+ if (node.data->type == mpack_type_bin)
+ return mpack_node_data_unchecked(node);
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+}
+
+size_t mpack_node_bin_size(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_bin)
+ return (size_t)node.data->len;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+size_t mpack_node_array_length(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type != mpack_type_array) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+ }
+
+ return (size_t)node.data->len;
+}
+
+mpack_node_t mpack_node_array_at(mpack_node_t node, size_t index) {
+ if (mpack_node_error(node) != mpack_ok)
+ return mpack_tree_nil_node(node.tree);
+
+ if (node.data->type != mpack_type_array) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return mpack_tree_nil_node(node.tree);
+ }
+
+ if (index >= node.data->len) {
+ mpack_node_flag_error(node, mpack_error_data);
+ return mpack_tree_nil_node(node.tree);
+ }
+
+ return mpack_node(node.tree, mpack_node_child(node, index));
+}
+
+size_t mpack_node_map_count(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type != mpack_type_map) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+ }
+
+ return node.data->len;
+}
+
+// internal node map lookup
+static mpack_node_t mpack_node_map_at(mpack_node_t node, size_t index, size_t offset) {
+ if (mpack_node_error(node) != mpack_ok)
+ return mpack_tree_nil_node(node.tree);
+
+ if (node.data->type != mpack_type_map) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return mpack_tree_nil_node(node.tree);
+ }
+
+ if (index >= node.data->len) {
+ mpack_node_flag_error(node, mpack_error_data);
+ return mpack_tree_nil_node(node.tree);
+ }
+
+ return mpack_node(node.tree, mpack_node_child(node, index * 2 + offset));
+}
+
+mpack_node_t mpack_node_map_key_at(mpack_node_t node, size_t index) {
+ return mpack_node_map_at(node, index, 0);
+}
+
+mpack_node_t mpack_node_map_value_at(mpack_node_t node, size_t index) {
+ return mpack_node_map_at(node, index, 1);
+}
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/lib/mpack/src/mpack/mpack.h b/src/fluent-bit/lib/cmetrics/lib/mpack/src/mpack/mpack.h
new file mode 100644
index 000000000..7c0b3f17e
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/lib/mpack/src/mpack/mpack.h
@@ -0,0 +1,7203 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015-2018 Nicholas Fraser
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+/*
+ * This is the MPack 1.0 amalgamation package.
+ *
+ * http://github.com/ludocode/mpack
+ */
+
+#ifndef MPACK_H
+#define MPACK_H 1
+
+#define MPACK_AMALGAMATED 1
+
+#if defined(MPACK_HAS_CONFIG) && MPACK_HAS_CONFIG
+#include "mpack-config.h"
+#endif
+
+
+/* mpack/mpack-defaults.h.h */
+
+
+/**
+ * @name Features
+ * @{
+ */
+
+/**
+ * @def MPACK_READER
+ *
+ * Enables compilation of the base Tag Reader.
+ */
+#ifndef MPACK_READER
+#define MPACK_READER 1
+#endif
+
+/**
+ * @def MPACK_EXPECT
+ *
+ * Enables compilation of the static Expect API.
+ */
+#ifndef MPACK_EXPECT
+#define MPACK_EXPECT 1
+#endif
+
+/**
+ * @def MPACK_NODE
+ *
+ * Enables compilation of the dynamic Node API.
+ */
+#ifndef MPACK_NODE
+#define MPACK_NODE 1
+#endif
+
+/**
+ * @def MPACK_WRITER
+ *
+ * Enables compilation of the Writer.
+ */
+#ifndef MPACK_WRITER
+#define MPACK_WRITER 1
+#endif
+
+/**
+ * @def MPACK_COMPATIBILITY
+ *
+ * Enables compatibility features for reading and writing older
+ * versions of MessagePack.
+ *
+ * This is disabled by default. When disabled, the behaviour is equivalent to
+ * using the default version, @ref mpack_version_current.
+ *
+ * Enable this if you need to interoperate with applications or data that do
+ * not support the new (v5) MessagePack spec. See the section on v4
+ * compatibility in @ref docs/protocol.md for more information.
+ */
+#ifndef MPACK_COMPATIBILITY
+#define MPACK_COMPATIBILITY 0
+#endif
+
+/**
+ * @def MPACK_EXTENSIONS
+ *
+ * Enables the use of extension types.
+ *
+ * This is disabled by default. Define it to 1 to enable it. If disabled,
+ * functions to read and write extensions will not exist, and any occurrence of
+ * extension types in parsed messages will flag @ref mpack_error_invalid.
+ *
+ * MPack discourages the use of extension types. See the section on extension
+ * types in @ref docs/protocol.md for more information.
+ */
+#ifndef MPACK_EXTENSIONS
+#define MPACK_EXTENSIONS 0
+#endif
+
+
+/**
+ * @}
+ */
+
+
+/**
+ * @name Dependencies
+ * @{
+ */
+
+/**
+ * @def MPACK_HAS_CONFIG
+ *
+ * Enables the use of an @c mpack-config.h configuration file for MPack.
+ * This file must be in the same folder as @c mpack.h, or it must be
+ * available from your project's include paths.
+ */
+// This goes in your project settings.
+
+/**
+ * @def MPACK_STDLIB
+ *
+ * Enables the use of C stdlib. This allows the library to use malloc
+ * for debugging and in allocation helpers.
+ */
+#ifndef MPACK_STDLIB
+#define MPACK_STDLIB 1
+#endif
+
+/**
+ * @def MPACK_STDIO
+ *
+ * Enables the use of C stdio. This adds helpers for easily
+ * reading/writing C files and makes debugging easier.
+ */
+#ifndef MPACK_STDIO
+#define MPACK_STDIO 1
+#endif
+
+/**
+ * @}
+ */
+
+
+/**
+ * @name System Functions
+ * @{
+ */
+
+/**
+ * @def MPACK_MALLOC
+ *
+ * Defines the memory allocation function used by MPack. This is used by
+ * helpers for automatically allocating data the correct size, and for
+ * debugging functions. If this macro is undefined, the allocation helpers
+ * will not be compiled.
+ *
+ * The default is @c malloc() if @ref MPACK_STDLIB is enabled.
+ */
+/**
+ * @def MPACK_FREE
+ *
+ * Defines the memory free function used by MPack. This is used by helpers
+ * for automatically allocating data the correct size. If this macro is
+ * undefined, the allocation helpers will not be compiled.
+ *
+ * The default is @c free() if @ref MPACK_MALLOC has not been customized and
+ * @ref MPACK_STDLIB is enabled.
+ */
+/**
+ * @def MPACK_REALLOC
+ *
+ * Defines the realloc function used by MPack. It is used by growable
+ * buffers to resize more efficiently.
+ *
+ * The default is @c realloc() if @ref MPACK_MALLOC has not been customized and
+ * @ref MPACK_STDLIB is enabled.
+ *
+ * This is optional, even when @ref MPACK_MALLOC is used. If @ref MPACK_MALLOC is
+ * set and @ref MPACK_REALLOC is not, @ref MPACK_MALLOC is used with a simple copy
+ * to grow buffers.
+ */
+#if defined(MPACK_STDLIB) && MPACK_STDLIB && !defined(MPACK_MALLOC)
+#define MPACK_MALLOC malloc
+#define MPACK_REALLOC realloc
+#define MPACK_FREE free
+#endif
+
+/**
+ * @}
+ */
+
+
+/**
+ * @name Debugging Options
+ */
+
+/**
+ * @def MPACK_DEBUG
+ *
+ * Enables debug features. You may want to wrap this around your
+ * own debug preprocs. By default, this is enabled if @c DEBUG or @c _DEBUG
+ * are defined. (@c NDEBUG is not used since it is allowed to have
+ * different values in different translation units.)
+ */
+#if !defined(MPACK_DEBUG) && (defined(DEBUG) || defined(_DEBUG))
+#define MPACK_DEBUG 1
+#endif
+
+/**
+ * @def MPACK_STRINGS
+ *
+ * Enables descriptive error and type strings.
+ *
+ * This can be turned off (by defining it to 0) to maximize space savings
+ * on embedded devices. If this is disabled, string functions such as
+ * mpack_error_to_string() and mpack_type_to_string() return an empty string.
+ */
+#ifndef MPACK_STRINGS
+#define MPACK_STRINGS 1
+#endif
+
+/**
+ * Set this to 1 to implement a custom @ref mpack_assert_fail() function.
+ * See the documentation on @ref mpack_assert_fail() for details.
+ *
+ * Asserts are only used when @ref MPACK_DEBUG is enabled, and can be
+ * triggered by bugs in MPack or bugs due to incorrect usage of MPack.
+ */
+#ifndef MPACK_CUSTOM_ASSERT
+#define MPACK_CUSTOM_ASSERT 0
+#endif
+
+/**
+ * @def MPACK_READ_TRACKING
+ *
+ * Enables compound type size tracking for readers. This ensures that the
+ * correct number of elements or bytes are read from a compound type.
+ *
+ * This is enabled by default in debug builds (provided a @c malloc() is
+ * available.)
+ */
+#if !defined(MPACK_READ_TRACKING) && \
+ defined(MPACK_DEBUG) && MPACK_DEBUG && \
+ defined(MPACK_READER) && MPACK_READER && \
+ defined(MPACK_MALLOC)
+#define MPACK_READ_TRACKING 1
+#endif
+
+/**
+ * @def MPACK_WRITE_TRACKING
+ *
+ * Enables compound type size tracking for writers. This ensures that the
+ * correct number of elements or bytes are written in a compound type.
+ *
+ * Note that without write tracking enabled, it is possible for buggy code
+ * to emit invalid MessagePack without flagging an error by writing the wrong
+ * number of elements or bytes in a compound type. With tracking enabled,
+ * MPack will catch such errors and break on the offending line of code.
+ *
+ * This is enabled by default in debug builds (provided a @c malloc() is
+ * available.)
+ */
+#if !defined(MPACK_WRITE_TRACKING) && \
+ defined(MPACK_DEBUG) && MPACK_DEBUG && \
+ defined(MPACK_WRITER) && MPACK_WRITER && \
+ defined(MPACK_MALLOC)
+#define MPACK_WRITE_TRACKING 1
+#endif
+
+/**
+ * @}
+ */
+
+
+/**
+ * @name Miscellaneous Options
+ * @{
+ */
+
+/**
+ * Whether to optimize for size or speed.
+ *
+ * Optimizing for size simplifies some parsing and encoding algorithms
+ * at the expense of speed, and saves a few kilobytes of space in the
+ * resulting executable.
+ *
+ * This automatically detects -Os with GCC/Clang. Unfortunately there
+ * doesn't seem to be a macro defined for /Os under MSVC.
+ */
+#ifndef MPACK_OPTIMIZE_FOR_SIZE
+#ifdef __OPTIMIZE_SIZE__
+#define MPACK_OPTIMIZE_FOR_SIZE 1
+#else
+#define MPACK_OPTIMIZE_FOR_SIZE 0
+#endif
+#endif
+
+/**
+ * Stack space in bytes to use when initializing a reader or writer
+ * with a stack-allocated buffer.
+ */
+#ifndef MPACK_STACK_SIZE
+#define MPACK_STACK_SIZE 4096
+#endif
+
+/**
+ * Buffer size to use for allocated buffers (such as for a file writer.)
+ *
+ * Starting with a single page and growing as needed seems to
+ * provide the best performance with minimal memory waste.
+ * Increasing this does not improve performance even when writing
+ * huge messages.
+ */
+#ifndef MPACK_BUFFER_SIZE
+#define MPACK_BUFFER_SIZE 4096
+#endif
+
+/**
+ * Minimum size of an allocated node page in bytes.
+ *
+ * The children for a given compound element must be contiguous, so
+ * larger pages than this may be allocated as needed. (Safety checks
+ * exist to prevent malicious data from causing too large allocations.)
+ *
+ * See @ref mpack_node_data_t for the size of nodes.
+ *
+ * Using as many nodes fit in one memory page seems to provide the
+ * best performance, and has very little waste when parsing small
+ * messages.
+ */
+#ifndef MPACK_NODE_PAGE_SIZE
+#define MPACK_NODE_PAGE_SIZE 4096
+#endif
+
+/**
+ * The initial depth for the node parser. When MPACK_MALLOC is available,
+ * the node parser has no practical depth limit, and it is not recursive
+ * so there is no risk of overflowing the call stack.
+ */
+#ifndef MPACK_NODE_INITIAL_DEPTH
+#define MPACK_NODE_INITIAL_DEPTH 8
+#endif
+
+/**
+ * The maximum depth for the node parser if @ref MPACK_MALLOC is not available.
+ */
+#ifndef MPACK_NODE_MAX_DEPTH_WITHOUT_MALLOC
+#define MPACK_NODE_MAX_DEPTH_WITHOUT_MALLOC 32
+#endif
+
+/**
+ * @}
+ */
+
+
+/**
+ * @}
+ */
+
+
+/* mpack/mpack-platform.h.h */
+
+/**
+ * @file
+ *
+ * Abstracts all platform-specific code from MPack. This contains
+ * implementations of standard C functions when libc is not available,
+ * as well as wrappers to library functions.
+ */
+
+#ifndef MPACK_PLATFORM_H
+#define MPACK_PLATFORM_H 1
+
+
+
+/* Pre-include checks */
+
+#if defined(_MSC_VER) && _MSC_VER < 1800 && !defined(__cplusplus)
+#error "In Visual Studio 2012 and earlier, MPack must be compiled as C++. Enable the /Tp compiler flag."
+#endif
+
+#if defined(WIN32) && defined(MPACK_INTERNAL) && MPACK_INTERNAL
+#define _CRT_SECURE_NO_WARNINGS 1
+#endif
+
+
+
+/* Doxygen preprocs */
+#if defined(MPACK_DOXYGEN) && MPACK_DOXYGEN
+#define MPACK_HAS_CONFIG 0
+// We give these their default values of 0 here even though they are defined to
+// 1 in the doxyfile. Doxygen will show this as the value in the docs, even
+// though it ignores it when parsing the rest of the source. This is what we
+// want, since we want the documentation to show these defaults but still
+// generate documentation for the functions they add when they're on.
+#define MPACK_COMPATIBILITY 0
+#define MPACK_EXTENSIONS 0
+#endif
+
+
+
+/* Include the custom config file if enabled */
+
+#if defined(MPACK_HAS_CONFIG) && MPACK_HAS_CONFIG
+/* #include "mpack-config.h" */
+#endif
+
+/*
+ * Now that the optional config is included, we define the defaults
+ * for any of the configuration options and other switches that aren't
+ * yet defined.
+ */
+#if defined(MPACK_DOXYGEN) && MPACK_DOXYGEN
+/* #include "mpack-defaults-doxygen.h" */
+#else
+/* #include "mpack-defaults.h" */
+#endif
+
+/*
+ * All remaining configuration options that have not yet been set must
+ * be defined here in order to support -Wundef.
+ */
+#ifndef MPACK_DEBUG
+#define MPACK_DEBUG 0
+#endif
+#ifndef MPACK_CUSTOM_BREAK
+#define MPACK_CUSTOM_BREAK 0
+#endif
+#ifndef MPACK_READ_TRACKING
+#define MPACK_READ_TRACKING 0
+#endif
+#ifndef MPACK_WRITE_TRACKING
+#define MPACK_WRITE_TRACKING 0
+#endif
+#ifndef MPACK_EMIT_INLINE_DEFS
+#define MPACK_EMIT_INLINE_DEFS 0
+#endif
+#ifndef MPACK_AMALGAMATED
+#define MPACK_AMALGAMATED 0
+#endif
+#ifndef MPACK_RELEASE_VERSION
+#define MPACK_RELEASE_VERSION 0
+#endif
+#ifndef MPACK_INTERNAL
+#define MPACK_INTERNAL 0
+#endif
+#ifndef MPACK_NO_BUILTINS
+#define MPACK_NO_BUILTINS 0
+#endif
+
+
+
+/* System headers (based on configuration) */
+
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+#ifndef __STDC_FORMAT_MACROS
+#define __STDC_FORMAT_MACROS 1
+#endif
+#ifndef __STDC_CONSTANT_MACROS
+#define __STDC_CONSTANT_MACROS 1
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <limits.h>
+
+#if MPACK_STDLIB
+#include <string.h>
+#include <stdlib.h>
+#endif
+
+#if MPACK_STDIO
+#include <stdio.h>
+#include <errno.h>
+#endif
+
+
+
+/*
+ * Header configuration
+ */
+
+#ifdef __cplusplus
+ #define MPACK_EXTERN_C_START extern "C" {
+ #define MPACK_EXTERN_C_END }
+#else
+ #define MPACK_EXTERN_C_START /* nothing */
+ #define MPACK_EXTERN_C_END /* nothing */
+#endif
+
+/* We can't push/pop diagnostics before GCC 4.6, so if you're on a really old
+ * compiler, MPack does not support the below warning flags. You will have to
+ * manually disable them to use MPack. */
+
+/* GCC versions before 5.1 warn about defining a C99 non-static inline function
+ * before declaring it (see issue #20). Diagnostic push is not supported before
+ * GCC 4.6. */
+#if defined(__GNUC__) && !defined(__clang__)
+ #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || (__GNUC__ == 5 && __GNUC_MINOR__ < 1)
+ #ifdef __cplusplus
+ #define MPACK_DECLARED_INLINE_WARNING_START \
+ _Pragma ("GCC diagnostic push") \
+ _Pragma ("GCC diagnostic ignored \"-Wmissing-declarations\"")
+ #else
+ #define MPACK_DECLARED_INLINE_WARNING_START \
+ _Pragma ("GCC diagnostic push") \
+ _Pragma ("GCC diagnostic ignored \"-Wmissing-prototypes\"")
+ #endif
+ #define MPACK_DECLARED_INLINE_WARNING_END \
+ _Pragma ("GCC diagnostic pop")
+ #endif
+#endif
+#ifndef MPACK_DECLARED_INLINE_WARNING_START
+ #define MPACK_DECLARED_INLINE_WARNING_START /* nothing */
+ #define MPACK_DECLARED_INLINE_WARNING_END /* nothing */
+#endif
+
+/* GCC versions before 4.8 warn about shadowing a function with a variable that
+ * isn't a function or function pointer (like "index"). Diagnostic push is not
+ * supported before GCC 4.6. */
+#if defined(__GNUC__) && !defined(__clang__)
+ #if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && __GNUC_MINOR__ < 8
+ #define MPACK_WSHADOW_WARNING_START \
+ _Pragma ("GCC diagnostic push") \
+ _Pragma ("GCC diagnostic ignored \"-Wshadow\"")
+ #define MPACK_WSHADOW_WARNING_END \
+ _Pragma ("GCC diagnostic pop")
+ #endif
+#endif
+#ifndef MPACK_WSHADOW_WARNING_START
+ #define MPACK_WSHADOW_WARNING_START /* nothing */
+ #define MPACK_WSHADOW_WARNING_END /* nothing */
+#endif
+
+#define MPACK_HEADER_START \
+ MPACK_WSHADOW_WARNING_START \
+ MPACK_DECLARED_INLINE_WARNING_START
+
+#define MPACK_HEADER_END \
+ MPACK_DECLARED_INLINE_WARNING_END \
+ MPACK_WSHADOW_WARNING_END
+
+MPACK_HEADER_START
+MPACK_EXTERN_C_START
+
+
+
+/* Miscellaneous helper macros */
+
+#define MPACK_UNUSED(var) ((void)(var))
+
+#define MPACK_STRINGIFY_IMPL(arg) #arg
+#define MPACK_STRINGIFY(arg) MPACK_STRINGIFY_IMPL(arg)
+
+// This is a workaround for MSVC's incorrect expansion of __VA_ARGS__. It
+// treats __VA_ARGS__ as a single preprocessor token when passed in the
+// argument list of another macro unless we use an outer wrapper to expand it
+// lexically first. (For safety/consistency we use this in all variadic macros
+// that don't ignore the variadic arguments regardless of whether __VA_ARGS__
+// is passed to another macro.)
+// https://stackoverflow.com/a/32400131
+#define MPACK_EXPAND(x) x
+
+// Extracts the first argument of a variadic macro list, where there might only
+// be one argument.
+#define MPACK_EXTRACT_ARG0_IMPL(first, ...) first
+#define MPACK_EXTRACT_ARG0(...) MPACK_EXPAND(MPACK_EXTRACT_ARG0_IMPL( __VA_ARGS__ , ignored))
+
+// Stringifies the first argument of a variadic macro list, where there might
+// only be one argument.
+#define MPACK_STRINGIFY_ARG0_impl(first, ...) #first
+#define MPACK_STRINGIFY_ARG0(...) MPACK_EXPAND(MPACK_STRINGIFY_ARG0_impl( __VA_ARGS__ , ignored))
+
+
+
+/*
+ * Definition of inline macros.
+ *
+ * MPack does not use static inline in header files; only one non-inline definition
+ * of each function should exist in the final build. This can reduce the binary size
+ * in cases where the compiler cannot or chooses not to inline a function.
+ * The addresses of functions should also compare equal across translation units
+ * regardless of whether they are declared inline.
+ *
+ * The above requirements mean that the declaration and definition of non-trivial
+ * inline functions must be separated so that the definitions will only
+ * appear when necessary. In addition, three different linkage models need
+ * to be supported:
+ *
+ * - The C99 model, where a standalone function is emitted only if there is any
+ * `extern inline` or non-`inline` declaration (including the definition itself)
+ *
+ * - The GNU model, where an `inline` definition emits a standalone function and an
+ * `extern inline` definition does not, regardless of other declarations
+ *
+ * - The C++ model, where `inline` emits a standalone function with special
+ * (COMDAT) linkage
+ *
+ * The macros below wrap up everything above. All inline functions defined in header
+ * files have a single non-inline definition emitted in the compilation of
+ * mpack-platform.c. All inline declarations and definitions use the same MPACK_INLINE
+ * specification to simplify the rules on when standalone functions are emitted.
+ * Inline functions in source files are defined MPACK_STATIC_INLINE.
+ *
+ * Additional reading:
+ * http://www.greenend.org.uk/rjk/tech/inline.html
+ */
+
+#if defined(__cplusplus)
+ // C++ rules
+ // The linker will need COMDAT support to link C++ object files,
+ // so we don't need to worry about emitting definitions from C++
+ // translation units. If mpack-platform.c (or the amalgamation)
+ // is compiled as C, its definition will be used, otherwise a
+ // C++ definition will be used, and no other C files will emit
+ // a definition.
+ #define MPACK_INLINE inline
+
+#elif defined(_MSC_VER)
+ // MSVC 2013 always uses COMDAT linkage, but it doesn't treat 'inline' as a
+ // keyword in C99 mode. (This appears to be fixed in a later version of
+ // MSVC but we don't bother detecting it.)
+ #define MPACK_INLINE __inline
+ #define MPACK_STATIC_INLINE static __inline
+
+#elif defined(__GNUC__) && (defined(__GNUC_GNU_INLINE__) || \
+ (!defined(__GNUC_STDC_INLINE__) && !defined(__GNUC_GNU_INLINE__)))
+ // GNU rules
+ #if MPACK_EMIT_INLINE_DEFS
+ #define MPACK_INLINE inline
+ #else
+ #define MPACK_INLINE extern inline
+ #endif
+
+#elif defined(__TINYC__)
+ // tcc ignores the inline keyword, so we have to use static inline. We
+ // issue a warning to make sure you are aware. You can define the below
+ // macro to disable the warning. Hopefully this will be fixed soon:
+ // https://lists.nongnu.org/archive/html/tinycc-devel/2019-06/msg00000.html
+ #ifndef MPACK_DISABLE_TINYC_INLINE_WARNING
+ #warning "Single-definition inline is not supported by tcc. All inlines will be static. Define MPACK_DISABLE_TINYC_INLINE_WARNING to disable this warning."
+ #endif
+ #define MPACK_INLINE static inline
+
+#else
+ // C99 rules
+ #if MPACK_EMIT_INLINE_DEFS
+ #define MPACK_INLINE extern inline
+ #else
+ #define MPACK_INLINE inline
+ #endif
+#endif
+
+#ifndef MPACK_STATIC_INLINE
+#define MPACK_STATIC_INLINE static inline
+#endif
+
+#ifdef MPACK_OPTIMIZE_FOR_SPEED
+ #error "You should define MPACK_OPTIMIZE_FOR_SIZE, not MPACK_OPTIMIZE_FOR_SPEED."
+#endif
+
+
+
+/*
+ * Prevent inlining
+ *
+ * When a function is only used once, it is almost always inlined
+ * automatically. This can cause poor instruction cache usage because a
+ * function that should rarely be called (such as buffer exhaustion handling)
+ * will get inlined into the middle of a hot code path.
+ */
+
+#if !MPACK_NO_BUILTINS
+ #if defined(_MSC_VER)
+ #define MPACK_NOINLINE __declspec(noinline)
+ #elif defined(__GNUC__) || defined(__clang__)
+ #define MPACK_NOINLINE __attribute__((noinline))
+ #endif
+#endif
+#ifndef MPACK_NOINLINE
+ #define MPACK_NOINLINE /* nothing */
+#endif
+
+
+
+/* Some compiler-specific keywords and builtins */
+
+#if !MPACK_NO_BUILTINS
+ #if defined(__GNUC__) || defined(__clang__)
+ #define MPACK_UNREACHABLE __builtin_unreachable()
+ #define MPACK_NORETURN(fn) fn __attribute__((noreturn))
+ #define MPACK_RESTRICT __restrict__
+ #elif defined(_MSC_VER)
+ #define MPACK_UNREACHABLE __assume(0)
+ #define MPACK_NORETURN(fn) __declspec(noreturn) fn
+ #define MPACK_RESTRICT __restrict
+ #endif
+#endif
+
+#ifndef MPACK_RESTRICT
+#ifdef __cplusplus
+#define MPACK_RESTRICT /* nothing, unavailable in C++ */
+#else
+#define MPACK_RESTRICT restrict /* required in C99 */
+#endif
+#endif
+
+#ifndef MPACK_UNREACHABLE
+#define MPACK_UNREACHABLE ((void)0)
+#endif
+#ifndef MPACK_NORETURN
+#define MPACK_NORETURN(fn) fn
+#endif
+
+
+
+/*
+ * Likely/unlikely
+ *
+ * These should only really be used when a branch is taken (or not taken) less
+ * than 1/1000th of the time. Buffer flush checks when writing very small
+ * elements are a good example.
+ */
+
+#if !MPACK_NO_BUILTINS
+ #if defined(__GNUC__) || defined(__clang__)
+ #ifndef MPACK_LIKELY
+ #define MPACK_LIKELY(x) __builtin_expect((x),1)
+ #endif
+ #ifndef MPACK_UNLIKELY
+ #define MPACK_UNLIKELY(x) __builtin_expect((x),0)
+ #endif
+ #endif
+#endif
+
+#ifndef MPACK_LIKELY
+ #define MPACK_LIKELY(x) (x)
+#endif
+#ifndef MPACK_UNLIKELY
+ #define MPACK_UNLIKELY(x) (x)
+#endif
+
+
+
+/* Static assert */
+
+#ifndef MPACK_STATIC_ASSERT
+ #if defined(__cplusplus)
+ #if __cplusplus >= 201103L
+ #define MPACK_STATIC_ASSERT static_assert
+ #endif
+ #elif defined(__STDC_VERSION__)
+ #if __STDC_VERSION__ >= 201112L
+ #define MPACK_STATIC_ASSERT _Static_assert
+ #endif
+ #endif
+#endif
+
+#if !MPACK_NO_BUILTINS
+ #ifndef MPACK_STATIC_ASSERT
+ #if defined(__has_feature)
+ #if __has_feature(cxx_static_assert)
+ #define MPACK_STATIC_ASSERT static_assert
+ #elif __has_feature(c_static_assert)
+ #define MPACK_STATIC_ASSERT _Static_assert
+ #endif
+ #endif
+ #endif
+
+ #ifndef MPACK_STATIC_ASSERT
+ #if defined(__GNUC__)
+ /* Diagnostic push is not supported before GCC 4.6. */
+ #if defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+ #ifndef __cplusplus
+ #if defined(__clang__) || __GNUC__ >= 5
+ #define MPACK_IGNORE_PEDANTIC "GCC diagnostic ignored \"-Wpedantic\""
+ #else
+ #define MPACK_IGNORE_PEDANTIC "GCC diagnostic ignored \"-pedantic\""
+ #endif
+ #define MPACK_STATIC_ASSERT(expr, str) do { \
+ _Pragma ("GCC diagnostic push") \
+ _Pragma (MPACK_IGNORE_PEDANTIC) \
+ _Pragma ("GCC diagnostic ignored \"-Wc++-compat\"") \
+ _Static_assert(expr, str); \
+ _Pragma ("GCC diagnostic pop") \
+ } while (0)
+ #endif
+ #endif
+ #endif
+ #endif
+
+ #ifndef MPACK_STATIC_ASSERT
+ #ifdef _MSC_VER
+ #if _MSC_VER >= 1600
+ #define MPACK_STATIC_ASSERT static_assert
+ #endif
+ #endif
+ #endif
+#endif
+
+#ifndef MPACK_STATIC_ASSERT
+ #define MPACK_STATIC_ASSERT(expr, str) (MPACK_UNUSED(sizeof(char[1 - 2*!(expr)])))
+#endif
+
+
+
+/* _Generic */
+
+#ifndef MPACK_HAS_GENERIC
+ #if defined(__clang__) && defined(__has_feature)
+ // With Clang we can test for _Generic support directly
+ // and ignore C/C++ version
+ #if __has_feature(c_generic_selections)
+ #define MPACK_HAS_GENERIC 1
+ #else
+ #define MPACK_HAS_GENERIC 0
+ #endif
+ #endif
+#endif
+
+#ifndef MPACK_HAS_GENERIC
+ #if defined(__STDC_VERSION__)
+ #if __STDC_VERSION__ >= 201112L
+ #if defined(__GNUC__) && !defined(__clang__)
+ // GCC does not have full C11 support in GCC 4.7 and 4.8
+ #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)
+ #define MPACK_HAS_GENERIC 1
+ #endif
+ #else
+ // We hope other compilers aren't lying about C11/_Generic support
+ #define MPACK_HAS_GENERIC 1
+ #endif
+ #endif
+ #endif
+#endif
+
+#ifndef MPACK_HAS_GENERIC
+ #define MPACK_HAS_GENERIC 0
+#endif
+
+
+
+/*
+ * Finite Math
+ *
+ * -ffinite-math-only, included in -ffast-math, breaks functions that
+ * that check for non-finite real values such as isnan() and isinf().
+ *
+ * We should use this to trap errors when reading data that contains
+ * non-finite reals. This isn't currently implemented.
+ */
+
+#ifndef MPACK_FINITE_MATH
+#if defined(__FINITE_MATH_ONLY__) && __FINITE_MATH_ONLY__
+#define MPACK_FINITE_MATH 1
+#endif
+#endif
+
+#ifndef MPACK_FINITE_MATH
+#define MPACK_FINITE_MATH 0
+#endif
+
+
+
+/*
+ * Endianness checks
+ *
+ * These define MPACK_NHSWAP*() which swap network<->host byte
+ * order when needed.
+ *
+ * We leave them undefined if we can't determine the endianness
+ * at compile-time, in which case we fall back to bit-shifts.
+ *
+ * See the notes in mpack-common.h.
+ */
+
+#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__)
+ #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ #define MPACK_NHSWAP16(x) (x)
+ #define MPACK_NHSWAP32(x) (x)
+ #define MPACK_NHSWAP64(x) (x)
+ #elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+
+ #if !MPACK_NO_BUILTINS
+ #if defined(__clang__)
+ #ifdef __has_builtin
+ // Unlike the GCC builtins, the bswap builtins in Clang
+ // significantly improve ARM performance.
+ #if __has_builtin(__builtin_bswap16)
+ #define MPACK_NHSWAP16(x) __builtin_bswap16(x)
+ #endif
+ #if __has_builtin(__builtin_bswap32)
+ #define MPACK_NHSWAP32(x) __builtin_bswap32(x)
+ #endif
+ #if __has_builtin(__builtin_bswap64)
+ #define MPACK_NHSWAP64(x) __builtin_bswap64(x)
+ #endif
+ #endif
+
+ #elif defined(__GNUC__)
+
+ // The GCC bswap builtins are apparently poorly optimized on older
+ // versions of GCC, so we set a minimum version here just in case.
+ // http://hardwarebug.org/2010/01/14/beware-the-builtins/
+
+ #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
+ #define MPACK_NHSWAP64(x) __builtin_bswap64(x)
+ #endif
+
+ // __builtin_bswap16() was not implemented on all platforms
+ // until GCC 4.8.0:
+ // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52624
+ //
+ // The 16- and 32-bit versions in GCC significantly reduce performance
+ // on ARM with little effect on code size so we don't use them.
+
+ #endif
+ #endif
+ #endif
+
+#elif defined(_MSC_VER) && defined(_WIN32) && !MPACK_NO_BUILTINS
+
+ // On Windows, we assume x86 and x86_64 are always little-endian.
+ // We make no assumptions about ARM even though all current
+ // Windows Phone devices are little-endian in case Microsoft's
+ // compiler is ever used with a big-endian ARM device.
+
+ #if defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64)
+ #define MPACK_NHSWAP16(x) _byteswap_ushort(x)
+ #define MPACK_NHSWAP32(x) _byteswap_ulong(x)
+ #define MPACK_NHSWAP64(x) _byteswap_uint64(x)
+ #endif
+
+#endif
+
+#if defined(__FLOAT_WORD_ORDER__) && defined(__BYTE_ORDER__)
+
+ // We check where possible that the float byte order matches the
+ // integer byte order. This is extremely unlikely to fail, but
+ // we check anyway just in case.
+ //
+ // (The static assert is placed in float/double encoders instead
+ // of here because our static assert fallback doesn't work at
+ // file scope)
+
+ #define MPACK_CHECK_FLOAT_ORDER() \
+ MPACK_STATIC_ASSERT(__FLOAT_WORD_ORDER__ == __BYTE_ORDER__, \
+ "float byte order does not match int byte order! float/double " \
+ "encoding is not properly implemented on this platform.")
+
+#endif
+
+#ifndef MPACK_CHECK_FLOAT_ORDER
+ #define MPACK_CHECK_FLOAT_ORDER() /* nothing */
+#endif
+
+
+/*
+ * Here we define mpack_assert() and mpack_break(). They both work like a normal
+ * assertion function in debug mode, causing a trap or abort. However, on some platforms
+ * you can safely resume execution from mpack_break(), whereas mpack_assert() is
+ * always fatal.
+ *
+ * In release mode, mpack_assert() is converted to an assurance to the compiler
+ * that the expression cannot be false (via e.g. __assume() or __builtin_unreachable())
+ * to improve optimization where supported. There is thus no point in "safely" handling
+ * the case of this being false. Writing mpack_assert(0) rarely makes sense (except
+ * possibly as a default handler in a switch) since the compiler will throw away any
+ * code after it. If at any time an mpack_assert() is not true, the behaviour is
+ * undefined. This also means the expression is evaluated even in release.
+ *
+ * mpack_break() on the other hand is compiled to nothing in release. It is
+ * used in situations where we want to highlight a programming error as early as
+ * possible (in the debugger), but we still handle the situation safely if it
+ * happens in release to avoid producing incorrect results (such as in
+ * MPACK_WRITE_TRACKING.) It does not take an expression to test because it
+ * belongs in a safe-handling block after its failing condition has been tested.
+ *
+ * If stdio is available, we can add a format string describing the error, and
+ * on some compilers we can declare it noreturn to get correct results from static
+ * analysis tools. Note that the format string and arguments are not evaluated unless
+ * the assertion is hit.
+ *
+ * Note that any arguments to mpack_assert() beyond the first are only evaluated
+ * if the expression is false (and are never evaluated in release.)
+ *
+ * mpack_assert_fail() and mpack_break_hit() are defined separately
+ * because assert is noreturn and break isn't. This distinction is very
+ * important for static analysis tools to give correct results.
+ */
+
+#if MPACK_DEBUG
+
+ /**
+ * @addtogroup config
+ * @{
+ */
+ /**
+ * @name Debug Functions
+ */
+ /**
+ * Implement this and define @ref MPACK_CUSTOM_ASSERT to use a custom
+ * assertion function.
+ *
+ * This function should not return. If it does, MPack will @c abort().
+ *
+ * If you use C++, make sure you include @c mpack.h where you define
+ * this to get the correct linkage (or define it <code>extern "C"</code>.)
+ *
+ * Asserts are only used when @ref MPACK_DEBUG is enabled, and can be
+ * triggered by bugs in MPack or bugs due to incorrect usage of MPack.
+ */
+ void mpack_assert_fail(const char* message);
+ /**
+ * @}
+ */
+ /**
+ * @}
+ */
+
+ MPACK_NORETURN(void mpack_assert_fail_wrapper(const char* message));
+ #if MPACK_STDIO
+ MPACK_NORETURN(void mpack_assert_fail_format(const char* format, ...));
+ #define mpack_assert_fail_at(line, file, exprstr, format, ...) \
+ MPACK_EXPAND(mpack_assert_fail_format("mpack assertion failed at " file ":" #line "\n%s\n" format, exprstr, __VA_ARGS__))
+ #else
+ #define mpack_assert_fail_at(line, file, exprstr, format, ...) \
+ mpack_assert_fail_wrapper("mpack assertion failed at " file ":" #line "\n" exprstr "\n")
+ #endif
+
+ #define mpack_assert_fail_pos(line, file, exprstr, expr, ...) \
+ MPACK_EXPAND(mpack_assert_fail_at(line, file, exprstr, __VA_ARGS__))
+
+ // This contains a workaround to the pedantic C99 requirement of having at
+ // least one argument to a variadic macro. The first argument is the
+ // boolean expression, the optional second argument (if provided) must be a
+ // literal format string, and any additional arguments are the format
+ // argument list.
+ //
+ // Unfortunately this means macros are expanded in the expression before it
+ // gets stringified. I haven't found a workaround to this.
+ //
+ // This adds two unused arguments to the format argument list when a
+ // format string is provided, so this would complicate the use of
+ // -Wformat and __attribute__((format)) on mpack_assert_fail_format() if we
+ // ever bothered to implement it.
+ #define mpack_assert(...) \
+ MPACK_EXPAND(((!(MPACK_EXTRACT_ARG0(__VA_ARGS__))) ? \
+ mpack_assert_fail_pos(__LINE__, __FILE__, MPACK_STRINGIFY_ARG0(__VA_ARGS__) , __VA_ARGS__ , "", NULL) : \
+ (void)0))
+
+ void mpack_break_hit(const char* message);
+ #if MPACK_STDIO
+ void mpack_break_hit_format(const char* format, ...);
+ #define mpack_break_hit_at(line, file, ...) \
+ MPACK_EXPAND(mpack_break_hit_format("mpack breakpoint hit at " file ":" #line "\n" __VA_ARGS__))
+ #else
+ #define mpack_break_hit_at(line, file, ...) \
+ mpack_break_hit("mpack breakpoint hit at " file ":" #line )
+ #endif
+ #define mpack_break_hit_pos(line, file, ...) MPACK_EXPAND(mpack_break_hit_at(line, file, __VA_ARGS__))
+ #define mpack_break(...) MPACK_EXPAND(mpack_break_hit_pos(__LINE__, __FILE__, __VA_ARGS__))
+#else
+ #define mpack_assert(...) \
+ (MPACK_EXPAND((!(MPACK_EXTRACT_ARG0(__VA_ARGS__))) ? \
+ (MPACK_UNREACHABLE, (void)0) : \
+ (void)0))
+ #define mpack_break(...) ((void)0)
+#endif
+
+
+
+/* Wrap some needed libc functions */
+
+#if MPACK_STDLIB
+ #define mpack_memcmp memcmp
+ #define mpack_memcpy memcpy
+ #define mpack_memmove memmove
+ #define mpack_memset memset
+ #ifndef mpack_strlen
+ #define mpack_strlen strlen
+ #endif
+
+ #if defined(MPACK_UNIT_TESTS) && MPACK_INTERNAL && defined(__GNUC__)
+ // make sure we don't use the stdlib directly during development
+ #undef memcmp
+ #undef memcpy
+ #undef memmove
+ #undef memset
+ #undef strlen
+ #undef malloc
+ #undef free
+ #pragma GCC poison memcmp
+ #pragma GCC poison memcpy
+ #pragma GCC poison memmove
+ #pragma GCC poison memset
+ #pragma GCC poison strlen
+ #pragma GCC poison malloc
+ #pragma GCC poison free
+ #endif
+
+#elif defined(__GNUC__) && !MPACK_NO_BUILTINS
+ // there's not always a builtin memmove for GCC,
+ // and we don't have a way to test for it
+ #define mpack_memcmp __builtin_memcmp
+ #define mpack_memcpy __builtin_memcpy
+ #define mpack_memset __builtin_memset
+ #define mpack_strlen __builtin_strlen
+
+#elif defined(__clang__) && defined(__has_builtin) && !MPACK_NO_BUILTINS
+ #if __has_builtin(__builtin_memcmp)
+ #define mpack_memcmp __builtin_memcmp
+ #endif
+ #if __has_builtin(__builtin_memcpy)
+ #define mpack_memcpy __builtin_memcpy
+ #endif
+ #if __has_builtin(__builtin_memmove)
+ #define mpack_memmove __builtin_memmove
+ #endif
+ #if __has_builtin(__builtin_memset)
+ #define mpack_memset __builtin_memset
+ #endif
+ #if __has_builtin(__builtin_strlen)
+ #define mpack_strlen __builtin_strlen
+ #endif
+#endif
+
+#ifndef mpack_memcmp
+int mpack_memcmp(const void* s1, const void* s2, size_t n);
+#endif
+#ifndef mpack_memcpy
+void* mpack_memcpy(void* MPACK_RESTRICT s1, const void* MPACK_RESTRICT s2, size_t n);
+#endif
+#ifndef mpack_memmove
+void* mpack_memmove(void* s1, const void* s2, size_t n);
+#endif
+#ifndef mpack_memset
+void* mpack_memset(void* s, int c, size_t n);
+#endif
+#ifndef mpack_strlen
+size_t mpack_strlen(const char* s);
+#endif
+
+#if MPACK_STDIO
+ #if defined(WIN32)
+ #define mpack_snprintf _snprintf
+ #else
+ #define mpack_snprintf snprintf
+ #endif
+#endif
+
+
+
+/* Debug logging */
+#if 0
+ #include <stdio.h>
+ #define mpack_log(...) (MPACK_EXPAND(printf(__VA_ARGS__)), fflush(stdout))
+#else
+ #define mpack_log(...) ((void)0)
+#endif
+
+
+
+/* Make sure our configuration makes sense */
+#if defined(MPACK_MALLOC) && !defined(MPACK_FREE)
+ #error "MPACK_MALLOC requires MPACK_FREE."
+#endif
+#if !defined(MPACK_MALLOC) && defined(MPACK_FREE)
+ #error "MPACK_FREE requires MPACK_MALLOC."
+#endif
+#if MPACK_READ_TRACKING && !defined(MPACK_READER)
+ #error "MPACK_READ_TRACKING requires MPACK_READER."
+#endif
+#if MPACK_WRITE_TRACKING && !defined(MPACK_WRITER)
+ #error "MPACK_WRITE_TRACKING requires MPACK_WRITER."
+#endif
+#ifndef MPACK_MALLOC
+ #if MPACK_STDIO
+ #error "MPACK_STDIO requires preprocessor definitions for MPACK_MALLOC and MPACK_FREE."
+ #endif
+ #if MPACK_READ_TRACKING
+ #error "MPACK_READ_TRACKING requires preprocessor definitions for MPACK_MALLOC and MPACK_FREE."
+ #endif
+ #if MPACK_WRITE_TRACKING
+ #error "MPACK_WRITE_TRACKING requires preprocessor definitions for MPACK_MALLOC and MPACK_FREE."
+ #endif
+#endif
+
+
+
+/* Implement realloc if unavailable */
+#ifdef MPACK_MALLOC
+ #ifdef MPACK_REALLOC
+ MPACK_INLINE void* mpack_realloc(void* old_ptr, size_t used_size, size_t new_size) {
+ MPACK_UNUSED(used_size);
+ return MPACK_REALLOC(old_ptr, new_size);
+ }
+ #else
+ void* mpack_realloc(void* old_ptr, size_t used_size, size_t new_size);
+ #endif
+#endif
+
+
+
+/**
+ * @}
+ */
+
+MPACK_EXTERN_C_END
+MPACK_HEADER_END
+
+#endif
+
+
+/* mpack/mpack-common.h.h */
+
+/**
+ * @file
+ *
+ * Defines types and functions shared by the MPack reader and writer.
+ */
+
+#ifndef MPACK_COMMON_H
+#define MPACK_COMMON_H 1
+
+/* #include "mpack-platform.h" */
+
+#ifndef MPACK_PRINT_BYTE_COUNT
+#define MPACK_PRINT_BYTE_COUNT 12
+#endif
+
+MPACK_HEADER_START
+MPACK_EXTERN_C_START
+
+
+
+/**
+ * @defgroup common Tags and Common Elements
+ *
+ * Contains types, constants and functions shared by both the encoding
+ * and decoding portions of MPack.
+ *
+ * @{
+ */
+
+/* Version information */
+
+#define MPACK_VERSION_MAJOR 1 /**< The major version number of MPack. */
+#define MPACK_VERSION_MINOR 0 /**< The minor version number of MPack. */
+#define MPACK_VERSION_PATCH 0 /**< The patch version number of MPack. */
+
+/** A number containing the version number of MPack for comparison purposes. */
+#define MPACK_VERSION ((MPACK_VERSION_MAJOR * 10000) + \
+ (MPACK_VERSION_MINOR * 100) + MPACK_VERSION_PATCH)
+
+/** A macro to test for a minimum version of MPack. */
+#define MPACK_VERSION_AT_LEAST(major, minor, patch) \
+ (MPACK_VERSION >= (((major) * 10000) + ((minor) * 100) + (patch)))
+
+/** @cond */
+#if (MPACK_VERSION_PATCH > 0)
+#define MPACK_VERSION_STRING_BASE \
+ MPACK_STRINGIFY(MPACK_VERSION_MAJOR) "." \
+ MPACK_STRINGIFY(MPACK_VERSION_MINOR) "." \
+ MPACK_STRINGIFY(MPACK_VERSION_PATCH)
+#else
+#define MPACK_VERSION_STRING_BASE \
+ MPACK_STRINGIFY(MPACK_VERSION_MAJOR) "." \
+ MPACK_STRINGIFY(MPACK_VERSION_MINOR)
+#endif
+/** @endcond */
+
+/**
+ * @def MPACK_VERSION_STRING
+ * @hideinitializer
+ *
+ * A string containing the MPack version.
+ */
+#if MPACK_RELEASE_VERSION
+#define MPACK_VERSION_STRING MPACK_VERSION_STRING_BASE
+#else
+#define MPACK_VERSION_STRING MPACK_VERSION_STRING_BASE "dev"
+#endif
+
+/**
+ * @def MPACK_LIBRARY_STRING
+ * @hideinitializer
+ *
+ * A string describing MPack, containing the library name, version and debug mode.
+ */
+#if MPACK_DEBUG
+#define MPACK_LIBRARY_STRING "MPack " MPACK_VERSION_STRING "-debug"
+#else
+#define MPACK_LIBRARY_STRING "MPack " MPACK_VERSION_STRING
+#endif
+
+/** @cond */
+/**
+ * @def MPACK_MAXIMUM_TAG_SIZE
+ *
+ * The maximum encoded size of a tag in bytes.
+ */
+#define MPACK_MAXIMUM_TAG_SIZE 9
+/** @endcond */
+
+#if MPACK_EXTENSIONS
+/**
+ * @def MPACK_TIMESTAMP_NANOSECONDS_MAX
+ *
+ * The maximum value of nanoseconds for a timestamp.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ */
+#define MPACK_TIMESTAMP_NANOSECONDS_MAX 999999999
+#endif
+
+
+
+#if MPACK_COMPATIBILITY
+/**
+ * Versions of the MessagePack format.
+ *
+ * A reader, writer, or tree can be configured to serialize in an older
+ * version of the MessagePack spec. This is necessary to interface with
+ * older MessagePack libraries that do not support new MessagePack features.
+ *
+ * @note This requires @ref MPACK_COMPATIBILITY.
+ */
+typedef enum mpack_version_t {
+
+ /**
+ * Version 1.0/v4, supporting only the @c raw type without @c str8.
+ */
+ mpack_version_v4 = 4,
+
+ /**
+ * Version 2.0/v5, supporting the @c str8, @c bin and @c ext types.
+ */
+ mpack_version_v5 = 5,
+
+ /**
+ * The most recent supported version of MessagePack. This is the default.
+ */
+ mpack_version_current = mpack_version_v5,
+
+} mpack_version_t;
+#endif
+
+/**
+ * Error states for MPack objects.
+ *
+ * When a reader, writer, or tree is in an error state, all subsequent calls
+ * are ignored and their return values are nil/zero. You should check whether
+ * the source is in an error state before using such values.
+ */
+typedef enum mpack_error_t {
+ mpack_ok = 0, /**< No error. */
+ mpack_error_io = 2, /**< The reader or writer failed to fill or flush, or some other file or socket error occurred. */
+ mpack_error_invalid, /**< The data read is not valid MessagePack. */
+ mpack_error_unsupported, /**< The data read is not supported by this configuration of MPack. (See @ref MPACK_EXTENSIONS.) */
+ mpack_error_type, /**< The type or value range did not match what was expected by the caller. */
+ mpack_error_too_big, /**< A read or write was bigger than the maximum size allowed for that operation. */
+ mpack_error_memory, /**< An allocation failure occurred. */
+ mpack_error_bug, /**< The MPack API was used incorrectly. (This will always assert in debug mode.) */
+ mpack_error_data, /**< The contained data is not valid. */
+ mpack_error_eof, /**< The reader failed to read because of file or socket EOF */
+} mpack_error_t;
+
+/**
+ * Converts an MPack error to a string. This function returns an empty
+ * string when MPACK_DEBUG is not set.
+ */
+const char* mpack_error_to_string(mpack_error_t error);
+
+/**
+ * Defines the type of a MessagePack tag.
+ *
+ * Note that extension types, both user defined and built-in, are represented
+ * in tags as @ref mpack_type_ext. The value for an extension type is stored
+ * separately.
+ */
+typedef enum mpack_type_t {
+ mpack_type_missing = 0, /**< Special type indicating a missing optional value. */
+ mpack_type_nil, /**< A null value. */
+ mpack_type_bool, /**< A boolean (true or false.) */
+ mpack_type_int, /**< A 64-bit signed integer. */
+ mpack_type_uint, /**< A 64-bit unsigned integer. */
+ mpack_type_float, /**< A 32-bit IEEE 754 floating point number. */
+ mpack_type_double, /**< A 64-bit IEEE 754 floating point number. */
+ mpack_type_str, /**< A string. */
+ mpack_type_bin, /**< A chunk of binary data. */
+ mpack_type_array, /**< An array of MessagePack objects. */
+ mpack_type_map, /**< An ordered map of key/value pairs of MessagePack objects. */
+
+ #if MPACK_EXTENSIONS
+ /**
+ * A typed MessagePack extension object containing a chunk of binary data.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ */
+ mpack_type_ext,
+ #endif
+} mpack_type_t;
+
+/**
+ * Converts an MPack type to a string. This function returns an empty
+ * string when MPACK_DEBUG is not set.
+ */
+const char* mpack_type_to_string(mpack_type_t type);
+
+#if MPACK_EXTENSIONS
+/**
+ * A timestamp.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ */
+typedef struct mpack_timestamp_t {
+ int64_t seconds; /*< The number of seconds (signed) since 1970-01-01T00:00:00Z. */
+ uint32_t nanoseconds; /*< The number of additional nanoseconds, between 0 and 999,999,999. */
+} mpack_timestamp_t;
+#endif
+
+/**
+ * An MPack tag is a MessagePack object header. It is a variant type
+ * representing any kind of object, and includes the length of compound types
+ * (e.g. map, array, string) or the value of non-compound types (e.g. boolean,
+ * integer, float.)
+ *
+ * If the type is compound (str, bin, ext, array or map), the contained
+ * elements or bytes are stored separately.
+ *
+ * This structure is opaque; its fields should not be accessed outside
+ * of MPack.
+ */
+typedef struct mpack_tag_t mpack_tag_t;
+
+/* Hide internals from documentation */
+/** @cond */
+struct mpack_tag_t {
+ mpack_type_t type; /*< The type of value. */
+
+ #if MPACK_EXTENSIONS
+ int8_t exttype; /*< The extension type if the type is @ref mpack_type_ext. */
+ #endif
+
+ /* The value for non-compound types. */
+ union {
+ uint64_t u; /*< The value if the type is unsigned int. */
+ int64_t i; /*< The value if the type is signed int. */
+ double d; /*< The value if the type is double. */
+ float f; /*< The value if the type is float. */
+ bool b; /*< The value if the type is bool. */
+
+ /* The number of bytes if the type is str, bin or ext. */
+ uint32_t l;
+
+ /* The element count if the type is an array, or the number of
+ key/value pairs if the type is map. */
+ uint32_t n;
+ } v;
+};
+/** @endcond */
+
+/**
+ * @name Tag Generators
+ * @{
+ */
+
+/**
+ * @def MPACK_TAG_ZERO
+ *
+ * An @ref mpack_tag_t initializer that zeroes the given tag.
+ *
+ * @warning This does not make the tag nil! The tag's type is invalid when
+ * initialized this way. Use @ref mpack_tag_make_nil() to generate a nil tag.
+ */
+#if MPACK_EXTENSIONS
+#define MPACK_TAG_ZERO {(mpack_type_t)0, 0, {0}}
+#else
+#define MPACK_TAG_ZERO {(mpack_type_t)0, {0}}
+#endif
+
+/** Generates a nil tag. */
+MPACK_INLINE mpack_tag_t mpack_tag_make_nil(void) {
+ mpack_tag_t ret = MPACK_TAG_ZERO;
+ ret.type = mpack_type_nil;
+ return ret;
+}
+
+/** Generates a bool tag. */
+MPACK_INLINE mpack_tag_t mpack_tag_make_bool(bool value) {
+ mpack_tag_t ret = MPACK_TAG_ZERO;
+ ret.type = mpack_type_bool;
+ ret.v.b = value;
+ return ret;
+}
+
+/** Generates a bool tag with value true. */
+MPACK_INLINE mpack_tag_t mpack_tag_make_true(void) {
+ mpack_tag_t ret = MPACK_TAG_ZERO;
+ ret.type = mpack_type_bool;
+ ret.v.b = true;
+ return ret;
+}
+
+/** Generates a bool tag with value false. */
+MPACK_INLINE mpack_tag_t mpack_tag_make_false(void) {
+ mpack_tag_t ret = MPACK_TAG_ZERO;
+ ret.type = mpack_type_bool;
+ ret.v.b = false;
+ return ret;
+}
+
+/** Generates a signed int tag. */
+MPACK_INLINE mpack_tag_t mpack_tag_make_int(int64_t value) {
+ mpack_tag_t ret = MPACK_TAG_ZERO;
+ ret.type = mpack_type_int;
+ ret.v.i = value;
+ return ret;
+}
+
+/** Generates an unsigned int tag. */
+MPACK_INLINE mpack_tag_t mpack_tag_make_uint(uint64_t value) {
+ mpack_tag_t ret = MPACK_TAG_ZERO;
+ ret.type = mpack_type_uint;
+ ret.v.u = value;
+ return ret;
+}
+
+/** Generates a float tag. */
+MPACK_INLINE mpack_tag_t mpack_tag_make_float(float value) {
+ mpack_tag_t ret = MPACK_TAG_ZERO;
+ ret.type = mpack_type_float;
+ ret.v.f = value;
+ return ret;
+}
+
+/** Generates a double tag. */
+MPACK_INLINE mpack_tag_t mpack_tag_make_double(double value) {
+ mpack_tag_t ret = MPACK_TAG_ZERO;
+ ret.type = mpack_type_double;
+ ret.v.d = value;
+ return ret;
+}
+
+/** Generates an array tag. */
+MPACK_INLINE mpack_tag_t mpack_tag_make_array(uint32_t count) {
+ mpack_tag_t ret = MPACK_TAG_ZERO;
+ ret.type = mpack_type_array;
+ ret.v.n = count;
+ return ret;
+}
+
+/** Generates a map tag. */
+MPACK_INLINE mpack_tag_t mpack_tag_make_map(uint32_t count) {
+ mpack_tag_t ret = MPACK_TAG_ZERO;
+ ret.type = mpack_type_map;
+ ret.v.n = count;
+ return ret;
+}
+
+/** Generates a str tag. */
+MPACK_INLINE mpack_tag_t mpack_tag_make_str(uint32_t length) {
+ mpack_tag_t ret = MPACK_TAG_ZERO;
+ ret.type = mpack_type_str;
+ ret.v.l = length;
+ return ret;
+}
+
+/** Generates a bin tag. */
+MPACK_INLINE mpack_tag_t mpack_tag_make_bin(uint32_t length) {
+ mpack_tag_t ret = MPACK_TAG_ZERO;
+ ret.type = mpack_type_bin;
+ ret.v.l = length;
+ return ret;
+}
+
+#if MPACK_EXTENSIONS
+/**
+ * Generates an ext tag.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ */
+MPACK_INLINE mpack_tag_t mpack_tag_make_ext(int8_t exttype, uint32_t length) {
+ mpack_tag_t ret = MPACK_TAG_ZERO;
+ ret.type = mpack_type_ext;
+ ret.exttype = exttype;
+ ret.v.l = length;
+ return ret;
+}
+#endif
+
+/**
+ * @}
+ */
+
+/**
+ * @name Tag Querying Functions
+ * @{
+ */
+
+/**
+ * Gets the type of a tag.
+ */
+MPACK_INLINE mpack_type_t mpack_tag_type(mpack_tag_t* tag) {
+ return tag->type;
+}
+
+/**
+ * Gets the boolean value of a bool-type tag. The tag must be of type @ref
+ * mpack_type_bool.
+ *
+ * This asserts that the type in the tag is @ref mpack_type_bool. (No check is
+ * performed if MPACK_DEBUG is not set.)
+ */
+MPACK_INLINE bool mpack_tag_bool_value(mpack_tag_t* tag) {
+ mpack_assert(tag->type == mpack_type_bool, "tag is not a bool!");
+ return tag->v.b;
+}
+
+/**
+ * Gets the signed integer value of an int-type tag.
+ *
+ * This asserts that the type in the tag is @ref mpack_type_int. (No check is
+ * performed if MPACK_DEBUG is not set.)
+ *
+ * @warning This does not convert between signed and unsigned tags! A positive
+ * integer may be stored in a tag as either @ref mpack_type_int or @ref
+ * mpack_type_uint. You must check the type first; this can only be used if the
+ * type is @ref mpack_type_int.
+ *
+ * @see mpack_type_int
+ */
+MPACK_INLINE int64_t mpack_tag_int_value(mpack_tag_t* tag) {
+ mpack_assert(tag->type == mpack_type_int, "tag is not an int!");
+ return tag->v.i;
+}
+
+/**
+ * Gets the unsigned integer value of a uint-type tag.
+ *
+ * This asserts that the type in the tag is @ref mpack_type_uint. (No check is
+ * performed if MPACK_DEBUG is not set.)
+ *
+ * @warning This does not convert between signed and unsigned tags! A positive
+ * integer may be stored in a tag as either @ref mpack_type_int or @ref
+ * mpack_type_uint. You must check the type first; this can only be used if the
+ * type is @ref mpack_type_uint.
+ *
+ * @see mpack_type_uint
+ */
+MPACK_INLINE uint64_t mpack_tag_uint_value(mpack_tag_t* tag) {
+ mpack_assert(tag->type == mpack_type_uint, "tag is not a uint!");
+ return tag->v.u;
+}
+
+/**
+ * Gets the float value of a float-type tag.
+ *
+ * This asserts that the type in the tag is @ref mpack_type_float. (No check is
+ * performed if MPACK_DEBUG is not set.)
+ *
+ * @warning This does not convert between float and double tags! This can only
+ * be used if the type is @ref mpack_type_float.
+ *
+ * @see mpack_type_float
+ */
+MPACK_INLINE float mpack_tag_float_value(mpack_tag_t* tag) {
+ mpack_assert(tag->type == mpack_type_float, "tag is not a float!");
+ return tag->v.f;
+}
+
+/**
+ * Gets the double value of a double-type tag.
+ *
+ * This asserts that the type in the tag is @ref mpack_type_double. (No check
+ * is performed if MPACK_DEBUG is not set.)
+ *
+ * @warning This does not convert between float and double tags! This can only
+ * be used if the type is @ref mpack_type_double.
+ *
+ * @see mpack_type_double
+ */
+MPACK_INLINE double mpack_tag_double_value(mpack_tag_t* tag) {
+ mpack_assert(tag->type == mpack_type_double, "tag is not a double!");
+ return tag->v.d;
+}
+
+/**
+ * Gets the number of elements in an array tag.
+ *
+ * This asserts that the type in the tag is @ref mpack_type_array. (No check is
+ * performed if MPACK_DEBUG is not set.)
+ *
+ * @see mpack_type_array
+ */
+MPACK_INLINE uint32_t mpack_tag_array_count(mpack_tag_t* tag) {
+ mpack_assert(tag->type == mpack_type_array, "tag is not an array!");
+ return tag->v.n;
+}
+
+/**
+ * Gets the number of key-value pairs in a map tag.
+ *
+ * This asserts that the type in the tag is @ref mpack_type_map. (No check is
+ * performed if MPACK_DEBUG is not set.)
+ *
+ * @see mpack_type_map
+ */
+MPACK_INLINE uint32_t mpack_tag_map_count(mpack_tag_t* tag) {
+ mpack_assert(tag->type == mpack_type_map, "tag is not a map!");
+ return tag->v.n;
+}
+
+/**
+ * Gets the length in bytes of a str-type tag.
+ *
+ * This asserts that the type in the tag is @ref mpack_type_str. (No check is
+ * performed if MPACK_DEBUG is not set.)
+ *
+ * @see mpack_type_str
+ */
+MPACK_INLINE uint32_t mpack_tag_str_length(mpack_tag_t* tag) {
+ mpack_assert(tag->type == mpack_type_str, "tag is not a str!");
+ return tag->v.l;
+}
+
+/**
+ * Gets the length in bytes of a bin-type tag.
+ *
+ * This asserts that the type in the tag is @ref mpack_type_bin. (No check is
+ * performed if MPACK_DEBUG is not set.)
+ *
+ * @see mpack_type_bin
+ */
+MPACK_INLINE uint32_t mpack_tag_bin_length(mpack_tag_t* tag) {
+ mpack_assert(tag->type == mpack_type_bin, "tag is not a bin!");
+ return tag->v.l;
+}
+
+#if MPACK_EXTENSIONS
+/**
+ * Gets the length in bytes of an ext-type tag.
+ *
+ * This asserts that the type in the tag is @ref mpack_type_ext. (No check is
+ * performed if MPACK_DEBUG is not set.)
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ *
+ * @see mpack_type_ext
+ */
+MPACK_INLINE uint32_t mpack_tag_ext_length(mpack_tag_t* tag) {
+ mpack_assert(tag->type == mpack_type_ext, "tag is not an ext!");
+ return tag->v.l;
+}
+
+/**
+ * Gets the extension type (exttype) of an ext-type tag.
+ *
+ * This asserts that the type in the tag is @ref mpack_type_ext. (No check is
+ * performed if MPACK_DEBUG is not set.)
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ *
+ * @see mpack_type_ext
+ */
+MPACK_INLINE int8_t mpack_tag_ext_exttype(mpack_tag_t* tag) {
+ mpack_assert(tag->type == mpack_type_ext, "tag is not an ext!");
+ return tag->exttype;
+}
+#endif
+
+/**
+ * Gets the length in bytes of a str-, bin- or ext-type tag.
+ *
+ * This asserts that the type in the tag is @ref mpack_type_str, @ref
+ * mpack_type_bin or @ref mpack_type_ext. (No check is performed if MPACK_DEBUG
+ * is not set.)
+ *
+ * @see mpack_type_str
+ * @see mpack_type_bin
+ * @see mpack_type_ext
+ */
+MPACK_INLINE uint32_t mpack_tag_bytes(mpack_tag_t* tag) {
+ #if MPACK_EXTENSIONS
+ mpack_assert(tag->type == mpack_type_str || tag->type == mpack_type_bin
+ || tag->type == mpack_type_ext, "tag is not a str, bin or ext!");
+ #else
+ mpack_assert(tag->type == mpack_type_str || tag->type == mpack_type_bin,
+ "tag is not a str or bin!");
+ #endif
+ return tag->v.l;
+}
+
+/**
+ * @}
+ */
+
+/**
+ * @name Other tag functions
+ * @{
+ */
+
+#if MPACK_EXTENSIONS
+/**
+ * The extension type for a timestamp.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ */
+#define MPACK_EXTTYPE_TIMESTAMP ((int8_t)(-1))
+#endif
+
+/**
+ * Compares two tags with an arbitrary fixed ordering. Returns 0 if the tags are
+ * equal, a negative integer if left comes before right, or a positive integer
+ * otherwise.
+ *
+ * \warning The ordering is not guaranteed to be preserved across MPack versions; do
+ * not rely on it in persistent data.
+ *
+ * \warning Floating point numbers are compared bit-for-bit, not using the language's
+ * operator==. This means that NaNs with matching representation will compare equal.
+ * This behaviour is up for debate; see comments in the definition of mpack_tag_cmp().
+ *
+ * See mpack_tag_equal() for more information on when tags are considered equal.
+ */
+int mpack_tag_cmp(mpack_tag_t left, mpack_tag_t right);
+
+/**
+ * Compares two tags for equality. Tags are considered equal if the types are compatible
+ * and the values (for non-compound types) are equal.
+ *
+ * The field width of variable-width fields is ignored (and in fact is not stored
+ * in a tag), and positive numbers in signed integers are considered equal to their
+ * unsigned counterparts. So for example the value 1 stored as a positive fixint
+ * is equal to the value 1 stored in a 64-bit unsigned integer field.
+ *
+ * The "extension type" of an extension object is considered part of the value
+ * and must match exactly.
+ *
+ * \warning Floating point numbers are compared bit-for-bit, not using the language's
+ * operator==. This means that NaNs with matching representation will compare equal.
+ * This behaviour is up for debate; see comments in the definition of mpack_tag_cmp().
+ */
+MPACK_INLINE bool mpack_tag_equal(mpack_tag_t left, mpack_tag_t right) {
+ return mpack_tag_cmp(left, right) == 0;
+}
+
+#if MPACK_DEBUG && MPACK_STDIO
+/**
+ * Generates a json-like debug description of the given tag into the given buffer.
+ *
+ * This is only available in debug mode, and only if stdio is available (since
+ * it uses snprintf().) It's strictly for debugging purposes.
+ *
+ * The prefix is used to print the first few hexadecimal bytes of a bin or ext
+ * type. Pass NULL if not a bin or ext.
+ */
+void mpack_tag_debug_pseudo_json(mpack_tag_t tag, char* buffer, size_t buffer_size,
+ const char* prefix, size_t prefix_size);
+
+/**
+ * Generates a debug string description of the given tag into the given buffer.
+ *
+ * This is only available in debug mode, and only if stdio is available (since
+ * it uses snprintf().) It's strictly for debugging purposes.
+ */
+void mpack_tag_debug_describe(mpack_tag_t tag, char* buffer, size_t buffer_size);
+
+/** @cond */
+
+/*
+ * A callback function for printing pseudo-JSON for debugging purposes.
+ *
+ * @see mpack_node_print_callback
+ */
+typedef void (*mpack_print_callback_t)(void* context, const char* data, size_t count);
+
+// helpers for printing debug output
+// i feel a bit like i'm re-implementing a buffered writer again...
+typedef struct mpack_print_t {
+ char* buffer;
+ size_t size;
+ size_t count;
+ mpack_print_callback_t callback;
+ void* context;
+} mpack_print_t;
+
+void mpack_print_append(mpack_print_t* print, const char* data, size_t count);
+
+MPACK_INLINE void mpack_print_append_cstr(mpack_print_t* print, const char* cstr) {
+ mpack_print_append(print, cstr, mpack_strlen(cstr));
+}
+
+void mpack_print_flush(mpack_print_t* print);
+
+void mpack_print_file_callback(void* context, const char* data, size_t count);
+
+/** @endcond */
+
+#endif
+
+/**
+ * @}
+ */
+
+/**
+ * @name Deprecated Tag Generators
+ * @{
+ */
+
+/*
+ * "make" has been added to their names to disambiguate them from the
+ * value-fetching functions (e.g. mpack_tag_make_bool() vs
+ * mpack_tag_bool_value().)
+ *
+ * The length and count for all compound types was the wrong sign (int32_t
+ * instead of uint32_t.) These preserve the old behaviour; the new "make"
+ * functions have the correct sign.
+ */
+
+/** \deprecated Renamed to mpack_tag_make_nil(). */
+MPACK_INLINE mpack_tag_t mpack_tag_nil(void) {
+ return mpack_tag_make_nil();
+}
+
+/** \deprecated Renamed to mpack_tag_make_bool(). */
+MPACK_INLINE mpack_tag_t mpack_tag_bool(bool value) {
+ return mpack_tag_make_bool(value);
+}
+
+/** \deprecated Renamed to mpack_tag_make_true(). */
+MPACK_INLINE mpack_tag_t mpack_tag_true(void) {
+ return mpack_tag_make_true();
+}
+
+/** \deprecated Renamed to mpack_tag_make_false(). */
+MPACK_INLINE mpack_tag_t mpack_tag_false(void) {
+ return mpack_tag_make_false();
+}
+
+/** \deprecated Renamed to mpack_tag_make_int(). */
+MPACK_INLINE mpack_tag_t mpack_tag_int(int64_t value) {
+ return mpack_tag_make_int(value);
+}
+
+/** \deprecated Renamed to mpack_tag_make_uint(). */
+MPACK_INLINE mpack_tag_t mpack_tag_uint(uint64_t value) {
+ return mpack_tag_make_uint(value);
+}
+
+/** \deprecated Renamed to mpack_tag_make_float(). */
+MPACK_INLINE mpack_tag_t mpack_tag_float(float value) {
+ return mpack_tag_make_float(value);
+}
+
+/** \deprecated Renamed to mpack_tag_make_double(). */
+MPACK_INLINE mpack_tag_t mpack_tag_double(double value) {
+ return mpack_tag_make_double(value);
+}
+
+/** \deprecated Renamed to mpack_tag_make_array(). */
+MPACK_INLINE mpack_tag_t mpack_tag_array(int32_t count) {
+ return mpack_tag_make_array((uint32_t)count);
+}
+
+/** \deprecated Renamed to mpack_tag_make_map(). */
+MPACK_INLINE mpack_tag_t mpack_tag_map(int32_t count) {
+ return mpack_tag_make_map((uint32_t)count);
+}
+
+/** \deprecated Renamed to mpack_tag_make_str(). */
+MPACK_INLINE mpack_tag_t mpack_tag_str(int32_t length) {
+ return mpack_tag_make_str((uint32_t)length);
+}
+
+/** \deprecated Renamed to mpack_tag_make_bin(). */
+MPACK_INLINE mpack_tag_t mpack_tag_bin(int32_t length) {
+ return mpack_tag_make_bin((uint32_t)length);
+}
+
+#if MPACK_EXTENSIONS
+/** \deprecated Renamed to mpack_tag_make_ext(). */
+MPACK_INLINE mpack_tag_t mpack_tag_ext(int8_t exttype, int32_t length) {
+ return mpack_tag_make_ext(exttype, (uint32_t)length);
+}
+#endif
+
+/**
+ * @}
+ */
+
+/** @cond */
+
+/*
+ * Helpers to perform unaligned network-endian loads and stores
+ * at arbitrary addresses. Byte-swapping builtins are used if they
+ * are available and if they improve performance.
+ *
+ * These will remain available in the public API so feel free to
+ * use them for other purposes, but they are undocumented.
+ */
+
+MPACK_INLINE uint8_t mpack_load_u8(const char* p) {
+ return (uint8_t)p[0];
+}
+
+MPACK_INLINE uint16_t mpack_load_u16(const char* p) {
+ #ifdef MPACK_NHSWAP16
+ uint16_t val;
+ mpack_memcpy(&val, p, sizeof(val));
+ return MPACK_NHSWAP16(val);
+ #else
+ return (uint16_t)((((uint16_t)(uint8_t)p[0]) << 8) |
+ ((uint16_t)(uint8_t)p[1]));
+ #endif
+}
+
+MPACK_INLINE uint32_t mpack_load_u32(const char* p) {
+ #ifdef MPACK_NHSWAP32
+ uint32_t val;
+ mpack_memcpy(&val, p, sizeof(val));
+ return MPACK_NHSWAP32(val);
+ #else
+ return (((uint32_t)(uint8_t)p[0]) << 24) |
+ (((uint32_t)(uint8_t)p[1]) << 16) |
+ (((uint32_t)(uint8_t)p[2]) << 8) |
+ ((uint32_t)(uint8_t)p[3]);
+ #endif
+}
+
+MPACK_INLINE uint64_t mpack_load_u64(const char* p) {
+ #ifdef MPACK_NHSWAP64
+ uint64_t val;
+ mpack_memcpy(&val, p, sizeof(val));
+ return MPACK_NHSWAP64(val);
+ #else
+ return (((uint64_t)(uint8_t)p[0]) << 56) |
+ (((uint64_t)(uint8_t)p[1]) << 48) |
+ (((uint64_t)(uint8_t)p[2]) << 40) |
+ (((uint64_t)(uint8_t)p[3]) << 32) |
+ (((uint64_t)(uint8_t)p[4]) << 24) |
+ (((uint64_t)(uint8_t)p[5]) << 16) |
+ (((uint64_t)(uint8_t)p[6]) << 8) |
+ ((uint64_t)(uint8_t)p[7]);
+ #endif
+}
+
+MPACK_INLINE void mpack_store_u8(char* p, uint8_t val) {
+ uint8_t* u = (uint8_t*)p;
+ u[0] = val;
+}
+
+MPACK_INLINE void mpack_store_u16(char* p, uint16_t val) {
+ #ifdef MPACK_NHSWAP16
+ val = MPACK_NHSWAP16(val);
+ mpack_memcpy(p, &val, sizeof(val));
+ #else
+ uint8_t* u = (uint8_t*)p;
+ u[0] = (uint8_t)((val >> 8) & 0xFF);
+ u[1] = (uint8_t)( val & 0xFF);
+ #endif
+}
+
+MPACK_INLINE void mpack_store_u32(char* p, uint32_t val) {
+ #ifdef MPACK_NHSWAP32
+ val = MPACK_NHSWAP32(val);
+ mpack_memcpy(p, &val, sizeof(val));
+ #else
+ uint8_t* u = (uint8_t*)p;
+ u[0] = (uint8_t)((val >> 24) & 0xFF);
+ u[1] = (uint8_t)((val >> 16) & 0xFF);
+ u[2] = (uint8_t)((val >> 8) & 0xFF);
+ u[3] = (uint8_t)( val & 0xFF);
+ #endif
+}
+
+MPACK_INLINE void mpack_store_u64(char* p, uint64_t val) {
+ #ifdef MPACK_NHSWAP64
+ val = MPACK_NHSWAP64(val);
+ mpack_memcpy(p, &val, sizeof(val));
+ #else
+ uint8_t* u = (uint8_t*)p;
+ u[0] = (uint8_t)((val >> 56) & 0xFF);
+ u[1] = (uint8_t)((val >> 48) & 0xFF);
+ u[2] = (uint8_t)((val >> 40) & 0xFF);
+ u[3] = (uint8_t)((val >> 32) & 0xFF);
+ u[4] = (uint8_t)((val >> 24) & 0xFF);
+ u[5] = (uint8_t)((val >> 16) & 0xFF);
+ u[6] = (uint8_t)((val >> 8) & 0xFF);
+ u[7] = (uint8_t)( val & 0xFF);
+ #endif
+}
+
+MPACK_INLINE int8_t mpack_load_i8 (const char* p) {return (int8_t) mpack_load_u8 (p);}
+MPACK_INLINE int16_t mpack_load_i16(const char* p) {return (int16_t)mpack_load_u16(p);}
+MPACK_INLINE int32_t mpack_load_i32(const char* p) {return (int32_t)mpack_load_u32(p);}
+MPACK_INLINE int64_t mpack_load_i64(const char* p) {return (int64_t)mpack_load_u64(p);}
+MPACK_INLINE void mpack_store_i8 (char* p, int8_t val) {mpack_store_u8 (p, (uint8_t) val);}
+MPACK_INLINE void mpack_store_i16(char* p, int16_t val) {mpack_store_u16(p, (uint16_t)val);}
+MPACK_INLINE void mpack_store_i32(char* p, int32_t val) {mpack_store_u32(p, (uint32_t)val);}
+MPACK_INLINE void mpack_store_i64(char* p, int64_t val) {mpack_store_u64(p, (uint64_t)val);}
+
+MPACK_INLINE float mpack_load_float(const char* p) {
+ MPACK_CHECK_FLOAT_ORDER();
+ MPACK_STATIC_ASSERT(sizeof(float) == sizeof(uint32_t), "float is wrong size??");
+ union {
+ float f;
+ uint32_t u;
+ } v;
+ v.u = mpack_load_u32(p);
+ return v.f;
+}
+
+MPACK_INLINE double mpack_load_double(const char* p) {
+ MPACK_CHECK_FLOAT_ORDER();
+ MPACK_STATIC_ASSERT(sizeof(double) == sizeof(uint64_t), "double is wrong size??");
+ union {
+ double d;
+ uint64_t u;
+ } v;
+ v.u = mpack_load_u64(p);
+ return v.d;
+}
+
+MPACK_INLINE void mpack_store_float(char* p, float value) {
+ MPACK_CHECK_FLOAT_ORDER();
+ union {
+ float f;
+ uint32_t u;
+ } v;
+ v.f = value;
+ mpack_store_u32(p, v.u);
+}
+
+MPACK_INLINE void mpack_store_double(char* p, double value) {
+ MPACK_CHECK_FLOAT_ORDER();
+ union {
+ double d;
+ uint64_t u;
+ } v;
+ v.d = value;
+ mpack_store_u64(p, v.u);
+}
+
+/** @endcond */
+
+
+
+/** @cond */
+
+// Sizes in bytes for the various possible tags
+#define MPACK_TAG_SIZE_FIXUINT 1
+#define MPACK_TAG_SIZE_U8 2
+#define MPACK_TAG_SIZE_U16 3
+#define MPACK_TAG_SIZE_U32 5
+#define MPACK_TAG_SIZE_U64 9
+#define MPACK_TAG_SIZE_FIXINT 1
+#define MPACK_TAG_SIZE_I8 2
+#define MPACK_TAG_SIZE_I16 3
+#define MPACK_TAG_SIZE_I32 5
+#define MPACK_TAG_SIZE_I64 9
+#define MPACK_TAG_SIZE_FLOAT 5
+#define MPACK_TAG_SIZE_DOUBLE 9
+#define MPACK_TAG_SIZE_FIXARRAY 1
+#define MPACK_TAG_SIZE_ARRAY16 3
+#define MPACK_TAG_SIZE_ARRAY32 5
+#define MPACK_TAG_SIZE_FIXMAP 1
+#define MPACK_TAG_SIZE_MAP16 3
+#define MPACK_TAG_SIZE_MAP32 5
+#define MPACK_TAG_SIZE_FIXSTR 1
+#define MPACK_TAG_SIZE_STR8 2
+#define MPACK_TAG_SIZE_STR16 3
+#define MPACK_TAG_SIZE_STR32 5
+#define MPACK_TAG_SIZE_BIN8 2
+#define MPACK_TAG_SIZE_BIN16 3
+#define MPACK_TAG_SIZE_BIN32 5
+#define MPACK_TAG_SIZE_FIXEXT1 2
+#define MPACK_TAG_SIZE_FIXEXT2 2
+#define MPACK_TAG_SIZE_FIXEXT4 2
+#define MPACK_TAG_SIZE_FIXEXT8 2
+#define MPACK_TAG_SIZE_FIXEXT16 2
+#define MPACK_TAG_SIZE_EXT8 3
+#define MPACK_TAG_SIZE_EXT16 4
+#define MPACK_TAG_SIZE_EXT32 6
+
+// size in bytes for complete ext types
+#define MPACK_EXT_SIZE_TIMESTAMP4 (MPACK_TAG_SIZE_FIXEXT4 + 4)
+#define MPACK_EXT_SIZE_TIMESTAMP8 (MPACK_TAG_SIZE_FIXEXT8 + 8)
+#define MPACK_EXT_SIZE_TIMESTAMP12 (MPACK_TAG_SIZE_EXT8 + 12)
+
+/** @endcond */
+
+
+
+#if MPACK_READ_TRACKING || MPACK_WRITE_TRACKING
+/* Tracks the write state of compound elements (maps, arrays, */
+/* strings, binary blobs and extension types) */
+/** @cond */
+
+typedef struct mpack_track_element_t {
+ mpack_type_t type;
+ uint32_t left;
+
+ // indicates that a value still needs to be read/written for an already
+ // read/written key. left is not decremented until both key and value are
+ // read/written.
+ bool key_needs_value;
+} mpack_track_element_t;
+
+typedef struct mpack_track_t {
+ size_t count;
+ size_t capacity;
+ mpack_track_element_t* elements;
+} mpack_track_t;
+
+#if MPACK_INTERNAL
+mpack_error_t mpack_track_init(mpack_track_t* track);
+mpack_error_t mpack_track_grow(mpack_track_t* track);
+mpack_error_t mpack_track_push(mpack_track_t* track, mpack_type_t type, uint32_t count);
+mpack_error_t mpack_track_pop(mpack_track_t* track, mpack_type_t type);
+mpack_error_t mpack_track_element(mpack_track_t* track, bool read);
+mpack_error_t mpack_track_peek_element(mpack_track_t* track, bool read);
+mpack_error_t mpack_track_bytes(mpack_track_t* track, bool read, size_t count);
+mpack_error_t mpack_track_str_bytes_all(mpack_track_t* track, bool read, size_t count);
+mpack_error_t mpack_track_check_empty(mpack_track_t* track);
+mpack_error_t mpack_track_destroy(mpack_track_t* track, bool cancel);
+#endif
+
+/** @endcond */
+#endif
+
+
+
+#if MPACK_INTERNAL
+/** @cond */
+
+
+
+/* Miscellaneous string functions */
+
+/**
+ * Returns true if the given UTF-8 string is valid.
+ */
+bool mpack_utf8_check(const char* str, size_t bytes);
+
+/**
+ * Returns true if the given UTF-8 string is valid and contains no null characters.
+ */
+bool mpack_utf8_check_no_null(const char* str, size_t bytes);
+
+/**
+ * Returns true if the given string has no null bytes.
+ */
+bool mpack_str_check_no_null(const char* str, size_t bytes);
+
+
+
+/** @endcond */
+#endif
+
+
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+MPACK_EXTERN_C_END
+MPACK_HEADER_END
+
+#endif
+
+
+/* mpack/mpack-writer.h.h */
+
+/**
+ * @file
+ *
+ * Declares the MPack Writer.
+ */
+
+#ifndef MPACK_WRITER_H
+#define MPACK_WRITER_H 1
+
+/* #include "mpack-common.h" */
+
+#if MPACK_WRITER
+
+MPACK_HEADER_START
+MPACK_EXTERN_C_START
+
+#if MPACK_WRITE_TRACKING
+struct mpack_track_t;
+#endif
+
+/**
+ * @defgroup writer Write API
+ *
+ * The MPack Write API encodes structured data of a fixed (hardcoded) schema to MessagePack.
+ *
+ * @{
+ */
+
+/**
+ * @def MPACK_WRITER_MINIMUM_BUFFER_SIZE
+ *
+ * The minimum buffer size for a writer with a flush function.
+ */
+#define MPACK_WRITER_MINIMUM_BUFFER_SIZE 32
+
+/**
+ * A buffered MessagePack encoder.
+ *
+ * The encoder wraps an existing buffer and, optionally, a flush function.
+ * This allows efficiently encoding to an in-memory buffer or to a stream.
+ *
+ * All write operations are synchronous; they will block until the
+ * data is fully written, or an error occurs.
+ */
+typedef struct mpack_writer_t mpack_writer_t;
+
+/**
+ * The MPack writer's flush function to flush the buffer to the output stream.
+ * It should flag an appropriate error on the writer if flushing fails (usually
+ * mpack_error_io or mpack_error_memory.)
+ *
+ * The specified context for callbacks is at writer->context.
+ */
+typedef void (*mpack_writer_flush_t)(mpack_writer_t* writer, const char* buffer, size_t count);
+
+/**
+ * An error handler function to be called when an error is flagged on
+ * the writer.
+ *
+ * The error handler will only be called once on the first error flagged;
+ * any subsequent writes and errors are ignored, and the writer is
+ * permanently in that error state.
+ *
+ * MPack is safe against non-local jumps out of error handler callbacks.
+ * This means you are allowed to longjmp or throw an exception (in C++,
+ * Objective-C, or with SEH) out of this callback.
+ *
+ * Bear in mind when using longjmp that local non-volatile variables that
+ * have changed are undefined when setjmp() returns, so you can't put the
+ * writer on the stack in the same activation frame as the setjmp without
+ * declaring it volatile.
+ *
+ * You must still eventually destroy the writer. It is not destroyed
+ * automatically when an error is flagged. It is safe to destroy the
+ * writer within this error callback, but you will either need to perform
+ * a non-local jump, or store something in your context to identify
+ * that the writer is destroyed since any future accesses to it cause
+ * undefined behavior.
+ */
+typedef void (*mpack_writer_error_t)(mpack_writer_t* writer, mpack_error_t error);
+
+/**
+ * A teardown function to be called when the writer is destroyed.
+ */
+typedef void (*mpack_writer_teardown_t)(mpack_writer_t* writer);
+
+/* Hide internals from documentation */
+/** @cond */
+
+struct mpack_writer_t {
+ #if MPACK_COMPATIBILITY
+ mpack_version_t version; /* Version of the MessagePack spec to write */
+ #endif
+ mpack_writer_flush_t flush; /* Function to write bytes to the output stream */
+ mpack_writer_error_t error_fn; /* Function to call on error */
+ mpack_writer_teardown_t teardown; /* Function to teardown the context on destroy */
+ void* context; /* Context for writer callbacks */
+
+ char* buffer; /* Byte buffer */
+ char* current; /* Current position within the buffer */
+ char* end; /* The end of the buffer */
+ mpack_error_t error; /* Error state */
+
+ #if MPACK_WRITE_TRACKING
+ mpack_track_t track; /* Stack of map/array/str/bin/ext writes */
+ #endif
+
+ #ifdef MPACK_MALLOC
+ /* Reserved. You can use this space to allocate a custom
+ * context in order to reduce heap allocations. */
+ void* reserved[2];
+ #endif
+};
+
+#if MPACK_WRITE_TRACKING
+void mpack_writer_track_push(mpack_writer_t* writer, mpack_type_t type, uint32_t count);
+void mpack_writer_track_pop(mpack_writer_t* writer, mpack_type_t type);
+void mpack_writer_track_element(mpack_writer_t* writer);
+void mpack_writer_track_bytes(mpack_writer_t* writer, size_t count);
+#else
+MPACK_INLINE void mpack_writer_track_push(mpack_writer_t* writer, mpack_type_t type, uint32_t count) {
+ MPACK_UNUSED(writer);
+ MPACK_UNUSED(type);
+ MPACK_UNUSED(count);
+}
+MPACK_INLINE void mpack_writer_track_pop(mpack_writer_t* writer, mpack_type_t type) {
+ MPACK_UNUSED(writer);
+ MPACK_UNUSED(type);
+}
+MPACK_INLINE void mpack_writer_track_element(mpack_writer_t* writer) {
+ MPACK_UNUSED(writer);
+}
+MPACK_INLINE void mpack_writer_track_bytes(mpack_writer_t* writer, size_t count) {
+ MPACK_UNUSED(writer);
+ MPACK_UNUSED(count);
+}
+#endif
+
+/** @endcond */
+
+/**
+ * @name Lifecycle Functions
+ * @{
+ */
+
+/**
+ * Initializes an MPack writer with the given buffer. The writer
+ * does not assume ownership of the buffer.
+ *
+ * Trying to write past the end of the buffer will result in mpack_error_too_big
+ * unless a flush function is set with mpack_writer_set_flush(). To use the data
+ * without flushing, call mpack_writer_buffer_used() to determine the number of
+ * bytes written.
+ *
+ * @param writer The MPack writer.
+ * @param buffer The buffer into which to write MessagePack data.
+ * @param size The size of the buffer.
+ */
+void mpack_writer_init(mpack_writer_t* writer, char* buffer, size_t size);
+
+#ifdef MPACK_MALLOC
+/**
+ * Initializes an MPack writer using a growable buffer.
+ *
+ * The data is placed in the given data pointer if and when the writer
+ * is destroyed without error. The data pointer is NULL during writing,
+ * and will remain NULL if an error occurs.
+ *
+ * The allocated data must be freed with MPACK_FREE() (or simply free()
+ * if MPack's allocator hasn't been customized.)
+ *
+ * @throws mpack_error_memory if the buffer fails to grow when
+ * flushing.
+ *
+ * @param writer The MPack writer.
+ * @param data Where to place the allocated data.
+ * @param size Where to write the size of the data.
+ */
+void mpack_writer_init_growable(mpack_writer_t* writer, char** data, size_t* size);
+#endif
+
+/**
+ * Initializes an MPack writer directly into an error state. Use this if you
+ * are writing a wrapper to mpack_writer_init() which can fail its setup.
+ */
+void mpack_writer_init_error(mpack_writer_t* writer, mpack_error_t error);
+
+#if MPACK_STDIO
+/**
+ * Initializes an MPack writer that writes to a file.
+ *
+ * @throws mpack_error_memory if allocation fails
+ * @throws mpack_error_io if the file cannot be opened
+ */
+void mpack_writer_init_filename(mpack_writer_t* writer, const char* filename);
+
+/**
+ * Deprecated.
+ *
+ * \deprecated Renamed to mpack_writer_init_filename().
+ */
+MPACK_INLINE void mpack_writer_init_file(mpack_writer_t* writer, const char* filename) {
+ mpack_writer_init_filename(writer, filename);
+}
+
+/**
+ * Initializes an MPack writer that writes to a libc FILE. This can be used to
+ * write to stdout or stderr, or to a file opened separately.
+ *
+ * @param writer The MPack writer.
+ * @param stdfile The FILE.
+ * @param close_when_done If true, fclose() will be called on the FILE when it
+ * is no longer needed. If false, the file will not be flushed or
+ * closed when writing is done.
+ *
+ * @note The writer is buffered. If you want to write other data to the FILE in
+ * between messages, you must flush it first.
+ *
+ * @see mpack_writer_flush_message
+ */
+void mpack_writer_init_stdfile(mpack_writer_t* writer, FILE* stdfile, bool close_when_done);
+#endif
+
+/** @cond */
+
+#define mpack_writer_init_stack_line_ex(line, writer) \
+ char mpack_buf_##line[MPACK_STACK_SIZE]; \
+ mpack_writer_init(writer, mpack_buf_##line, sizeof(mpack_buf_##line))
+
+#define mpack_writer_init_stack_line(line, writer) \
+ mpack_writer_init_stack_line_ex(line, writer)
+
+/*
+ * Initializes an MPack writer using stack space as a buffer. A flush function
+ * should be added to the writer to flush the buffer.
+ *
+ * This is currently undocumented since it's not entirely useful on its own.
+ */
+
+#define mpack_writer_init_stack(writer) \
+ mpack_writer_init_stack_line(__LINE__, (writer))
+
+/** @endcond */
+
+/**
+ * Cleans up the MPack writer, flushing and closing the underlying stream,
+ * if any. Returns the final error state of the writer.
+ *
+ * No flushing is performed if the writer is in an error state. The attached
+ * teardown function is called whether or not the writer is in an error state.
+ *
+ * This will assert in tracking mode if the writer is not in an error
+ * state and has any unclosed compound types. If you want to cancel
+ * writing in the middle of a document, you need to flag an error on
+ * the writer before destroying it (such as mpack_error_data).
+ *
+ * Note that a writer may raise an error and call your error handler during
+ * the final flush. It is safe to longjmp or throw out of this error handler,
+ * but if you do, the writer will not be destroyed, and the teardown function
+ * will not be called. You can still get the writer's error state, and you
+ * must call @ref mpack_writer_destroy() again. (The second call is guaranteed
+ * not to call your error handler again since the writer is already in an error
+ * state.)
+ *
+ * @see mpack_writer_set_error_handler
+ * @see mpack_writer_set_flush
+ * @see mpack_writer_set_teardown
+ * @see mpack_writer_flag_error
+ * @see mpack_error_data
+ */
+mpack_error_t mpack_writer_destroy(mpack_writer_t* writer);
+
+/**
+ * @}
+ */
+
+/**
+ * @name Configuration
+ * @{
+ */
+
+#if MPACK_COMPATIBILITY
+/**
+ * Sets the version of the MessagePack spec that will be generated.
+ *
+ * This can be used to interface with older libraries that do not support
+ * the newest MessagePack features (such as the @c str8 type.)
+ *
+ * @note This requires @ref MPACK_COMPATIBILITY.
+ */
+MPACK_INLINE void mpack_writer_set_version(mpack_writer_t* writer, mpack_version_t version) {
+ writer->version = version;
+}
+#endif
+
+/**
+ * Sets the custom pointer to pass to the writer callbacks, such as flush
+ * or teardown.
+ *
+ * @param writer The MPack writer.
+ * @param context User data to pass to the writer callbacks.
+ *
+ * @see mpack_writer_context()
+ */
+MPACK_INLINE void mpack_writer_set_context(mpack_writer_t* writer, void* context) {
+ writer->context = context;
+}
+
+/**
+ * Returns the custom context for writer callbacks.
+ *
+ * @see mpack_writer_set_context
+ * @see mpack_writer_set_flush
+ */
+MPACK_INLINE void* mpack_writer_context(mpack_writer_t* writer) {
+ return writer->context;
+}
+
+/**
+ * Sets the flush function to write out the data when the buffer is full.
+ *
+ * If no flush function is used, trying to write past the end of the
+ * buffer will result in mpack_error_too_big.
+ *
+ * This should normally be used with mpack_writer_set_context() to register
+ * a custom pointer to pass to the flush function.
+ *
+ * @param writer The MPack writer.
+ * @param flush The function to write out data from the buffer.
+ *
+ * @see mpack_writer_context()
+ */
+void mpack_writer_set_flush(mpack_writer_t* writer, mpack_writer_flush_t flush);
+
+/**
+ * Sets the error function to call when an error is flagged on the writer.
+ *
+ * This should normally be used with mpack_writer_set_context() to register
+ * a custom pointer to pass to the error function.
+ *
+ * See the definition of mpack_writer_error_t for more information about
+ * what you can do from an error callback.
+ *
+ * @see mpack_writer_error_t
+ * @param writer The MPack writer.
+ * @param error_fn The function to call when an error is flagged on the writer.
+ */
+MPACK_INLINE void mpack_writer_set_error_handler(mpack_writer_t* writer, mpack_writer_error_t error_fn) {
+ writer->error_fn = error_fn;
+}
+
+/**
+ * Sets the teardown function to call when the writer is destroyed.
+ *
+ * This should normally be used with mpack_writer_set_context() to register
+ * a custom pointer to pass to the teardown function.
+ *
+ * @param writer The MPack writer.
+ * @param teardown The function to call when the writer is destroyed.
+ */
+MPACK_INLINE void mpack_writer_set_teardown(mpack_writer_t* writer, mpack_writer_teardown_t teardown) {
+ writer->teardown = teardown;
+}
+
+/**
+ * @}
+ */
+
+/**
+ * @name Core Writer Functions
+ * @{
+ */
+
+/**
+ * Flushes any buffered data to the underlying stream.
+ *
+ * If write tracking is enabled, this will break and flag @ref
+ * mpack_error_bug if the writer has any open compound types, ensuring
+ * that no compound types are still open. This prevents a "missing
+ * finish" bug from causing a never-ending message.
+ *
+ * If the writer is connected to a socket and you are keeping it open,
+ * you will want to call this after writing a message (or set of
+ * messages) so that the data is actually sent.
+ *
+ * It is not necessary to call this if you are not keeping the writer
+ * open afterwards. You can just call `mpack_writer_destroy()`, and it
+ * will flush before cleaning up.
+ *
+ * This will assert if no flush function is assigned to the writer.
+ */
+void mpack_writer_flush_message(mpack_writer_t* writer);
+
+/**
+ * Returns the number of bytes currently stored in the buffer. This
+ * may be less than the total number of bytes written if bytes have
+ * been flushed to an underlying stream.
+ */
+MPACK_INLINE size_t mpack_writer_buffer_used(mpack_writer_t* writer) {
+ return (size_t)(writer->current - writer->buffer);
+}
+
+/**
+ * Returns the amount of space left in the buffer. This may be reset
+ * after a write if bytes are flushed to an underlying stream.
+ */
+MPACK_INLINE size_t mpack_writer_buffer_left(mpack_writer_t* writer) {
+ return (size_t)(writer->end - writer->current);
+}
+
+/**
+ * Returns the (current) size of the buffer. This may change after a write if
+ * the flush callback changes the buffer.
+ */
+MPACK_INLINE size_t mpack_writer_buffer_size(mpack_writer_t* writer) {
+ return (size_t)(writer->end - writer->buffer);
+}
+
+/**
+ * Places the writer in the given error state, calling the error callback if one
+ * is set.
+ *
+ * This allows you to externally flag errors, for example if you are validating
+ * data as you write it, or if you want to cancel writing in the middle of a
+ * document. (The writer will assert if you try to destroy it without error and
+ * with unclosed compound types. In this case you should flag mpack_error_data
+ * before destroying it.)
+ *
+ * If the writer is already in an error state, this call is ignored and no
+ * error callback is called.
+ *
+ * @see mpack_writer_destroy
+ * @see mpack_error_data
+ */
+void mpack_writer_flag_error(mpack_writer_t* writer, mpack_error_t error);
+
+/**
+ * Queries the error state of the MPack writer.
+ *
+ * If a writer is in an error state, you should discard all data since the
+ * last time the error flag was checked. The error flag cannot be cleared.
+ */
+MPACK_INLINE mpack_error_t mpack_writer_error(mpack_writer_t* writer) {
+ return writer->error;
+}
+
+/**
+ * Writes a MessagePack object header (an MPack Tag.)
+ *
+ * If the value is a map, array, string, binary or extension type, the
+ * containing elements or bytes must be written separately and the
+ * appropriate finish function must be called (as though one of the
+ * mpack_start_*() functions was called.)
+ *
+ * @see mpack_write_bytes()
+ * @see mpack_finish_map()
+ * @see mpack_finish_array()
+ * @see mpack_finish_str()
+ * @see mpack_finish_bin()
+ * @see mpack_finish_ext()
+ * @see mpack_finish_type()
+ */
+void mpack_write_tag(mpack_writer_t* writer, mpack_tag_t tag);
+
+/**
+ * @}
+ */
+
+/**
+ * @name Integers
+ * @{
+ */
+
+/** Writes an 8-bit integer in the most efficient packing available. */
+void mpack_write_i8(mpack_writer_t* writer, int8_t value);
+
+/** Writes a 16-bit integer in the most efficient packing available. */
+void mpack_write_i16(mpack_writer_t* writer, int16_t value);
+
+/** Writes a 32-bit integer in the most efficient packing available. */
+void mpack_write_i32(mpack_writer_t* writer, int32_t value);
+
+/** Writes a 64-bit integer in the most efficient packing available. */
+void mpack_write_i64(mpack_writer_t* writer, int64_t value);
+
+/** Writes an integer in the most efficient packing available. */
+MPACK_INLINE void mpack_write_int(mpack_writer_t* writer, int64_t value) {
+ mpack_write_i64(writer, value);
+}
+
+/** Writes an 8-bit unsigned integer in the most efficient packing available. */
+void mpack_write_u8(mpack_writer_t* writer, uint8_t value);
+
+/** Writes an 16-bit unsigned integer in the most efficient packing available. */
+void mpack_write_u16(mpack_writer_t* writer, uint16_t value);
+
+/** Writes an 32-bit unsigned integer in the most efficient packing available. */
+void mpack_write_u32(mpack_writer_t* writer, uint32_t value);
+
+/** Writes an 64-bit unsigned integer in the most efficient packing available. */
+void mpack_write_u64(mpack_writer_t* writer, uint64_t value);
+
+/** Writes an unsigned integer in the most efficient packing available. */
+MPACK_INLINE void mpack_write_uint(mpack_writer_t* writer, uint64_t value) {
+ mpack_write_u64(writer, value);
+}
+
+/**
+ * @}
+ */
+
+/**
+ * @name Other Basic Types
+ * @{
+ */
+
+/** Writes a float. */
+void mpack_write_float(mpack_writer_t* writer, float value);
+
+/** Writes a double. */
+void mpack_write_double(mpack_writer_t* writer, double value);
+
+/** Writes a boolean. */
+void mpack_write_bool(mpack_writer_t* writer, bool value);
+
+/** Writes a boolean with value true. */
+void mpack_write_true(mpack_writer_t* writer);
+
+/** Writes a boolean with value false. */
+void mpack_write_false(mpack_writer_t* writer);
+
+/** Writes a nil. */
+void mpack_write_nil(mpack_writer_t* writer);
+
+/** Write a pre-encoded messagepack object */
+void mpack_write_object_bytes(mpack_writer_t* writer, const char* data, size_t bytes);
+
+#if MPACK_EXTENSIONS
+/**
+ * Writes a timestamp.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ *
+ * @param writer The writer
+ * @param seconds The (signed) number of seconds since 1970-01-01T00:00:00Z.
+ * @param nanoseconds The additional number of nanoseconds from 0 to 999,999,999 inclusive.
+ */
+void mpack_write_timestamp(mpack_writer_t* writer, int64_t seconds, uint32_t nanoseconds);
+
+/**
+ * Writes a timestamp with the given number of seconds (and zero nanoseconds).
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ *
+ * @param writer The writer
+ * @param seconds The (signed) number of seconds since 1970-01-01T00:00:00Z.
+ */
+MPACK_INLINE void mpack_write_timestamp_seconds(mpack_writer_t* writer, int64_t seconds) {
+ mpack_write_timestamp(writer, seconds, 0);
+}
+
+/**
+ * Writes a timestamp.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ */
+MPACK_INLINE void mpack_write_timestamp_struct(mpack_writer_t* writer, mpack_timestamp_t timestamp) {
+ mpack_write_timestamp(writer, timestamp.seconds, timestamp.nanoseconds);
+}
+#endif
+
+/**
+ * @}
+ */
+
+/**
+ * @name Map and Array Functions
+ * @{
+ */
+
+/**
+ * Opens an array.
+ *
+ * `count` elements must follow, and mpack_finish_array() must be called
+ * when done.
+ *
+ * @see mpack_finish_array()
+ */
+void mpack_start_array(mpack_writer_t* writer, uint32_t count);
+
+/**
+ * Opens a map.
+ *
+ * `count * 2` elements must follow, and mpack_finish_map() must be called
+ * when done.
+ *
+ * Remember that while map elements in MessagePack are implicitly ordered,
+ * they are not ordered in JSON. If you need elements to be read back
+ * in the order they are written, consider use an array instead.
+ *
+ * @see mpack_finish_map()
+ */
+void mpack_start_map(mpack_writer_t* writer, uint32_t count);
+
+/**
+ * Finishes writing an array.
+ *
+ * This should be called only after a corresponding call to mpack_start_array()
+ * and after the array contents are written.
+ *
+ * This will track writes to ensure that the correct number of elements are written.
+ *
+ * @see mpack_start_array()
+ */
+MPACK_INLINE void mpack_finish_array(mpack_writer_t* writer) {
+ mpack_writer_track_pop(writer, mpack_type_array);
+}
+
+/**
+ * Finishes writing a map.
+ *
+ * This should be called only after a corresponding call to mpack_start_map()
+ * and after the map contents are written.
+ *
+ * This will track writes to ensure that the correct number of elements are written.
+ *
+ * @see mpack_start_map()
+ */
+MPACK_INLINE void mpack_finish_map(mpack_writer_t* writer) {
+ mpack_writer_track_pop(writer, mpack_type_map);
+}
+
+/**
+ * @}
+ */
+
+/**
+ * @name Data Helpers
+ * @{
+ */
+
+/**
+ * Writes a string.
+ *
+ * To stream a string in chunks, use mpack_start_str() instead.
+ *
+ * MPack does not care about the underlying encoding, but UTF-8 is highly
+ * recommended, especially for compatibility with JSON. You should consider
+ * calling mpack_write_utf8() instead, especially if you will be reading
+ * it back as UTF-8.
+ *
+ * You should not call mpack_finish_str() after calling this; this
+ * performs both start and finish.
+ */
+void mpack_write_str(mpack_writer_t* writer, const char* str, uint32_t length);
+
+/**
+ * Writes a string, ensuring that it is valid UTF-8.
+ *
+ * This does not accept any UTF-8 variant such as Modified UTF-8, CESU-8 or
+ * WTF-8. Only pure UTF-8 is allowed.
+ *
+ * You should not call mpack_finish_str() after calling this; this
+ * performs both start and finish.
+ *
+ * @throws mpack_error_invalid if the string is not valid UTF-8
+ */
+void mpack_write_utf8(mpack_writer_t* writer, const char* str, uint32_t length);
+
+/**
+ * Writes a null-terminated string. (The null-terminator is not written.)
+ *
+ * MPack does not care about the underlying encoding, but UTF-8 is highly
+ * recommended, especially for compatibility with JSON. You should consider
+ * calling mpack_write_utf8_cstr() instead, especially if you will be reading
+ * it back as UTF-8.
+ *
+ * You should not call mpack_finish_str() after calling this; this
+ * performs both start and finish.
+ */
+void mpack_write_cstr(mpack_writer_t* writer, const char* cstr);
+
+/**
+ * Writes a null-terminated string, or a nil node if the given cstr pointer
+ * is NULL. (The null-terminator is not written.)
+ *
+ * MPack does not care about the underlying encoding, but UTF-8 is highly
+ * recommended, especially for compatibility with JSON. You should consider
+ * calling mpack_write_utf8_cstr_or_nil() instead, especially if you will
+ * be reading it back as UTF-8.
+ *
+ * You should not call mpack_finish_str() after calling this; this
+ * performs both start and finish.
+ */
+void mpack_write_cstr_or_nil(mpack_writer_t* writer, const char* cstr);
+
+/**
+ * Writes a null-terminated string, ensuring that it is valid UTF-8. (The
+ * null-terminator is not written.)
+ *
+ * This does not accept any UTF-8 variant such as Modified UTF-8, CESU-8 or
+ * WTF-8. Only pure UTF-8 is allowed.
+ *
+ * You should not call mpack_finish_str() after calling this; this
+ * performs both start and finish.
+ *
+ * @throws mpack_error_invalid if the string is not valid UTF-8
+ */
+void mpack_write_utf8_cstr(mpack_writer_t* writer, const char* cstr);
+
+/**
+ * Writes a null-terminated string ensuring that it is valid UTF-8, or
+ * writes nil if the given cstr pointer is NULL. (The null-terminator
+ * is not written.)
+ *
+ * This does not accept any UTF-8 variant such as Modified UTF-8, CESU-8 or
+ * WTF-8. Only pure UTF-8 is allowed.
+ *
+ * You should not call mpack_finish_str() after calling this; this
+ * performs both start and finish.
+ *
+ * @throws mpack_error_invalid if the string is not valid UTF-8
+ */
+void mpack_write_utf8_cstr_or_nil(mpack_writer_t* writer, const char* cstr);
+
+/**
+ * Writes a binary blob.
+ *
+ * To stream a binary blob in chunks, use mpack_start_bin() instead.
+ *
+ * You should not call mpack_finish_bin() after calling this; this
+ * performs both start and finish.
+ */
+void mpack_write_bin(mpack_writer_t* writer, const char* data, uint32_t count);
+
+#if MPACK_EXTENSIONS
+/**
+ * Writes an extension type.
+ *
+ * To stream an extension blob in chunks, use mpack_start_ext() instead.
+ *
+ * Extension types [0, 127] are available for application-specific types. Extension
+ * types [-128, -1] are reserved for future extensions of MessagePack.
+ *
+ * You should not call mpack_finish_ext() after calling this; this
+ * performs both start and finish.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ */
+void mpack_write_ext(mpack_writer_t* writer, int8_t exttype, const char* data, uint32_t count);
+#endif
+
+/**
+ * @}
+ */
+
+/**
+ * @name Chunked Data Functions
+ * @{
+ */
+
+/**
+ * Opens a string. `count` bytes should be written with calls to
+ * mpack_write_bytes(), and mpack_finish_str() should be called
+ * when done.
+ *
+ * To write an entire string at once, use mpack_write_str() or
+ * mpack_write_cstr() instead.
+ *
+ * MPack does not care about the underlying encoding, but UTF-8 is highly
+ * recommended, especially for compatibility with JSON.
+ */
+void mpack_start_str(mpack_writer_t* writer, uint32_t count);
+
+/**
+ * Opens a binary blob. `count` bytes should be written with calls to
+ * mpack_write_bytes(), and mpack_finish_bin() should be called
+ * when done.
+ */
+void mpack_start_bin(mpack_writer_t* writer, uint32_t count);
+
+#if MPACK_EXTENSIONS
+/**
+ * Opens an extension type. `count` bytes should be written with calls
+ * to mpack_write_bytes(), and mpack_finish_ext() should be called
+ * when done.
+ *
+ * Extension types [0, 127] are available for application-specific types. Extension
+ * types [-128, -1] are reserved for future extensions of MessagePack.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ */
+void mpack_start_ext(mpack_writer_t* writer, int8_t exttype, uint32_t count);
+#endif
+
+/**
+ * Writes a portion of bytes for a string, binary blob or extension type which
+ * was opened by mpack_write_tag() or one of the mpack_start_*() functions.
+ *
+ * This can be called multiple times to write the data in chunks, as long as
+ * the total amount of bytes written matches the count given when the compound
+ * type was started.
+ *
+ * The corresponding mpack_finish_*() function must be called when done.
+ *
+ * To write an entire string, binary blob or extension type at
+ * once, use one of the mpack_write_*() functions instead.
+ *
+ * @see mpack_write_tag()
+ * @see mpack_start_str()
+ * @see mpack_start_bin()
+ * @see mpack_start_ext()
+ * @see mpack_finish_str()
+ * @see mpack_finish_bin()
+ * @see mpack_finish_ext()
+ * @see mpack_finish_type()
+ */
+void mpack_write_bytes(mpack_writer_t* writer, const char* data, size_t count);
+
+/**
+ * Finishes writing a string.
+ *
+ * This should be called only after a corresponding call to mpack_start_str()
+ * and after the string bytes are written with mpack_write_bytes().
+ *
+ * This will track writes to ensure that the correct number of elements are written.
+ *
+ * @see mpack_start_str()
+ * @see mpack_write_bytes()
+ */
+MPACK_INLINE void mpack_finish_str(mpack_writer_t* writer) {
+ mpack_writer_track_pop(writer, mpack_type_str);
+}
+
+/**
+ * Finishes writing a binary blob.
+ *
+ * This should be called only after a corresponding call to mpack_start_bin()
+ * and after the binary bytes are written with mpack_write_bytes().
+ *
+ * This will track writes to ensure that the correct number of bytes are written.
+ *
+ * @see mpack_start_bin()
+ * @see mpack_write_bytes()
+ */
+MPACK_INLINE void mpack_finish_bin(mpack_writer_t* writer) {
+ mpack_writer_track_pop(writer, mpack_type_bin);
+}
+
+#if MPACK_EXTENSIONS
+/**
+ * Finishes writing an extended type binary data blob.
+ *
+ * This should be called only after a corresponding call to mpack_start_bin()
+ * and after the binary bytes are written with mpack_write_bytes().
+ *
+ * This will track writes to ensure that the correct number of bytes are written.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ *
+ * @see mpack_start_ext()
+ * @see mpack_write_bytes()
+ */
+MPACK_INLINE void mpack_finish_ext(mpack_writer_t* writer) {
+ mpack_writer_track_pop(writer, mpack_type_ext);
+}
+#endif
+
+/**
+ * Finishes writing the given compound type.
+ *
+ * This will track writes to ensure that the correct number of elements
+ * or bytes are written.
+ *
+ * This can be called with the appropriate type instead the corresponding
+ * mpack_finish_*() function if you want to finish a dynamic type.
+ */
+MPACK_INLINE void mpack_finish_type(mpack_writer_t* writer, mpack_type_t type) {
+ mpack_writer_track_pop(writer, type);
+}
+
+/**
+ * @}
+ */
+
+#if MPACK_HAS_GENERIC && !defined(__cplusplus)
+
+/**
+ * @name Type-Generic Writers
+ * @{
+ */
+
+/**
+ * @def mpack_write(writer, value)
+ *
+ * Type-generic writer for primitive types.
+ *
+ * The compiler will dispatch to an appropriate write function based
+ * on the type of the @a value parameter.
+ *
+ * @note This requires C11 `_Generic` support. (A set of inline overloads
+ * are used in C++ to provide the same functionality.)
+ *
+ * @warning In C11, the indentifiers `true`, `false` and `NULL` are
+ * all of type `int`, not `bool` or `void*`! They will emit unexpected
+ * types when passed uncast, so be careful when using them.
+ */
+#define mpack_write(writer, value) \
+ _Generic(((void)0, value), \
+ int8_t: mpack_write_i8, \
+ int16_t: mpack_write_i16, \
+ int32_t: mpack_write_i32, \
+ int64_t: mpack_write_i64, \
+ uint8_t: mpack_write_u8, \
+ uint16_t: mpack_write_u16, \
+ uint32_t: mpack_write_u32, \
+ uint64_t: mpack_write_u64, \
+ bool: mpack_write_bool, \
+ float: mpack_write_float, \
+ double: mpack_write_double, \
+ char *: mpack_write_cstr_or_nil, \
+ const char *: mpack_write_cstr_or_nil \
+ )(writer, value)
+
+/**
+ * @def mpack_write_kv(writer, key, value)
+ *
+ * Type-generic writer for key-value pairs of null-terminated string
+ * keys and primitive values.
+ *
+ * @warning @a writer may be evaluated multiple times.
+ *
+ * @warning In C11, the indentifiers `true`, `false` and `NULL` are
+ * all of type `int`, not `bool` or `void*`! They will emit unexpected
+ * types when passed uncast, so be careful when using them.
+ *
+ * @param writer The writer.
+ * @param key A null-terminated C string.
+ * @param value A primitive type supported by mpack_write().
+ */
+#define mpack_write_kv(writer, key, value) do { \
+ mpack_write_cstr(writer, key); \
+ mpack_write(writer, value); \
+} while (0)
+
+/**
+ * @}
+ */
+
+#endif // MPACK_HAS_GENERIC && !defined(__cplusplus)
+
+// The rest of this file contains C++ overloads, so we end extern "C" here.
+MPACK_EXTERN_C_END
+
+#if defined(__cplusplus) || defined(MPACK_DOXYGEN)
+
+/**
+ * @name C++ write overloads
+ * @{
+ */
+
+/*
+ * C++ generic writers for primitive values
+ */
+
+#ifdef MPACK_DOXYGEN
+#undef mpack_write
+#undef mpack_write_kv
+#endif
+
+MPACK_INLINE void mpack_write(mpack_writer_t* writer, int8_t value) {
+ mpack_write_i8(writer, value);
+}
+
+MPACK_INLINE void mpack_write(mpack_writer_t* writer, int16_t value) {
+ mpack_write_i16(writer, value);
+}
+
+MPACK_INLINE void mpack_write(mpack_writer_t* writer, int32_t value) {
+ mpack_write_i32(writer, value);
+}
+
+MPACK_INLINE void mpack_write(mpack_writer_t* writer, int64_t value) {
+ mpack_write_i64(writer, value);
+}
+
+MPACK_INLINE void mpack_write(mpack_writer_t* writer, uint8_t value) {
+ mpack_write_u8(writer, value);
+}
+
+MPACK_INLINE void mpack_write(mpack_writer_t* writer, uint16_t value) {
+ mpack_write_u16(writer, value);
+}
+
+MPACK_INLINE void mpack_write(mpack_writer_t* writer, uint32_t value) {
+ mpack_write_u32(writer, value);
+}
+
+MPACK_INLINE void mpack_write(mpack_writer_t* writer, uint64_t value) {
+ mpack_write_u64(writer, value);
+}
+
+MPACK_INLINE void mpack_write(mpack_writer_t* writer, bool value) {
+ mpack_write_bool(writer, value);
+}
+
+MPACK_INLINE void mpack_write(mpack_writer_t* writer, float value) {
+ mpack_write_float(writer, value);
+}
+
+MPACK_INLINE void mpack_write(mpack_writer_t* writer, double value) {
+ mpack_write_double(writer, value);
+}
+
+MPACK_INLINE void mpack_write(mpack_writer_t* writer, char *value) {
+ mpack_write_cstr_or_nil(writer, value);
+}
+
+MPACK_INLINE void mpack_write(mpack_writer_t* writer, const char *value) {
+ mpack_write_cstr_or_nil(writer, value);
+}
+
+/* C++ generic write for key-value pairs */
+
+MPACK_INLINE void mpack_write_kv(mpack_writer_t* writer, const char *key, int8_t value) {
+ mpack_write_cstr(writer, key);
+ mpack_write_i8(writer, value);
+}
+
+MPACK_INLINE void mpack_write_kv(mpack_writer_t* writer, const char *key, int16_t value) {
+ mpack_write_cstr(writer, key);
+ mpack_write_i16(writer, value);
+}
+
+MPACK_INLINE void mpack_write_kv(mpack_writer_t* writer, const char *key, int32_t value) {
+ mpack_write_cstr(writer, key);
+ mpack_write_i32(writer, value);
+}
+
+MPACK_INLINE void mpack_write_kv(mpack_writer_t* writer, const char *key, int64_t value) {
+ mpack_write_cstr(writer, key);
+ mpack_write_i64(writer, value);
+}
+
+MPACK_INLINE void mpack_write_kv(mpack_writer_t* writer, const char *key, uint8_t value) {
+ mpack_write_cstr(writer, key);
+ mpack_write_u8(writer, value);
+}
+
+MPACK_INLINE void mpack_write_kv(mpack_writer_t* writer, const char *key, uint16_t value) {
+ mpack_write_cstr(writer, key);
+ mpack_write_u16(writer, value);
+}
+
+MPACK_INLINE void mpack_write_kv(mpack_writer_t* writer, const char *key, uint32_t value) {
+ mpack_write_cstr(writer, key);
+ mpack_write_u32(writer, value);
+}
+
+MPACK_INLINE void mpack_write_kv(mpack_writer_t* writer, const char *key, uint64_t value) {
+ mpack_write_cstr(writer, key);
+ mpack_write_u64(writer, value);
+}
+
+MPACK_INLINE void mpack_write_kv(mpack_writer_t* writer, const char *key, bool value) {
+ mpack_write_cstr(writer, key);
+ mpack_write_bool(writer, value);
+}
+
+MPACK_INLINE void mpack_write_kv(mpack_writer_t* writer, const char *key, float value) {
+ mpack_write_cstr(writer, key);
+ mpack_write_float(writer, value);
+}
+
+MPACK_INLINE void mpack_write_kv(mpack_writer_t* writer, const char *key, double value) {
+ mpack_write_cstr(writer, key);
+ mpack_write_double(writer, value);
+}
+
+MPACK_INLINE void mpack_write_kv(mpack_writer_t* writer, const char *key, char *value) {
+ mpack_write_cstr(writer, key);
+ mpack_write_cstr_or_nil(writer, value);
+}
+
+MPACK_INLINE void mpack_write_kv(mpack_writer_t* writer, const char *key, const char *value) {
+ mpack_write_cstr(writer, key);
+ mpack_write_cstr_or_nil(writer, value);
+}
+
+/**
+ * @}
+ */
+
+#endif /* __cplusplus */
+
+/**
+ * @}
+ */
+
+MPACK_HEADER_END
+
+#endif // MPACK_WRITER
+
+#endif
+
+/* mpack/mpack-reader.h.h */
+
+/**
+ * @file
+ *
+ * Declares the core MPack Tag Reader.
+ */
+
+#ifndef MPACK_READER_H
+#define MPACK_READER_H 1
+
+/* #include "mpack-common.h" */
+
+MPACK_HEADER_START
+MPACK_EXTERN_C_START
+
+#if MPACK_READER
+
+#if MPACK_READ_TRACKING
+struct mpack_track_t;
+#endif
+
+// The denominator to determine whether a read is a small
+// fraction of the buffer size.
+#define MPACK_READER_SMALL_FRACTION_DENOMINATOR 32
+
+/**
+ * @defgroup reader Reader API
+ *
+ * The MPack Reader API contains functions for imperatively reading dynamically
+ * typed data from a MessagePack stream.
+ *
+ * See @ref docs/reader.md for examples.
+ *
+ * @note If you are not writing code for an embedded device (or otherwise do
+ * not need maximum performance with minimal memory usage), you should not use
+ * this. You probably want to use the @link node Node API@endlink instead.
+ *
+ * This forms the basis of the @link expect Expect API@endlink, which can be
+ * used to interpret the stream of elements in expected types and value ranges.
+ *
+ * @{
+ */
+
+/**
+ * @def MPACK_READER_MINIMUM_BUFFER_SIZE
+ *
+ * The minimum buffer size for a reader with a fill function.
+ */
+#define MPACK_READER_MINIMUM_BUFFER_SIZE 32
+
+/**
+ * A buffered MessagePack decoder.
+ *
+ * The decoder wraps an existing buffer and, optionally, a fill function.
+ * This allows efficiently decoding data from existing memory buffers, files,
+ * streams, etc.
+ *
+ * All read operations are synchronous; they will block until the
+ * requested data is fully read, or an error occurs.
+ *
+ * This structure is opaque; its fields should not be accessed outside
+ * of MPack.
+ */
+typedef struct mpack_reader_t mpack_reader_t;
+
+/**
+ * The MPack reader's fill function. It should fill the buffer with at
+ * least one byte and at most the given @c count, returning the number
+ * of bytes written to the buffer.
+ *
+ * In case of error, it should flag an appropriate error on the reader
+ * (usually @ref mpack_error_io), or simply return zero. If zero is
+ * returned, mpack_error_io is raised.
+ *
+ * @note When reading from a stream, you should only copy and return
+ * the bytes that are immediately available. It is always safe to return
+ * less than the requested count as long as some non-zero number of bytes
+ * are read; if more bytes are needed, the read function will simply be
+ * called again.
+ *
+ * @see mpack_reader_context()
+ */
+typedef size_t (*mpack_reader_fill_t)(mpack_reader_t* reader, char* buffer, size_t count);
+
+/**
+ * The MPack reader's skip function. It should discard the given number
+ * of bytes from the source (for example by seeking forward.)
+ *
+ * In case of error, it should flag an appropriate error on the reader.
+ *
+ * @see mpack_reader_context()
+ */
+typedef void (*mpack_reader_skip_t)(mpack_reader_t* reader, size_t count);
+
+/**
+ * An error handler function to be called when an error is flagged on
+ * the reader.
+ *
+ * The error handler will only be called once on the first error flagged;
+ * any subsequent reads and errors are ignored, and the reader is
+ * permanently in that error state.
+ *
+ * MPack is safe against non-local jumps out of error handler callbacks.
+ * This means you are allowed to longjmp or throw an exception (in C++,
+ * Objective-C, or with SEH) out of this callback.
+ *
+ * Bear in mind when using longjmp that local non-volatile variables that
+ * have changed are undefined when setjmp() returns, so you can't put the
+ * reader on the stack in the same activation frame as the setjmp without
+ * declaring it volatile.
+ *
+ * You must still eventually destroy the reader. It is not destroyed
+ * automatically when an error is flagged. It is safe to destroy the
+ * reader within this error callback, but you will either need to perform
+ * a non-local jump, or store something in your context to identify
+ * that the reader is destroyed since any future accesses to it cause
+ * undefined behavior.
+ */
+typedef void (*mpack_reader_error_t)(mpack_reader_t* reader, mpack_error_t error);
+
+/**
+ * A teardown function to be called when the reader is destroyed.
+ */
+typedef void (*mpack_reader_teardown_t)(mpack_reader_t* reader);
+
+/* Hide internals from documentation */
+/** @cond */
+
+struct mpack_reader_t {
+ void* context; /* Context for reader callbacks */
+ mpack_reader_fill_t fill; /* Function to read bytes into the buffer */
+ mpack_reader_error_t error_fn; /* Function to call on error */
+ mpack_reader_teardown_t teardown; /* Function to teardown the context on destroy */
+ mpack_reader_skip_t skip; /* Function to skip bytes from the source */
+
+ char* buffer; /* Writeable byte buffer */
+ size_t size; /* Size of the buffer */
+
+ const char* data; /* Current data pointer (in the buffer, if it is used) */
+ const char* end; /* The end of available data (in the buffer, if it is used) */
+
+ mpack_error_t error; /* Error state */
+
+ #if MPACK_READ_TRACKING
+ mpack_track_t track; /* Stack of map/array/str/bin/ext reads */
+ #endif
+};
+
+/** @endcond */
+
+/**
+ * @name Lifecycle Functions
+ * @{
+ */
+
+/**
+ * Initializes an MPack reader with the given buffer. The reader does
+ * not assume ownership of the buffer, but the buffer must be writeable
+ * if a fill function will be used to refill it.
+ *
+ * @param reader The MPack reader.
+ * @param buffer The buffer with which to read MessagePack data.
+ * @param size The size of the buffer.
+ * @param count The number of bytes already in the buffer.
+ */
+void mpack_reader_init(mpack_reader_t* reader, char* buffer, size_t size, size_t count);
+
+/**
+ * Initializes an MPack reader directly into an error state. Use this if you
+ * are writing a wrapper to mpack_reader_init() which can fail its setup.
+ */
+void mpack_reader_init_error(mpack_reader_t* reader, mpack_error_t error);
+
+/**
+ * Initializes an MPack reader to parse a pre-loaded contiguous chunk of data. The
+ * reader does not assume ownership of the data.
+ *
+ * @param reader The MPack reader.
+ * @param data The data to parse.
+ * @param count The number of bytes pointed to by data.
+ */
+void mpack_reader_init_data(mpack_reader_t* reader, const char* data, size_t count);
+
+#if MPACK_STDIO
+/**
+ * Initializes an MPack reader that reads from a file.
+ *
+ * The file will be automatically opened and closed by the reader.
+ */
+void mpack_reader_init_filename(mpack_reader_t* reader, const char* filename);
+
+/**
+ * Deprecated.
+ *
+ * \deprecated Renamed to mpack_reader_init_filename().
+ */
+MPACK_INLINE void mpack_reader_init_file(mpack_reader_t* reader, const char* filename) {
+ mpack_reader_init_filename(reader, filename);
+}
+
+/**
+ * Initializes an MPack reader that reads from a libc FILE. This can be used to
+ * read from stdin, or from a file opened separately.
+ *
+ * @param reader The MPack reader.
+ * @param stdfile The FILE.
+ * @param close_when_done If true, fclose() will be called on the FILE when it
+ * is no longer needed. If false, the file will not be closed when
+ * reading is done.
+ *
+ * @warning The reader is buffered. It will read data in advance of parsing it,
+ * and it may read more data than it parsed. See mpack_reader_remaining() to
+ * access the extra data.
+ */
+void mpack_reader_init_stdfile(mpack_reader_t* reader, FILE* stdfile, bool close_when_done);
+#endif
+
+/**
+ * @def mpack_reader_init_stack(reader)
+ * @hideinitializer
+ *
+ * Initializes an MPack reader using stack space as a buffer. A fill function
+ * should be added to the reader to fill the buffer.
+ *
+ * @see mpack_reader_set_fill
+ */
+
+/** @cond */
+#define mpack_reader_init_stack_line_ex(line, reader) \
+ char mpack_buf_##line[MPACK_STACK_SIZE]; \
+ mpack_reader_init((reader), mpack_buf_##line, sizeof(mpack_buf_##line), 0)
+
+#define mpack_reader_init_stack_line(line, reader) \
+ mpack_reader_init_stack_line_ex(line, reader)
+/** @endcond */
+
+#define mpack_reader_init_stack(reader) \
+ mpack_reader_init_stack_line(__LINE__, (reader))
+
+/**
+ * Cleans up the MPack reader, ensuring that all compound elements
+ * have been completely read. Returns the final error state of the
+ * reader.
+ *
+ * This will assert in tracking mode if the reader is not in an error
+ * state and has any incomplete reads. If you want to cancel reading
+ * in the middle of a document, you need to flag an error on the reader
+ * before destroying it (such as mpack_error_data).
+ *
+ * @see mpack_read_tag()
+ * @see mpack_reader_flag_error()
+ * @see mpack_error_data
+ */
+mpack_error_t mpack_reader_destroy(mpack_reader_t* reader);
+
+/**
+ * @}
+ */
+
+/**
+ * @name Callbacks
+ * @{
+ */
+
+/**
+ * Sets the custom pointer to pass to the reader callbacks, such as fill
+ * or teardown.
+ *
+ * @param reader The MPack reader.
+ * @param context User data to pass to the reader callbacks.
+ *
+ * @see mpack_reader_context()
+ */
+MPACK_INLINE void mpack_reader_set_context(mpack_reader_t* reader, void* context) {
+ reader->context = context;
+}
+
+/**
+ * Returns the custom context for reader callbacks.
+ *
+ * @see mpack_reader_set_context
+ * @see mpack_reader_set_fill
+ * @see mpack_reader_set_skip
+ */
+MPACK_INLINE void* mpack_reader_context(mpack_reader_t* reader) {
+ return reader->context;
+}
+
+/**
+ * Sets the fill function to refill the data buffer when it runs out of data.
+ *
+ * If no fill function is used, truncated MessagePack data results in
+ * mpack_error_invalid (since the buffer is assumed to contain a
+ * complete MessagePack object.)
+ *
+ * If a fill function is used, truncated MessagePack data usually
+ * results in mpack_error_io (since the fill function fails to get
+ * the missing data.)
+ *
+ * This should normally be used with mpack_reader_set_context() to register
+ * a custom pointer to pass to the fill function.
+ *
+ * @param reader The MPack reader.
+ * @param fill The function to fetch additional data into the buffer.
+ */
+void mpack_reader_set_fill(mpack_reader_t* reader, mpack_reader_fill_t fill);
+
+/**
+ * Sets the skip function to discard bytes from the source stream.
+ *
+ * It's not necessary to implement this function. If the stream is not
+ * seekable, don't set a skip callback. The reader will fall back to
+ * using the fill function instead.
+ *
+ * This should normally be used with mpack_reader_set_context() to register
+ * a custom pointer to pass to the skip function.
+ *
+ * The skip function is ignored in size-optimized builds to reduce code
+ * size. Data will be skipped with the fill function when necessary.
+ *
+ * @param reader The MPack reader.
+ * @param skip The function to discard bytes from the source stream.
+ */
+void mpack_reader_set_skip(mpack_reader_t* reader, mpack_reader_skip_t skip);
+
+/**
+ * Sets the error function to call when an error is flagged on the reader.
+ *
+ * This should normally be used with mpack_reader_set_context() to register
+ * a custom pointer to pass to the error function.
+ *
+ * See the definition of mpack_reader_error_t for more information about
+ * what you can do from an error callback.
+ *
+ * @see mpack_reader_error_t
+ * @param reader The MPack reader.
+ * @param error_fn The function to call when an error is flagged on the reader.
+ */
+MPACK_INLINE void mpack_reader_set_error_handler(mpack_reader_t* reader, mpack_reader_error_t error_fn) {
+ reader->error_fn = error_fn;
+}
+
+/**
+ * Sets the teardown function to call when the reader is destroyed.
+ *
+ * This should normally be used with mpack_reader_set_context() to register
+ * a custom pointer to pass to the teardown function.
+ *
+ * @param reader The MPack reader.
+ * @param teardown The function to call when the reader is destroyed.
+ */
+MPACK_INLINE void mpack_reader_set_teardown(mpack_reader_t* reader, mpack_reader_teardown_t teardown) {
+ reader->teardown = teardown;
+}
+
+/**
+ * @}
+ */
+
+/**
+ * @name Core Reader Functions
+ * @{
+ */
+
+/**
+ * Queries the error state of the MPack reader.
+ *
+ * If a reader is in an error state, you should discard all data since the
+ * last time the error flag was checked. The error flag cannot be cleared.
+ */
+MPACK_INLINE mpack_error_t mpack_reader_error(mpack_reader_t* reader) {
+ return reader->error;
+}
+
+/**
+ * Places the reader in the given error state, calling the error callback if one
+ * is set.
+ *
+ * This allows you to externally flag errors, for example if you are validating
+ * data as you read it.
+ *
+ * If the reader is already in an error state, this call is ignored and no
+ * error callback is called.
+ */
+void mpack_reader_flag_error(mpack_reader_t* reader, mpack_error_t error);
+
+/**
+ * Places the reader in the given error state if the given error is not mpack_ok,
+ * returning the resulting error state of the reader.
+ *
+ * This allows you to externally flag errors, for example if you are validating
+ * data as you read it.
+ *
+ * If the given error is mpack_ok or if the reader is already in an error state,
+ * this call is ignored and the actual error state of the reader is returned.
+ */
+MPACK_INLINE mpack_error_t mpack_reader_flag_if_error(mpack_reader_t* reader, mpack_error_t error) {
+ if (error != mpack_ok)
+ mpack_reader_flag_error(reader, error);
+ return mpack_reader_error(reader);
+}
+
+/**
+ * Returns bytes left in the reader's buffer.
+ *
+ * If you are done reading MessagePack data but there is other interesting data
+ * following it, the reader may have buffered too much data. The number of bytes
+ * remaining in the buffer and a pointer to the position of those bytes can be
+ * queried here.
+ *
+ * If you know the length of the MPack chunk beforehand, it's better to instead
+ * have your fill function limit the data it reads so that the reader does not
+ * have extra data. In this case you can simply check that this returns zero.
+ *
+ * Returns 0 if the reader is in an error state.
+ *
+ * @param reader The MPack reader from which to query remaining data.
+ * @param data [out] A pointer to the remaining data, or NULL.
+ * @return The number of bytes remaining in the buffer.
+ */
+size_t mpack_reader_remaining(mpack_reader_t* reader, const char** data);
+
+/**
+ * Reads a MessagePack object header (an MPack tag.)
+ *
+ * If an error occurs, the reader is placed in an error state and a
+ * nil tag is returned. If the reader is already in an error state,
+ * a nil tag is returned.
+ *
+ * If the type is compound (i.e. is a map, array, string, binary or
+ * extension type), additional reads are required to get the contained
+ * data, and the corresponding done function must be called when done.
+ *
+ * @note Maps in JSON are unordered, so it is recommended not to expect
+ * a specific ordering for your map values in case your data is converted
+ * to/from JSON.
+ *
+ * @see mpack_read_bytes()
+ * @see mpack_done_array()
+ * @see mpack_done_map()
+ * @see mpack_done_str()
+ * @see mpack_done_bin()
+ * @see mpack_done_ext()
+ */
+mpack_tag_t mpack_read_tag(mpack_reader_t* reader);
+
+/**
+ * Parses the next MessagePack object header (an MPack tag) without
+ * advancing the reader.
+ *
+ * If an error occurs, the reader is placed in an error state and a
+ * nil tag is returned. If the reader is already in an error state,
+ * a nil tag is returned.
+ *
+ * @note Maps in JSON are unordered, so it is recommended not to expect
+ * a specific ordering for your map values in case your data is converted
+ * to/from JSON.
+ *
+ * @see mpack_read_tag()
+ * @see mpack_discard()
+ */
+mpack_tag_t mpack_peek_tag(mpack_reader_t* reader);
+
+/**
+ * @}
+ */
+
+/**
+ * @name String and Data Functions
+ * @{
+ */
+
+/**
+ * Skips bytes from the underlying stream. This is used only to
+ * skip the contents of a string, binary blob or extension object.
+ */
+void mpack_skip_bytes(mpack_reader_t* reader, size_t count);
+
+/**
+ * Reads bytes from a string, binary blob or extension object, copying
+ * them into the given buffer.
+ *
+ * A str, bin or ext must have been opened by a call to mpack_read_tag()
+ * which yielded one of these types, or by a call to an expect function
+ * such as mpack_expect_str() or mpack_expect_bin().
+ *
+ * If an error occurs, the buffer contents are undefined.
+ *
+ * This can be called multiple times for a single str, bin or ext
+ * to read the data in chunks. The total data read must add up
+ * to the size of the object.
+ *
+ * @param reader The MPack reader
+ * @param p The buffer in which to copy the bytes
+ * @param count The number of bytes to read
+ */
+void mpack_read_bytes(mpack_reader_t* reader, char* p, size_t count);
+
+/**
+ * Reads bytes from a string, ensures that the string is valid UTF-8,
+ * and copies the bytes into the given buffer.
+ *
+ * A string must have been opened by a call to mpack_read_tag() which
+ * yielded a string, or by a call to an expect function such as
+ * mpack_expect_str().
+ *
+ * The given byte count must match the complete size of the string as
+ * returned by the tag or expect function. You must ensure that the
+ * buffer fits the data.
+ *
+ * This does not accept any UTF-8 variant such as Modified UTF-8, CESU-8 or
+ * WTF-8. Only pure UTF-8 is allowed.
+ *
+ * If an error occurs, the buffer contents are undefined.
+ *
+ * Unlike mpack_read_bytes(), this cannot be used to read the data in
+ * chunks (since this might split a character's UTF-8 bytes, and the
+ * reader does not keep track of the UTF-8 decoding state between reads.)
+ *
+ * @throws mpack_error_type if the string contains invalid UTF-8.
+ */
+void mpack_read_utf8(mpack_reader_t* reader, char* p, size_t byte_count);
+
+/**
+ * Reads bytes from a string, ensures that the string contains no NUL
+ * bytes, copies the bytes into the given buffer and adds a null-terminator.
+ *
+ * A string must have been opened by a call to mpack_read_tag() which
+ * yielded a string, or by a call to an expect function such as
+ * mpack_expect_str().
+ *
+ * The given byte count must match the size of the string as returned
+ * by the tag or expect function. The string will only be copied if
+ * the buffer is large enough to store it.
+ *
+ * If an error occurs, the buffer will contain an empty string.
+ *
+ * @note If you know the object will be a string before reading it,
+ * it is highly recommended to use mpack_expect_cstr() instead.
+ * Alternatively you could use mpack_peek_tag() and call
+ * mpack_expect_cstr() if it's a string.
+ *
+ * @throws mpack_error_too_big if the string plus null-terminator is larger than the given buffer size
+ * @throws mpack_error_type if the string contains a null byte.
+ *
+ * @see mpack_peek_tag()
+ * @see mpack_expect_cstr()
+ * @see mpack_expect_utf8_cstr()
+ */
+void mpack_read_cstr(mpack_reader_t* reader, char* buf, size_t buffer_size, size_t byte_count);
+
+/**
+ * Reads bytes from a string, ensures that the string is valid UTF-8
+ * with no NUL bytes, copies the bytes into the given buffer and adds a
+ * null-terminator.
+ *
+ * A string must have been opened by a call to mpack_read_tag() which
+ * yielded a string, or by a call to an expect function such as
+ * mpack_expect_str().
+ *
+ * The given byte count must match the size of the string as returned
+ * by the tag or expect function. The string will only be copied if
+ * the buffer is large enough to store it.
+ *
+ * This does not accept any UTF-8 variant such as Modified UTF-8, CESU-8 or
+ * WTF-8. Only pure UTF-8 is allowed, but without the NUL character, since
+ * it cannot be represented in a null-terminated string.
+ *
+ * If an error occurs, the buffer will contain an empty string.
+ *
+ * @note If you know the object will be a string before reading it,
+ * it is highly recommended to use mpack_expect_utf8_cstr() instead.
+ * Alternatively you could use mpack_peek_tag() and call
+ * mpack_expect_utf8_cstr() if it's a string.
+ *
+ * @throws mpack_error_too_big if the string plus null-terminator is larger than the given buffer size
+ * @throws mpack_error_type if the string contains invalid UTF-8 or a null byte.
+ *
+ * @see mpack_peek_tag()
+ * @see mpack_expect_utf8_cstr()
+ */
+void mpack_read_utf8_cstr(mpack_reader_t* reader, char* buf, size_t buffer_size, size_t byte_count);
+
+#ifdef MPACK_MALLOC
+/** @cond */
+// This can optionally add a null-terminator, but it does not check
+// whether the data contains null bytes. This must be done separately
+// in a cstring read function (possibly as part of a UTF-8 check.)
+char* mpack_read_bytes_alloc_impl(mpack_reader_t* reader, size_t count, bool null_terminated);
+/** @endcond */
+
+/**
+ * Reads bytes from a string, binary blob or extension object, allocating
+ * storage for them and returning the allocated pointer.
+ *
+ * The allocated string must be freed with MPACK_FREE() (or simply free()
+ * if MPack's allocator hasn't been customized.)
+ *
+ * Returns NULL if any error occurs, or if count is zero.
+ */
+MPACK_INLINE char* mpack_read_bytes_alloc(mpack_reader_t* reader, size_t count) {
+ return mpack_read_bytes_alloc_impl(reader, count, false);
+}
+#endif
+
+/**
+ * Reads bytes from a string, binary blob or extension object in-place in
+ * the buffer. This can be used to avoid copying the data.
+ *
+ * A str, bin or ext must have been opened by a call to mpack_read_tag()
+ * which yielded one of these types, or by a call to an expect function
+ * such as mpack_expect_str() or mpack_expect_bin().
+ *
+ * If the bytes are from a string, the string is not null-terminated! Use
+ * mpack_read_cstr() to copy the string into a buffer and add a null-terminator.
+ *
+ * The returned pointer is invalidated on the next read, or when the buffer
+ * is destroyed.
+ *
+ * The reader will move data around in the buffer if needed to ensure that
+ * the pointer can always be returned, so this should only be used if
+ * count is very small compared to the buffer size. If you need to check
+ * whether a small size is reasonable (for example you intend to handle small and
+ * large sizes differently), you can call mpack_should_read_bytes_inplace().
+ *
+ * This can be called multiple times for a single str, bin or ext
+ * to read the data in chunks. The total data read must add up
+ * to the size of the object.
+ *
+ * NULL is returned if the reader is in an error state.
+ *
+ * @throws mpack_error_too_big if the requested size is larger than the buffer size
+ *
+ * @see mpack_should_read_bytes_inplace()
+ */
+const char* mpack_read_bytes_inplace(mpack_reader_t* reader, size_t count);
+
+/**
+ * Reads bytes from a string in-place in the buffer and ensures they are
+ * valid UTF-8. This can be used to avoid copying the data.
+ *
+ * A string must have been opened by a call to mpack_read_tag() which
+ * yielded a string, or by a call to an expect function such as
+ * mpack_expect_str().
+ *
+ * The string is not null-terminated! Use mpack_read_utf8_cstr() to
+ * copy the string into a buffer and add a null-terminator.
+ *
+ * The returned pointer is invalidated on the next read, or when the buffer
+ * is destroyed.
+ *
+ * The reader will move data around in the buffer if needed to ensure that
+ * the pointer can always be returned, so this should only be used if
+ * count is very small compared to the buffer size. If you need to check
+ * whether a small size is reasonable (for example you intend to handle small and
+ * large sizes differently), you can call mpack_should_read_bytes_inplace().
+ *
+ * This does not accept any UTF-8 variant such as Modified UTF-8, CESU-8 or
+ * WTF-8. Only pure UTF-8 is allowed.
+ *
+ * Unlike mpack_read_bytes_inplace(), this cannot be used to read the data in
+ * chunks (since this might split a character's UTF-8 bytes, and the
+ * reader does not keep track of the UTF-8 decoding state between reads.)
+ *
+ * NULL is returned if the reader is in an error state.
+ *
+ * @throws mpack_error_type if the string contains invalid UTF-8
+ * @throws mpack_error_too_big if the requested size is larger than the buffer size
+ *
+ * @see mpack_should_read_bytes_inplace()
+ */
+const char* mpack_read_utf8_inplace(mpack_reader_t* reader, size_t count);
+
+/**
+ * Returns true if it's a good idea to read the given number of bytes
+ * in-place.
+ *
+ * If the read will be larger than some small fraction of the buffer size,
+ * this will return false to avoid shuffling too much data back and forth
+ * in the buffer.
+ *
+ * Use this if you're expecting arbitrary size data, and you want to read
+ * in-place for the best performance when possible but will fall back to
+ * a normal read if the data is too large.
+ *
+ * @see mpack_read_bytes_inplace()
+ */
+MPACK_INLINE bool mpack_should_read_bytes_inplace(mpack_reader_t* reader, size_t count) {
+ return (reader->size == 0 || count <= reader->size / MPACK_READER_SMALL_FRACTION_DENOMINATOR);
+}
+
+#if MPACK_EXTENSIONS
+/**
+ * Reads a timestamp contained in an ext object of the given size, closing the
+ * ext type.
+ *
+ * An ext object of exttype @ref MPACK_EXTTYPE_TIMESTAMP must have been opened
+ * by a call to e.g. mpack_read_tag() or mpack_expect_ext().
+ *
+ * You must NOT call mpack_done_ext() after calling this. A timestamp ext
+ * object can only contain a single timestamp value, so this calls
+ * mpack_done_ext() automatically.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ *
+ * @throws mpack_error_invalid if the size is not one of the supported
+ * timestamp sizes, or if the nanoseconds are out of range.
+ */
+mpack_timestamp_t mpack_read_timestamp(mpack_reader_t* reader, size_t size);
+#endif
+
+/**
+ * @}
+ */
+
+/**
+ * @name Core Reader Functions
+ * @{
+ */
+
+#if MPACK_READ_TRACKING
+/**
+ * Finishes reading the given type.
+ *
+ * This will track reads to ensure that the correct number of elements
+ * or bytes are read.
+ */
+void mpack_done_type(mpack_reader_t* reader, mpack_type_t type);
+#else
+MPACK_INLINE void mpack_done_type(mpack_reader_t* reader, mpack_type_t type) {
+ MPACK_UNUSED(reader);
+ MPACK_UNUSED(type);
+}
+#endif
+
+/**
+ * Finishes reading an array.
+ *
+ * This will track reads to ensure that the correct number of elements are read.
+ */
+MPACK_INLINE void mpack_done_array(mpack_reader_t* reader) {
+ mpack_done_type(reader, mpack_type_array);
+}
+
+/**
+ * @fn mpack_done_map(mpack_reader_t* reader)
+ *
+ * Finishes reading a map.
+ *
+ * This will track reads to ensure that the correct number of elements are read.
+ */
+MPACK_INLINE void mpack_done_map(mpack_reader_t* reader) {
+ mpack_done_type(reader, mpack_type_map);
+}
+
+/**
+ * @fn mpack_done_str(mpack_reader_t* reader)
+ *
+ * Finishes reading a string.
+ *
+ * This will track reads to ensure that the correct number of bytes are read.
+ */
+MPACK_INLINE void mpack_done_str(mpack_reader_t* reader) {
+ mpack_done_type(reader, mpack_type_str);
+}
+
+/**
+ * @fn mpack_done_bin(mpack_reader_t* reader)
+ *
+ * Finishes reading a binary data blob.
+ *
+ * This will track reads to ensure that the correct number of bytes are read.
+ */
+MPACK_INLINE void mpack_done_bin(mpack_reader_t* reader) {
+ mpack_done_type(reader, mpack_type_bin);
+}
+
+#if MPACK_EXTENSIONS
+/**
+ * @fn mpack_done_ext(mpack_reader_t* reader)
+ *
+ * Finishes reading an extended type binary data blob.
+ *
+ * This will track reads to ensure that the correct number of bytes are read.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ */
+MPACK_INLINE void mpack_done_ext(mpack_reader_t* reader) {
+ mpack_done_type(reader, mpack_type_ext);
+}
+#endif
+
+/**
+ * Reads and discards the next object. This will read and discard all
+ * contained data as well if it is a compound type.
+ */
+void mpack_discard(mpack_reader_t* reader);
+
+/**
+ * @}
+ */
+
+/** @cond */
+
+#if MPACK_DEBUG && MPACK_STDIO
+/**
+ * @name Debugging Functions
+ * @{
+ */
+/*
+ * Converts a blob of MessagePack to a pseudo-JSON string for debugging
+ * purposes, placing the result in the given buffer with a null-terminator.
+ *
+ * If the buffer does not have enough space, the result will be truncated (but
+ * it is guaranteed to be null-terminated.)
+ *
+ * This is only available in debug mode, and only if stdio is available (since
+ * it uses snprintf().) It's strictly for debugging purposes.
+ */
+void mpack_print_data_to_buffer(const char* data, size_t data_size, char* buffer, size_t buffer_size);
+
+/*
+ * Converts a node to pseudo-JSON for debugging purposes, calling the given
+ * callback as many times as is necessary to output the character data.
+ *
+ * No null-terminator or trailing newline will be written.
+ *
+ * This is only available in debug mode, and only if stdio is available (since
+ * it uses snprintf().) It's strictly for debugging purposes.
+ */
+void mpack_print_data_to_callback(const char* data, size_t size, mpack_print_callback_t callback, void* context);
+
+/*
+ * Converts a blob of MessagePack to pseudo-JSON for debugging purposes
+ * and pretty-prints it to the given file.
+ */
+void mpack_print_data_to_file(const char* data, size_t len, FILE* file);
+
+/*
+ * Converts a blob of MessagePack to pseudo-JSON for debugging purposes
+ * and pretty-prints it to stdout.
+ */
+MPACK_INLINE void mpack_print_data_to_stdout(const char* data, size_t len) {
+ mpack_print_data_to_file(data, len, stdout);
+}
+
+/*
+ * Converts the MessagePack contained in the given `FILE*` to pseudo-JSON for
+ * debugging purposes, calling the given callback as many times as is necessary
+ * to output the character data.
+ */
+void mpack_print_stdfile_to_callback(FILE* file, mpack_print_callback_t callback, void* context);
+
+/*
+ * Deprecated.
+ *
+ * \deprecated Renamed to mpack_print_data_to_stdout().
+ */
+MPACK_INLINE void mpack_print(const char* data, size_t len) {
+ mpack_print_data_to_stdout(data, len);
+}
+
+/**
+ * @}
+ */
+#endif
+
+/** @endcond */
+
+/**
+ * @}
+ */
+
+
+
+#if MPACK_INTERNAL
+
+bool mpack_reader_ensure_straddle(mpack_reader_t* reader, size_t count);
+
+/*
+ * Ensures there are at least @c count bytes left in the
+ * data, raising an error and returning false if more
+ * data cannot be made available.
+ */
+MPACK_INLINE bool mpack_reader_ensure(mpack_reader_t* reader, size_t count) {
+ mpack_assert(count != 0, "cannot ensure zero bytes!");
+ mpack_assert(reader->error == mpack_ok, "reader cannot be in an error state!");
+
+ if (count <= (size_t)(reader->end - reader->data))
+ return true;
+ return mpack_reader_ensure_straddle(reader, count);
+}
+
+void mpack_read_native_straddle(mpack_reader_t* reader, char* p, size_t count);
+
+// Reads count bytes into p, deferring to mpack_read_native_straddle() if more
+// bytes are needed than are available in the buffer.
+MPACK_INLINE void mpack_read_native(mpack_reader_t* reader, char* p, size_t count) {
+ mpack_assert(count == 0 || p != NULL, "data pointer for %i bytes is NULL", (int)count);
+
+ if (count > (size_t)(reader->end - reader->data)) {
+ mpack_read_native_straddle(reader, p, count);
+ } else {
+ mpack_memcpy(p, reader->data, count);
+ reader->data += count;
+ }
+}
+
+#if MPACK_READ_TRACKING
+#define MPACK_READER_TRACK(reader, error_expr) \
+ (((reader)->error == mpack_ok) ? mpack_reader_flag_if_error((reader), (error_expr)) : (reader)->error)
+#else
+#define MPACK_READER_TRACK(reader, error_expr) (MPACK_UNUSED(reader), mpack_ok)
+#endif
+
+MPACK_INLINE mpack_error_t mpack_reader_track_element(mpack_reader_t* reader) {
+ return MPACK_READER_TRACK(reader, mpack_track_element(&reader->track, true));
+}
+
+MPACK_INLINE mpack_error_t mpack_reader_track_peek_element(mpack_reader_t* reader) {
+ return MPACK_READER_TRACK(reader, mpack_track_peek_element(&reader->track, true));
+}
+
+MPACK_INLINE mpack_error_t mpack_reader_track_bytes(mpack_reader_t* reader, size_t count) {
+ MPACK_UNUSED(count);
+ return MPACK_READER_TRACK(reader, mpack_track_bytes(&reader->track, true, count));
+}
+
+MPACK_INLINE mpack_error_t mpack_reader_track_str_bytes_all(mpack_reader_t* reader, size_t count) {
+ MPACK_UNUSED(count);
+ return MPACK_READER_TRACK(reader, mpack_track_str_bytes_all(&reader->track, true, count));
+}
+
+#endif
+
+
+
+#endif
+
+MPACK_EXTERN_C_END
+MPACK_HEADER_END
+
+#endif
+
+
+/* mpack/mpack-expect.h.h */
+
+/**
+ * @file
+ *
+ * Declares the MPack static Expect API.
+ */
+
+#ifndef MPACK_EXPECT_H
+#define MPACK_EXPECT_H 1
+
+/* #include "mpack-reader.h" */
+
+MPACK_HEADER_START
+MPACK_EXTERN_C_START
+
+#if MPACK_EXPECT
+
+#if !MPACK_READER
+#error "MPACK_EXPECT requires MPACK_READER."
+#endif
+
+/**
+ * @defgroup expect Expect API
+ *
+ * The MPack Expect API allows you to easily read MessagePack data when you
+ * expect it to follow a predefined schema.
+ *
+ * @note If you are not writing code for an embedded device (or otherwise do
+ * not need maximum performance with minimal memory usage), you should not use
+ * this. You probably want to use the @link node Node API@endlink instead.
+ *
+ * See @ref docs/expect.md for examples.
+ *
+ * The main purpose of the Expect API is convenience, so the API is lax. It
+ * automatically converts between similar types where there is no loss of
+ * precision.
+ *
+ * When using any of the expect functions, if the type or value of what was
+ * read does not match what is expected, @ref mpack_error_type is raised.
+ *
+ * @{
+ */
+
+/**
+ * @name Basic Number Functions
+ * @{
+ */
+
+/**
+ * Reads an 8-bit unsigned integer.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in an 8-bit unsigned int.
+ *
+ * Returns zero if an error occurs.
+ */
+uint8_t mpack_expect_u8(mpack_reader_t* reader);
+
+/**
+ * Reads a 16-bit unsigned integer.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 16-bit unsigned int.
+ *
+ * Returns zero if an error occurs.
+ */
+uint16_t mpack_expect_u16(mpack_reader_t* reader);
+
+/**
+ * Reads a 32-bit unsigned integer.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 32-bit unsigned int.
+ *
+ * Returns zero if an error occurs.
+ */
+uint32_t mpack_expect_u32(mpack_reader_t* reader);
+
+/**
+ * Reads a 64-bit unsigned integer.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 64-bit unsigned int.
+ *
+ * Returns zero if an error occurs.
+ */
+uint64_t mpack_expect_u64(mpack_reader_t* reader);
+
+/**
+ * Reads an 8-bit signed integer.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in an 8-bit signed int.
+ *
+ * Returns zero if an error occurs.
+ */
+int8_t mpack_expect_i8(mpack_reader_t* reader);
+
+/**
+ * Reads a 16-bit signed integer.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 16-bit signed int.
+ *
+ * Returns zero if an error occurs.
+ */
+int16_t mpack_expect_i16(mpack_reader_t* reader);
+
+/**
+ * Reads a 32-bit signed integer.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 32-bit signed int.
+ *
+ * Returns zero if an error occurs.
+ */
+int32_t mpack_expect_i32(mpack_reader_t* reader);
+
+/**
+ * Reads a 64-bit signed integer.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 64-bit signed int.
+ *
+ * Returns zero if an error occurs.
+ */
+int64_t mpack_expect_i64(mpack_reader_t* reader);
+
+/**
+ * Reads a number, returning the value as a float. The underlying value can be an
+ * integer, float or double; the value is converted to a float.
+ *
+ * @note Reading a double or a large integer with this function can incur a
+ * loss of precision.
+ *
+ * @throws mpack_error_type if the underlying value is not a float, double or integer.
+ */
+float mpack_expect_float(mpack_reader_t* reader);
+
+/**
+ * Reads a number, returning the value as a double. The underlying value can be an
+ * integer, float or double; the value is converted to a double.
+ *
+ * @note Reading a very large integer with this function can incur a
+ * loss of precision.
+ *
+ * @throws mpack_error_type if the underlying value is not a float, double or integer.
+ */
+double mpack_expect_double(mpack_reader_t* reader);
+
+/**
+ * Reads a float. The underlying value must be a float, not a double or an integer.
+ * This ensures no loss of precision can occur.
+ *
+ * @throws mpack_error_type if the underlying value is not a float.
+ */
+float mpack_expect_float_strict(mpack_reader_t* reader);
+
+/**
+ * Reads a double. The underlying value must be a float or double, not an integer.
+ * This ensures no loss of precision can occur.
+ *
+ * @throws mpack_error_type if the underlying value is not a float or double.
+ */
+double mpack_expect_double_strict(mpack_reader_t* reader);
+
+/**
+ * @}
+ */
+
+/**
+ * @name Ranged Number Functions
+ * @{
+ */
+
+/**
+ * Reads an 8-bit unsigned integer, ensuring that it falls within the given range.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in an 8-bit unsigned int.
+ *
+ * Returns min_value if an error occurs.
+ */
+uint8_t mpack_expect_u8_range(mpack_reader_t* reader, uint8_t min_value, uint8_t max_value);
+
+/**
+ * Reads a 16-bit unsigned integer, ensuring that it falls within the given range.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 16-bit unsigned int.
+ *
+ * Returns min_value if an error occurs.
+ */
+uint16_t mpack_expect_u16_range(mpack_reader_t* reader, uint16_t min_value, uint16_t max_value);
+
+/**
+ * Reads a 32-bit unsigned integer, ensuring that it falls within the given range.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 32-bit unsigned int.
+ *
+ * Returns min_value if an error occurs.
+ */
+uint32_t mpack_expect_u32_range(mpack_reader_t* reader, uint32_t min_value, uint32_t max_value);
+
+/**
+ * Reads a 64-bit unsigned integer, ensuring that it falls within the given range.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 64-bit unsigned int.
+ *
+ * Returns min_value if an error occurs.
+ */
+uint64_t mpack_expect_u64_range(mpack_reader_t* reader, uint64_t min_value, uint64_t max_value);
+
+/**
+ * Reads an unsigned integer, ensuring that it falls within the given range.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in an unsigned int.
+ *
+ * Returns min_value if an error occurs.
+ */
+MPACK_INLINE unsigned int mpack_expect_uint_range(mpack_reader_t* reader, unsigned int min_value, unsigned int max_value) {
+ // This should be true at compile-time, so this just wraps the 32-bit
+ // function. We fallback to 64-bit if for some reason sizeof(int) isn't 4.
+ if (sizeof(unsigned int) == 4)
+ return (unsigned int)mpack_expect_u32_range(reader, (uint32_t)min_value, (uint32_t)max_value);
+ return (unsigned int)mpack_expect_u64_range(reader, min_value, max_value);
+}
+
+/**
+ * Reads an 8-bit unsigned integer, ensuring that it is at most @a max_value.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in an 8-bit unsigned int.
+ *
+ * Returns 0 if an error occurs.
+ */
+MPACK_INLINE uint8_t mpack_expect_u8_max(mpack_reader_t* reader, uint8_t max_value) {
+ return mpack_expect_u8_range(reader, 0, max_value);
+}
+
+/**
+ * Reads a 16-bit unsigned integer, ensuring that it is at most @a max_value.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 16-bit unsigned int.
+ *
+ * Returns 0 if an error occurs.
+ */
+MPACK_INLINE uint16_t mpack_expect_u16_max(mpack_reader_t* reader, uint16_t max_value) {
+ return mpack_expect_u16_range(reader, 0, max_value);
+}
+
+/**
+ * Reads a 32-bit unsigned integer, ensuring that it is at most @a max_value.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 32-bit unsigned int.
+ *
+ * Returns 0 if an error occurs.
+ */
+MPACK_INLINE uint32_t mpack_expect_u32_max(mpack_reader_t* reader, uint32_t max_value) {
+ return mpack_expect_u32_range(reader, 0, max_value);
+}
+
+/**
+ * Reads a 64-bit unsigned integer, ensuring that it is at most @a max_value.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 64-bit unsigned int.
+ *
+ * Returns 0 if an error occurs.
+ */
+MPACK_INLINE uint64_t mpack_expect_u64_max(mpack_reader_t* reader, uint64_t max_value) {
+ return mpack_expect_u64_range(reader, 0, max_value);
+}
+
+/**
+ * Reads an unsigned integer, ensuring that it is at most @a max_value.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in an unsigned int.
+ *
+ * Returns 0 if an error occurs.
+ */
+MPACK_INLINE unsigned int mpack_expect_uint_max(mpack_reader_t* reader, unsigned int max_value) {
+ return mpack_expect_uint_range(reader, 0, max_value);
+}
+
+/**
+ * Reads an 8-bit signed integer, ensuring that it falls within the given range.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in an 8-bit signed int.
+ *
+ * Returns min_value if an error occurs.
+ */
+int8_t mpack_expect_i8_range(mpack_reader_t* reader, int8_t min_value, int8_t max_value);
+
+/**
+ * Reads a 16-bit signed integer, ensuring that it falls within the given range.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 16-bit signed int.
+ *
+ * Returns min_value if an error occurs.
+ */
+int16_t mpack_expect_i16_range(mpack_reader_t* reader, int16_t min_value, int16_t max_value);
+
+/**
+ * Reads a 32-bit signed integer, ensuring that it falls within the given range.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 32-bit signed int.
+ *
+ * Returns min_value if an error occurs.
+ */
+int32_t mpack_expect_i32_range(mpack_reader_t* reader, int32_t min_value, int32_t max_value);
+
+/**
+ * Reads a 64-bit signed integer, ensuring that it falls within the given range.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 64-bit signed int.
+ *
+ * Returns min_value if an error occurs.
+ */
+int64_t mpack_expect_i64_range(mpack_reader_t* reader, int64_t min_value, int64_t max_value);
+
+/**
+ * Reads a signed integer, ensuring that it falls within the given range.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a signed int.
+ *
+ * Returns min_value if an error occurs.
+ */
+MPACK_INLINE int mpack_expect_int_range(mpack_reader_t* reader, int min_value, int max_value) {
+ // This should be true at compile-time, so this just wraps the 32-bit
+ // function. We fallback to 64-bit if for some reason sizeof(int) isn't 4.
+ if (sizeof(int) == 4)
+ return (int)mpack_expect_i32_range(reader, (int32_t)min_value, (int32_t)max_value);
+ return (int)mpack_expect_i64_range(reader, min_value, max_value);
+}
+
+/**
+ * Reads an 8-bit signed integer, ensuring that it is at least zero and at
+ * most @a max_value.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in an 8-bit signed int.
+ *
+ * Returns 0 if an error occurs.
+ */
+MPACK_INLINE int8_t mpack_expect_i8_max(mpack_reader_t* reader, int8_t max_value) {
+ return mpack_expect_i8_range(reader, 0, max_value);
+}
+
+/**
+ * Reads a 16-bit signed integer, ensuring that it is at least zero and at
+ * most @a max_value.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 16-bit signed int.
+ *
+ * Returns 0 if an error occurs.
+ */
+MPACK_INLINE int16_t mpack_expect_i16_max(mpack_reader_t* reader, int16_t max_value) {
+ return mpack_expect_i16_range(reader, 0, max_value);
+}
+
+/**
+ * Reads a 32-bit signed integer, ensuring that it is at least zero and at
+ * most @a max_value.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 32-bit signed int.
+ *
+ * Returns 0 if an error occurs.
+ */
+MPACK_INLINE int32_t mpack_expect_i32_max(mpack_reader_t* reader, int32_t max_value) {
+ return mpack_expect_i32_range(reader, 0, max_value);
+}
+
+/**
+ * Reads a 64-bit signed integer, ensuring that it is at least zero and at
+ * most @a max_value.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 64-bit signed int.
+ *
+ * Returns 0 if an error occurs.
+ */
+MPACK_INLINE int64_t mpack_expect_i64_max(mpack_reader_t* reader, int64_t max_value) {
+ return mpack_expect_i64_range(reader, 0, max_value);
+}
+
+/**
+ * Reads an int, ensuring that it is at least zero and at most @a max_value.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a signed int.
+ *
+ * Returns 0 if an error occurs.
+ */
+MPACK_INLINE int mpack_expect_int_max(mpack_reader_t* reader, int max_value) {
+ return mpack_expect_int_range(reader, 0, max_value);
+}
+
+/**
+ * Reads a number, ensuring that it falls within the given range and returning
+ * the value as a float. The underlying value can be an integer, float or
+ * double; the value is converted to a float.
+ *
+ * @note Reading a double or a large integer with this function can incur a
+ * loss of precision.
+ *
+ * @throws mpack_error_type if the underlying value is not a float, double or integer.
+ */
+float mpack_expect_float_range(mpack_reader_t* reader, float min_value, float max_value);
+
+/**
+ * Reads a number, ensuring that it falls within the given range and returning
+ * the value as a double. The underlying value can be an integer, float or
+ * double; the value is converted to a double.
+ *
+ * @note Reading a very large integer with this function can incur a
+ * loss of precision.
+ *
+ * @throws mpack_error_type if the underlying value is not a float, double or integer.
+ */
+double mpack_expect_double_range(mpack_reader_t* reader, double min_value, double max_value);
+
+/**
+ * @}
+ */
+
+
+
+// These are additional Basic Number functions that wrap inline range functions.
+
+/**
+ * @name Basic Number Functions
+ * @{
+ */
+
+/**
+ * Reads an unsigned int.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in an unsigned int.
+ *
+ * Returns zero if an error occurs.
+ */
+MPACK_INLINE unsigned int mpack_expect_uint(mpack_reader_t* reader) {
+
+ // This should be true at compile-time, so this just wraps the 32-bit function.
+ if (sizeof(unsigned int) == 4)
+ return (unsigned int)mpack_expect_u32(reader);
+
+ // Otherwise we wrap the max function to ensure it fits.
+ return (unsigned int)mpack_expect_u64_max(reader, UINT_MAX);
+
+}
+
+/**
+ * Reads a signed int.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a signed int.
+ *
+ * Returns zero if an error occurs.
+ */
+MPACK_INLINE int mpack_expect_int(mpack_reader_t* reader) {
+
+ // This should be true at compile-time, so this just wraps the 32-bit function.
+ if (sizeof(int) == 4)
+ return (int)mpack_expect_i32(reader);
+
+ // Otherwise we wrap the range function to ensure it fits.
+ return (int)mpack_expect_i64_range(reader, INT_MIN, INT_MAX);
+
+}
+
+/**
+ * @}
+ */
+
+
+
+/**
+ * @name Matching Number Functions
+ * @{
+ */
+
+/**
+ * Reads an unsigned integer, ensuring that it exactly matches the given value.
+ *
+ * mpack_error_type is raised if the value is not representable as an unsigned
+ * integer or if it does not exactly match the given value.
+ */
+void mpack_expect_uint_match(mpack_reader_t* reader, uint64_t value);
+
+/**
+ * Reads a signed integer, ensuring that it exactly matches the given value.
+ *
+ * mpack_error_type is raised if the value is not representable as a signed
+ * integer or if it does not exactly match the given value.
+ */
+void mpack_expect_int_match(mpack_reader_t* reader, int64_t value);
+
+/**
+ * @name Other Basic Types
+ * @{
+ */
+
+/**
+ * Reads a nil, raising @ref mpack_error_type if the value is not nil.
+ */
+void mpack_expect_nil(mpack_reader_t* reader);
+
+/**
+ * Reads a boolean.
+ *
+ * @note Integers will raise mpack_error_type; the value must be strictly a boolean.
+ */
+bool mpack_expect_bool(mpack_reader_t* reader);
+
+/**
+ * Reads a boolean, raising @ref mpack_error_type if its value is not @c true.
+ */
+void mpack_expect_true(mpack_reader_t* reader);
+
+/**
+ * Reads a boolean, raising @ref mpack_error_type if its value is not @c false.
+ */
+void mpack_expect_false(mpack_reader_t* reader);
+
+/**
+ * @}
+ */
+
+/**
+ * @name Extension Functions
+ * @{
+ */
+
+#if MPACK_EXTENSIONS
+/**
+ * Reads a timestamp.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ */
+mpack_timestamp_t mpack_expect_timestamp(mpack_reader_t* reader);
+
+/**
+ * Reads a timestamp in seconds, truncating the nanoseconds (if any).
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ */
+int64_t mpack_expect_timestamp_truncate(mpack_reader_t* reader);
+#endif
+
+/**
+ * @}
+ */
+
+/**
+ * @name Compound Types
+ * @{
+ */
+
+/**
+ * Reads the start of a map, returning its element count.
+ *
+ * A number of values follow equal to twice the element count of the map,
+ * alternating between keys and values. @ref mpack_done_map() must be called
+ * once all elements have been read.
+ *
+ * @note Maps in JSON are unordered, so it is recommended not to expect
+ * a specific ordering for your map values in case your data is converted
+ * to/from JSON.
+ *
+ * @warning This call is dangerous! It does not have a size limit, and it
+ * does not have any way of checking whether there is enough data in the
+ * message (since the data could be coming from a stream.) When looping
+ * through the map's contents, you must check for errors on each iteration
+ * of the loop. Otherwise an attacker could craft a message declaring a map
+ * of a billion elements which would throw your parsing code into an
+ * infinite loop! You should strongly consider using mpack_expect_map_max()
+ * with a safe maximum size instead.
+ *
+ * @throws mpack_error_type if the value is not a map.
+ */
+uint32_t mpack_expect_map(mpack_reader_t* reader);
+
+/**
+ * Reads the start of a map with a number of elements in the given range, returning
+ * its element count.
+ *
+ * A number of values follow equal to twice the element count of the map,
+ * alternating between keys and values. @ref mpack_done_map() must be called
+ * once all elements have been read.
+ *
+ * @note Maps in JSON are unordered, so it is recommended not to expect
+ * a specific ordering for your map values in case your data is converted
+ * to/from JSON.
+ *
+ * min_count is returned if an error occurs.
+ *
+ * @throws mpack_error_type if the value is not a map or if its size does
+ * not fall within the given range.
+ */
+uint32_t mpack_expect_map_range(mpack_reader_t* reader, uint32_t min_count, uint32_t max_count);
+
+/**
+ * Reads the start of a map with a number of elements at most @a max_count,
+ * returning its element count.
+ *
+ * A number of values follow equal to twice the element count of the map,
+ * alternating between keys and values. @ref mpack_done_map() must be called
+ * once all elements have been read.
+ *
+ * @note Maps in JSON are unordered, so it is recommended not to expect
+ * a specific ordering for your map values in case your data is converted
+ * to/from JSON.
+ *
+ * Zero is returned if an error occurs.
+ *
+ * @throws mpack_error_type if the value is not a map or if its size is
+ * greater than max_count.
+ */
+MPACK_INLINE uint32_t mpack_expect_map_max(mpack_reader_t* reader, uint32_t max_count) {
+ return mpack_expect_map_range(reader, 0, max_count);
+}
+
+/**
+ * Reads the start of a map of the exact size given.
+ *
+ * A number of values follow equal to twice the element count of the map,
+ * alternating between keys and values. @ref mpack_done_map() must be called
+ * once all elements have been read.
+ *
+ * @note Maps in JSON are unordered, so it is recommended not to expect
+ * a specific ordering for your map values in case your data is converted
+ * to/from JSON.
+ *
+ * @throws mpack_error_type if the value is not a map or if its size
+ * does not match the given count.
+ */
+void mpack_expect_map_match(mpack_reader_t* reader, uint32_t count);
+
+/**
+ * Reads a nil node or the start of a map, returning whether a map was
+ * read and placing its number of key/value pairs in count.
+ *
+ * If a map was read, a number of values follow equal to twice the element count
+ * of the map, alternating between keys and values. @ref mpack_done_map() should
+ * also be called once all elements have been read (only if a map was read.)
+ *
+ * @note Maps in JSON are unordered, so it is recommended not to expect
+ * a specific ordering for your map values in case your data is converted
+ * to/from JSON.
+ *
+ * @warning This call is dangerous! It does not have a size limit, and it
+ * does not have any way of checking whether there is enough data in the
+ * message (since the data could be coming from a stream.) When looping
+ * through the map's contents, you must check for errors on each iteration
+ * of the loop. Otherwise an attacker could craft a message declaring a map
+ * of a billion elements which would throw your parsing code into an
+ * infinite loop! You should strongly consider using mpack_expect_map_max_or_nil()
+ * with a safe maximum size instead.
+ *
+ * @returns @c true if a map was read successfully; @c false if nil was read
+ * or an error occurred.
+ * @throws mpack_error_type if the value is not a nil or map.
+ */
+bool mpack_expect_map_or_nil(mpack_reader_t* reader, uint32_t* count);
+
+/**
+ * Reads a nil node or the start of a map with a number of elements at most
+ * max_count, returning whether a map was read and placing its number of
+ * key/value pairs in count.
+ *
+ * If a map was read, a number of values follow equal to twice the element count
+ * of the map, alternating between keys and values. @ref mpack_done_map() should
+ * anlso be called once all elements have been read (only if a map was read.)
+ *
+ * @note Maps in JSON are unordered, so it is recommended not to expect
+ * a specific ordering for your map values in case your data is converted
+ * to/from JSON. Consider using mpack_expect_key_cstr() or mpack_expect_key_uint()
+ * to switch on the key; see @ref docs/expect.md for examples.
+ *
+ * @returns @c true if a map was read successfully; @c false if nil was read
+ * or an error occurred.
+ * @throws mpack_error_type if the value is not a nil or map.
+ */
+bool mpack_expect_map_max_or_nil(mpack_reader_t* reader, uint32_t max_count, uint32_t* count);
+
+/**
+ * Reads the start of an array, returning its element count.
+ *
+ * A number of values follow equal to the element count of the array.
+ * @ref mpack_done_array() must be called once all elements have been read.
+ *
+ * @warning This call is dangerous! It does not have a size limit, and it
+ * does not have any way of checking whether there is enough data in the
+ * message (since the data could be coming from a stream.) When looping
+ * through the array's contents, you must check for errors on each iteration
+ * of the loop. Otherwise an attacker could craft a message declaring an array
+ * of a billion elements which would throw your parsing code into an
+ * infinite loop! You should strongly consider using mpack_expect_array_max()
+ * with a safe maximum size instead.
+ */
+uint32_t mpack_expect_array(mpack_reader_t* reader);
+
+/**
+ * Reads the start of an array with a number of elements in the given range,
+ * returning its element count.
+ *
+ * A number of values follow equal to the element count of the array.
+ * @ref mpack_done_array() must be called once all elements have been read.
+ *
+ * min_count is returned if an error occurs.
+ *
+ * @throws mpack_error_type if the value is not an array or if its size does
+ * not fall within the given range.
+ */
+uint32_t mpack_expect_array_range(mpack_reader_t* reader, uint32_t min_count, uint32_t max_count);
+
+/**
+ * Reads the start of an array with a number of elements at most @a max_count,
+ * returning its element count.
+ *
+ * A number of values follow equal to the element count of the array.
+ * @ref mpack_done_array() must be called once all elements have been read.
+ *
+ * Zero is returned if an error occurs.
+ *
+ * @throws mpack_error_type if the value is not an array or if its size is
+ * greater than max_count.
+ */
+MPACK_INLINE uint32_t mpack_expect_array_max(mpack_reader_t* reader, uint32_t max_count) {
+ return mpack_expect_array_range(reader, 0, max_count);
+}
+
+/**
+ * Reads the start of an array of the exact size given.
+ *
+ * A number of values follow equal to the element count of the array.
+ * @ref mpack_done_array() must be called once all elements have been read.
+ *
+ * @throws mpack_error_type if the value is not an array or if its size does
+ * not match the given count.
+ */
+void mpack_expect_array_match(mpack_reader_t* reader, uint32_t count);
+
+/**
+ * Reads a nil node or the start of an array, returning whether an array was
+ * read and placing its number of elements in count.
+ *
+ * If an array was read, a number of values follow equal to the element count
+ * of the array. @ref mpack_done_array() should also be called once all elements
+ * have been read (only if an array was read.)
+ *
+ * @warning This call is dangerous! It does not have a size limit, and it
+ * does not have any way of checking whether there is enough data in the
+ * message (since the data could be coming from a stream.) When looping
+ * through the array's contents, you must check for errors on each iteration
+ * of the loop. Otherwise an attacker could craft a message declaring an array
+ * of a billion elements which would throw your parsing code into an
+ * infinite loop! You should strongly consider using mpack_expect_array_max_or_nil()
+ * with a safe maximum size instead.
+ *
+ * @returns @c true if an array was read successfully; @c false if nil was read
+ * or an error occurred.
+ * @throws mpack_error_type if the value is not a nil or array.
+ */
+bool mpack_expect_array_or_nil(mpack_reader_t* reader, uint32_t* count);
+
+/**
+ * Reads a nil node or the start of an array with a number of elements at most
+ * max_count, returning whether an array was read and placing its number of
+ * key/value pairs in count.
+ *
+ * If an array was read, a number of values follow equal to the element count
+ * of the array. @ref mpack_done_array() should also be called once all elements
+ * have been read (only if an array was read.)
+ *
+ * @returns @c true if an array was read successfully; @c false if nil was read
+ * or an error occurred.
+ * @throws mpack_error_type if the value is not a nil or array.
+ */
+bool mpack_expect_array_max_or_nil(mpack_reader_t* reader, uint32_t max_count, uint32_t* count);
+
+#ifdef MPACK_MALLOC
+/**
+ * @hideinitializer
+ *
+ * Reads the start of an array and allocates storage for it, placing its
+ * size in out_count. A number of objects follow equal to the element count
+ * of the array. You must call @ref mpack_done_array() when done (even
+ * if the element count is zero.)
+ *
+ * If an error occurs, NULL is returned and the reader is placed in an
+ * error state.
+ *
+ * If the count is zero, NULL is returned. This does not indicate error.
+ * You should not check the return value for NULL to check for errors; only
+ * check the reader's error state.
+ *
+ * The allocated array must be freed with MPACK_FREE() (or simply free()
+ * if MPack's allocator hasn't been customized.)
+ *
+ * @throws mpack_error_type if the value is not an array or if its size is
+ * greater than max_count.
+ */
+#define mpack_expect_array_alloc(reader, Type, max_count, out_count) \
+ ((Type*)mpack_expect_array_alloc_impl(reader, sizeof(Type), max_count, out_count, false))
+
+/**
+ * @hideinitializer
+ *
+ * Reads a nil node or the start of an array and allocates storage for it,
+ * placing its size in out_count. A number of objects follow equal to the element
+ * count of the array if a non-empty array was read.
+ *
+ * If an error occurs, NULL is returned and the reader is placed in an
+ * error state.
+ *
+ * If a nil node was read, NULL is returned. If an empty array was read,
+ * mpack_done_array() is called automatically and NULL is returned. These
+ * do not indicate error. You should not check the return value for NULL
+ * to check for errors; only check the reader's error state.
+ *
+ * The allocated array must be freed with MPACK_FREE() (or simply free()
+ * if MPack's allocator hasn't been customized.)
+ *
+ * @warning You must call @ref mpack_done_array() if and only if a non-zero
+ * element count is read. This function does not differentiate between nil
+ * and an empty array.
+ *
+ * @throws mpack_error_type if the value is not an array or if its size is
+ * greater than max_count.
+ */
+#define mpack_expect_array_or_nil_alloc(reader, Type, max_count, out_count) \
+ ((Type*)mpack_expect_array_alloc_impl(reader, sizeof(Type), max_count, out_count, true))
+#endif
+
+/**
+ * @}
+ */
+
+/** @cond */
+#ifdef MPACK_MALLOC
+void* mpack_expect_array_alloc_impl(mpack_reader_t* reader,
+ size_t element_size, uint32_t max_count, uint32_t* out_count, bool allow_nil);
+#endif
+/** @endcond */
+
+
+/**
+ * @name String Functions
+ * @{
+ */
+
+/**
+ * Reads the start of a string, returning its size in bytes.
+ *
+ * The bytes follow and must be read separately with mpack_read_bytes()
+ * or mpack_read_bytes_inplace(). mpack_done_str() must be called
+ * once all bytes have been read.
+ *
+ * NUL bytes are allowed in the string, and no encoding checks are done.
+ *
+ * mpack_error_type is raised if the value is not a string.
+ */
+uint32_t mpack_expect_str(mpack_reader_t* reader);
+
+/**
+ * Reads a string of at most the given size, writing it into the
+ * given buffer and returning its size in bytes.
+ *
+ * This does not add a null-terminator! Use mpack_expect_cstr() to
+ * add a null-terminator.
+ *
+ * NUL bytes are allowed in the string, and no encoding checks are done.
+ */
+size_t mpack_expect_str_buf(mpack_reader_t* reader, char* buf, size_t bufsize);
+
+/**
+ * Reads a string into the given buffer, ensuring it is a valid UTF-8 string
+ * and returning its size in bytes.
+ *
+ * This does not add a null-terminator! Use mpack_expect_utf8_cstr() to
+ * add a null-terminator.
+ *
+ * This does not accept any UTF-8 variant such as Modified UTF-8, CESU-8 or
+ * WTF-8. Only pure UTF-8 is allowed.
+ *
+ * NUL bytes are allowed in the string (as they are in UTF-8.)
+ *
+ * Raises mpack_error_too_big if there is not enough room for the string.
+ * Raises mpack_error_type if the value is not a string or is not a valid UTF-8 string.
+ */
+size_t mpack_expect_utf8(mpack_reader_t* reader, char* buf, size_t bufsize);
+
+/**
+ * Reads the start of a string, raising an error if its length is not
+ * at most the given number of bytes (not including any null-terminator.)
+ *
+ * The bytes follow and must be read separately with mpack_read_bytes()
+ * or mpack_read_bytes_inplace(). @ref mpack_done_str() must be called
+ * once all bytes have been read.
+ *
+ * @throws mpack_error_type If the value is not a string.
+ * @throws mpack_error_too_big If the string's length in bytes is larger than the given maximum size.
+ */
+MPACK_INLINE uint32_t mpack_expect_str_max(mpack_reader_t* reader, uint32_t maxsize) {
+ uint32_t length = mpack_expect_str(reader);
+ if (length > maxsize) {
+ mpack_reader_flag_error(reader, mpack_error_too_big);
+ return 0;
+ }
+ return length;
+}
+
+/**
+ * Reads the start of a string, raising an error if its length is not
+ * exactly the given number of bytes (not including any null-terminator.)
+ *
+ * The bytes follow and must be read separately with mpack_read_bytes()
+ * or mpack_read_bytes_inplace(). @ref mpack_done_str() must be called
+ * once all bytes have been read.
+ *
+ * mpack_error_type is raised if the value is not a string or if its
+ * length does not match.
+ */
+MPACK_INLINE void mpack_expect_str_length(mpack_reader_t* reader, uint32_t count) {
+ if (mpack_expect_str(reader) != count)
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+/**
+ * Reads a string, ensuring it exactly matches the given string.
+ *
+ * Remember that maps are unordered in JSON. Don't use this for map keys
+ * unless the map has only a single key!
+ */
+void mpack_expect_str_match(mpack_reader_t* reader, const char* str, size_t length);
+
+/**
+ * Reads a string into the given buffer, ensures it has no null bytes,
+ * and adds a null-terminator at the end.
+ *
+ * Raises mpack_error_too_big if there is not enough room for the string and null-terminator.
+ * Raises mpack_error_type if the value is not a string or contains a null byte.
+ */
+void mpack_expect_cstr(mpack_reader_t* reader, char* buf, size_t size);
+
+/**
+ * Reads a string into the given buffer, ensures it is a valid UTF-8 string
+ * without NUL characters, and adds a null-terminator at the end.
+ *
+ * This does not accept any UTF-8 variant such as Modified UTF-8, CESU-8 or
+ * WTF-8. Only pure UTF-8 is allowed, but without the NUL character, since
+ * it cannot be represented in a null-terminated string.
+ *
+ * Raises mpack_error_too_big if there is not enough room for the string and null-terminator.
+ * Raises mpack_error_type if the value is not a string or is not a valid UTF-8 string.
+ */
+void mpack_expect_utf8_cstr(mpack_reader_t* reader, char* buf, size_t size);
+
+#ifdef MPACK_MALLOC
+/**
+ * Reads a string with the given total maximum size (including space for a
+ * null-terminator), allocates storage for it, ensures it has no null-bytes,
+ * and adds a null-terminator at the end. You assume ownership of the
+ * returned pointer if reading succeeds.
+ *
+ * The allocated string must be freed with MPACK_FREE() (or simply free()
+ * if MPack's allocator hasn't been customized.)
+ *
+ * @throws mpack_error_too_big If the string plus null-terminator is larger than the given maxsize.
+ * @throws mpack_error_type If the value is not a string or contains a null byte.
+ */
+char* mpack_expect_cstr_alloc(mpack_reader_t* reader, size_t maxsize);
+
+/**
+ * Reads a string with the given total maximum size (including space for a
+ * null-terminator), allocates storage for it, ensures it is valid UTF-8
+ * with no null-bytes, and adds a null-terminator at the end. You assume
+ * ownership of the returned pointer if reading succeeds.
+ *
+ * The length in bytes of the string, not including the null-terminator,
+ * will be written to size.
+ *
+ * This does not accept any UTF-8 variant such as Modified UTF-8, CESU-8 or
+ * WTF-8. Only pure UTF-8 is allowed, but without the NUL character, since
+ * it cannot be represented in a null-terminated string.
+ *
+ * The allocated string must be freed with MPACK_FREE() (or simply free()
+ * if MPack's allocator hasn't been customized.)
+ * if you want a null-terminator.
+ *
+ * @throws mpack_error_too_big If the string plus null-terminator is larger
+ * than the given maxsize.
+ * @throws mpack_error_type If the value is not a string or contains
+ * invalid UTF-8 or a null byte.
+ */
+char* mpack_expect_utf8_cstr_alloc(mpack_reader_t* reader, size_t maxsize);
+#endif
+
+/**
+ * Reads a string, ensuring it exactly matches the given null-terminated
+ * string.
+ *
+ * Remember that maps are unordered in JSON. Don't use this for map keys
+ * unless the map has only a single key!
+ */
+MPACK_INLINE void mpack_expect_cstr_match(mpack_reader_t* reader, const char* cstr) {
+ mpack_assert(cstr != NULL, "cstr pointer is NULL");
+ mpack_expect_str_match(reader, cstr, mpack_strlen(cstr));
+}
+
+/**
+ * @}
+ */
+
+/**
+ * @name Binary Data
+ * @{
+ */
+
+/**
+ * Reads the start of a binary blob, returning its size in bytes.
+ *
+ * The bytes follow and must be read separately with mpack_read_bytes()
+ * or mpack_read_bytes_inplace(). @ref mpack_done_bin() must be called
+ * once all bytes have been read.
+ *
+ * mpack_error_type is raised if the value is not a binary blob.
+ */
+uint32_t mpack_expect_bin(mpack_reader_t* reader);
+
+/**
+ * Reads the start of a binary blob, raising an error if its length is not
+ * at most the given number of bytes.
+ *
+ * The bytes follow and must be read separately with mpack_read_bytes()
+ * or mpack_read_bytes_inplace(). @ref mpack_done_bin() must be called
+ * once all bytes have been read.
+ *
+ * mpack_error_type is raised if the value is not a binary blob or if its
+ * length does not match.
+ */
+MPACK_INLINE uint32_t mpack_expect_bin_max(mpack_reader_t* reader, uint32_t maxsize) {
+ uint32_t length = mpack_expect_bin(reader);
+ if (length > maxsize) {
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+ }
+ return length;
+}
+
+/**
+ * Reads the start of a binary blob, raising an error if its length is not
+ * exactly the given number of bytes.
+ *
+ * The bytes follow and must be read separately with mpack_read_bytes()
+ * or mpack_read_bytes_inplace(). @ref mpack_done_bin() must be called
+ * once all bytes have been read.
+ *
+ * @throws mpack_error_type if the value is not a binary blob or if its size
+ * does not match.
+ */
+MPACK_INLINE void mpack_expect_bin_size(mpack_reader_t* reader, uint32_t count) {
+ if (mpack_expect_bin(reader) != count)
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+/**
+ * Reads a binary blob into the given buffer, returning its size in bytes.
+ *
+ * For compatibility, this will accept if the underlying type is string or
+ * binary (since in MessagePack 1.0, strings and binary data were combined
+ * under the "raw" type which became string in 1.1.)
+ */
+size_t mpack_expect_bin_buf(mpack_reader_t* reader, char* buf, size_t size);
+
+/**
+ * Reads a binary blob with the exact given size into the given buffer.
+ *
+ * For compatibility, this will accept if the underlying type is string or
+ * binary (since in MessagePack 1.0, strings and binary data were combined
+ * under the "raw" type which became string in 1.1.)
+ *
+ * @throws mpack_error_type if the value is not a binary blob or if its size
+ * does not match.
+ */
+void mpack_expect_bin_size_buf(mpack_reader_t* reader, char* buf, uint32_t size);
+
+/**
+ * Reads a binary blob with the given total maximum size, allocating storage for it.
+ */
+char* mpack_expect_bin_alloc(mpack_reader_t* reader, size_t maxsize, size_t* size);
+
+/**
+ * @}
+ */
+
+/**
+ * @name Extension Functions
+ * @{
+ */
+
+#if MPACK_EXTENSIONS
+/**
+ * Reads the start of an extension blob, returning its size in bytes and
+ * placing the type into @p type.
+ *
+ * The bytes follow and must be read separately with mpack_read_bytes()
+ * or mpack_read_bytes_inplace(). @ref mpack_done_ext() must be called
+ * once all bytes have been read.
+ *
+ * @p type will be a user-defined type in the range [0,127] or a reserved type
+ * in the range [-128,-2].
+ *
+ * mpack_error_type is raised if the value is not an extension blob. The @p
+ * type value is zero if an error occurs.
+ *
+ * @note This cannot be used to match a timestamp. @ref mpack_error_type will
+ * be flagged if the value is a timestamp. Use mpack_expect_timestamp() or
+ * mpack_expect_timestamp_truncate() instead.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ *
+ * @warning Be careful when using reserved types. They may no longer be ext
+ * types in the future, and previously valid data containing reserved types may
+ * become invalid in the future.
+ */
+uint32_t mpack_expect_ext(mpack_reader_t* reader, int8_t* type);
+
+/**
+ * Reads the start of an extension blob, raising an error if its length is not
+ * at most the given number of bytes and placing the type into @p type.
+ *
+ * The bytes follow and must be read separately with mpack_read_bytes()
+ * or mpack_read_bytes_inplace(). @ref mpack_done_ext() must be called
+ * once all bytes have been read.
+ *
+ * mpack_error_type is raised if the value is not an extension blob or if its
+ * length does not match. The @p type value is zero if an error is raised.
+ *
+ * @p type will be a user-defined type in the range [0,127] or a reserved type
+ * in the range [-128,-2].
+ *
+ * @note This cannot be used to match a timestamp. @ref mpack_error_type will
+ * be flagged if the value is a timestamp. Use mpack_expect_timestamp() or
+ * mpack_expect_timestamp_truncate() instead.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ *
+ * @warning Be careful when using reserved types. They may no longer be ext
+ * types in the future, and previously valid data containing reserved types may
+ * become invalid in the future.
+ *
+ * @see mpack_expect_ext()
+ */
+MPACK_INLINE uint32_t mpack_expect_ext_max(mpack_reader_t* reader, int8_t* type, uint32_t maxsize) {
+ uint32_t length = mpack_expect_ext(reader, type);
+ if (length > maxsize) {
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+ }
+ return length;
+}
+
+/**
+ * Reads the start of an extension blob, raising an error if its length is not
+ * exactly the given number of bytes and placing the type into @p type.
+ *
+ * The bytes follow and must be read separately with mpack_read_bytes()
+ * or mpack_read_bytes_inplace(). @ref mpack_done_ext() must be called
+ * once all bytes have been read.
+ *
+ * mpack_error_type is raised if the value is not an extension blob or if its
+ * length does not match. The @p type value is zero if an error is raised.
+ *
+ * @p type will be a user-defined type in the range [0,127] or a reserved type
+ * in the range [-128,-2].
+ *
+ * @note This cannot be used to match a timestamp. @ref mpack_error_type will
+ * be flagged if the value is a timestamp. Use mpack_expect_timestamp() or
+ * mpack_expect_timestamp_truncate() instead.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ *
+ * @warning Be careful when using reserved types. They may no longer be ext
+ * types in the future, and previously valid data containing reserved types may
+ * become invalid in the future.
+ *
+ * @see mpack_expect_ext()
+ */
+MPACK_INLINE void mpack_expect_ext_size(mpack_reader_t* reader, int8_t* type, uint32_t count) {
+ if (mpack_expect_ext(reader, type) != count) {
+ *type = 0;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ }
+}
+
+/**
+ * Reads an extension blob into the given buffer, returning its size in bytes
+ * and placing the type into @p type.
+ *
+ * mpack_error_type is raised if the value is not an extension blob or if its
+ * length does not match. The @p type value is zero if an error is raised.
+ *
+ * @p type will be a user-defined type in the range [0,127] or a reserved type
+ * in the range [-128,-2].
+ *
+ * @note This cannot be used to match a timestamp. @ref mpack_error_type will
+ * be flagged if the value is a timestamp. Use mpack_expect_timestamp() or
+ * mpack_expect_timestamp_truncate() instead.
+ *
+ * @warning Be careful when using reserved types. They may no longer be ext
+ * types in the future, and previously valid data containing reserved types may
+ * become invalid in the future.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ *
+ * @see mpack_expect_ext()
+ */
+size_t mpack_expect_ext_buf(mpack_reader_t* reader, int8_t* type, char* buf, size_t size);
+#endif
+
+#if MPACK_EXTENSIONS && defined(MPACK_MALLOC)
+/**
+ * Reads an extension blob with the given total maximum size, allocating
+ * storage for it, and placing the type into @p type.
+ *
+ * mpack_error_type is raised if the value is not an extension blob or if its
+ * length does not match. The @p type value is zero if an error is raised.
+ *
+ * @p type will be a user-defined type in the range [0,127] or a reserved type
+ * in the range [-128,-2].
+ *
+ * @note This cannot be used to match a timestamp. @ref mpack_error_type will
+ * be flagged if the value is a timestamp. Use mpack_expect_timestamp() or
+ * mpack_expect_timestamp_truncate() instead.
+ *
+ * @warning Be careful when using reserved types. They may no longer be ext
+ * types in the future, and previously valid data containing reserved types may
+ * become invalid in the future.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS and @ref MPACK_MALLOC.
+ *
+ * @see mpack_expect_ext()
+ */
+char* mpack_expect_ext_alloc(mpack_reader_t* reader, int8_t* type, size_t maxsize, size_t* size);
+#endif
+
+/**
+ * @}
+ */
+
+/**
+ * @name Special Functions
+ * @{
+ */
+
+/**
+ * Reads a MessagePack object header (an MPack tag), expecting it to exactly
+ * match the given tag.
+ *
+ * If the type is compound (i.e. is a map, array, string, binary or
+ * extension type), additional reads are required to get the contained
+ * data, and the corresponding done function must be called when done.
+ *
+ * @throws mpack_error_type if the tag does not match
+ *
+ * @see mpack_read_bytes()
+ * @see mpack_done_array()
+ * @see mpack_done_map()
+ * @see mpack_done_str()
+ * @see mpack_done_bin()
+ * @see mpack_done_ext()
+ */
+void mpack_expect_tag(mpack_reader_t* reader, mpack_tag_t tag);
+
+/**
+ * Expects a string matching one of the strings in the given array,
+ * returning its array index.
+ *
+ * If the value does not match any of the given strings,
+ * @ref mpack_error_type is flagged. Use mpack_expect_enum_optional()
+ * if you want to allow other values than the given strings.
+ *
+ * If any error occurs or the reader is in an error state, @a count
+ * is returned.
+ *
+ * This can be used to quickly parse a string into an enum when the
+ * enum values range from 0 to @a count-1. If the last value in the
+ * enum is a special "count" value, it can be passed as the count,
+ * and the return value can be cast directly to the enum type.
+ *
+ * @code{.c}
+ * typedef enum { APPLE , BANANA , ORANGE , COUNT} fruit_t;
+ * const char* fruits[] = {"apple", "banana", "orange"};
+ *
+ * fruit_t fruit = (fruit_t)mpack_expect_enum(reader, fruits, COUNT);
+ * @endcode
+ *
+ * See @ref docs/expect.md for more examples.
+ *
+ * The maximum string length is the size of the buffer (strings are read in-place.)
+ *
+ * @param reader The reader
+ * @param strings An array of expected strings of length count
+ * @param count The number of strings
+ * @return The index of the matched string, or @a count in case of error
+ */
+size_t mpack_expect_enum(mpack_reader_t* reader, const char* strings[], size_t count);
+
+/**
+ * Expects a string matching one of the strings in the given array
+ * returning its array index, or @a count if no strings match.
+ *
+ * If the value is not a string, or it does not match any of the
+ * given strings, @a count is returned and no error is flagged.
+ *
+ * If any error occurs or the reader is in an error state, @a count
+ * is returned.
+ *
+ * This can be used to quickly parse a string into an enum when the
+ * enum values range from 0 to @a count-1. If the last value in the
+ * enum is a special "count" value, it can be passed as the count,
+ * and the return value can be cast directly to the enum type.
+ *
+ * @code{.c}
+ * typedef enum { APPLE , BANANA , ORANGE , COUNT} fruit_t;
+ * const char* fruits[] = {"apple", "banana", "orange"};
+ *
+ * fruit_t fruit = (fruit_t)mpack_expect_enum_optional(reader, fruits, COUNT);
+ * @endcode
+ *
+ * See @ref docs/expect.md for more examples.
+ *
+ * The maximum string length is the size of the buffer (strings are read in-place.)
+ *
+ * @param reader The reader
+ * @param strings An array of expected strings of length count
+ * @param count The number of strings
+ *
+ * @return The index of the matched string, or @a count if it does not
+ * match or an error occurs
+ */
+size_t mpack_expect_enum_optional(mpack_reader_t* reader, const char* strings[], size_t count);
+
+/**
+ * Expects an unsigned integer map key between 0 and count-1, marking it
+ * as found in the given bool array and returning it.
+ *
+ * This is a helper for switching among int keys in a map. It is
+ * typically used with an enum to define the key values. It should
+ * be called in the expression of a switch() statement. See @ref
+ * docs/expect.md for an example.
+ *
+ * The found array must be cleared before expecting the first key. If the
+ * flag for a given key is already set when found (i.e. the map contains a
+ * duplicate key), mpack_error_invalid is flagged.
+ *
+ * If the key is not a non-negative integer, or if the key is @a count or
+ * larger, @a count is returned and no error is flagged. If you want an error
+ * on unrecognized keys, flag an error in the default case in your switch;
+ * otherwise you must call mpack_discard() to discard its content.
+ *
+ * @param reader The reader
+ * @param found An array of bool flags of length count
+ * @param count The number of values in the found array, and one more than the
+ * maximum allowed key
+ *
+ * @see @ref docs/expect.md
+ */
+size_t mpack_expect_key_uint(mpack_reader_t* reader, bool found[], size_t count);
+
+/**
+ * Expects a string map key matching one of the strings in the given key list,
+ * marking it as found in the given bool array and returning its index.
+ *
+ * This is a helper for switching among string keys in a map. It is
+ * typically used with an enum with names matching the strings in the
+ * array to define the key indices. It should be called in the expression
+ * of a switch() statement. See @ref docs/expect.md for an example.
+ *
+ * The found array must be cleared before expecting the first key. If the
+ * flag for a given key is already set when found (i.e. the map contains a
+ * duplicate key), mpack_error_invalid is flagged.
+ *
+ * If the key is unrecognized, count is returned and no error is flagged. If
+ * you want an error on unrecognized keys, flag an error in the default case
+ * in your switch; otherwise you must call mpack_discard() to discard its content.
+ *
+ * The maximum key length is the size of the buffer (keys are read in-place.)
+ *
+ * @param reader The reader
+ * @param keys An array of expected string keys of length count
+ * @param found An array of bool flags of length count
+ * @param count The number of values in the keys and found arrays
+ *
+ * @see @ref docs/expect.md
+ */
+size_t mpack_expect_key_cstr(mpack_reader_t* reader, const char* keys[],
+ bool found[], size_t count);
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif
+
+MPACK_EXTERN_C_END
+MPACK_HEADER_END
+
+#endif
+
+
+
+/* mpack/mpack-node.h.h */
+
+/**
+ * @file
+ *
+ * Declares the MPack dynamic Node API.
+ */
+
+#ifndef MPACK_NODE_H
+#define MPACK_NODE_H 1
+
+/* #include "mpack-reader.h" */
+
+MPACK_HEADER_START
+MPACK_EXTERN_C_START
+
+#if MPACK_NODE
+
+/**
+ * @defgroup node Node API
+ *
+ * The MPack Node API allows you to parse a chunk of MessagePack into a
+ * dynamically typed data structure, providing random access to the parsed
+ * data.
+ *
+ * See @ref docs/node.md for examples.
+ *
+ * @{
+ */
+
+/**
+ * A handle to node data in a parsed MPack tree.
+ *
+ * Nodes represent either primitive values or compound types. If a
+ * node is a compound type, it contains a pointer to its child nodes,
+ * or a pointer to its underlying data.
+ *
+ * Nodes are immutable.
+ *
+ * @note @ref mpack_node_t is an opaque reference to the node data, not the
+ * node data itself. (It contains pointers to both the node data and the tree.)
+ * It is passed by value in the Node API.
+ */
+typedef struct mpack_node_t mpack_node_t;
+
+/**
+ * The storage for nodes in an MPack tree.
+ *
+ * You only need to use this if you intend to provide your own storage
+ * for nodes instead of letting the tree allocate it.
+ *
+ * @ref mpack_node_data_t is 16 bytes on most common architectures (32-bit
+ * and 64-bit.)
+ */
+typedef struct mpack_node_data_t mpack_node_data_t;
+
+/**
+ * An MPack tree parser to parse a blob or stream of MessagePack.
+ *
+ * When a message is parsed, the tree contains a single root node which
+ * contains all parsed data. The tree and its nodes are immutable.
+ */
+typedef struct mpack_tree_t mpack_tree_t;
+
+/**
+ * An error handler function to be called when an error is flagged on
+ * the tree.
+ *
+ * The error handler will only be called once on the first error flagged;
+ * any subsequent node reads and errors are ignored, and the tree is
+ * permanently in that error state.
+ *
+ * MPack is safe against non-local jumps out of error handler callbacks.
+ * This means you are allowed to longjmp or throw an exception (in C++,
+ * Objective-C, or with SEH) out of this callback.
+ *
+ * Bear in mind when using longjmp that local non-volatile variables that
+ * have changed are undefined when setjmp() returns, so you can't put the
+ * tree on the stack in the same activation frame as the setjmp without
+ * declaring it volatile.
+ *
+ * You must still eventually destroy the tree. It is not destroyed
+ * automatically when an error is flagged. It is safe to destroy the
+ * tree within this error callback, but you will either need to perform
+ * a non-local jump, or store something in your context to identify
+ * that the tree is destroyed since any future accesses to it cause
+ * undefined behavior.
+ */
+typedef void (*mpack_tree_error_t)(mpack_tree_t* tree, mpack_error_t error);
+
+/**
+ * The MPack tree's read function. It should fill the buffer with as many bytes
+ * as are immediately available up to the given @c count, returning the number
+ * of bytes written to the buffer.
+ *
+ * In case of error, it should flag an appropriate error on the reader
+ * (usually @ref mpack_error_io.)
+ *
+ * The blocking or non-blocking behaviour of the read should match whether you
+ * are using mpack_tree_parse() or mpack_tree_try_parse().
+ *
+ * If you are using mpack_tree_parse(), the read should block until at least
+ * one byte is read. If you return 0, mpack_tree_parse() will raise @ref
+ * mpack_error_io.
+ *
+ * If you are using mpack_tree_try_parse(), the read function can always
+ * return 0, and must never block waiting for data (otherwise
+ * mpack_tree_try_parse() would be equivalent to mpack_tree_parse().)
+ * When you return 0, mpack_tree_try_parse() will return false without flagging
+ * an error.
+ */
+typedef size_t (*mpack_tree_read_t)(mpack_tree_t* tree, char* buffer, size_t count);
+
+/**
+ * A teardown function to be called when the tree is destroyed.
+ */
+typedef void (*mpack_tree_teardown_t)(mpack_tree_t* tree);
+
+
+
+/* Hide internals from documentation */
+/** @cond */
+
+struct mpack_node_t {
+ mpack_node_data_t* data;
+ mpack_tree_t* tree;
+};
+
+struct mpack_node_data_t {
+ mpack_type_t type;
+
+ /*
+ * The element count if the type is an array;
+ * the number of key/value pairs if the type is map;
+ * or the number of bytes if the type is str, bin or ext.
+ */
+ uint32_t len;
+
+ union
+ {
+ bool b; /* The value if the type is bool. */
+ float f; /* The value if the type is float. */
+ double d; /* The value if the type is double. */
+ int64_t i; /* The value if the type is signed int. */
+ uint64_t u; /* The value if the type is unsigned int. */
+ size_t offset; /* The byte offset for str, bin and ext */
+ mpack_node_data_t* children; /* The children for map or array */
+ } value;
+};
+
+typedef struct mpack_tree_page_t {
+ struct mpack_tree_page_t* next;
+ mpack_node_data_t nodes[1]; // variable size
+} mpack_tree_page_t;
+
+typedef enum mpack_tree_parse_state_t {
+ mpack_tree_parse_state_not_started,
+ mpack_tree_parse_state_in_progress,
+ mpack_tree_parse_state_parsed,
+} mpack_tree_parse_state_t;
+
+typedef struct mpack_level_t {
+ mpack_node_data_t* child;
+ size_t left; // children left in level
+} mpack_level_t;
+
+typedef struct mpack_tree_parser_t {
+ mpack_tree_parse_state_t state;
+
+ // We keep track of the number of "possible nodes" left in the data rather
+ // than the number of bytes.
+ //
+ // When a map or array is parsed, we ensure at least one byte for each child
+ // exists and subtract them right away. This ensures that if ever a map or
+ // array declares more elements than could possibly be contained in the data,
+ // we will error out immediately rather than allocating storage for them.
+ //
+ // For example malicious data that repeats 0xDE 0xFF 0xFF (start of a map
+ // with 65536 key-value pairs) would otherwise cause us to run out of
+ // memory. With this, the parser can allocate at most as many nodes as
+ // there are bytes in the data (plus the paging overhead, 12%.) An error
+ // will be flagged immediately if and when there isn't enough data left to
+ // fully read all children of all open compound types on the parsing stack.
+ //
+ // Once an entire message has been parsed (and there are no nodes left to
+ // parse whose bytes have been subtracted), this matches the number of left
+ // over bytes in the data.
+ size_t possible_nodes_left;
+
+ mpack_node_data_t* nodes; // next node in current page/pool
+ size_t nodes_left; // nodes left in current page/pool
+
+ size_t current_node_reserved;
+ size_t level;
+
+ #ifdef MPACK_MALLOC
+ // It's much faster to allocate the initial parsing stack inline within the
+ // parser. We replace it with a heap allocation if we need to grow it.
+ mpack_level_t* stack;
+ size_t stack_capacity;
+ bool stack_owned;
+ mpack_level_t stack_local[MPACK_NODE_INITIAL_DEPTH];
+ #else
+ // Without malloc(), we have to reserve a parsing stack the maximum allowed
+ // parsing depth.
+ mpack_level_t stack[MPACK_NODE_MAX_DEPTH_WITHOUT_MALLOC];
+ #endif
+} mpack_tree_parser_t;
+
+struct mpack_tree_t {
+ mpack_tree_error_t error_fn; /* Function to call on error */
+ mpack_tree_read_t read_fn; /* Function to call to read more data */
+ mpack_tree_teardown_t teardown; /* Function to teardown the context on destroy */
+ void* context; /* Context for tree callbacks */
+
+ mpack_node_data_t nil_node; /* a nil node to be returned in case of error */
+ mpack_node_data_t missing_node; /* a missing node to be returned in optional lookups */
+ mpack_error_t error;
+
+ #ifdef MPACK_MALLOC
+ char* buffer;
+ size_t buffer_capacity;
+ #endif
+
+ const char* data;
+ size_t data_length; // length of data (and content of buffer, if used)
+
+ size_t size; // size in bytes of tree (usually matches data_length, but not if tree has trailing data)
+ size_t node_count; // total number of nodes in tree (across all pages)
+
+ size_t max_size; // maximum message size
+ size_t max_nodes; // maximum nodes in a message
+
+ mpack_tree_parser_t parser;
+ mpack_node_data_t* root;
+
+ mpack_node_data_t* pool; // pool, or NULL if no pool provided
+ size_t pool_count;
+
+ #ifdef MPACK_MALLOC
+ mpack_tree_page_t* next;
+ #endif
+};
+
+// internal functions
+
+MPACK_INLINE mpack_node_t mpack_node(mpack_tree_t* tree, mpack_node_data_t* data) {
+ mpack_node_t node;
+ node.data = data;
+ node.tree = tree;
+ return node;
+}
+
+MPACK_INLINE mpack_node_data_t* mpack_node_child(mpack_node_t node, size_t child) {
+ return node.data->value.children + child;
+}
+
+MPACK_INLINE mpack_node_t mpack_tree_nil_node(mpack_tree_t* tree) {
+ return mpack_node(tree, &tree->nil_node);
+}
+
+MPACK_INLINE mpack_node_t mpack_tree_missing_node(mpack_tree_t* tree) {
+ return mpack_node(tree, &tree->missing_node);
+}
+
+/** @endcond */
+
+
+
+/**
+ * @name Tree Initialization
+ * @{
+ */
+
+#ifdef MPACK_MALLOC
+/**
+ * Initializes a tree parser with the given data.
+ *
+ * Configure the tree if desired, then call mpack_tree_parse() to parse it. The
+ * tree will allocate pages of nodes as needed and will free them when
+ * destroyed.
+ *
+ * The tree must be destroyed with mpack_tree_destroy().
+ *
+ * Any string or blob data types reference the original data, so the given data
+ * pointer must remain valid until after the tree is destroyed.
+ */
+void mpack_tree_init_data(mpack_tree_t* tree, const char* data, size_t length);
+
+/**
+ * Deprecated.
+ *
+ * \deprecated Renamed to mpack_tree_init_data().
+ */
+MPACK_INLINE void mpack_tree_init(mpack_tree_t* tree, const char* data, size_t length) {
+ mpack_tree_init_data(tree, data, length);
+}
+
+/**
+ * Initializes a tree parser from an unbounded stream, or a stream of
+ * unknown length.
+ *
+ * The parser can be used to read a single message from a stream of unknown
+ * length, or multiple messages from an unbounded stream, allowing it to
+ * be used for RPC communication. Call @ref mpack_tree_parse() to parse
+ * a message from a blocking stream, or @ref mpack_tree_try_parse() for a
+ * non-blocking stream.
+ *
+ * The stream will use a growable internal buffer to store the most recent
+ * message, as well as allocated pages of nodes for the parse tree.
+ *
+ * Maximum allowances for message size and node count must be specified in this
+ * function (since the stream is unbounded.) They can be changed later with
+ * @ref mpack_tree_set_limits().
+ *
+ * @param tree The tree parser
+ * @param read_fn The read function
+ * @param context The context for the read function
+ * @param max_message_size The maximum size of a message in bytes
+ * @param max_message_nodes The maximum number of nodes per message. See
+ * @ref mpack_node_data_t for the size of nodes.
+ *
+ * @see mpack_tree_read_t
+ * @see mpack_reader_context()
+ */
+void mpack_tree_init_stream(mpack_tree_t* tree, mpack_tree_read_t read_fn, void* context,
+ size_t max_message_size, size_t max_message_nodes);
+#endif
+
+/**
+ * Initializes a tree parser with the given data, using the given node data
+ * pool to store the results.
+ *
+ * Configure the tree if desired, then call mpack_tree_parse() to parse it.
+ *
+ * If the data does not fit in the pool, @ref mpack_error_too_big will be flagged
+ * on the tree.
+ *
+ * The tree must be destroyed with mpack_tree_destroy(), even if parsing fails.
+ */
+void mpack_tree_init_pool(mpack_tree_t* tree, const char* data, size_t length,
+ mpack_node_data_t* node_pool, size_t node_pool_count);
+
+/**
+ * Initializes an MPack tree directly into an error state. Use this if you
+ * are writing a wrapper to another <tt>mpack_tree_init*()</tt> function which
+ * can fail its setup.
+ */
+void mpack_tree_init_error(mpack_tree_t* tree, mpack_error_t error);
+
+#if MPACK_STDIO
+/**
+ * Initializes a tree to parse the given file. The tree must be destroyed with
+ * mpack_tree_destroy(), even if parsing fails.
+ *
+ * The file is opened, loaded fully into memory, and closed before this call
+ * returns.
+ *
+ * @param tree The tree to initialize
+ * @param filename The filename passed to fopen() to read the file
+ * @param max_bytes The maximum size of file to load, or 0 for unlimited size.
+ */
+void mpack_tree_init_filename(mpack_tree_t* tree, const char* filename, size_t max_bytes);
+
+/**
+ * Deprecated.
+ *
+ * \deprecated Renamed to mpack_tree_init_filename().
+ */
+MPACK_INLINE void mpack_tree_init_file(mpack_tree_t* tree, const char* filename, size_t max_bytes) {
+ mpack_tree_init_filename(tree, filename, max_bytes);
+}
+
+/**
+ * Initializes a tree to parse the given libc FILE. This can be used to
+ * read from stdin, or from a file opened separately.
+ *
+ * The tree must be destroyed with mpack_tree_destroy(), even if parsing fails.
+ *
+ * The FILE is fully loaded fully into memory (and closed if requested) before
+ * this call returns.
+ *
+ * @param tree The tree to initialize.
+ * @param stdfile The FILE.
+ * @param max_bytes The maximum size of file to load, or 0 for unlimited size.
+ * @param close_when_done If true, fclose() will be called on the FILE when it
+ * is no longer needed. If false, the file will not be closed when
+ * reading is done.
+ *
+ * @warning The tree will read all data in the FILE before parsing it. If this
+ * is used on stdin, the parser will block until it is closed, even if
+ * a complete message has been written to it!
+ */
+void mpack_tree_init_stdfile(mpack_tree_t* tree, FILE* stdfile, size_t max_bytes, bool close_when_done);
+#endif
+
+/**
+ * @}
+ */
+
+/**
+ * @name Tree Functions
+ * @{
+ */
+
+/**
+ * Sets the maximum byte size and maximum number of nodes allowed per message.
+ *
+ * The default is SIZE_MAX (no limit) unless @ref mpack_tree_init_stream() is
+ * called (where maximums are required.)
+ *
+ * If a pool of nodes is used, the node limit is the lesser of this limit and
+ * the pool size.
+ *
+ * @param tree The tree parser
+ * @param max_message_size The maximum size of a message in bytes
+ * @param max_message_nodes The maximum number of nodes per message. See
+ * @ref mpack_node_data_t for the size of nodes.
+ */
+void mpack_tree_set_limits(mpack_tree_t* tree, size_t max_message_size,
+ size_t max_message_nodes);
+
+/**
+ * Parses a MessagePack message into a tree of immutable nodes.
+ *
+ * If successful, the root node will be available under @ref mpack_tree_root().
+ * If not, an appropriate error will be flagged.
+ *
+ * This can be called repeatedly to parse a series of messages from a data
+ * source. When this is called, all previous nodes from this tree and their
+ * contents (including the root node) are invalidated.
+ *
+ * If this is called with a stream (see @ref mpack_tree_init_stream()), the
+ * stream must block until data is available. (Otherwise, if this is called on
+ * a non-blocking stream, parsing will fail with @ref mpack_error_io when the
+ * fill function returns 0.)
+ *
+ * There is no way to recover a tree in an error state. It must be destroyed.
+ */
+void mpack_tree_parse(mpack_tree_t* tree);
+
+/**
+ * Attempts to parse a MessagePack message from a non-blocking stream into a
+ * tree of immutable nodes.
+ *
+ * A non-blocking read function must have been passed to the tree in
+ * mpack_tree_init_stream().
+ *
+ * If this returns true, a message is available under
+ * @ref mpack_tree_root(). The tree nodes and data will be valid until
+ * the next time a parse is started.
+ *
+ * If this returns false, no message is available, because either not enough
+ * data is available yet or an error has occurred. You must check the tree for
+ * errors whenever this returns false. If there is no error, you should try
+ * again later when more data is available. (You will want to select()/poll()
+ * on the underlying socket or use some other asynchronous mechanism to
+ * determine when it has data.)
+ *
+ * There is no way to recover a tree in an error state. It must be destroyed.
+ *
+ * @see mpack_tree_init_stream()
+ */
+bool mpack_tree_try_parse(mpack_tree_t* tree);
+
+/**
+ * Returns the root node of the tree, if the tree is not in an error state.
+ * Returns a nil node otherwise.
+ *
+ * @warning You must call mpack_tree_parse() before calling this. If
+ * @ref mpack_tree_parse() was never called, the tree will assert.
+ */
+mpack_node_t mpack_tree_root(mpack_tree_t* tree);
+
+/**
+ * Returns the error state of the tree.
+ */
+MPACK_INLINE mpack_error_t mpack_tree_error(mpack_tree_t* tree) {
+ return tree->error;
+}
+
+/**
+ * Returns the size in bytes of the current parsed message.
+ *
+ * If there is something in the buffer after the MessagePack object, this can
+ * be used to find it.
+ *
+ * This is zero if an error occurred during tree parsing (since the
+ * portion of the data that the first complete object occupies cannot
+ * be determined if the data is invalid or corrupted.)
+ */
+MPACK_INLINE size_t mpack_tree_size(mpack_tree_t* tree) {
+ return tree->size;
+}
+
+/**
+ * Destroys the tree.
+ */
+mpack_error_t mpack_tree_destroy(mpack_tree_t* tree);
+
+/**
+ * Sets the custom pointer to pass to the tree callbacks, such as teardown.
+ *
+ * @param tree The MPack tree.
+ * @param context User data to pass to the tree callbacks.
+ *
+ * @see mpack_reader_context()
+ */
+MPACK_INLINE void mpack_tree_set_context(mpack_tree_t* tree, void* context) {
+ tree->context = context;
+}
+
+/**
+ * Returns the custom context for tree callbacks.
+ *
+ * @see mpack_tree_set_context
+ * @see mpack_tree_init_stream
+ */
+MPACK_INLINE void* mpack_tree_context(mpack_tree_t* tree) {
+ return tree->context;
+}
+
+/**
+ * Sets the error function to call when an error is flagged on the tree.
+ *
+ * This should normally be used with mpack_tree_set_context() to register
+ * a custom pointer to pass to the error function.
+ *
+ * See the definition of mpack_tree_error_t for more information about
+ * what you can do from an error callback.
+ *
+ * @see mpack_tree_error_t
+ * @param tree The MPack tree.
+ * @param error_fn The function to call when an error is flagged on the tree.
+ */
+MPACK_INLINE void mpack_tree_set_error_handler(mpack_tree_t* tree, mpack_tree_error_t error_fn) {
+ tree->error_fn = error_fn;
+}
+
+/**
+ * Sets the teardown function to call when the tree is destroyed.
+ *
+ * This should normally be used with mpack_tree_set_context() to register
+ * a custom pointer to pass to the teardown function.
+ *
+ * @param tree The MPack tree.
+ * @param teardown The function to call when the tree is destroyed.
+ */
+MPACK_INLINE void mpack_tree_set_teardown(mpack_tree_t* tree, mpack_tree_teardown_t teardown) {
+ tree->teardown = teardown;
+}
+
+/**
+ * Places the tree in the given error state, calling the error callback if one
+ * is set.
+ *
+ * This allows you to externally flag errors, for example if you are validating
+ * data as you read it.
+ *
+ * If the tree is already in an error state, this call is ignored and no
+ * error callback is called.
+ */
+void mpack_tree_flag_error(mpack_tree_t* tree, mpack_error_t error);
+
+/**
+ * @}
+ */
+
+/**
+ * @name Node Core Functions
+ * @{
+ */
+
+/**
+ * Places the node's tree in the given error state, calling the error callback
+ * if one is set.
+ *
+ * This allows you to externally flag errors, for example if you are validating
+ * data as you read it.
+ *
+ * If the tree is already in an error state, this call is ignored and no
+ * error callback is called.
+ */
+void mpack_node_flag_error(mpack_node_t node, mpack_error_t error);
+
+/**
+ * Returns the error state of the node's tree.
+ */
+MPACK_INLINE mpack_error_t mpack_node_error(mpack_node_t node) {
+ return mpack_tree_error(node.tree);
+}
+
+/**
+ * Returns a tag describing the given node, or a nil tag if the
+ * tree is in an error state.
+ */
+mpack_tag_t mpack_node_tag(mpack_node_t node);
+
+/** @cond */
+
+#if MPACK_DEBUG && MPACK_STDIO
+/*
+ * Converts a node to a pseudo-JSON string for debugging purposes, placing the
+ * result in the given buffer with a null-terminator.
+ *
+ * If the buffer does not have enough space, the result will be truncated (but
+ * it is guaranteed to be null-terminated.)
+ *
+ * This is only available in debug mode, and only if stdio is available (since
+ * it uses snprintf().) It's strictly for debugging purposes.
+ */
+void mpack_node_print_to_buffer(mpack_node_t node, char* buffer, size_t buffer_size);
+
+/*
+ * Converts a node to pseudo-JSON for debugging purposes, calling the given
+ * callback as many times as is necessary to output the character data.
+ *
+ * No null-terminator or trailing newline will be written.
+ *
+ * This is only available in debug mode, and only if stdio is available (since
+ * it uses snprintf().) It's strictly for debugging purposes.
+ */
+void mpack_node_print_to_callback(mpack_node_t node, mpack_print_callback_t callback, void* context);
+
+/*
+ * Converts a node to pseudo-JSON for debugging purposes
+ * and pretty-prints it to the given file.
+ *
+ * This is only available in debug mode, and only if stdio is available (since
+ * it uses snprintf().) It's strictly for debugging purposes.
+ */
+void mpack_node_print_to_file(mpack_node_t node, FILE* file);
+
+/*
+ * Converts a node to pseudo-JSON for debugging purposes
+ * and pretty-prints it to stdout.
+ *
+ * This is only available in debug mode, and only if stdio is available (since
+ * it uses snprintf().) It's strictly for debugging purposes.
+ */
+MPACK_INLINE void mpack_node_print_to_stdout(mpack_node_t node) {
+ mpack_node_print_to_file(node, stdout);
+}
+
+/*
+ * Deprecated.
+ *
+ * \deprecated Renamed to mpack_node_print_to_stdout().
+ */
+MPACK_INLINE void mpack_node_print(mpack_node_t node) {
+ mpack_node_print_to_stdout(node);
+}
+#endif
+
+/** @endcond */
+
+/**
+ * @}
+ */
+
+/**
+ * @name Node Primitive Value Functions
+ * @{
+ */
+
+/**
+ * Returns the type of the node.
+ */
+mpack_type_t mpack_node_type(mpack_node_t node);
+
+/**
+ * Returns true if the given node is a nil node; false otherwise.
+ *
+ * To ensure that a node is nil and flag an error otherwise, use
+ * mpack_node_nil().
+ */
+bool mpack_node_is_nil(mpack_node_t node);
+
+/**
+ * Returns true if the given node handle indicates a missing node; false otherwise.
+ *
+ * To ensure that a node is missing and flag an error otherwise, use
+ * mpack_node_missing().
+ */
+bool mpack_node_is_missing(mpack_node_t node);
+
+/**
+ * Checks that the given node is of nil type, raising @ref mpack_error_type
+ * otherwise.
+ *
+ * Use mpack_node_is_nil() to return whether the node is nil.
+ */
+void mpack_node_nil(mpack_node_t node);
+
+/**
+ * Checks that the given node indicates a missing node, raising @ref
+ * mpack_error_type otherwise.
+ *
+ * Use mpack_node_is_missing() to return whether the node is missing.
+ */
+void mpack_node_missing(mpack_node_t node);
+
+/**
+ * Returns the bool value of the node. If this node is not of the correct
+ * type, false is returned and mpack_error_type is raised.
+ */
+bool mpack_node_bool(mpack_node_t node);
+
+/**
+ * Checks if the given node is of bool type with value true, raising
+ * mpack_error_type otherwise.
+ */
+void mpack_node_true(mpack_node_t node);
+
+/**
+ * Checks if the given node is of bool type with value false, raising
+ * mpack_error_type otherwise.
+ */
+void mpack_node_false(mpack_node_t node);
+
+/**
+ * Returns the 8-bit unsigned value of the node. If this node is not
+ * of a compatible type, @ref mpack_error_type is raised and zero is returned.
+ */
+uint8_t mpack_node_u8(mpack_node_t node);
+
+/**
+ * Returns the 8-bit signed value of the node. If this node is not
+ * of a compatible type, @ref mpack_error_type is raised and zero is returned.
+ */
+int8_t mpack_node_i8(mpack_node_t node);
+
+/**
+ * Returns the 16-bit unsigned value of the node. If this node is not
+ * of a compatible type, @ref mpack_error_type is raised and zero is returned.
+ */
+uint16_t mpack_node_u16(mpack_node_t node);
+
+/**
+ * Returns the 16-bit signed value of the node. If this node is not
+ * of a compatible type, @ref mpack_error_type is raised and zero is returned.
+ */
+int16_t mpack_node_i16(mpack_node_t node);
+
+/**
+ * Returns the 32-bit unsigned value of the node. If this node is not
+ * of a compatible type, @ref mpack_error_type is raised and zero is returned.
+ */
+uint32_t mpack_node_u32(mpack_node_t node);
+
+/**
+ * Returns the 32-bit signed value of the node. If this node is not
+ * of a compatible type, @ref mpack_error_type is raised and zero is returned.
+ */
+int32_t mpack_node_i32(mpack_node_t node);
+
+/**
+ * Returns the 64-bit unsigned value of the node. If this node is not
+ * of a compatible type, @ref mpack_error_type is raised, and zero is returned.
+ */
+uint64_t mpack_node_u64(mpack_node_t node);
+
+/**
+ * Returns the 64-bit signed value of the node. If this node is not
+ * of a compatible type, @ref mpack_error_type is raised and zero is returned.
+ */
+int64_t mpack_node_i64(mpack_node_t node);
+
+/**
+ * Returns the unsigned int value of the node.
+ *
+ * Returns zero if an error occurs.
+ *
+ * @throws mpack_error_type If the node is not an integer type or does not fit in the range of an unsigned int
+ */
+unsigned int mpack_node_uint(mpack_node_t node);
+
+/**
+ * Returns the int value of the node.
+ *
+ * Returns zero if an error occurs.
+ *
+ * @throws mpack_error_type If the node is not an integer type or does not fit in the range of an int
+ */
+int mpack_node_int(mpack_node_t node);
+
+/**
+ * Returns the float value of the node. The underlying value can be an
+ * integer, float or double; the value is converted to a float.
+ *
+ * @note Reading a double or a large integer with this function can incur a
+ * loss of precision.
+ *
+ * @throws mpack_error_type if the underlying value is not a float, double or integer.
+ */
+float mpack_node_float(mpack_node_t node);
+
+/**
+ * Returns the double value of the node. The underlying value can be an
+ * integer, float or double; the value is converted to a double.
+ *
+ * @note Reading a very large integer with this function can incur a
+ * loss of precision.
+ *
+ * @throws mpack_error_type if the underlying value is not a float, double or integer.
+ */
+double mpack_node_double(mpack_node_t node);
+
+/**
+ * Returns the float value of the node. The underlying value must be a float,
+ * not a double or an integer. This ensures no loss of precision can occur.
+ *
+ * @throws mpack_error_type if the underlying value is not a float.
+ */
+float mpack_node_float_strict(mpack_node_t node);
+
+/**
+ * Returns the double value of the node. The underlying value must be a float
+ * or double, not an integer. This ensures no loss of precision can occur.
+ *
+ * @throws mpack_error_type if the underlying value is not a float or double.
+ */
+double mpack_node_double_strict(mpack_node_t node);
+
+#if MPACK_EXTENSIONS
+/**
+ * Returns a timestamp.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ *
+ * @throws mpack_error_type if the underlying value is not a timestamp.
+ */
+mpack_timestamp_t mpack_node_timestamp(mpack_node_t node);
+
+/**
+ * Returns a timestamp's (signed) seconds since 1970-01-01T00:00:00Z.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ *
+ * @throws mpack_error_type if the underlying value is not a timestamp.
+ */
+int64_t mpack_node_timestamp_seconds(mpack_node_t node);
+
+/**
+ * Returns a timestamp's additional nanoseconds.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ *
+ * @return A nanosecond count between 0 and 999,999,999 inclusive.
+ * @throws mpack_error_type if the underlying value is not a timestamp.
+ */
+uint32_t mpack_node_timestamp_nanoseconds(mpack_node_t node);
+#endif
+
+/**
+ * @}
+ */
+
+/**
+ * @name Node String and Data Functions
+ * @{
+ */
+
+/**
+ * Checks that the given node contains a valid UTF-8 string.
+ *
+ * If the string is invalid, this flags an error, which would cause subsequent calls
+ * to mpack_node_str() to return NULL and mpack_node_strlen() to return zero. So you
+ * can check the node for error immediately after calling this, or you can call those
+ * functions to use the data anyway and check for errors later.
+ *
+ * @throws mpack_error_type If this node is not a string or does not contain valid UTF-8.
+ *
+ * @param node The string node to test
+ *
+ * @see mpack_node_str()
+ * @see mpack_node_strlen()
+ */
+void mpack_node_check_utf8(mpack_node_t node);
+
+/**
+ * Checks that the given node contains a valid UTF-8 string with no NUL bytes.
+ *
+ * This does not check that the string has a null-terminator! It only checks whether
+ * the string could safely be represented as a C-string by appending a null-terminator.
+ * (If the string does already contain a null-terminator, this will flag an error.)
+ *
+ * This is performed automatically by other UTF-8 cstr helper functions. Only
+ * call this if you will do something else with the data directly, but you still
+ * want to ensure it will be valid as a UTF-8 C-string.
+ *
+ * @throws mpack_error_type If this node is not a string, does not contain valid UTF-8,
+ * or contains a NUL byte.
+ *
+ * @param node The string node to test
+ *
+ * @see mpack_node_str()
+ * @see mpack_node_strlen()
+ * @see mpack_node_copy_utf8_cstr()
+ * @see mpack_node_utf8_cstr_alloc()
+ */
+void mpack_node_check_utf8_cstr(mpack_node_t node);
+
+#if MPACK_EXTENSIONS
+/**
+ * Returns the extension type of the given ext node.
+ *
+ * This returns zero if the tree is in an error state.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ */
+int8_t mpack_node_exttype(mpack_node_t node);
+#endif
+
+/**
+ * Returns the number of bytes in the given bin node.
+ *
+ * This returns zero if the tree is in an error state.
+ *
+ * If this node is not a bin, @ref mpack_error_type is raised and zero is returned.
+ */
+size_t mpack_node_bin_size(mpack_node_t node);
+
+/**
+ * Returns the length of the given str, bin or ext node.
+ *
+ * This returns zero if the tree is in an error state.
+ *
+ * If this node is not a str, bin or map, @ref mpack_error_type is raised and zero
+ * is returned.
+ */
+uint32_t mpack_node_data_len(mpack_node_t node);
+
+/**
+ * Returns the length in bytes of the given string node. This does not
+ * include any null-terminator.
+ *
+ * This returns zero if the tree is in an error state.
+ *
+ * If this node is not a str, @ref mpack_error_type is raised and zero is returned.
+ */
+size_t mpack_node_strlen(mpack_node_t node);
+
+/**
+ * Returns a pointer to the data contained by this node, ensuring the node is a
+ * string.
+ *
+ * @warning Strings are not null-terminated! Use one of the cstr functions
+ * to get a null-terminated string.
+ *
+ * The pointer is valid as long as the data backing the tree is valid.
+ *
+ * If this node is not a string, @ref mpack_error_type is raised and @c NULL is returned.
+ *
+ * @see mpack_node_copy_cstr()
+ * @see mpack_node_cstr_alloc()
+ * @see mpack_node_utf8_cstr_alloc()
+ */
+const char* mpack_node_str(mpack_node_t node);
+
+/**
+ * Returns a pointer to the data contained by this node.
+ *
+ * @note Strings are not null-terminated! Use one of the cstr functions
+ * to get a null-terminated string.
+ *
+ * The pointer is valid as long as the data backing the tree is valid.
+ *
+ * If this node is not of a str, bin or map, @ref mpack_error_type is raised, and
+ * @c NULL is returned.
+ *
+ * @see mpack_node_copy_cstr()
+ * @see mpack_node_cstr_alloc()
+ * @see mpack_node_utf8_cstr_alloc()
+ */
+const char* mpack_node_data(mpack_node_t node);
+
+/**
+ * Returns a pointer to the data contained by this bin node.
+ *
+ * The pointer is valid as long as the data backing the tree is valid.
+ *
+ * If this node is not a bin, @ref mpack_error_type is raised and @c NULL is
+ * returned.
+ */
+const char* mpack_node_bin_data(mpack_node_t node);
+
+/**
+ * Copies the bytes contained by this node into the given buffer, returning the
+ * number of bytes in the node.
+ *
+ * @throws mpack_error_type If this node is not a str, bin or ext type
+ * @throws mpack_error_too_big If the string does not fit in the given buffer
+ *
+ * @param node The string node from which to copy data
+ * @param buffer A buffer in which to copy the node's bytes
+ * @param bufsize The size of the given buffer
+ *
+ * @return The number of bytes in the node, or zero if an error occurs.
+ */
+size_t mpack_node_copy_data(mpack_node_t node, char* buffer, size_t bufsize);
+
+/**
+ * Checks that the given node contains a valid UTF-8 string and copies the
+ * string into the given buffer, returning the number of bytes in the string.
+ *
+ * @throws mpack_error_type If this node is not a string
+ * @throws mpack_error_too_big If the string does not fit in the given buffer
+ *
+ * @param node The string node from which to copy data
+ * @param buffer A buffer in which to copy the node's bytes
+ * @param bufsize The size of the given buffer
+ *
+ * @return The number of bytes in the node, or zero if an error occurs.
+ */
+size_t mpack_node_copy_utf8(mpack_node_t node, char* buffer, size_t bufsize);
+
+/**
+ * Checks that the given node contains a string with no NUL bytes, copies the string
+ * into the given buffer, and adds a null terminator.
+ *
+ * If this node is not of a string type, @ref mpack_error_type is raised. If the string
+ * does not fit, @ref mpack_error_data is raised.
+ *
+ * If any error occurs, the buffer will contain an empty null-terminated string.
+ *
+ * @param node The string node from which to copy data
+ * @param buffer A buffer in which to copy the node's string
+ * @param size The size of the given buffer
+ */
+void mpack_node_copy_cstr(mpack_node_t node, char* buffer, size_t size);
+
+/**
+ * Checks that the given node contains a valid UTF-8 string with no NUL bytes,
+ * copies the string into the given buffer, and adds a null terminator.
+ *
+ * If this node is not of a string type, @ref mpack_error_type is raised. If the string
+ * does not fit, @ref mpack_error_data is raised.
+ *
+ * If any error occurs, the buffer will contain an empty null-terminated string.
+ *
+ * @param node The string node from which to copy data
+ * @param buffer A buffer in which to copy the node's string
+ * @param size The size of the given buffer
+ */
+void mpack_node_copy_utf8_cstr(mpack_node_t node, char* buffer, size_t size);
+
+#ifdef MPACK_MALLOC
+/**
+ * Allocates a new chunk of data using MPACK_MALLOC() with the bytes
+ * contained by this node.
+ *
+ * The allocated data must be freed with MPACK_FREE() (or simply free()
+ * if MPack's allocator hasn't been customized.)
+ *
+ * @throws mpack_error_type If this node is not a str, bin or ext type
+ * @throws mpack_error_too_big If the size of the data is larger than the
+ * given maximum size
+ * @throws mpack_error_memory If an allocation failure occurs
+ *
+ * @param node The node from which to allocate and copy data
+ * @param maxsize The maximum size to allocate
+ *
+ * @return The allocated data, or NULL if any error occurs.
+ */
+char* mpack_node_data_alloc(mpack_node_t node, size_t maxsize);
+
+/**
+ * Allocates a new null-terminated string using MPACK_MALLOC() with the string
+ * contained by this node.
+ *
+ * The allocated string must be freed with MPACK_FREE() (or simply free()
+ * if MPack's allocator hasn't been customized.)
+ *
+ * @throws mpack_error_type If this node is not a string or contains NUL bytes
+ * @throws mpack_error_too_big If the size of the string plus null-terminator
+ * is larger than the given maximum size
+ * @throws mpack_error_memory If an allocation failure occurs
+ *
+ * @param node The node from which to allocate and copy string data
+ * @param maxsize The maximum size to allocate, including the null-terminator
+ *
+ * @return The allocated string, or NULL if any error occurs.
+ */
+char* mpack_node_cstr_alloc(mpack_node_t node, size_t maxsize);
+
+/**
+ * Allocates a new null-terminated string using MPACK_MALLOC() with the UTF-8
+ * string contained by this node.
+ *
+ * The allocated string must be freed with MPACK_FREE() (or simply free()
+ * if MPack's allocator hasn't been customized.)
+ *
+ * @throws mpack_error_type If this node is not a string, is not valid UTF-8,
+ * or contains NUL bytes
+ * @throws mpack_error_too_big If the size of the string plus null-terminator
+ * is larger than the given maximum size
+ * @throws mpack_error_memory If an allocation failure occurs
+ *
+ * @param node The node from which to allocate and copy string data
+ * @param maxsize The maximum size to allocate, including the null-terminator
+ *
+ * @return The allocated string, or NULL if any error occurs.
+ */
+char* mpack_node_utf8_cstr_alloc(mpack_node_t node, size_t maxsize);
+#endif
+
+/**
+ * Searches the given string array for a string matching the given
+ * node and returns its index.
+ *
+ * If the node does not match any of the given strings,
+ * @ref mpack_error_type is flagged. Use mpack_node_enum_optional()
+ * if you want to allow values other than the given strings.
+ *
+ * If any error occurs or if the tree is in an error state, @a count
+ * is returned.
+ *
+ * This can be used to quickly parse a string into an enum when the
+ * enum values range from 0 to @a count-1. If the last value in the
+ * enum is a special "count" value, it can be passed as the count,
+ * and the return value can be cast directly to the enum type.
+ *
+ * @code{.c}
+ * typedef enum { APPLE , BANANA , ORANGE , COUNT} fruit_t;
+ * const char* fruits[] = {"apple", "banana", "orange"};
+ *
+ * fruit_t fruit = (fruit_t)mpack_node_enum(node, fruits, COUNT);
+ * @endcode
+ *
+ * @param node The node
+ * @param strings An array of expected strings of length count
+ * @param count The number of strings
+ * @return The index of the matched string, or @a count in case of error
+ */
+size_t mpack_node_enum(mpack_node_t node, const char* strings[], size_t count);
+
+/**
+ * Searches the given string array for a string matching the given node,
+ * returning its index or @a count if no strings match.
+ *
+ * If the value is not a string, or it does not match any of the
+ * given strings, @a count is returned and no error is flagged.
+ *
+ * If any error occurs or if the tree is in an error state, @a count
+ * is returned.
+ *
+ * This can be used to quickly parse a string into an enum when the
+ * enum values range from 0 to @a count-1. If the last value in the
+ * enum is a special "count" value, it can be passed as the count,
+ * and the return value can be cast directly to the enum type.
+ *
+ * @code{.c}
+ * typedef enum { APPLE , BANANA , ORANGE , COUNT} fruit_t;
+ * const char* fruits[] = {"apple", "banana", "orange"};
+ *
+ * fruit_t fruit = (fruit_t)mpack_node_enum_optional(node, fruits, COUNT);
+ * @endcode
+ *
+ * @param node The node
+ * @param strings An array of expected strings of length count
+ * @param count The number of strings
+ * @return The index of the matched string, or @a count in case of error
+ */
+size_t mpack_node_enum_optional(mpack_node_t node, const char* strings[], size_t count);
+
+/**
+ * @}
+ */
+
+/**
+ * @name Compound Node Functions
+ * @{
+ */
+
+/**
+ * Returns the length of the given array node. Raises mpack_error_type
+ * and returns 0 if the given node is not an array.
+ */
+size_t mpack_node_array_length(mpack_node_t node);
+
+/**
+ * Returns the node in the given array at the given index. If the node
+ * is not an array, @ref mpack_error_type is raised and a nil node is returned.
+ * If the given index is out of bounds, @ref mpack_error_data is raised and
+ * a nil node is returned.
+ */
+mpack_node_t mpack_node_array_at(mpack_node_t node, size_t index);
+
+/**
+ * Returns the number of key/value pairs in the given map node. Raises
+ * mpack_error_type and returns 0 if the given node is not a map.
+ */
+size_t mpack_node_map_count(mpack_node_t node);
+
+/**
+ * Returns the key node in the given map at the given index.
+ *
+ * A nil node is returned in case of error.
+ *
+ * @throws mpack_error_type if the node is not a map
+ * @throws mpack_error_data if the given index is out of bounds
+ */
+mpack_node_t mpack_node_map_key_at(mpack_node_t node, size_t index);
+
+/**
+ * Returns the value node in the given map at the given index.
+ *
+ * A nil node is returned in case of error.
+ *
+ * @throws mpack_error_type if the node is not a map
+ * @throws mpack_error_data if the given index is out of bounds
+ */
+mpack_node_t mpack_node_map_value_at(mpack_node_t node, size_t index);
+
+/**
+ * Returns the value node in the given map for the given integer key.
+ *
+ * The key must exist within the map. Use mpack_node_map_int_optional() to
+ * check for optional keys.
+ *
+ * The key must be unique. An error is flagged if the node has multiple
+ * entries with the given key.
+ *
+ * @throws mpack_error_type If the node is not a map
+ * @throws mpack_error_data If the node does not contain exactly one entry with the given key
+ *
+ * @return The value node for the given key, or a nil node in case of error
+ */
+mpack_node_t mpack_node_map_int(mpack_node_t node, int64_t num);
+
+/**
+ * Returns the value node in the given map for the given integer key, or a
+ * missing node if the map does not contain the given key.
+ *
+ * The key must be unique. An error is flagged if the node has multiple
+ * entries with the given key.
+ *
+ * @throws mpack_error_type If the node is not a map
+ * @throws mpack_error_data If the node contains more than one entry with the given key
+ *
+ * @return The value node for the given key, or a missing node if the key does
+ * not exist, or a nil node in case of error
+ *
+ * @see mpack_node_is_missing()
+ */
+mpack_node_t mpack_node_map_int_optional(mpack_node_t node, int64_t num);
+
+/**
+ * Returns the value node in the given map for the given unsigned integer key.
+ *
+ * The key must exist within the map. Use mpack_node_map_uint_optional() to
+ * check for optional keys.
+ *
+ * The key must be unique. An error is flagged if the node has multiple
+ * entries with the given key.
+ *
+ * @throws mpack_error_type If the node is not a map
+ * @throws mpack_error_data If the node does not contain exactly one entry with the given key
+ *
+ * @return The value node for the given key, or a nil node in case of error
+ */
+mpack_node_t mpack_node_map_uint(mpack_node_t node, uint64_t num);
+
+/**
+ * Returns the value node in the given map for the given unsigned integer
+ * key, or a missing node if the map does not contain the given key.
+ *
+ * The key must be unique. An error is flagged if the node has multiple
+ * entries with the given key.
+ *
+ * @throws mpack_error_type If the node is not a map
+ * @throws mpack_error_data If the node contains more than one entry with the given key
+ *
+ * @return The value node for the given key, or a missing node if the key does
+ * not exist, or a nil node in case of error
+ *
+ * @see mpack_node_is_missing()
+ */
+mpack_node_t mpack_node_map_uint_optional(mpack_node_t node, uint64_t num);
+
+/**
+ * Returns the value node in the given map for the given string key.
+ *
+ * The key must exist within the map. Use mpack_node_map_str_optional() to
+ * check for optional keys.
+ *
+ * The key must be unique. An error is flagged if the node has multiple
+ * entries with the given key.
+ *
+ * @throws mpack_error_type If the node is not a map
+ * @throws mpack_error_data If the node does not contain exactly one entry with the given key
+ *
+ * @return The value node for the given key, or a nil node in case of error
+ */
+mpack_node_t mpack_node_map_str(mpack_node_t node, const char* str, size_t length);
+
+/**
+ * Returns the value node in the given map for the given string key, or a missing
+ * node if the map does not contain the given key.
+ *
+ * The key must be unique. An error is flagged if the node has multiple
+ * entries with the given key.
+ *
+ * @throws mpack_error_type If the node is not a map
+ * @throws mpack_error_data If the node contains more than one entry with the given key
+ *
+ * @return The value node for the given key, or a missing node if the key does
+ * not exist, or a nil node in case of error
+ *
+ * @see mpack_node_is_missing()
+ */
+mpack_node_t mpack_node_map_str_optional(mpack_node_t node, const char* str, size_t length);
+
+/**
+ * Returns the value node in the given map for the given null-terminated
+ * string key.
+ *
+ * The key must exist within the map. Use mpack_node_map_cstr_optional() to
+ * check for optional keys.
+ *
+ * The key must be unique. An error is flagged if the node has multiple
+ * entries with the given key.
+ *
+ * @throws mpack_error_type If the node is not a map
+ * @throws mpack_error_data If the node does not contain exactly one entry with the given key
+ *
+ * @return The value node for the given key, or a nil node in case of error
+ */
+mpack_node_t mpack_node_map_cstr(mpack_node_t node, const char* cstr);
+
+/**
+ * Returns the value node in the given map for the given null-terminated
+ * string key, or a missing node if the map does not contain the given key.
+ *
+ * The key must be unique. An error is flagged if the node has multiple
+ * entries with the given key.
+ *
+ * @throws mpack_error_type If the node is not a map
+ * @throws mpack_error_data If the node contains more than one entry with the given key
+ *
+ * @return The value node for the given key, or a missing node if the key does
+ * not exist, or a nil node in case of error
+ *
+ * @see mpack_node_is_missing()
+ */
+mpack_node_t mpack_node_map_cstr_optional(mpack_node_t node, const char* cstr);
+
+/**
+ * Returns true if the given node map contains exactly one entry with the
+ * given integer key.
+ *
+ * The key must be unique. An error is flagged if the node has multiple
+ * entries with the given key.
+ *
+ * @throws mpack_error_type If the node is not a map
+ * @throws mpack_error_data If the node contains more than one entry with the given key
+ */
+bool mpack_node_map_contains_int(mpack_node_t node, int64_t num);
+
+/**
+ * Returns true if the given node map contains exactly one entry with the
+ * given unsigned integer key.
+ *
+ * The key must be unique. An error is flagged if the node has multiple
+ * entries with the given key.
+ *
+ * @throws mpack_error_type If the node is not a map
+ * @throws mpack_error_data If the node contains more than one entry with the given key
+ */
+bool mpack_node_map_contains_uint(mpack_node_t node, uint64_t num);
+
+/**
+ * Returns true if the given node map contains exactly one entry with the
+ * given string key.
+ *
+ * The key must be unique. An error is flagged if the node has multiple
+ * entries with the given key.
+ *
+ * @throws mpack_error_type If the node is not a map
+ * @throws mpack_error_data If the node contains more than one entry with the given key
+ */
+bool mpack_node_map_contains_str(mpack_node_t node, const char* str, size_t length);
+
+/**
+ * Returns true if the given node map contains exactly one entry with the
+ * given null-terminated string key.
+ *
+ * The key must be unique. An error is flagged if the node has multiple
+ * entries with the given key.
+ *
+ * @throws mpack_error_type If the node is not a map
+ * @throws mpack_error_data If the node contains more than one entry with the given key
+ */
+bool mpack_node_map_contains_cstr(mpack_node_t node, const char* cstr);
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif
+
+MPACK_EXTERN_C_END
+MPACK_HEADER_END
+
+#endif
+
+
+#endif
+
diff --git a/src/fluent-bit/lib/cmetrics/markdownlint.rb b/src/fluent-bit/lib/cmetrics/markdownlint.rb
new file mode 100644
index 000000000..598a9b5b6
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/markdownlint.rb
@@ -0,0 +1,16 @@
+#!/usr/bin/ruby
+
+# Enable all rules by default
+all
+
+# Extend line length, since each sentence should be on a separate line.
+rule 'MD013', :line_length => 99999, :ignore_code_blocks => true
+
+# Allow in-line HTML
+exclude_rule 'MD033'
+
+# Nested lists should be indented with two spaces.
+rule 'MD007', :indent => 2
+
+# Bash defaulting confuses this and now way to ignore code blocks
+exclude_rule 'MD029'
diff --git a/src/fluent-bit/lib/cmetrics/scripts/win_build.bat b/src/fluent-bit/lib/cmetrics/scripts/win_build.bat
new file mode 100755
index 000000000..f1fb6d87b
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/scripts/win_build.bat
@@ -0,0 +1,8 @@
+setlocal
+call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\VsDevCmd.bat"
+path "C:\Program Files (x86)\MSBuild\16.0\Bin;C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin";%path%
+git submodule update --init --recursive
+cd build
+cmake -G "NMake Makefiles" -DCMT_TESTS=On ..\
+cmake --build .
+endlocal
diff --git a/src/fluent-bit/lib/cmetrics/src/CMakeLists.txt b/src/fluent-bit/lib/cmetrics/src/CMakeLists.txt
new file mode 100644
index 000000000..e7db7596c
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/CMakeLists.txt
@@ -0,0 +1,80 @@
+if (CMT_BUILD_PROMETHEUS_DECODER)
+ flex_target(cmt_decode_prometheus_lexer cmt_decode_prometheus.l
+ "${FLEX_BISON_GENERATED_DIR}/cmt_decode_prometheus_lexer.c"
+ DEFINES_FILE "${FLEX_BISON_GENERATED_DIR}/cmt_decode_prometheus_lexer.h"
+ )
+ bison_target(cmt_decode_prometheus_parser cmt_decode_prometheus.y
+ "${FLEX_BISON_GENERATED_DIR}/cmt_decode_prometheus_parser.c")
+ add_flex_bison_dependency(cmt_decode_prometheus_lexer cmt_decode_prometheus_parser)
+endif()
+
+set(src
+ cmt_gauge.c
+ cmt_counter.c
+ cmt_untyped.c
+ cmt_summary.c
+ cmt_histogram.c
+ cmt_metric.c
+ cmt_metric_histogram.c
+ cmt_map.c
+ cmt_log.c
+ cmt_opts.c
+ cmt_time.c
+ cmt_label.c
+ cmt_cat.c
+ cmetrics.c
+ cmt_encode_opentelemetry.c
+ cmt_decode_opentelemetry.c
+ cmt_encode_prometheus.c
+ cmt_encode_prometheus_remote_write.c
+ cmt_encode_splunk_hec.c
+ cmt_encode_text.c
+ cmt_encode_influx.c
+ cmt_encode_msgpack.c
+ cmt_decode_msgpack.c
+ cmt_mpack_utils.c
+ external/remote.pb-c.c
+ external/types.pb-c.c
+ )
+
+if (${CMAKE_C_COMPILER_ID} STREQUAL "Clang")
+ set(PLATFORM_SPECIFIC_ATOMIC_MODULE cmt_atomic_clang.c)
+elseif (${CMAKE_C_COMPILER_ID} MATCHES "AppleClang")
+ set(PLATFORM_SPECIFIC_ATOMIC_MODULE cmt_atomic_clang.c)
+elseif (${CMAKE_C_COMPILER_ID} STREQUAL "GNU")
+ set(PLATFORM_SPECIFIC_ATOMIC_MODULE cmt_atomic_gcc.c)
+elseif (${CMAKE_C_COMPILER_ID} STREQUAL "MSVC")
+ set(PLATFORM_SPECIFIC_ATOMIC_MODULE cmt_atomic_msvc.c)
+else()
+ set(PLATFORM_SPECIFIC_ATOMIC_MODULE cmt_atomic_generic.c)
+endif()
+
+set(src
+ ${src}
+ ${PLATFORM_SPECIFIC_ATOMIC_MODULE}
+ )
+
+if (CMT_BUILD_PROMETHEUS_DECODER)
+ set(src ${src}
+ ${FLEX_cmt_decode_prometheus_lexer_OUTPUTS}
+ ${BISON_cmt_decode_prometheus_parser_OUTPUTS}
+ )
+endif()
+
+# Static Library
+add_library(cmetrics-static STATIC ${src})
+target_link_libraries(cmetrics-static mpack-static cfl-static fluent-otel-proto)
+
+# Install Library
+if(MSVC)
+ # Rename the output for Windows environment to avoid naming issues
+ set_target_properties(cmetrics-static PROPERTIES OUTPUT_NAME libcmetrics)
+else()
+ set_target_properties(cmetrics-static PROPERTIES OUTPUT_NAME cmetrics)
+endif(MSVC)
+
+install(TARGETS cmetrics-static
+ RUNTIME DESTINATION ${CMT_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMT_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMT_INSTALL_LIBDIR}
+ COMPONENT library)
diff --git a/src/fluent-bit/lib/cmetrics/src/cmetrics.c b/src/fluent-bit/lib/cmetrics/src/cmetrics.c
new file mode 100644
index 000000000..9817809d1
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmetrics.c
@@ -0,0 +1,147 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_log.h>
+#include <cmetrics/cmt_counter.h>
+#include <cmetrics/cmt_gauge.h>
+#include <cmetrics/cmt_summary.h>
+#include <cmetrics/cmt_histogram.h>
+#include <cmetrics/cmt_untyped.h>
+#include <cmetrics/cmt_atomic.h>
+#include <cmetrics/cmt_compat.h>
+#include <cmetrics/cmt_label.h>
+#include <cmetrics/cmt_version.h>
+
+#include <cfl/cfl_kvlist.h>
+
+void cmt_initialize()
+{
+ cmt_atomic_initialize();
+}
+
+struct cmt *cmt_create()
+{
+ struct cmt *cmt;
+
+ cmt = calloc(1, sizeof(struct cmt));
+ if (!cmt) {
+ cmt_errno();
+ return NULL;
+ }
+
+ cmt->static_labels = cmt_labels_create();
+ if (!cmt->static_labels) {
+ free(cmt);
+ return NULL;
+ }
+
+ cmt->internal_metadata = cfl_kvlist_create();
+
+ if (cmt->internal_metadata == NULL) {
+ cmt_labels_destroy(cmt->static_labels);
+
+ free(cmt);
+ return NULL;
+ }
+
+ cmt->external_metadata = cfl_kvlist_create();
+
+ if (cmt->external_metadata == NULL) {
+ cfl_kvlist_destroy(cmt->internal_metadata);
+ cmt_labels_destroy(cmt->static_labels);
+
+ free(cmt);
+ return NULL;
+ }
+
+ cfl_list_init(&cmt->counters);
+ cfl_list_init(&cmt->gauges);
+ cfl_list_init(&cmt->histograms);
+ cfl_list_init(&cmt->summaries);
+ cfl_list_init(&cmt->untypeds);
+
+ cmt->log_level = CMT_LOG_ERROR;
+
+ cfl_list_entry_init(&cmt->_head);
+
+ return cmt;
+}
+
+void cmt_destroy(struct cmt *cmt)
+{
+ struct cfl_list *tmp;
+ struct cfl_list *head;
+ struct cmt_counter *c;
+ struct cmt_gauge *g;
+ struct cmt_summary *s;
+ struct cmt_histogram *h;
+ struct cmt_untyped *u;
+
+ cfl_list_foreach_safe(head, tmp, &cmt->counters) {
+ c = cfl_list_entry(head, struct cmt_counter, _head);
+ cmt_counter_destroy(c);
+ }
+
+ cfl_list_foreach_safe(head, tmp, &cmt->gauges) {
+ g = cfl_list_entry(head, struct cmt_gauge, _head);
+ cmt_gauge_destroy(g);
+ }
+
+ cfl_list_foreach_safe(head, tmp, &cmt->summaries) {
+ s = cfl_list_entry(head, struct cmt_summary, _head);
+ cmt_summary_destroy(s);
+ }
+
+ cfl_list_foreach_safe(head, tmp, &cmt->histograms) {
+ h = cfl_list_entry(head, struct cmt_histogram, _head);
+ cmt_histogram_destroy(h);
+ }
+
+ cfl_list_foreach_safe(head, tmp, &cmt->untypeds) {
+ u = cfl_list_entry(head, struct cmt_untyped, _head);
+ cmt_untyped_destroy(u);
+ }
+
+ if (cmt->static_labels) {
+ cmt_labels_destroy(cmt->static_labels);
+ }
+
+ if (cmt->internal_metadata != NULL) {
+ cfl_kvlist_destroy(cmt->internal_metadata);
+ }
+
+ if (cmt->external_metadata != NULL) {
+ cfl_kvlist_destroy(cmt->external_metadata);
+ }
+
+ free(cmt);
+}
+
+int cmt_label_add(struct cmt *cmt, char *key, char *val)
+{
+ return cmt_labels_add_kv(cmt->static_labels, key, val);
+}
+
+char *cmt_version()
+{
+ return CMT_VERSION_STR;
+}
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_atomic_clang.c b/src/fluent-bit/lib/cmetrics/src/cmt_atomic_clang.c
new file mode 100644
index 000000000..5e8734ade
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_atomic_clang.c
@@ -0,0 +1,42 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmt_atomic.h>
+
+inline int cmt_atomic_initialize()
+{
+ return 0;
+}
+
+inline int cmt_atomic_compare_exchange(uint64_t *storage,
+ uint64_t old_value, uint64_t new_value)
+{
+ return __atomic_compare_exchange(storage, &old_value, &new_value, 0,
+ __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
+}
+
+inline void cmt_atomic_store(uint64_t *storage, uint64_t new_value)
+{
+ __atomic_store_n(storage, new_value, __ATOMIC_SEQ_CST);
+}
+
+inline uint64_t cmt_atomic_load(uint64_t *storage)
+{
+ return __atomic_load_n(storage, __ATOMIC_SEQ_CST);
+}
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_atomic_gcc.c b/src/fluent-bit/lib/cmetrics/src/cmt_atomic_gcc.c
new file mode 100644
index 000000000..5e8734ade
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_atomic_gcc.c
@@ -0,0 +1,42 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmt_atomic.h>
+
+inline int cmt_atomic_initialize()
+{
+ return 0;
+}
+
+inline int cmt_atomic_compare_exchange(uint64_t *storage,
+ uint64_t old_value, uint64_t new_value)
+{
+ return __atomic_compare_exchange(storage, &old_value, &new_value, 0,
+ __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
+}
+
+inline void cmt_atomic_store(uint64_t *storage, uint64_t new_value)
+{
+ __atomic_store_n(storage, new_value, __ATOMIC_SEQ_CST);
+}
+
+inline uint64_t cmt_atomic_load(uint64_t *storage)
+{
+ return __atomic_load_n(storage, __ATOMIC_SEQ_CST);
+}
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_atomic_generic.c b/src/fluent-bit/lib/cmetrics/src/cmt_atomic_generic.c
new file mode 100644
index 000000000..40e59b615
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_atomic_generic.c
@@ -0,0 +1,123 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <cmetrics/cmt_atomic.h>
+
+pthread_mutex_t atomic_operation_lock;
+static int atomic_operation_system_initialized = 0;
+
+/* TODO: Determne if we want to keep this backend as well as how / if we want to handle
+ * pthread_mutex_unlock errors (investigate and understand what could cause them),
+ * as well as pthread_mutex_lock (determine if we want to apply some sort of retry
+ * limit there as well)
+ *
+ */
+
+inline int cmt_atomic_initialize()
+{
+ int result;
+
+ if (0 == atomic_operation_system_initialized) {
+ result = pthread_mutex_init(&atomic_operation_lock, NULL);
+
+ if (0 != result) {
+ return 1;
+ }
+
+ atomic_operation_system_initialized = 1;
+ }
+
+ return 0;
+}
+
+inline int cmt_atomic_compare_exchange(uint64_t *storage,
+ uint64_t old_value, uint64_t new_value)
+{
+ int result;
+
+ if (0 == atomic_operation_system_initialized) {
+ printf("CMT ATOMIC : Atomic operation backend not initalized\n");
+ exit(1);
+ }
+
+ result = pthread_mutex_lock(&atomic_operation_lock);
+
+ if (result != 0) {
+ return 0;
+ }
+
+ if (*storage == old_value) {
+ *storage = new_value;
+
+ result = 1;
+ }
+ else {
+ result = 0;
+ }
+
+ pthread_mutex_unlock(&atomic_operation_lock);
+
+ return result;
+}
+
+inline void cmt_atomic_store(uint64_t *storage, uint64_t new_value)
+{
+ int result;
+
+ if (0 == atomic_operation_system_initialized) {
+ printf("CMT ATOMIC : Atomic operation backend not initalized\n");
+ exit(1);
+ }
+
+ result = pthread_mutex_lock(&atomic_operation_lock);
+
+ if (result != 0) {
+ /* We should notify the user somehow */
+ }
+
+ *storage = new_value;
+
+ pthread_mutex_unlock(&atomic_operation_lock);
+}
+
+inline uint64_t cmt_atomic_load(uint64_t *storage)
+{
+ int result;
+ uint64_t retval;
+
+ if (0 == atomic_operation_system_initialized) {
+ printf("CMT ATOMIC : Atomic operation backend not initalized\n");
+ exit(1);
+ }
+
+ result = pthread_mutex_lock(&atomic_operation_lock);
+
+ if (result != 0) {
+ /* We should notify the user somehow */
+ }
+
+ retval = *storage;
+
+ pthread_mutex_unlock(&atomic_operation_lock);
+
+ return retval;
+}
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_atomic_msvc.c b/src/fluent-bit/lib/cmetrics/src/cmt_atomic_msvc.c
new file mode 100644
index 000000000..65bfd2f80
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_atomic_msvc.c
@@ -0,0 +1,131 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmt_atomic.h>
+#include <windows.h>
+
+/* This allows cmt_atomic_initialize to be automatically called
+ * as soon as the program starts if enabled.
+ */
+
+#ifndef _WIN64
+CRITICAL_SECTION atomic_operation_lock;
+static int atomic_operation_system_initialized = 0;
+
+int cmt_atomic_initialize()
+{
+ if (0 == atomic_operation_system_initialized) {
+ InitializeCriticalSection(&atomic_operation_lock);
+
+ atomic_operation_system_initialized = 1;
+ }
+
+ return 0;
+}
+
+int cmt_atomic_compare_exchange(uint64_t *storage,
+ uint64_t old_value, uint64_t new_value)
+{
+ uint64_t result;
+
+ if (0 == atomic_operation_system_initialized) {
+ printf("CMT ATOMIC : Atomic operation backend not initalized\n");
+ exit(1);
+ }
+
+ EnterCriticalSection(&atomic_operation_lock);
+
+ if (*storage == old_value) {
+ *storage = new_value;
+
+ result = 1;
+ }
+ else {
+ result = 0;
+ }
+
+ LeaveCriticalSection(&atomic_operation_lock);
+
+ return result;
+}
+
+void cmt_atomic_store(uint64_t *storage, uint64_t new_value)
+{
+ if (0 == atomic_operation_system_initialized) {
+ printf("CMT ATOMIC : Atomic operation backend not initalized\n");
+ exit(1);
+ }
+
+ EnterCriticalSection(&atomic_operation_lock);
+
+ *storage = new_value;
+
+ LeaveCriticalSection(&atomic_operation_lock);
+}
+
+uint64_t cmt_atomic_load(uint64_t *storage)
+{
+ uint64_t result;
+
+ if (0 == atomic_operation_system_initialized) {
+ printf("CMT ATOMIC : Atomic operation backend not initalized\n");
+ exit(1);
+ }
+
+ EnterCriticalSection(&atomic_operation_lock);
+
+ result = *storage;
+
+ LeaveCriticalSection(&atomic_operation_lock);
+
+ return result;
+}
+
+#else /* _WIN64 */
+
+int cmt_atomic_initialize()
+{
+ return 0;
+}
+
+int cmt_atomic_compare_exchange(uint64_t *storage,
+ uint64_t old_value, uint64_t new_value)
+{
+ uint64_t result;
+
+ result = _InterlockedCompareExchange64(storage, new_value, old_value);
+
+ if (result != old_value) {
+ return 0;
+ }
+
+ return 1;
+}
+
+void cmt_atomic_store(uint64_t *storage, uint64_t new_value)
+{
+ _InterlockedExchange64(storage, new_value);
+}
+
+uint64_t cmt_atomic_load(uint64_t *storage)
+{
+ return _InterlockedOr64(storage, 0);
+}
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_cat.c b/src/fluent-bit/lib/cmetrics/src/cmt_cat.c
new file mode 100644
index 000000000..5475a98a3
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_cat.c
@@ -0,0 +1,454 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_log.h>
+#include <cmetrics/cmt_map.h>
+#include <cmetrics/cmt_counter.h>
+#include <cmetrics/cmt_gauge.h>
+#include <cmetrics/cmt_untyped.h>
+#include <cmetrics/cmt_histogram.h>
+#include <cmetrics/cmt_summary.h>
+
+static int copy_label_keys(struct cmt_map *map, char **out)
+{
+ int i;
+ int s;
+ char **labels = NULL;
+ struct cfl_list *head;
+ struct cmt_map_label *label;
+
+ /* labels array */
+ s = map->label_count;
+ if (s == 0) {
+ *out = NULL;
+ return 0;
+ }
+
+ if (s > 0) {
+ labels = malloc(sizeof(char *) * s);
+ if (!labels) {
+ cmt_errno();
+ return -1;
+ }
+ }
+
+ /* label keys: by using the labels array, just point out the names */
+ i = 0;
+ cfl_list_foreach(head, &map->label_keys) {
+ label = cfl_list_entry(head, struct cmt_map_label, _head);
+ labels[i] = label->name;
+ i++;
+ }
+
+ *out = (char *) labels;
+ return i;
+}
+
+static int copy_label_values(struct cmt_metric *metric, char **out)
+{
+ int i;
+ int s;
+ char **labels = NULL;
+ struct cfl_list *head;
+ struct cmt_map_label *label;
+
+ /* labels array */
+ s = cfl_list_size(&metric->labels);
+ if (s == 0) {
+ *out = NULL;
+ return 0;
+ }
+
+ if (s > 0) {
+ labels = malloc(sizeof(char *) * s);
+ if (!labels) {
+ cmt_errno();
+ return -1;
+ }
+ }
+
+ /* label keys: by using the labels array, just point out the names */
+ i = 0;
+ cfl_list_foreach(head, &metric->labels) {
+ label = cfl_list_entry(head, struct cmt_map_label, _head);
+ labels[i] = label->name;
+ i++;
+ }
+
+ *out = (char *) labels;
+ return i;
+}
+
+static int copy_map(struct cmt_opts *opts, struct cmt_map *dst, struct cmt_map *src)
+{
+ int i;
+ int c;
+ int ret;
+ uint64_t ts;
+ double val;
+ char **labels = NULL;
+ struct cfl_list *head;
+ struct cmt_metric *metric_dst;
+ struct cmt_metric *metric_src;
+
+ /* Handle static metric (no labels case) */
+ if (src->metric_static_set) {
+ dst->metric_static_set = CMT_TRUE;
+
+ /* destination and source metric */
+ metric_dst = &dst->metric;
+ metric_src = &src->metric;
+
+ ts = cmt_metric_get_timestamp(metric_src);
+ val = cmt_metric_get_value(metric_src);
+
+ cmt_metric_set(metric_dst, ts, val);
+ }
+
+ /* Process map dynamic metrics */
+ cfl_list_foreach(head, &src->metrics) {
+ metric_src = cfl_list_entry(head, struct cmt_metric, _head);
+
+ ret = copy_label_values(metric_src, (char **) &labels);
+ if (ret == -1) {
+ return -1;
+ }
+
+ c = cfl_list_size(&metric_src->labels);
+ metric_dst = cmt_map_metric_get(opts, dst, c, labels, CMT_TRUE);
+ free(labels);
+
+ if (!metric_dst) {
+ return -1;
+ }
+
+ if (src->type == CMT_HISTOGRAM) {
+ if (!metric_dst->hist_buckets) {
+ metric_dst->hist_buckets = calloc(1, sizeof(uint64_t) * (metric_src->hist_count + 1));
+ if (!metric_dst->hist_buckets) {
+ return -1;
+ }
+ }
+ for (i = 0; i < metric_src->hist_count; i++) {
+ metric_dst->hist_buckets[i] = metric_src->hist_buckets[i];
+ }
+ metric_dst->hist_count = metric_src->hist_count;
+ metric_dst->hist_sum = metric_src->hist_sum;
+ }
+ else if (src->type == CMT_SUMMARY) {
+ metric_dst->sum_quantiles_count = metric_src->sum_quantiles_count;
+ metric_dst->sum_quantiles_set = metric_src->sum_quantiles_set;
+ if (!metric_dst->sum_quantiles) {
+ metric_dst->sum_quantiles = calloc(1, sizeof(uint64_t) * (metric_src->sum_quantiles_count));
+ if (!metric_dst->sum_quantiles) {
+ return -1;
+ }
+ }
+ for (i = 0; i < metric_src->sum_quantiles_count; i++) {
+ metric_dst->sum_quantiles[i] = metric_src->sum_quantiles[i];
+ }
+ metric_dst->sum_count = metric_src->sum_count;
+ metric_dst->sum_sum = metric_src->sum_sum;
+ }
+
+ ts = cmt_metric_get_timestamp(metric_src);
+ val = cmt_metric_get_value(metric_src);
+
+ cmt_metric_set(metric_dst, ts, val);
+ }
+
+ return 0;
+
+}
+
+static int copy_counter(struct cmt *cmt, struct cmt_counter *counter)
+{
+ int ret;
+ char **labels = NULL;
+ struct cmt_map *map;
+ struct cmt_opts *opts;
+ struct cmt_counter *c;
+
+ map = counter->map;
+ opts = map->opts;
+
+ ret = copy_label_keys(map, (char **) &labels);
+ if (ret == -1) {
+ return -1;
+ }
+
+ /* create counter */
+ c = cmt_counter_create(cmt,
+ opts->ns, opts->subsystem,
+ opts->name, opts->description,
+ map->label_count, labels);
+
+ free(labels);
+ if (!c) {
+ return -1;
+ }
+
+ ret = copy_map(&c->opts, c->map, map);
+ if (ret == -1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int copy_gauge(struct cmt *cmt, struct cmt_gauge *gauge)
+{
+ int ret;
+ char **labels = NULL;
+ struct cmt_map *map;
+ struct cmt_opts *opts;
+ struct cmt_gauge *g;
+
+ map = gauge->map;
+ opts = map->opts;
+
+ ret = copy_label_keys(map, (char **) &labels);
+ if (ret == -1) {
+ return -1;
+ }
+
+ /* create counter */
+ g = cmt_gauge_create(cmt,
+ opts->ns, opts->subsystem,
+ opts->name, opts->description,
+ map->label_count, labels);
+ free(labels);
+ if (!g) {
+ return -1;
+ }
+
+ ret = copy_map(&g->opts, g->map, map);
+ if (ret == -1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int copy_untyped(struct cmt *cmt, struct cmt_untyped *untyped)
+{
+ int ret;
+ char **labels = NULL;
+ struct cmt_map *map;
+ struct cmt_opts *opts;
+ struct cmt_untyped *u;
+
+ map = untyped->map;
+ opts = map->opts;
+
+ ret = copy_label_keys(map, (char **) &labels);
+ if (ret == -1) {
+ return -1;
+ }
+
+ /* create counter */
+ u = cmt_untyped_create(cmt,
+ opts->ns, opts->subsystem,
+ opts->name, opts->description,
+ map->label_count, labels);
+ free(labels);
+ if (!u) {
+ return -1;
+ }
+
+ ret = copy_map(&u->opts, u->map, map);
+ if (ret == -1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int copy_histogram(struct cmt *cmt, struct cmt_histogram *histogram)
+{
+ int i;
+ double val;
+ int ret;
+ char **labels = NULL;
+ struct cmt_map *map;
+ struct cmt_opts *opts;
+ struct cmt_histogram *hist;
+ uint64_t timestamp;
+ struct cmt_histogram_buckets *buckets;
+ int64_t buckets_count;
+
+ map = histogram->map;
+ opts = map->opts;
+ timestamp = cmt_metric_get_timestamp(&map->metric);
+
+ ret = copy_label_keys(map, (char **) &labels);
+ if (ret == -1) {
+ return -1;
+ }
+
+ buckets_count = histogram->buckets->count;
+ buckets = cmt_histogram_buckets_create_size(histogram->buckets->upper_bounds,
+ buckets_count);
+
+ /* create histogram */
+ hist = cmt_histogram_create(cmt,
+ opts->ns, opts->subsystem,
+ opts->name, opts->description,
+ buckets,
+ map->label_count, labels);
+ if (!hist) {
+ return -1;
+ }
+
+ for (i = 0; i < buckets_count; i++) {
+ val = histogram->buckets->upper_bounds[i];
+ cmt_histogram_observe(hist, timestamp, val, map->label_count, labels);
+ }
+ free(labels);
+
+ ret = copy_map(&hist->opts, hist->map, map);
+ if (ret == -1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int copy_summary(struct cmt *cmt, struct cmt_summary *summary)
+{
+ int i;
+ int ret;
+ char **labels = NULL;
+ struct cmt_map *map;
+ struct cmt_opts *opts;
+ struct cmt_summary *sum;
+ double *quantiles;
+ uint64_t timestamp;
+ double summary_sum;
+
+ map = summary->map;
+ opts = map->opts;
+ timestamp = cmt_metric_get_timestamp(&map->metric);
+
+ ret = copy_label_keys(map, (char **) &labels);
+ if (ret == -1) {
+ return -1;
+ }
+
+ quantiles = calloc(1, sizeof(double) * summary->quantiles_count);
+ for (i = 0; i < summary->quantiles_count; i++) {
+ quantiles[i] = summary->quantiles[i];
+ }
+
+ /* create summary */
+ sum = cmt_summary_create(cmt,
+ opts->ns, opts->subsystem,
+ opts->name, opts->description,
+ summary->quantiles_count,
+ quantiles,
+ map->label_count, labels);
+ if (!sum) {
+ return -1;
+ }
+
+ summary_sum = cmt_summary_get_sum_value(&summary->map->metric);
+
+ cmt_summary_set_default(sum, timestamp, quantiles, summary_sum, summary->quantiles_count, map->label_count, labels);
+ free(labels);
+ free(quantiles);
+
+ ret = copy_map(&sum->opts, sum->map, map);
+ if (ret == -1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int append_context(struct cmt *dst, struct cmt *src)
+{
+ int ret;
+ struct cfl_list *head;
+ struct cmt_counter *counter;
+ struct cmt_gauge *gauge;
+ struct cmt_untyped *untyped;
+ struct cmt_histogram *histogram;
+ struct cmt_summary *summary;
+
+ /* Counters */
+ cfl_list_foreach(head, &src->counters) {
+ counter = cfl_list_entry(head, struct cmt_counter, _head);
+ ret = copy_counter(dst, counter);
+ if (ret == -1) {
+ return -1;
+ }
+ }
+
+ /* Gauges */
+ cfl_list_foreach(head, &src->gauges) {
+ gauge = cfl_list_entry(head, struct cmt_gauge, _head);
+ ret = copy_gauge(dst, gauge);
+ if (ret == -1) {
+ return -1;
+ }
+ }
+
+ /* Untyped */
+ cfl_list_foreach(head, &src->untypeds) {
+ untyped = cfl_list_entry(head, struct cmt_untyped, _head);
+ ret = copy_untyped(dst, untyped);
+ if (ret == -1) {
+ return -1;
+ }
+ }
+
+ /* Histogram */
+ cfl_list_foreach(head, &src->histograms) {
+ histogram = cfl_list_entry(head, struct cmt_histogram, _head);
+ ret = copy_histogram(dst, histogram);
+ if (ret == -1) {
+ return -1;
+ }
+ }
+
+ /* Summary */
+ cfl_list_foreach(head, &src->summaries) {
+ summary = cfl_list_entry(head, struct cmt_summary, _head);
+ ret = copy_summary(dst, summary);
+ if (ret == -1) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int cmt_cat(struct cmt *dst, struct cmt *src)
+{
+ if (!dst) {
+ return -1;
+ }
+
+ if (!src) {
+ return -1;
+ }
+
+ return append_context(dst, src);
+}
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_counter.c b/src/fluent-bit/lib/cmetrics/src/cmt_counter.c
new file mode 100644
index 000000000..668e9168e
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_counter.c
@@ -0,0 +1,180 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_log.h>
+#include <cmetrics/cmt_map.h>
+#include <cmetrics/cmt_metric.h>
+#include <cmetrics/cmt_counter.h>
+
+struct cmt_counter *cmt_counter_create(struct cmt *cmt,
+ char *ns, char *subsystem,
+ char *name, char *help,
+ int label_count, char **label_keys)
+{
+ int ret;
+ struct cmt_counter *counter;
+
+ if (!ns) {
+ cmt_log_error(cmt, "null ns not allowed");
+ return NULL;
+ }
+
+ if (!subsystem) {
+ cmt_log_error(cmt, "null subsystem not allowed");
+ return NULL;
+ }
+
+ if (!name || strlen(name) == 0) {
+ cmt_log_error(cmt, "undefined name");
+ return NULL;
+ }
+
+ if (!help || strlen(help) == 0) {
+ cmt_log_error(cmt, "undefined help");
+ return NULL;
+ }
+
+ counter = calloc(1, sizeof(struct cmt_counter));
+ if (!counter) {
+ cmt_errno();
+ return NULL;
+ }
+ cfl_list_add(&counter->_head, &cmt->counters);
+
+ ret = cmt_opts_init(&counter->opts, ns, subsystem, name, help);
+ if (ret == -1) {
+ cmt_log_error(cmt, "unable to initialize options for counter");
+ cmt_counter_destroy(counter);
+ return NULL;
+ }
+
+ /* Create the map */
+ counter->map = cmt_map_create(CMT_COUNTER, &counter->opts, label_count, label_keys,
+ (void *) counter);
+ if (!counter->map) {
+ cmt_log_error(cmt, "unable to allocate map for counter");
+ cmt_counter_destroy(counter);
+ return NULL;
+ }
+ /* set default counter aggregation type to cumulative */
+ counter->aggregation_type = CMT_AGGREGATION_TYPE_CUMULATIVE;
+
+ counter->cmt = cmt;
+ return counter;
+}
+
+void cmt_counter_allow_reset(struct cmt_counter *counter)
+{
+ counter->allow_reset = 1;
+}
+
+int cmt_counter_destroy(struct cmt_counter *counter)
+{
+ cfl_list_del(&counter->_head);
+ cmt_opts_exit(&counter->opts);
+
+ if (counter->map) {
+ cmt_map_destroy(counter->map);
+ }
+ free(counter);
+ return 0;
+}
+
+int cmt_counter_inc(struct cmt_counter *counter,
+ uint64_t timestamp,
+ int labels_count, char **label_vals)
+{
+ struct cmt_metric *metric;
+
+ metric = cmt_map_metric_get(&counter->opts,
+ counter->map, labels_count, label_vals,
+ CMT_TRUE);
+ if (!metric) {
+ cmt_log_error(counter->cmt, "unable to retrieve metric: %s for counter %s_%s_%s",
+ counter->map, counter->opts.ns, counter->opts.subsystem,
+ counter->opts.name);
+ return -1;
+ }
+ cmt_metric_inc(metric, timestamp);
+ return 0;
+}
+
+int cmt_counter_add(struct cmt_counter *counter, uint64_t timestamp, double val,
+ int labels_count, char **label_vals)
+{
+ struct cmt_metric *metric;
+
+ metric = cmt_map_metric_get(&counter->opts,
+ counter->map, labels_count, label_vals,
+ CMT_TRUE);
+ if (!metric) {
+ cmt_log_error(counter->cmt, "unable to retrieve metric: %s for counter %s_%s_%s",
+ counter->map, counter->opts.ns, counter->opts.subsystem,
+ counter->opts.name);
+ return -1;
+ }
+ cmt_metric_add(metric, timestamp, val);
+ return 0;
+}
+
+/* Set counter value, new value cannot be smaller than current value */
+int cmt_counter_set(struct cmt_counter *counter, uint64_t timestamp, double val,
+ int labels_count, char **label_vals)
+{
+ struct cmt_metric *metric;
+
+ metric = cmt_map_metric_get(&counter->opts, counter->map,
+ labels_count, label_vals,
+ CMT_TRUE);
+ if (!metric) {
+ cmt_log_error(counter->cmt, "unable to retrieve metric: %s for counter %s_%s_%s",
+ counter->map, counter->opts.ns, counter->opts.subsystem,
+ counter->opts.name);
+ return -1;
+ }
+
+ if (cmt_metric_get_value(metric) > val && counter->allow_reset == 0) {
+ cmt_log_error(counter->cmt, "attempting to reset unresetable counter: %s_%s_%s",
+ counter->opts.ns, counter->opts.subsystem,
+ counter->opts.name);
+ return -1;
+ }
+ cmt_metric_set(metric, timestamp, val);
+ return 0;
+}
+
+int cmt_counter_get_val(struct cmt_counter *counter,
+ int labels_count, char **label_vals, double *out_val)
+{
+ int ret;
+ double val = 0;
+
+ ret = cmt_map_metric_get_val(&counter->opts,
+ counter->map, labels_count, label_vals,
+ &val);
+ if (ret == -1) {
+ cmt_log_error(counter->cmt, "unable to retrieve metric: %s for counter %s_%s_%s",
+ counter->map, counter->opts.ns, counter->opts.subsystem,
+ counter->opts.name);
+ return -1;
+ }
+ *out_val = val;
+ return 0;
+}
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_decode_msgpack.c b/src/fluent-bit/lib/cmetrics/src/cmt_decode_msgpack.c
new file mode 100644
index 000000000..2e1cf31be
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_decode_msgpack.c
@@ -0,0 +1,1474 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_metric.h>
+#include <cmetrics/cmt_map.h>
+#include <cmetrics/cmt_summary.h>
+#include <cmetrics/cmt_histogram.h>
+#include <cmetrics/cmt_counter.h>
+#include <cmetrics/cmt_gauge.h>
+#include <cmetrics/cmt_untyped.h>
+#include <cmetrics/cmt_compat.h>
+#include <cmetrics/cmt_encode_msgpack.h>
+#include <cmetrics/cmt_decode_msgpack.h>
+#include <cmetrics/cmt_variant_utils.h>
+#include <cmetrics/cmt_mpack_utils.h>
+
+
+static int create_counter_instance(struct cmt_map *map)
+{
+ struct cmt_counter *counter;
+
+ if (NULL == map) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ counter = calloc(1, sizeof(struct cmt_counter));
+
+ if (NULL == counter) {
+ return CMT_DECODE_MSGPACK_ALLOCATION_ERROR;
+ }
+
+ counter->map = map;
+
+ map->parent = (void *) counter;
+
+ return CMT_DECODE_MSGPACK_SUCCESS;
+}
+
+static int create_gauge_instance(struct cmt_map *map)
+{
+ struct cmt_gauge *gauge;
+
+ if (NULL == map) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ gauge = calloc(1, sizeof(struct cmt_gauge));
+
+ if (NULL == gauge) {
+ return CMT_DECODE_MSGPACK_ALLOCATION_ERROR;
+ }
+
+ gauge->map = map;
+
+ map->parent = (void *) gauge;
+
+ return CMT_DECODE_MSGPACK_SUCCESS;
+}
+
+static int create_untyped_instance(struct cmt_map *map)
+{
+ struct cmt_untyped *untyped;
+
+ if (NULL == map) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ untyped = calloc(1, sizeof(struct cmt_untyped));
+
+ if (NULL == untyped) {
+ return CMT_DECODE_MSGPACK_ALLOCATION_ERROR;
+ }
+
+ untyped->map = map;
+
+ map->parent = (void *) untyped;
+
+ return CMT_DECODE_MSGPACK_SUCCESS;
+}
+
+static int create_summary_instance(struct cmt_map *map)
+{
+ struct cmt_summary *summary;
+
+ if (NULL == map) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ summary = calloc(1, sizeof(struct cmt_summary));
+
+ if (NULL == summary) {
+ return CMT_DECODE_MSGPACK_ALLOCATION_ERROR;
+ }
+
+ summary->map = map;
+
+ map->parent = (void *) summary;
+
+ return CMT_DECODE_MSGPACK_SUCCESS;
+}
+
+static int create_histogram_instance(struct cmt_map *map)
+{
+ struct cmt_histogram *histogram;
+
+ if (NULL == map) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ histogram = calloc(1, sizeof(struct cmt_histogram));
+
+ if (NULL == histogram) {
+ return CMT_DECODE_MSGPACK_ALLOCATION_ERROR;
+ }
+
+ histogram->map = map;
+
+ map->parent = (void *) histogram;
+
+ return CMT_DECODE_MSGPACK_SUCCESS;
+}
+
+static int create_metric_instance(struct cmt_map *map)
+{
+ switch(map->type) {
+ case CMT_COUNTER:
+ return create_counter_instance(map);
+ case CMT_GAUGE:
+ return create_gauge_instance(map);
+ case CMT_SUMMARY:
+ return create_summary_instance(map);
+ case CMT_HISTOGRAM:
+ return create_histogram_instance(map);
+ case CMT_UNTYPED:
+ return create_untyped_instance(map);
+ }
+
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+}
+
+static int unpack_opts_ns(mpack_reader_t *reader, size_t index, void *context)
+{
+ struct cmt_opts *opts;
+
+ opts = (struct cmt_opts *) context;
+
+ return cmt_mpack_consume_string_tag(reader, &opts->ns);
+}
+
+static int unpack_opts_ss(mpack_reader_t *reader, size_t index, void *context)
+{
+ struct cmt_opts *opts;
+
+ opts = (struct cmt_opts *) context;
+
+ return cmt_mpack_consume_string_tag(reader, &opts->subsystem);
+}
+
+static int unpack_opts_name(mpack_reader_t *reader, size_t index, void *context)
+{
+ struct cmt_opts *opts;
+
+ opts = (struct cmt_opts *) context;
+
+ return cmt_mpack_consume_string_tag(reader, &opts->name);
+}
+
+static int unpack_opts_desc(mpack_reader_t *reader, size_t index, void *context)
+{
+ struct cmt_opts *opts;
+
+ opts = (struct cmt_opts *) context;
+
+ return cmt_mpack_consume_string_tag(reader, &opts->description);
+}
+
+static int unpack_opts(mpack_reader_t *reader, struct cmt_opts *opts)
+{
+ int result;
+ struct cmt_mpack_map_entry_callback_t callbacks[] = {
+ {"ns", unpack_opts_ns},
+ {"ss", unpack_opts_ss},
+ {"name", unpack_opts_name},
+ {"desc", unpack_opts_desc},
+ {NULL, NULL}
+ };
+
+ if (NULL == reader ||
+ NULL == opts ) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ memset(opts, 0, sizeof(struct cmt_opts));
+
+ result = cmt_mpack_unpack_map(reader, callbacks, (void *) opts);
+
+ if (CMT_DECODE_MSGPACK_SUCCESS == result) {
+ /* Allocate enough space for the three components, the separators
+ * and the terminator so we don't have to worry about possible realloc issues
+ * later on.
+ */
+
+ opts->fqname = cfl_sds_create_size(cfl_sds_len(opts->ns) + \
+ cfl_sds_len(opts->subsystem) + \
+ cfl_sds_len(opts->name) + \
+ 4);
+
+ if (NULL == opts->fqname) {
+ return CMT_DECODE_MSGPACK_ALLOCATION_ERROR;
+ }
+
+ cfl_sds_cat(opts->fqname, opts->ns, cfl_sds_len(opts->ns));
+ cfl_sds_cat(opts->fqname, "_", 1);
+
+ if (cfl_sds_len(opts->subsystem) > 0) {
+ cfl_sds_cat(opts->fqname, opts->subsystem, cfl_sds_len(opts->subsystem));
+ cfl_sds_cat(opts->fqname, "_", 1);
+ }
+ cfl_sds_cat(opts->fqname, opts->name, cfl_sds_len(opts->name));
+ }
+
+ return result;
+}
+
+static int unpack_label(mpack_reader_t *reader,
+ size_t index,
+ struct cfl_list *target_label_list)
+{
+ struct cmt_map_label *new_label;
+ int result;
+
+ if (NULL == reader ||
+ NULL == target_label_list ) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ new_label = calloc(1, sizeof(struct cmt_map_label));
+
+ if (NULL == new_label) {
+ return CMT_DECODE_MSGPACK_ALLOCATION_ERROR;
+ }
+
+ result = cmt_mpack_consume_string_tag(reader, &new_label->name);
+
+ if (result != CMT_DECODE_MSGPACK_SUCCESS) {
+ free(new_label);
+
+ return CMT_DECODE_MSGPACK_ALLOCATION_ERROR;
+ }
+
+ cfl_list_add(&new_label->_head, target_label_list);
+
+ return CMT_DECODE_MSGPACK_SUCCESS;
+}
+
+static int unpack_static_label_component(mpack_reader_t *reader,
+ size_t index,
+ void *context)
+{
+ struct cmt_label *label;
+
+ if (NULL == reader ||
+ NULL == context ) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ label = (struct cmt_label *) context;
+
+ if (index == 0) {
+ return cmt_mpack_consume_string_tag(reader, &label->key);
+ }
+ else if (index == 1) {
+ return cmt_mpack_consume_string_tag(reader, &label->val);
+ }
+
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+}
+
+static int unpack_static_label(mpack_reader_t *reader,
+ size_t index, void *context)
+{
+ struct cfl_list *target_label_list;
+ struct cmt_label *new_static_label;
+ struct cmt *decode_context;
+ int result;
+
+ if (NULL == context) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ decode_context = (struct cmt *) context;
+
+ target_label_list = &decode_context->static_labels->list;
+
+ if (NULL == reader ||
+ NULL == target_label_list) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ new_static_label = calloc(1, sizeof(struct cmt_label));
+
+ if (NULL == new_static_label) {
+ return CMT_DECODE_MSGPACK_ALLOCATION_ERROR;
+ }
+
+ result = cmt_mpack_unpack_array(reader,
+ unpack_static_label_component,
+ new_static_label);
+
+ if (CMT_DECODE_MSGPACK_SUCCESS != result) {
+ if (new_static_label->key != NULL) {
+ cfl_sds_destroy(new_static_label->key);
+ }
+
+ if (new_static_label->val != NULL) {
+ cfl_sds_destroy(new_static_label->val);
+ }
+
+ free(new_static_label);
+ }
+ else {
+ cfl_list_add(&new_static_label->_head, target_label_list);
+ }
+
+ return result;
+}
+
+static int unpack_metric_label(mpack_reader_t *reader, size_t index, void *context)
+{
+ struct cmt_msgpack_decode_context *decode_context;
+
+ if (NULL == reader ||
+ NULL == context ) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ decode_context = (struct cmt_msgpack_decode_context *) context;
+
+ return unpack_label(reader,
+ index,
+ &decode_context->metric->labels);
+}
+
+static int unpack_metric_ts(mpack_reader_t *reader, size_t index, void *context)
+{
+ struct cmt_msgpack_decode_context *decode_context;
+
+ if (NULL == reader ||
+ NULL == context ) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ decode_context = (struct cmt_msgpack_decode_context *) context;
+
+ return cmt_mpack_consume_uint_tag(reader, &decode_context->metric->timestamp);
+}
+
+static int unpack_metric_value(mpack_reader_t *reader, size_t index, void *context)
+{
+ double value;
+ int result;
+ struct cmt_msgpack_decode_context *decode_context;
+
+ if (NULL == reader ||
+ NULL == context ) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ decode_context = (struct cmt_msgpack_decode_context *) context;
+
+ result = cmt_mpack_consume_double_tag(reader, &value);
+
+ if(CMT_DECODE_MSGPACK_SUCCESS == result) {
+ decode_context->metric->val = cmt_math_d64_to_uint64(value);
+ }
+
+ return result;
+}
+
+static int unpack_metric_labels(mpack_reader_t *reader, size_t index, void *context)
+{
+ if (NULL == reader ||
+ NULL == context) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ return cmt_mpack_unpack_array(reader,
+ unpack_metric_label,
+ context);
+}
+
+
+static int unpack_summary_quantiles_set(mpack_reader_t *reader, size_t index, void *context)
+{
+ struct cmt_msgpack_decode_context *decode_context;
+ int result;
+ uint64_t value;
+
+ if (NULL == reader ||
+ NULL == context ) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ decode_context = (struct cmt_msgpack_decode_context *) context;
+
+ result = cmt_mpack_consume_uint_tag(reader, &value);
+
+ if (result == CMT_DECODE_MSGPACK_SUCCESS) {
+ decode_context->metric->sum_quantiles_set = value;
+ }
+
+ return result;
+}
+
+static int unpack_summary_quantile(mpack_reader_t *reader, size_t index, void *context)
+{
+ struct cmt_msgpack_decode_context *decode_context;
+
+ if (NULL == reader ||
+ NULL == context ) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ decode_context = (struct cmt_msgpack_decode_context *) context;
+ return cmt_mpack_consume_uint_tag(reader, &decode_context->metric->sum_quantiles[index]);
+}
+
+static int unpack_summary_quantiles(mpack_reader_t *reader, size_t index, void *context)
+{
+ if (NULL == reader ||
+ NULL == context ) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ return cmt_mpack_unpack_array(reader, unpack_summary_quantile, context);
+}
+
+static int unpack_summary_count(mpack_reader_t *reader, size_t index, void *context)
+{
+ struct cmt_msgpack_decode_context *decode_context;
+
+ if (NULL == reader ||
+ NULL == context ) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ decode_context = (struct cmt_msgpack_decode_context *) context;
+
+ return cmt_mpack_consume_uint_tag(reader, &decode_context->metric->sum_count);
+}
+
+static int unpack_summary_sum(mpack_reader_t *reader, size_t index, void *context)
+{
+ struct cmt_msgpack_decode_context *decode_context;
+
+ if (NULL == reader ||
+ NULL == context ) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ decode_context = (struct cmt_msgpack_decode_context *) context;
+
+ return cmt_mpack_consume_uint_tag(reader, &decode_context->metric->sum_sum);
+}
+
+static int unpack_metric_summary(mpack_reader_t *reader, size_t index, void *context)
+{
+ int result;
+ struct cmt_msgpack_decode_context *decode_context;
+ struct cmt_mpack_map_entry_callback_t callbacks[] = \
+ {
+ {"quantiles_set", unpack_summary_quantiles_set},
+ {"quantiles", unpack_summary_quantiles},
+ {"count", unpack_summary_count},
+ {"sum", unpack_summary_sum},
+ {NULL, NULL}
+ };
+
+ if (NULL == reader ||
+ NULL == context ) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ decode_context = (struct cmt_msgpack_decode_context *) context;
+
+ result = cmt_mpack_unpack_map(reader, callbacks, (void *) decode_context);
+
+ return result;
+}
+
+
+static int unpack_histogram_sum(mpack_reader_t *reader, size_t index, void *context)
+{
+ struct cmt_msgpack_decode_context *decode_context;
+ int result;
+ double value;
+
+ if (NULL == reader ||
+ NULL == context ) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ decode_context = (struct cmt_msgpack_decode_context *) context;
+
+ result = cmt_mpack_consume_double_tag(reader, &value);
+
+ if (result == CMT_DECODE_MSGPACK_SUCCESS) {
+ decode_context->metric->hist_sum = cmt_math_d64_to_uint64(value);
+ }
+
+ return result;
+}
+
+static int unpack_histogram_count(mpack_reader_t *reader, size_t index, void *context)
+{
+ struct cmt_msgpack_decode_context *decode_context;
+
+ if (NULL == reader ||
+ NULL == context ) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ decode_context = (struct cmt_msgpack_decode_context *) context;
+
+ return cmt_mpack_consume_uint_tag(reader, &decode_context->metric->hist_count);
+}
+
+static int unpack_histogram_bucket(mpack_reader_t *reader, size_t index, void *context)
+{
+ struct cmt_msgpack_decode_context *decode_context;
+
+ if (NULL == reader ||
+ NULL == context) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ decode_context = (struct cmt_msgpack_decode_context *) context;
+
+ return cmt_mpack_consume_uint_tag(reader, &decode_context->metric->hist_buckets[index]);
+}
+
+static int unpack_histogram_buckets(mpack_reader_t *reader, size_t index, void *context)
+{
+ struct cmt_msgpack_decode_context *decode_context;
+
+ if (NULL == reader ||
+ NULL == context ) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ decode_context = (struct cmt_msgpack_decode_context *) context;
+
+ return cmt_mpack_unpack_array(reader, unpack_histogram_bucket, decode_context);
+}
+
+static int unpack_metric_histogram(mpack_reader_t *reader, size_t index, void *context)
+{
+ int result;
+ struct cmt_msgpack_decode_context *decode_context;
+ struct cmt_mpack_map_entry_callback_t callbacks[] = \
+ {
+ {"buckets", unpack_histogram_buckets},
+ {"count", unpack_histogram_count},
+ {"sum", unpack_histogram_sum},
+ {NULL, NULL}
+ };
+
+ if (NULL == reader ||
+ NULL == context ) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ decode_context = (struct cmt_msgpack_decode_context *) context;
+
+ result = cmt_mpack_unpack_map(reader, callbacks, (void *) decode_context);
+
+ return result;
+}
+
+
+static int unpack_metric_hash(mpack_reader_t *reader, size_t index, void *context)
+{
+ struct cmt_msgpack_decode_context *decode_context;
+
+ if (NULL == reader ||
+ NULL == context ) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ decode_context = (struct cmt_msgpack_decode_context *) context;
+
+ return cmt_mpack_consume_uint_tag(reader, &decode_context->metric->hash);
+}
+
+static int unpack_metric(mpack_reader_t *reader,
+ struct cmt_msgpack_decode_context *decode_context,
+ struct cmt_metric **out_metric)
+{
+ int result;
+ struct cmt_metric *metric;
+ struct cmt_summary *summary;
+ struct cmt_histogram *histogram;
+ struct cmt_mpack_map_entry_callback_t callbacks[] = \
+ {
+ {"ts", unpack_metric_ts},
+ {"value", unpack_metric_value},
+ {"labels", unpack_metric_labels},
+ {"summary", unpack_metric_summary},
+ {"histogram", unpack_metric_histogram},
+ {"hash", unpack_metric_hash},
+ {NULL, NULL}
+ };
+
+ if (NULL == reader ||
+ NULL == decode_context ||
+ NULL == out_metric) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ /* Maybe we could move this cmt_metric constructor code to its own file, add a
+ * destructor and update map_metric_create and map_metric_destroy to use them right?
+ */
+
+ metric = calloc(1, sizeof(struct cmt_metric));
+
+ if (NULL == metric) {
+ return CMT_DECODE_MSGPACK_ALLOCATION_ERROR;
+ }
+
+ if (decode_context->map->type == CMT_HISTOGRAM) {
+ histogram = decode_context->map->parent;
+
+ metric->hist_buckets = calloc(histogram->buckets->count + 1, sizeof(uint64_t));
+
+ if (metric->hist_buckets == NULL) {
+ cmt_errno();
+
+ free(metric);
+
+ return CMT_DECODE_MSGPACK_ALLOCATION_ERROR;
+ }
+ }
+ else if (decode_context->map->type == CMT_SUMMARY) {
+ summary = decode_context->map->parent;
+
+ metric->sum_quantiles = calloc(summary->quantiles_count, sizeof(uint64_t));
+
+ if (metric->sum_quantiles == NULL) {
+ cmt_errno();
+
+ free(metric);
+
+ return CMT_DECODE_MSGPACK_ALLOCATION_ERROR;
+ }
+ metric->sum_quantiles_count = summary->quantiles_count;
+ }
+
+ cfl_list_init(&metric->labels);
+
+ decode_context->metric = metric;
+
+ result = cmt_mpack_unpack_map(reader, callbacks, (void *) decode_context);
+
+ if (CMT_DECODE_MSGPACK_SUCCESS != result) {
+ destroy_label_list(&metric->labels);
+
+ if (NULL != metric->hist_buckets) {
+ free(metric->hist_buckets);
+ }
+
+ if (NULL != metric->sum_quantiles) {
+ free(metric->sum_quantiles);
+ }
+
+ free(metric);
+ }
+ else {
+ *out_metric = metric;
+ }
+
+ return result;
+}
+
+static int unpack_metric_array_entry(mpack_reader_t *reader, size_t index, void *context)
+{
+ int result;
+ struct cmt_metric *metric;
+ struct cmt_msgpack_decode_context *decode_context;
+
+ if (NULL == reader ||
+ NULL == context) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ decode_context = (struct cmt_msgpack_decode_context *) context;
+
+ metric = NULL;
+ result = unpack_metric(reader, decode_context, &metric);
+
+ if (CMT_DECODE_MSGPACK_SUCCESS == result) {
+ if (0 == cfl_list_size(&metric->labels)) {
+ /* Should we care about finding more than one "implicitly static metric" in
+ * the array?
+ */
+ decode_context->map->metric_static_set = 1;
+
+ if (decode_context->map->type == CMT_HISTOGRAM) {
+ decode_context->map->metric.hist_buckets = metric->hist_buckets;
+ decode_context->map->metric.hist_count = metric->hist_count;
+ decode_context->map->metric.hist_sum = metric->hist_sum;
+ }
+ else if (decode_context->map->type == CMT_SUMMARY) {
+ decode_context->map->metric.sum_quantiles_set = metric->sum_quantiles_set;
+ decode_context->map->metric.sum_quantiles = metric->sum_quantiles;
+ decode_context->map->metric.sum_count = metric->sum_count;
+ decode_context->map->metric.sum_sum = metric->sum_sum;
+ }
+
+ decode_context->map->metric.val = metric->val;
+ decode_context->map->metric.hash = metric->hash;
+ decode_context->map->metric.timestamp = metric->timestamp;
+
+ free(metric);
+ }
+ else
+ {
+ cfl_list_add(&metric->_head, &decode_context->map->metrics);
+ }
+ }
+
+ return result;
+}
+
+static int unpack_meta_ver(mpack_reader_t *reader, size_t index, void *context)
+{
+ uint64_t value;
+ int result;
+
+ if (NULL == reader ||
+ NULL == context) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ result = cmt_mpack_consume_uint_tag(reader, &value);
+
+ if (CMT_DECODE_MSGPACK_SUCCESS == result) {
+ if (MSGPACK_ENCODER_VERSION != value) {
+ result = CMT_DECODE_MSGPACK_VERSION_ERROR;
+ }
+ }
+
+ return result;
+}
+
+static int unpack_meta_type(mpack_reader_t *reader, size_t index, void *context)
+{
+ uint64_t value;
+ int result;
+ struct cmt_msgpack_decode_context *decode_context;
+
+ if (NULL == reader ||
+ NULL == context) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ decode_context = (struct cmt_msgpack_decode_context *) context;
+
+ result = cmt_mpack_consume_uint_tag(reader, &value);
+
+ if (CMT_DECODE_MSGPACK_SUCCESS == result) {
+ decode_context->map->type = value;
+
+ result = create_metric_instance(decode_context->map);
+ }
+
+ return result;
+}
+
+static int unpack_meta_aggregation_type(mpack_reader_t *reader, size_t index, void *context)
+{
+ uint64_t value;
+ int result;
+ struct cmt_msgpack_decode_context *decode_context;
+
+ if (NULL == reader ||
+ NULL == context) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ decode_context = (struct cmt_msgpack_decode_context *) context;
+
+ result = cmt_mpack_consume_uint_tag(reader, &value);
+
+ if (CMT_DECODE_MSGPACK_SUCCESS == result) {
+ decode_context->aggregation_type = value;
+ }
+
+ return result;
+}
+
+static int unpack_meta_opts(mpack_reader_t *reader, size_t index, void *context)
+{
+ struct cmt_msgpack_decode_context *decode_context;
+
+ if (NULL == reader ||
+ NULL == context) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ decode_context = (struct cmt_msgpack_decode_context *) context;
+
+ return unpack_opts(reader, decode_context->map->opts);
+}
+
+static int unpack_meta_label(mpack_reader_t *reader, size_t index, void *context)
+{
+ struct cmt_msgpack_decode_context *decode_context;
+
+ if (NULL == reader ||
+ NULL == context) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ decode_context = (struct cmt_msgpack_decode_context *) context;
+
+ return unpack_label(reader,
+ index,
+ &decode_context->map->label_keys);
+}
+
+static int unpack_meta_static_labels(mpack_reader_t *reader, size_t index, void *context)
+{
+ if (NULL == reader ||
+ NULL == context) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ return cmt_mpack_unpack_array(reader, unpack_static_label, context);
+}
+
+static int unpack_meta_labels(mpack_reader_t *reader, size_t index, void *context)
+{
+ if (NULL == reader ||
+ NULL == context) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ return cmt_mpack_unpack_array(reader, unpack_meta_label, context);
+}
+
+static int unpack_meta_bucket(mpack_reader_t *reader, size_t index, void *context)
+{
+ struct cmt_msgpack_decode_context *decode_context;
+
+ if (NULL == reader ||
+ NULL == context) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ decode_context = (struct cmt_msgpack_decode_context *) context;
+
+ if (decode_context->bucket_count == 0) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ return cmt_mpack_consume_double_tag(reader, &decode_context->bucket_list[index]);
+}
+
+static int unpack_meta_buckets(mpack_reader_t *reader, size_t index, void *context)
+{
+ struct cmt_msgpack_decode_context *decode_context;
+
+ if (NULL == reader ||
+ NULL == context) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ decode_context = (struct cmt_msgpack_decode_context *) context;
+
+ decode_context->bucket_count = cmt_mpack_peek_array_length(reader);
+
+ if (0 < decode_context->bucket_count) {
+ decode_context->bucket_list = calloc(decode_context->bucket_count,
+ sizeof(double));
+
+ if (NULL == decode_context->bucket_list) {
+ return CMT_DECODE_MSGPACK_ALLOCATION_ERROR;
+ }
+
+ }
+
+ return cmt_mpack_unpack_array(reader, unpack_meta_bucket, context);
+}
+
+static int unpack_meta_quantile(mpack_reader_t *reader, size_t index, void *context)
+{
+ struct cmt_msgpack_decode_context *decode_context;
+
+ if (NULL == reader ||
+ NULL == context) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ decode_context = (struct cmt_msgpack_decode_context *) context;
+
+ return cmt_mpack_consume_double_tag(reader, &decode_context->quantile_list[index]);
+}
+
+static int unpack_meta_quantiles(mpack_reader_t *reader, size_t index, void *context)
+{
+ struct cmt_msgpack_decode_context *decode_context;
+
+ if (NULL == reader ||
+ NULL == context) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ decode_context = (struct cmt_msgpack_decode_context *) context;
+
+ decode_context->quantile_count = cmt_mpack_peek_array_length(reader);
+
+ if (0 < decode_context->quantile_count) {
+ decode_context->quantile_list = calloc(decode_context->quantile_count,
+ sizeof(double));
+
+ if (NULL == decode_context->quantile_list) {
+ return CMT_DECODE_MSGPACK_ALLOCATION_ERROR;
+ }
+ }
+
+ return cmt_mpack_unpack_array(reader, unpack_meta_quantile, context);
+}
+
+static int unpack_basic_type_meta(mpack_reader_t *reader, size_t index, void *context)
+{
+ int result;
+ struct cmt_summary *summary;
+ struct cmt_histogram *histogram;
+ struct cmt_counter *counter;
+ struct cmt_msgpack_decode_context *decode_context;
+ struct cmt_mpack_map_entry_callback_t callbacks[] = \
+ {
+ {"ver", unpack_meta_ver},
+ {"type", unpack_meta_type},
+ {"opts", unpack_meta_opts},
+ {"labels", unpack_meta_labels},
+ {"buckets", unpack_meta_buckets},
+ {"quantiles", unpack_meta_quantiles},
+ {"aggregation_type", unpack_meta_aggregation_type},
+ {NULL, NULL}
+ };
+
+ if (NULL == reader ||
+ NULL == context) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ decode_context = (struct cmt_msgpack_decode_context *) context;
+
+ result = cmt_mpack_unpack_map(reader, callbacks, context);
+
+ if (CMT_DECODE_MSGPACK_SUCCESS == result) {
+ decode_context->map->label_count = cfl_list_size(&decode_context->map->label_keys);
+
+ if (decode_context->map->type == CMT_HISTOGRAM) {
+ histogram = (struct cmt_histogram *) decode_context->map->parent;
+
+ if (decode_context->bucket_count > 0) {
+ histogram->buckets =
+ cmt_histogram_buckets_create_size(decode_context->bucket_list,
+ decode_context->bucket_count);
+
+ if (histogram->buckets == NULL) {
+ result = CMT_DECODE_MSGPACK_ALLOCATION_ERROR;
+ }
+ }
+ else {
+ histogram->buckets = NULL;
+ }
+ }
+ else if (decode_context->map->type == CMT_SUMMARY) {
+ summary = (struct cmt_summary *) decode_context->map->parent;
+
+ summary->quantiles = decode_context->quantile_list;
+ summary->quantiles_count = decode_context->quantile_count;
+
+ decode_context->quantile_list = NULL;
+ decode_context->quantile_count = 0;
+ }
+ else if(decode_context->map->type == CMT_COUNTER) {
+ counter = (struct cmt_counter *) decode_context->map->parent;
+ counter->aggregation_type = decode_context->aggregation_type;
+ }
+ }
+
+ return result;
+}
+
+static int unpack_basic_type_values(mpack_reader_t *reader, size_t index, void *context)
+{
+ if (NULL == reader ||
+ NULL == context) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ return cmt_mpack_unpack_array(reader,
+ unpack_metric_array_entry,
+ context);
+}
+
+static int unpack_basic_type(mpack_reader_t *reader, struct cmt *cmt, struct cmt_map **map)
+{
+ int result;
+ struct cmt_summary *summary;
+ struct cmt_histogram *histogram;
+ struct cmt_msgpack_decode_context decode_context;
+ struct cmt_mpack_map_entry_callback_t callbacks[] = \
+ {
+ {"meta", unpack_basic_type_meta},
+ {"values", unpack_basic_type_values},
+ {NULL, NULL}
+ };
+
+ if (NULL == reader ||
+ NULL == map) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ memset(&decode_context, 0, sizeof(struct cmt_msgpack_decode_context));
+
+ *map = cmt_map_create(0, NULL, 0, NULL, NULL);
+
+ if (NULL == *map) {
+ return CMT_DECODE_MSGPACK_ALLOCATION_ERROR;
+ }
+
+ (*map)->metric_static_set = 0;
+ (*map)->opts = calloc(1, sizeof(struct cmt_opts));
+
+ if (NULL == (*map)->opts) {
+ cmt_map_destroy(*map);
+
+ return CMT_DECODE_MSGPACK_ALLOCATION_ERROR;
+ }
+
+ decode_context.cmt = cmt;
+ decode_context.map = *map;
+
+ result = cmt_mpack_unpack_map(reader, callbacks, (void *) &decode_context);
+
+ if ((*map)->parent == NULL) {
+ result = CMT_DECODE_MSGPACK_CORRUPT_INPUT_DATA_ERROR;
+ }
+
+ if (CMT_DECODE_MSGPACK_SUCCESS != result) {
+ if ((*map)->opts != NULL) {
+ cmt_opts_exit((*map)->opts);
+
+ free((*map)->opts);
+ }
+
+ if ((*map)->parent != NULL) {
+ if ((*map)->type == CMT_HISTOGRAM) {
+ histogram = ((struct cmt_histogram *) (*map)->parent);
+
+ if (NULL != histogram->buckets) {
+ if (NULL != histogram->buckets->upper_bounds) {
+ free(histogram->buckets->upper_bounds);
+ }
+
+ free(histogram->buckets);
+ }
+ }
+ else if ((*map)->type == CMT_SUMMARY) {
+ summary = ((struct cmt_summary *) (*map)->parent);
+
+ if (NULL != summary->quantiles) {
+ free(summary->quantiles);
+ }
+ }
+
+ free((*map)->parent);
+ }
+
+ cmt_map_destroy(*map);
+
+ *map = NULL;
+ }
+
+ if (decode_context.bucket_list != NULL) {
+ free(decode_context.bucket_list);
+ }
+
+ if (decode_context.quantile_list != NULL) {
+ free(decode_context.quantile_list);
+ }
+
+ return result;
+}
+
+static int append_unpacked_counter_to_metrics_context(struct cmt *context,
+ struct cmt_map *map)
+{
+ struct cmt_counter *counter;
+
+ if (NULL == context ||
+ NULL == map ) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ counter = map->parent;
+
+ if (NULL == counter) {
+ return CMT_DECODE_MSGPACK_ALLOCATION_ERROR;
+ }
+
+ counter->cmt = context;
+ counter->map = map;
+ map->parent = (void *) counter;
+
+ memcpy(&counter->opts, map->opts, sizeof(struct cmt_opts));
+
+ free(map->opts);
+
+ map->opts = &counter->opts;
+
+ cfl_list_add(&counter->_head, &context->counters);
+
+ return CMT_DECODE_MSGPACK_SUCCESS;
+}
+
+static int append_unpacked_gauge_to_metrics_context(struct cmt *context,
+ struct cmt_map *map)
+{
+ struct cmt_gauge *gauge;
+
+ if (NULL == context ||
+ NULL == map ) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ gauge = map->parent;
+
+ if (NULL == gauge) {
+ return CMT_DECODE_MSGPACK_ALLOCATION_ERROR;
+ }
+
+ gauge->cmt = context;
+ gauge->map = map;
+ map->parent = (void *) gauge;
+
+ memcpy(&gauge->opts, map->opts, sizeof(struct cmt_opts));
+
+ free(map->opts);
+
+ map->opts = &gauge->opts;
+
+ cfl_list_add(&gauge->_head, &context->gauges);
+
+ return CMT_DECODE_MSGPACK_SUCCESS;
+}
+
+static int append_unpacked_untyped_to_metrics_context(struct cmt *context,
+ struct cmt_map *map)
+{
+ struct cmt_untyped *untyped;
+
+ if (NULL == context ||
+ NULL == map ) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ untyped = map->parent;
+ if (NULL == untyped) {
+ return CMT_DECODE_MSGPACK_ALLOCATION_ERROR;
+ }
+
+ untyped->cmt = context;
+ untyped->map = map;
+ map->parent = (void *) untyped;
+
+ memcpy(&untyped->opts, map->opts, sizeof(struct cmt_opts));
+
+ free(map->opts);
+
+ map->opts = &untyped->opts;
+
+ cfl_list_add(&untyped->_head, &context->untypeds);
+
+ return CMT_DECODE_MSGPACK_SUCCESS;
+}
+
+static int append_unpacked_summary_to_metrics_context(struct cmt *context,
+ struct cmt_map *map)
+{
+ struct cmt_summary *summary;
+
+ if (NULL == context ||
+ NULL == map ) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ summary = map->parent;
+ if (NULL == summary) {
+ return CMT_DECODE_MSGPACK_ALLOCATION_ERROR;
+ }
+
+ summary->cmt = context;
+ summary->map = map;
+ map->parent = (void *) summary;
+
+ memcpy(&summary->opts, map->opts, sizeof(struct cmt_opts));
+
+ free(map->opts);
+
+ map->opts = &summary->opts;
+
+ cfl_list_add(&summary->_head, &context->summaries);
+
+ return CMT_DECODE_MSGPACK_SUCCESS;
+}
+
+static int append_unpacked_histogram_to_metrics_context(
+ struct cmt *context,
+ struct cmt_map *map)
+{
+ struct cmt_histogram *histogram;
+
+ if (NULL == context ||
+ NULL == map ) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ histogram = map->parent;
+ if (NULL == histogram) {
+ return CMT_DECODE_MSGPACK_ALLOCATION_ERROR;
+ }
+
+ histogram->cmt = context;
+ histogram->map = map;
+ map->parent = (void *) histogram;
+
+ memcpy(&histogram->opts, map->opts, sizeof(struct cmt_opts));
+
+ free(map->opts);
+
+ map->opts = &histogram->opts;
+
+ cfl_list_add(&histogram->_head, &context->histograms);
+
+ return CMT_DECODE_MSGPACK_SUCCESS;
+}
+
+static int unpack_basic_type_entry(mpack_reader_t *reader, size_t index, void *context)
+{
+ int result;
+ struct cmt *cmt;
+ struct cmt_map *map;
+
+ if (NULL == reader ||
+ NULL == context) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ cmt = (struct cmt *) context;
+
+ result = unpack_basic_type(reader, cmt, &map);
+
+ if (CMT_DECODE_MSGPACK_SUCCESS == result) {
+ if (CMT_COUNTER == map->type) {
+ result = append_unpacked_counter_to_metrics_context(cmt, map);
+ }
+ else if (CMT_GAUGE == map->type) {
+ result = append_unpacked_gauge_to_metrics_context(cmt, map);
+ }
+ else if (CMT_SUMMARY == map->type) {
+ result = append_unpacked_summary_to_metrics_context(cmt, map);
+ }
+ else if (CMT_HISTOGRAM == map->type) {
+ result = append_unpacked_histogram_to_metrics_context(cmt, map);
+ }
+ else if (CMT_UNTYPED == map->type) {
+ result = append_unpacked_untyped_to_metrics_context(cmt, map);
+ }
+ }
+
+ return result;
+}
+
+static int unpack_context_internal_metadata(mpack_reader_t *reader, size_t index, void *context)
+{
+ struct cmt *metrics_context;
+ int result;
+
+ metrics_context = (struct cmt *) context;
+
+ if (metrics_context->internal_metadata != NULL) {
+ cfl_kvlist_destroy(metrics_context->internal_metadata);
+
+ metrics_context->internal_metadata = NULL;
+ }
+
+ result = unpack_cfl_kvlist(reader, &metrics_context->internal_metadata);
+
+ if (result == 0) {
+ result = CMT_DECODE_MSGPACK_SUCCESS;
+ }
+ else {
+ result = CMT_DECODE_MSGPACK_CORRUPT_INPUT_DATA_ERROR;
+ }
+
+ return result;
+}
+
+static int unpack_context_external_metadata(mpack_reader_t *reader, size_t index, void *context)
+{
+ struct cmt *metrics_context;
+ int result;
+
+ metrics_context = (struct cmt *) context;
+
+ if (metrics_context->external_metadata != NULL) {
+ cfl_kvlist_destroy(metrics_context->external_metadata);
+
+ metrics_context->external_metadata = NULL;
+ }
+
+ result = unpack_cfl_kvlist(reader, &metrics_context->external_metadata);
+
+ if (result == 0) {
+ result = CMT_DECODE_MSGPACK_SUCCESS;
+ }
+ else {
+ result = CMT_DECODE_MSGPACK_CORRUPT_INPUT_DATA_ERROR;
+ }
+
+ return result;
+}
+
+static int unpack_context_processing_section(mpack_reader_t *reader, size_t index, void *context)
+{
+ struct cmt_mpack_map_entry_callback_t callbacks[] = \
+ {
+ {"static_labels", unpack_meta_static_labels},
+ {NULL, NULL}
+ };
+
+ if (NULL == reader ||
+ NULL == context) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ return cmt_mpack_unpack_map(reader, callbacks, context);
+}
+
+static int unpack_context_header(mpack_reader_t *reader, size_t index, void *context)
+{
+ struct cmt_mpack_map_entry_callback_t callbacks[] = \
+ {
+ {"cmetrics", unpack_context_internal_metadata},
+ {"external", unpack_context_external_metadata},
+ {"processing", unpack_context_processing_section},
+ {NULL, NULL}
+ };
+
+ if (NULL == reader ||
+ NULL == context) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ return cmt_mpack_unpack_map(reader, callbacks, context);
+}
+
+static int unpack_context_metrics(mpack_reader_t *reader, size_t index, void *context)
+{
+ if (NULL == reader ||
+ NULL == context) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ return cmt_mpack_unpack_array(reader,
+ unpack_basic_type_entry,
+ context);
+}
+
+static int unpack_context(mpack_reader_t *reader, struct cmt *cmt)
+{
+ struct cmt_mpack_map_entry_callback_t callbacks[] = \
+ {
+ {"meta", unpack_context_header},
+ {"metrics", unpack_context_metrics},
+ {NULL, NULL}
+ };
+
+ if (NULL == reader ||
+ NULL == cmt) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ return cmt_mpack_unpack_map(reader, callbacks, (void *) cmt);
+}
+
+/* Convert cmetrics msgpack payload and generate a CMetrics context */
+int cmt_decode_msgpack_create(struct cmt **out_cmt, char *in_buf, size_t in_size,
+ size_t *offset)
+{
+ struct cmt *cmt;
+ mpack_reader_t reader;
+ int result;
+ size_t remainder;
+
+ if (NULL == out_cmt ||
+ NULL == in_buf ||
+ NULL == offset ||
+ in_size < *offset ) {
+ return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ if (0 == in_size ||
+ 0 == (in_size - *offset) ) {
+ return CMT_DECODE_MSGPACK_INSUFFICIENT_DATA;
+ }
+
+ cmt = cmt_create();
+
+ if (NULL == cmt) {
+ return CMT_DECODE_MSGPACK_ALLOCATION_ERROR;
+ }
+
+ in_size -= *offset;
+
+ mpack_reader_init_data(&reader, &in_buf[*offset], in_size);
+
+ result = unpack_context(&reader, cmt);
+
+ remainder = mpack_reader_remaining(&reader, NULL);
+
+ *offset += in_size - remainder;
+
+ mpack_reader_destroy(&reader);
+
+ if (CMT_DECODE_MSGPACK_SUCCESS != result) {
+ cmt_destroy(cmt);
+ }
+ else {
+ *out_cmt = cmt;
+ }
+
+ return result;
+}
+
+void cmt_decode_msgpack_destroy(struct cmt *cmt)
+{
+ if (NULL != cmt) {
+ cmt_destroy(cmt);
+ }
+}
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_decode_opentelemetry.c b/src/fluent-bit/lib/cmetrics/src/cmt_decode_opentelemetry.c
new file mode 100644
index 000000000..aad425071
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_decode_opentelemetry.c
@@ -0,0 +1,1289 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_metric.h>
+#include <cmetrics/cmt_map.h>
+#include <cmetrics/cmt_gauge.h>
+#include <cmetrics/cmt_counter.h>
+#include <cmetrics/cmt_summary.h>
+#include <cmetrics/cmt_histogram.h>
+#include <cmetrics/cmt_untyped.h>
+#include <cmetrics/cmt_compat.h>
+#include <cmetrics/cmt_decode_opentelemetry.h>
+
+static struct cfl_variant *clone_variant(Opentelemetry__Proto__Common__V1__AnyValue *source);
+
+static int clone_array(struct cfl_array *target,
+ Opentelemetry__Proto__Common__V1__ArrayValue *source);
+static int clone_array_entry(struct cfl_array *target,
+ Opentelemetry__Proto__Common__V1__AnyValue *source);
+static int clone_kvlist(struct cfl_kvlist *target,
+ Opentelemetry__Proto__Common__V1__KeyValueList *source);
+static int clone_kvlist_entry(struct cfl_kvlist *target,
+ Opentelemetry__Proto__Common__V1__KeyValue *source);
+
+static struct cmt_map_label *create_label(char *caption, size_t length);
+static int append_new_map_label_key(struct cmt_map *map, char *name);
+static int append_new_metric_label_value(struct cmt_metric *metric, char *name, size_t length);
+
+static struct cfl_variant *clone_variant(Opentelemetry__Proto__Common__V1__AnyValue *source)
+{
+ struct cfl_kvlist *new_child_kvlist;
+ struct cfl_array *new_child_array;
+ struct cfl_variant *result_instance = NULL;
+ int result;
+
+ if (source == NULL) {
+ return NULL;
+ }
+ if (source->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE) {
+ result_instance = cfl_variant_create_from_string(source->string_value);
+ }
+ else if (source->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_BOOL_VALUE) {
+ result_instance = cfl_variant_create_from_bool(source->bool_value);
+ }
+ else if (source->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_INT_VALUE) {
+ result_instance = cfl_variant_create_from_int64(source->int_value);
+ }
+ else if (source->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_DOUBLE_VALUE) {
+ result_instance = cfl_variant_create_from_double(source->double_value);
+ }
+ else if (source->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_KVLIST_VALUE) {
+ new_child_kvlist = cfl_kvlist_create();
+ if (new_child_kvlist == NULL) {
+ return NULL;
+ }
+
+ result_instance = cfl_variant_create_from_kvlist(new_child_kvlist);
+
+ if (result_instance == NULL) {
+ cfl_kvlist_destroy(new_child_kvlist);
+
+ return NULL;
+ }
+
+ result = clone_kvlist(new_child_kvlist, source->kvlist_value);
+ if (result) {
+ cfl_variant_destroy(result_instance);
+
+ return NULL;
+ }
+ }
+ else if (source->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_ARRAY_VALUE) {
+ new_child_array = cfl_array_create(source->array_value->n_values);
+
+ if (new_child_array == NULL) {
+ return NULL;
+ }
+
+ result_instance = cfl_variant_create_from_array(new_child_array);
+ if (result_instance == NULL) {
+ cfl_array_destroy(new_child_array);
+
+ return NULL;
+ }
+
+ result = clone_array(new_child_array, source->array_value);
+ if (result) {
+ cfl_variant_destroy(result_instance);
+
+ return NULL;
+ }
+ }
+ else if (source->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_BYTES_VALUE) {
+ result_instance = cfl_variant_create_from_bytes((char *) source->bytes_value.data, source->bytes_value.len);
+ }
+
+ return result_instance;
+}
+
+static int clone_array(struct cfl_array *target,
+ Opentelemetry__Proto__Common__V1__ArrayValue *source)
+{
+ int result;
+ size_t index;
+
+ result = CMT_DECODE_OPENTELEMETRY_SUCCESS;
+
+ for (index = 0 ;
+ result == CMT_DECODE_OPENTELEMETRY_SUCCESS &&
+ index < source->n_values ;
+ index++) {
+ result = clone_array_entry(target, source->values[index]);
+ }
+
+ return result;
+}
+
+static int clone_array_entry(struct cfl_array *target,
+ Opentelemetry__Proto__Common__V1__AnyValue *source)
+{
+ struct cfl_variant *new_child_instance;
+ int result;
+
+ new_child_instance = clone_variant(source);
+ if (new_child_instance == NULL) {
+ return CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+
+ result = cfl_array_append(target, new_child_instance);
+ if (result) {
+ cfl_variant_destroy(new_child_instance);
+ return CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+
+ return CMT_DECODE_OPENTELEMETRY_SUCCESS;
+}
+
+static int clone_kvlist(struct cfl_kvlist *target,
+ Opentelemetry__Proto__Common__V1__KeyValueList *source)
+{
+ int result;
+ size_t index;
+
+ result = CMT_DECODE_OPENTELEMETRY_SUCCESS;
+
+ for (index = 0 ;
+ result == CMT_DECODE_OPENTELEMETRY_SUCCESS &&
+ index < source->n_values ;
+ index++) {
+ result = clone_kvlist_entry(target, source->values[index]);
+ }
+
+ return 0;
+}
+
+static int clone_kvlist_entry(struct cfl_kvlist *target,
+ Opentelemetry__Proto__Common__V1__KeyValue *source)
+{
+ struct cfl_variant *new_child_instance;
+ int result;
+
+ new_child_instance = clone_variant(source->value);
+
+ if (new_child_instance == NULL) {
+ return CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+
+ result = cfl_kvlist_insert(target, source->key, new_child_instance);
+
+ if (result) {
+ cfl_variant_destroy(new_child_instance);
+
+ return CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+
+ return CMT_DECODE_OPENTELEMETRY_SUCCESS;
+}
+
+struct cfl_kvlist *get_or_create_external_metadata_kvlist(
+ struct cfl_kvlist *root, char *key)
+{
+ struct cfl_variant *entry_variant;
+ struct cfl_kvlist *entry_kvlist;
+ int result;
+
+ entry_variant = cfl_kvlist_fetch(root, key);
+
+ if (entry_variant == NULL) {
+ entry_kvlist = cfl_kvlist_create();
+
+ if (entry_kvlist == NULL) {
+ return NULL;
+ }
+
+ result = cfl_kvlist_insert_kvlist(root,
+ key,
+ entry_kvlist);
+
+ if (result != 0) {
+ cfl_kvlist_destroy(entry_kvlist);
+
+ return NULL;
+ }
+ }
+ else {
+ entry_kvlist = entry_variant->data.as_kvlist;
+ }
+
+ return entry_kvlist;
+}
+
+static struct cmt_map_label *create_label(char *caption, size_t length)
+{
+ struct cmt_map_label *instance;
+
+ instance = calloc(1, sizeof(struct cmt_map_label));
+
+ if (instance != NULL) {
+ if (caption != NULL) {
+ if (length == 0) {
+ length = strlen(caption);
+ }
+
+ instance->name = cfl_sds_create_len(caption, length);
+
+ if (instance->name == NULL) {
+ cmt_errno();
+
+ free(instance);
+
+ instance = NULL;
+ }
+ }
+ }
+
+ return instance;
+}
+
+static int append_new_map_label_key(struct cmt_map *map, char *name)
+{
+ struct cmt_map_label *label;
+
+ label = create_label(name, 0);
+
+ if (label == NULL) {
+ return CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+
+ cfl_list_add(&label->_head, &map->label_keys);
+ map->label_count++;
+
+ return CMT_DECODE_OPENTELEMETRY_SUCCESS;
+}
+
+static int append_new_metric_label_value(struct cmt_metric *metric, char *name, size_t length)
+{
+ struct cmt_map_label *label;
+
+ label = create_label(name, length);
+
+ if (label == NULL) {
+ return CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+
+ cfl_list_add(&label->_head, &metric->labels);
+
+ return CMT_DECODE_OPENTELEMETRY_SUCCESS;
+}
+
+static int decode_data_point_labels(struct cmt *cmt,
+ struct cmt_map *map,
+ struct cmt_metric *metric,
+ size_t attribute_count,
+ Opentelemetry__Proto__Common__V1__KeyValue **attribute_list)
+{
+ char dummy_label_value[32];
+ void **value_index_list;
+ size_t attribute_index;
+ size_t map_label_index;
+ size_t map_label_count;
+ struct cfl_list *label_iterator;
+ struct cmt_map_label *current_label;
+ size_t label_index;
+ int label_found;
+ Opentelemetry__Proto__Common__V1__KeyValue *attribute;
+ int result;
+
+ result = CMT_DECODE_OPENTELEMETRY_SUCCESS;
+
+ if (attribute_count == 0) {
+ return result;
+ }
+
+ if (attribute_count > 127) {
+ return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR;
+ }
+
+ value_index_list = calloc(128, sizeof(void *));
+
+ if (value_index_list == NULL) {
+ return CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+
+ for (attribute_index = 0 ;
+ result == CMT_DECODE_OPENTELEMETRY_SUCCESS &&
+ attribute_index < attribute_count ;
+ attribute_index++) {
+
+ attribute = attribute_list[attribute_index];
+
+ label_found = CMT_FALSE;
+ label_index = 0;
+
+ cfl_list_foreach(label_iterator, &map->label_keys) {
+ current_label = cfl_list_entry(label_iterator, struct cmt_map_label, _head);
+
+ if (strcmp(current_label->name, attribute->key) == 0) {
+ label_found = CMT_TRUE;
+
+ break;
+ }
+
+ label_index++;
+ }
+
+ if (label_found == CMT_FALSE) {
+ result = append_new_map_label_key(map, attribute->key);
+ }
+
+ if (result == CMT_DECODE_OPENTELEMETRY_SUCCESS) {
+ value_index_list[label_index] = (void *) attribute;
+ }
+ }
+
+ map_label_count = cfl_list_size(&map->label_keys);
+
+ for (map_label_index = 0 ;
+ result == CMT_DECODE_OPENTELEMETRY_SUCCESS &&
+ map_label_index < map_label_count ;
+ map_label_index++) {
+
+ if (value_index_list[map_label_index] != NULL) {
+ attribute = (Opentelemetry__Proto__Common__V1__KeyValue *)
+ value_index_list[map_label_index];
+
+ if (attribute->value->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE) {
+ result = append_new_metric_label_value(metric, attribute->value->string_value, 0);
+ }
+ else if (attribute->value->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_BYTES_VALUE) {
+ result = append_new_metric_label_value(metric,
+ (char *) attribute->value->bytes_value.data,
+ attribute->value->bytes_value.len);
+ }
+ else if (attribute->value->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_BOOL_VALUE) {
+ snprintf(dummy_label_value, sizeof(dummy_label_value) - 1, "%d", attribute->value->bool_value);
+
+ result = append_new_metric_label_value(metric, dummy_label_value, 0);
+ }
+ else if (attribute->value->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_INT_VALUE) {
+ snprintf(dummy_label_value, sizeof(dummy_label_value) - 1, "%" PRIi64, attribute->value->int_value);
+
+ result = append_new_metric_label_value(metric, dummy_label_value, 0);
+ }
+ else if (attribute->value->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_DOUBLE_VALUE) {
+ snprintf(dummy_label_value, sizeof(dummy_label_value) - 1, "%.17g", attribute->value->double_value);
+
+ result = append_new_metric_label_value(metric, dummy_label_value, 0);
+ }
+ else {
+ result = append_new_metric_label_value(metric, NULL, 0);
+ }
+ }
+ }
+
+ free(value_index_list);
+
+ return result;
+}
+
+static int decode_numerical_data_point(struct cmt *cmt,
+ struct cmt_map *map,
+ Opentelemetry__Proto__Metrics__V1__NumberDataPoint *data_point)
+{
+ int static_metric_detected;
+ struct cmt_metric *sample;
+ int result;
+ double value;
+
+ static_metric_detected = CMT_FALSE;
+
+ result = CMT_DECODE_OPENTELEMETRY_SUCCESS;
+
+ if (data_point->n_attributes == 0) {
+ if (map->metric_static_set == CMT_FALSE) {
+ static_metric_detected = CMT_TRUE;
+ }
+ }
+
+ if (static_metric_detected == CMT_FALSE) {
+ sample = calloc(1, sizeof(struct cmt_metric));
+
+ if (sample == NULL) {
+ return CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+
+ cfl_list_init(&sample->labels);
+
+ result = decode_data_point_labels(cmt,
+ map,
+ sample,
+ data_point->n_attributes,
+ data_point->attributes);
+
+ if (result) {
+ destroy_label_list(&sample->labels);
+
+ free(sample);
+ }
+ else {
+ cfl_list_add(&sample->_head, &map->metrics);
+ }
+ }
+ else {
+ sample = &map->metric;
+
+ map->metric_static_set = CMT_TRUE;
+ }
+
+ if (result == CMT_DECODE_OPENTELEMETRY_SUCCESS) {
+ value = 0;
+
+ if (data_point->value_case == OPENTELEMETRY__PROTO__METRICS__V1__NUMBER_DATA_POINT__VALUE_AS_INT) {
+ if (data_point->as_int < 0) {
+ value = 0;
+ }
+ else {
+ value = cmt_math_uint64_to_d64((uint64_t) data_point->as_int);
+ }
+ }
+ else if (data_point->value_case == OPENTELEMETRY__PROTO__METRICS__V1__NUMBER_DATA_POINT__VALUE_AS_DOUBLE) {
+ value = data_point->as_double;
+ }
+
+ cmt_metric_set(sample, data_point->time_unix_nano, value);
+ }
+
+ return result;
+}
+
+static int decode_numerical_data_point_list(struct cmt *cmt,
+ struct cmt_map *map,
+ size_t data_point_count,
+ Opentelemetry__Proto__Metrics__V1__NumberDataPoint **data_point_list)
+{
+ size_t index;
+ int result;
+
+ result = CMT_DECODE_OPENTELEMETRY_SUCCESS;
+
+ for (index = 0 ;
+ result == 0 &&
+ index < data_point_count ; index++) {
+ result = decode_numerical_data_point(cmt, map, data_point_list[index]);
+ }
+
+ return result;
+}
+
+static int decode_summary_data_point(struct cmt *cmt,
+ struct cmt_map *map,
+ Opentelemetry__Proto__Metrics__V1__SummaryDataPoint *data_point)
+{
+ int static_metric_detected;
+ struct cmt_summary *summary;
+ struct cmt_metric *sample;
+ int result;
+ size_t index;
+
+ result = CMT_DECODE_OPENTELEMETRY_SUCCESS;
+
+ summary = (struct cmt_summary *) map->parent;
+
+ if (summary->quantiles == NULL) {
+ summary->quantiles = calloc(data_point->n_quantile_values,
+ sizeof(double));
+
+ if (summary->quantiles == NULL) {
+ return CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+
+ summary->quantiles_count = data_point->n_quantile_values;
+
+ for (index = 0 ;
+ index < data_point->n_quantile_values ;
+ index++) {
+ summary->quantiles[index] = data_point->quantile_values[index]->quantile;
+ }
+ }
+
+ static_metric_detected = CMT_FALSE;
+
+ if (data_point->n_attributes == 0) {
+ if (map->metric_static_set == CMT_FALSE) {
+ static_metric_detected = CMT_TRUE;
+ }
+ }
+
+ if (static_metric_detected == CMT_FALSE) {
+ sample = calloc(1, sizeof(struct cmt_metric));
+
+ if (sample == NULL) {
+ return CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+
+ cfl_list_init(&sample->labels);
+
+ result = decode_data_point_labels(cmt,
+ map,
+ sample,
+ data_point->n_attributes,
+ data_point->attributes);
+
+ if (result) {
+ destroy_label_list(&sample->labels);
+
+ free(sample);
+
+ return result;
+ }
+ else {
+ cfl_list_add(&sample->_head, &map->metrics);
+ }
+ }
+ else {
+ sample = &map->metric;
+
+ map->metric_static_set = CMT_TRUE;
+ }
+
+ if (result == CMT_DECODE_OPENTELEMETRY_SUCCESS) {
+ if (sample->sum_quantiles_set == CMT_FALSE) {
+ sample->sum_quantiles = calloc(data_point->n_quantile_values,
+ sizeof(uint64_t));
+
+ if (sample->sum_quantiles == NULL) {
+ return CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+
+ sample->sum_quantiles_set = CMT_TRUE;
+ sample->sum_quantiles_count = data_point->n_quantile_values;
+ }
+
+ for (index = 0 ;
+ index < data_point->n_quantile_values ;
+ index++) {
+ cmt_summary_quantile_set(sample, data_point->time_unix_nano,
+ index, data_point->quantile_values[index]->value);
+ }
+
+ sample->sum_sum = cmt_math_d64_to_uint64(data_point->sum);
+ sample->sum_count = data_point->count;
+ }
+
+ return result;
+}
+
+static int decode_summary_data_point_list(struct cmt *cmt,
+ struct cmt_map *map,
+ size_t data_point_count,
+ Opentelemetry__Proto__Metrics__V1__SummaryDataPoint **data_point_list)
+{
+ size_t index;
+ int result;
+
+ result = CMT_DECODE_OPENTELEMETRY_SUCCESS;
+
+ for (index = 0 ;
+ result == CMT_DECODE_OPENTELEMETRY_SUCCESS &&
+ index < data_point_count ; index++) {
+ result = decode_summary_data_point(cmt, map, data_point_list[index]);
+ }
+
+ return result;
+}
+
+static int decode_histogram_data_point(struct cmt *cmt,
+ struct cmt_map *map,
+ Opentelemetry__Proto__Metrics__V1__HistogramDataPoint *data_point)
+{
+ int static_metric_detected;
+ struct cmt_histogram *histogram;
+ struct cmt_metric *sample;
+ int result;
+ size_t index;
+
+ result = CMT_DECODE_OPENTELEMETRY_SUCCESS;
+
+ histogram = (struct cmt_histogram *) map->parent;
+
+ if (data_point->n_bucket_counts > data_point->n_explicit_bounds + 1) {
+ return CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+
+ if (histogram->buckets == NULL) {
+ histogram->buckets = cmt_histogram_buckets_create_size(data_point->explicit_bounds,
+ data_point->n_explicit_bounds);
+
+ if (histogram->buckets == NULL) {
+ return CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+ }
+
+ static_metric_detected = CMT_FALSE;
+
+ if (data_point->n_attributes == 0) {
+ if (map->metric_static_set == CMT_FALSE) {
+ static_metric_detected = CMT_TRUE;
+ }
+ }
+
+ if (static_metric_detected == CMT_FALSE) {
+ sample = calloc(1, sizeof(struct cmt_metric));
+
+ if (sample == NULL) {
+ return CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+
+ cfl_list_init(&sample->labels);
+
+ result = decode_data_point_labels(cmt,
+ map,
+ sample,
+ data_point->n_attributes,
+ data_point->attributes);
+
+ if (result != 0) {
+ destroy_label_list(&sample->labels);
+
+ free(sample);
+
+ return result;
+ }
+ else {
+ cfl_list_add(&sample->_head, &map->metrics);
+ }
+ }
+ else {
+ sample = &map->metric;
+
+ map->metric_static_set = CMT_TRUE;
+ }
+
+ if (result == CMT_DECODE_OPENTELEMETRY_SUCCESS) {
+ if (sample->hist_buckets == NULL) {
+ sample->hist_buckets = calloc(data_point->n_bucket_counts + 1,
+ sizeof(uint64_t));
+
+ if (sample->hist_buckets == NULL) {
+ return CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+ }
+
+ for (index = 0 ;
+ index < data_point->n_bucket_counts;
+ index++) {
+ cmt_metric_hist_set(sample, data_point->time_unix_nano,
+ index, data_point->bucket_counts[index]);
+ }
+
+ sample->hist_sum = cmt_math_d64_to_uint64(data_point->sum);
+ sample->hist_count = data_point->count;
+ }
+
+ return result;
+}
+
+static int decode_histogram_data_point_list(struct cmt *cmt,
+ struct cmt_map *map,
+ size_t data_point_count,
+ Opentelemetry__Proto__Metrics__V1__HistogramDataPoint **data_point_list)
+{
+ size_t index;
+ int result;
+
+ result = CMT_DECODE_OPENTELEMETRY_SUCCESS;
+
+ for (index = 0 ;
+ result == 0 &&
+ index < data_point_count ; index++) {
+ result = decode_histogram_data_point(cmt, map, data_point_list[index]);
+ }
+
+ return result;
+}
+
+static int decode_counter_entry(struct cmt *cmt,
+ void *instance,
+ Opentelemetry__Proto__Metrics__V1__Sum *metric)
+{
+ struct cmt_counter *counter;
+ int result;
+
+ result = CMT_DECODE_OPENTELEMETRY_SUCCESS;
+
+ counter = (struct cmt_counter *) instance;
+
+ counter->map->metric_static_set = 0;
+
+ result = decode_numerical_data_point_list(cmt,
+ counter->map,
+ metric->n_data_points,
+ metric->data_points);
+
+ if (result == CMT_DECODE_OPENTELEMETRY_SUCCESS) {
+ if (metric->aggregation_temporality == OPENTELEMETRY__PROTO__METRICS__V1__AGGREGATION_TEMPORALITY__AGGREGATION_TEMPORALITY_DELTA) {
+ counter->aggregation_type = CMT_AGGREGATION_TYPE_DELTA;
+ }
+ else if (metric->aggregation_temporality == OPENTELEMETRY__PROTO__METRICS__V1__AGGREGATION_TEMPORALITY__AGGREGATION_TEMPORALITY_CUMULATIVE) {
+ counter->aggregation_type = CMT_AGGREGATION_TYPE_CUMULATIVE;
+ }
+ else {
+ counter->aggregation_type = CMT_AGGREGATION_TYPE_UNSPECIFIED;
+ }
+
+ counter->allow_reset = !metric->is_monotonic;
+ }
+
+ return result;
+}
+
+static int decode_gauge_entry(struct cmt *cmt,
+ void *instance,
+ Opentelemetry__Proto__Metrics__V1__Gauge *metric)
+{
+ struct cmt_gauge *gauge;
+ int result;
+
+ gauge = (struct cmt_gauge *) instance;
+
+ gauge->map->metric_static_set = 0;
+
+ result = decode_numerical_data_point_list(cmt,
+ gauge->map,
+ metric->n_data_points,
+ metric->data_points);
+
+ return result;
+}
+
+static int decode_summary_entry(struct cmt *cmt,
+ void *instance,
+ Opentelemetry__Proto__Metrics__V1__Summary *metric)
+{
+ struct cmt_summary *summary;
+ int result;
+
+ summary = (struct cmt_summary *) instance;
+
+ if (summary->quantiles != NULL) {
+ free(summary->quantiles);
+ }
+
+ summary->quantiles = NULL;
+ summary->quantiles_count = 0;
+ summary->map->metric_static_set = 0;
+
+ result = decode_summary_data_point_list(cmt,
+ summary->map,
+ metric->n_data_points,
+ metric->data_points);
+
+ return result;
+}
+
+static int decode_histogram_entry(struct cmt *cmt,
+ void *instance,
+ Opentelemetry__Proto__Metrics__V1__Histogram *metric)
+{
+ struct cmt_histogram *histogram;
+ int result;
+
+ histogram = (struct cmt_histogram *) instance;
+
+ histogram->buckets = NULL;
+ histogram->map->metric_static_set = 0;
+
+ result = decode_histogram_data_point_list(cmt,
+ histogram->map,
+ metric->n_data_points,
+ metric->data_points);
+
+ if (result == CMT_DECODE_OPENTELEMETRY_SUCCESS) {
+ if (metric->aggregation_temporality == OPENTELEMETRY__PROTO__METRICS__V1__AGGREGATION_TEMPORALITY__AGGREGATION_TEMPORALITY_DELTA) {
+ histogram->aggregation_type = CMT_AGGREGATION_TYPE_DELTA;
+ }
+ else if (metric->aggregation_temporality == OPENTELEMETRY__PROTO__METRICS__V1__AGGREGATION_TEMPORALITY__AGGREGATION_TEMPORALITY_CUMULATIVE) {
+ histogram->aggregation_type = CMT_AGGREGATION_TYPE_CUMULATIVE;
+ }
+ else {
+ histogram->aggregation_type = CMT_AGGREGATION_TYPE_UNSPECIFIED;
+ }
+ }
+
+ return result;
+}
+
+static int decode_metrics_entry(struct cmt *cmt,
+ Opentelemetry__Proto__Metrics__V1__Metric *metric)
+{
+ char *metric_description;
+ char *metric_namespace;
+ char *metric_subsystem;
+ char *metric_name;
+ void *instance;
+ int result;
+
+ result = CMT_DECODE_OPENTELEMETRY_SUCCESS;
+
+ metric_name = metric->name;
+ metric_namespace = "";
+ metric_subsystem = "";
+ metric_description = metric->description;
+
+ if (metric_description == NULL) {
+ metric_description = "-";
+ }
+ else if (strlen(metric_description) == 0) {
+ metric_description = "-";
+ }
+
+ if (metric->data_case == OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_SUM) {
+ instance = cmt_counter_create(cmt,
+ metric_namespace,
+ metric_subsystem,
+ metric_name,
+ metric_description,
+ 0, NULL);
+
+ if (instance == NULL) {
+ return CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+
+ result = decode_counter_entry(cmt, instance, metric->sum);
+
+ if (result) {
+ cmt_counter_destroy(instance);
+ }
+ }
+ else if (metric->data_case == OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_GAUGE) {
+ instance = cmt_gauge_create(cmt,
+ metric_namespace,
+ metric_subsystem,
+ metric_name,
+ metric_description,
+ 0, NULL);
+
+ if (instance == NULL) {
+ return CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+
+ result = decode_gauge_entry(cmt, instance, metric->gauge);
+
+ if (result) {
+ cmt_gauge_destroy(instance);
+ }
+ }
+ else if (metric->data_case == OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_SUMMARY) {
+ instance = cmt_summary_create(cmt,
+ metric_namespace,
+ metric_subsystem,
+ metric_name,
+ metric_description,
+ 1, (double []) { 0.0 },
+ 0, NULL);
+
+ if (instance == NULL) {
+ return CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+
+ /* We are forced to create at least one quantile by the constructor but we
+ * don't know the details about it at the moment so we just leave it "open"
+ */
+
+ result = decode_summary_entry(cmt, instance, metric->summary);
+
+ if (result) {
+ cmt_summary_destroy(instance);
+ }
+ }
+ else if (metric->data_case == OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_HISTOGRAM) {
+ instance = cmt_histogram_create(cmt,
+ metric_namespace,
+ metric_subsystem,
+ metric_name,
+ metric_description,
+ (struct cmt_histogram_buckets *) cmt,
+ 0, NULL);
+
+ if (instance == NULL) {
+ return CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+
+ result = decode_histogram_entry(cmt, instance, metric->histogram);
+
+ if (result) {
+ cmt_histogram_destroy(instance);
+ }
+ }
+
+ return result;
+}
+
+static int decode_scope_metadata_and_attributes(struct cfl_kvlist *external_metadata,
+ Opentelemetry__Proto__Common__V1__InstrumentationScope *scope)
+{
+ struct cfl_kvlist *attributes;
+ struct cfl_kvlist *metadata;
+ int result;
+ size_t index;
+ struct cfl_kvlist *root;
+
+ root = get_or_create_external_metadata_kvlist(external_metadata, "scope");
+
+ if (root == NULL) {
+ return -1;
+ }
+
+ metadata = get_or_create_external_metadata_kvlist(root, "metadata");
+
+ if (metadata == NULL) {
+ return -2;
+ }
+
+ attributes = get_or_create_external_metadata_kvlist(root, "attributes");
+
+ if (attributes == NULL) {
+ return -3;
+ }
+
+ if (scope == NULL) {
+ return 0;
+ }
+
+ if (scope->name != NULL) {
+ result = cfl_kvlist_insert_string(metadata, "name", scope->name);
+
+ if (result != 0) {
+ return -4;
+ }
+ }
+
+ if (scope->version != NULL) {
+ result = cfl_kvlist_insert_string(metadata, "version", scope->version);
+
+ if (result != 0) {
+ return -5;
+ }
+ }
+
+ result = cfl_kvlist_insert_int64(metadata, "dropped_attributes_count", scope->dropped_attributes_count);
+
+ if (result != 0) {
+ return -6;
+ }
+
+ for (index = 0 ;
+ result == CMT_DECODE_OPENTELEMETRY_SUCCESS &&
+ index < scope->n_attributes ;
+ index++) {
+ result = clone_kvlist_entry(attributes,
+ scope->attributes[index]);
+ }
+
+ if (result != 0) {
+ return -7;
+ }
+
+ return 0;
+}
+
+static int decode_scope_metrics_metadata(struct cfl_kvlist *external_metadata,
+ Opentelemetry__Proto__Metrics__V1__ScopeMetrics *scope_metrics)
+{
+ struct cfl_kvlist *scope_metrics_metadata;
+ struct cfl_kvlist *scope_metrics_root;
+ int result;
+
+ scope_metrics_root = get_or_create_external_metadata_kvlist(external_metadata, "scope_metrics");
+
+ if (scope_metrics_root == NULL) {
+ return -1;
+ }
+
+ scope_metrics_metadata = get_or_create_external_metadata_kvlist(scope_metrics_root, "metadata");
+
+ if (scope_metrics_metadata == NULL) {
+ return -2;
+ }
+
+ if (scope_metrics == NULL) {
+ return 0;
+ }
+
+ if (scope_metrics->schema_url != NULL) {
+ result = cfl_kvlist_insert_string(scope_metrics_metadata, "schema_url", scope_metrics->schema_url);
+
+ if (result != 0) {
+ return -3;
+ }
+ }
+
+ return 0;
+}
+
+static int decode_scope_metrics_entry(struct cfl_list *context_list,
+ Opentelemetry__Proto__Metrics__V1__ScopeMetrics *metrics)
+{
+ struct cmt *context;
+ int result;
+ size_t index;
+
+ context = cmt_create();
+
+ if (context == NULL) {
+ return CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+
+ cfl_list_add(&context->_head, context_list);
+
+ result = cfl_kvlist_insert_string(context->internal_metadata,
+ "producer",
+ "opentelemetry");
+
+ if (result != 0) {
+ result = CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+
+ if (result == CMT_DECODE_OPENTELEMETRY_SUCCESS) {
+ result = decode_scope_metadata_and_attributes(context->external_metadata,
+ metrics->scope);
+
+ if (result != 0) {
+ result = CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+ }
+
+ if (result == CMT_DECODE_OPENTELEMETRY_SUCCESS) {
+ result = decode_scope_metrics_metadata(context->external_metadata,
+ metrics);
+
+ if (result != 0) {
+ result = CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+ }
+
+ if (result != CMT_DECODE_OPENTELEMETRY_SUCCESS) {
+ return result;
+ }
+
+ for (index = 0 ;
+ result == CMT_DECODE_OPENTELEMETRY_SUCCESS &&
+ index < metrics->n_metrics ;
+ index++) {
+ result = decode_metrics_entry(context,
+ metrics->metrics[index]);
+ }
+
+ return result;
+}
+
+
+static int decode_resource_metadata_and_attributes(struct cfl_kvlist *external_metadata,
+ Opentelemetry__Proto__Resource__V1__Resource *resource)
+{
+ struct cfl_kvlist *attributes;
+ struct cfl_kvlist *metadata;
+ int result;
+ size_t index;
+ struct cfl_kvlist *root;
+
+ root = get_or_create_external_metadata_kvlist(external_metadata, "resource");
+
+ if (root == NULL) {
+ return -1;
+ }
+
+ metadata = get_or_create_external_metadata_kvlist(root, "metadata");
+
+ if (metadata == NULL) {
+ return -2;
+ }
+
+ attributes = get_or_create_external_metadata_kvlist(root, "attributes");
+
+ if (attributes == NULL) {
+ return -3;
+ }
+
+ if (resource == NULL) {
+ return 0;
+ }
+
+ result = cfl_kvlist_insert_int64(metadata, "dropped_attributes_count", (int64_t) resource->dropped_attributes_count);
+
+ if (result != 0) {
+ return -4;
+ }
+
+ for (index = 0 ;
+ result == CMT_DECODE_OPENTELEMETRY_SUCCESS &&
+ index < resource->n_attributes ;
+ index++) {
+ result = clone_kvlist_entry(attributes,
+ resource->attributes[index]);
+ }
+
+ if (result != CMT_DECODE_OPENTELEMETRY_SUCCESS) {
+ return -5;
+ }
+
+ return 0;
+}
+
+static int decode_resource_metrics_metadata(struct cfl_kvlist *external_metadata,
+ Opentelemetry__Proto__Metrics__V1__ResourceMetrics *resource_metrics)
+{
+ struct cfl_kvlist *resource_metrics_metadata;
+ struct cfl_kvlist *resource_metrics_root;
+ int result;
+
+ resource_metrics_root = get_or_create_external_metadata_kvlist(external_metadata, "resource_metrics");
+
+ if (resource_metrics_root == NULL) {
+ return -1;
+ }
+
+ resource_metrics_metadata = get_or_create_external_metadata_kvlist(resource_metrics_root, "metadata");
+
+ if (resource_metrics_metadata == NULL) {
+ return -2;
+ }
+
+ if (resource_metrics == NULL) {
+ return 0;
+ }
+
+ if (resource_metrics->schema_url != NULL) {
+ result = cfl_kvlist_insert_string(resource_metrics_metadata, "schema_url", resource_metrics->schema_url);
+
+ if (result != 0) {
+ return -3;
+ }
+ }
+
+ return 0;
+}
+
+static int decode_resource_metrics_entry(
+ struct cfl_list *context_list,
+ Opentelemetry__Proto__Metrics__V1__ResourceMetrics *resource_metrics)
+{
+ struct cmt *context;
+ int result;
+ size_t index;
+
+ result = CMT_DECODE_OPENTELEMETRY_SUCCESS;
+
+ for (index = 0 ;
+ result == CMT_DECODE_OPENTELEMETRY_SUCCESS &&
+ index < resource_metrics->n_scope_metrics ;
+ index++) {
+ result = decode_scope_metrics_entry(context_list,
+ resource_metrics->scope_metrics[index]);
+
+ if (result == CMT_DECODE_OPENTELEMETRY_SUCCESS) {
+ context = cfl_list_entry_last(context_list, struct cmt, _head);
+
+ if (context != NULL) {
+ if (resource_metrics->resource != NULL) {
+ result = decode_resource_metadata_and_attributes(context->external_metadata,
+ resource_metrics->resource);
+
+ if (result != 0) {
+ result = CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+ }
+
+ if (result == CMT_DECODE_OPENTELEMETRY_SUCCESS) {
+ result = decode_resource_metrics_metadata(context->external_metadata,
+ resource_metrics);
+
+ if (result != 0) {
+ result = CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+static void destroy_context_list(struct cfl_list *context_list)
+{
+ struct cfl_list *iterator;
+ struct cmt *context;
+ struct cfl_list *tmp;
+
+ cfl_list_foreach_safe(iterator, tmp, context_list) {
+ context = cfl_list_entry(iterator, struct cmt, _head);
+
+ cfl_list_del(&context->_head);
+
+ cmt_destroy(context);
+ }
+}
+
+static int decode_service_request(struct cfl_list *context_list,
+ Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceRequest *service_request)
+{
+ int result;
+ size_t index;
+
+ result = CMT_DECODE_OPENTELEMETRY_SUCCESS;
+
+ if (service_request->n_resource_metrics > 0) {
+ for (index = 0 ;
+ result == CMT_DECODE_OPENTELEMETRY_SUCCESS &&
+ index < service_request->n_resource_metrics ;
+ index++) {
+
+ result = decode_resource_metrics_entry(context_list,
+ service_request->resource_metrics[index]);
+ }
+ }
+
+ return result;
+}
+
+int cmt_decode_opentelemetry_create(struct cfl_list *result_context_list,
+ char *in_buf, size_t in_size,
+ size_t *offset)
+{
+ Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceRequest *service_request;
+ int result;
+
+ result = CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR;
+
+ cfl_list_init(result_context_list);
+
+ service_request = opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__unpack(NULL, in_size - *offset,
+ (unsigned char *) &in_buf[*offset]);
+
+ if (service_request != NULL) {
+ result = decode_service_request(result_context_list, service_request);
+
+ opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__free_unpacked(service_request, NULL);
+ }
+
+ if (result != CMT_DECODE_OPENTELEMETRY_SUCCESS) {
+ destroy_context_list(result_context_list);
+ }
+
+ return result;
+}
+
+void cmt_decode_opentelemetry_destroy(struct cfl_list *context_list)
+{
+ if (context_list != NULL) {
+ destroy_context_list(context_list);
+ }
+}
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_decode_prometheus.c b/src/fluent-bit/lib/cmetrics/src/cmt_decode_prometheus.c
new file mode 100644
index 000000000..e33a9ef16
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_decode_prometheus.c
@@ -0,0 +1,1160 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdint.h>
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_gauge.h>
+#include <cmetrics/cmt_untyped.h>
+#include <cmetrics/cmt_histogram.h>
+#include <cmetrics/cmt_summary.h>
+#include <cmetrics/cmt_counter.h>
+#include <cmetrics/cmt_decode_prometheus.h>
+
+#include <cmt_decode_prometheus_parser.h>
+#include <stdio.h>
+#include <string.h>
+#include <cmetrics/cmt_map.h>
+
+static void reset_context(struct cmt_decode_prometheus_context *context,
+ bool reset_summary)
+{
+ int i;
+ struct cmt_decode_prometheus_context_sample *sample;
+
+ while (!cfl_list_is_empty(&context->metric.samples)) {
+ sample = cfl_list_entry_first(&context->metric.samples,
+ struct cmt_decode_prometheus_context_sample, _head);
+ for (i = 0; i < context->metric.label_count; i++) {
+ cfl_sds_destroy(sample->label_values[i]);
+ }
+ cfl_list_del(&sample->_head);
+ free(sample);
+ }
+
+ for (i = 0; i < context->metric.label_count; i++) {
+ cfl_sds_destroy(context->metric.labels[i]);
+ }
+
+ if (context->metric.ns) {
+ if (strcmp(context->metric.ns, "")) {
+ /* when namespace is empty, "name" contains a pointer to the
+ * allocated string */
+ free(context->metric.ns);
+ }
+ else {
+ free(context->metric.name);
+ }
+ }
+
+ cfl_sds_destroy(context->strbuf);
+ context->strbuf = NULL;
+ if (reset_summary) {
+ context->current.summary = NULL;
+ }
+ cfl_sds_destroy(context->metric.name_orig);
+ cfl_sds_destroy(context->metric.docstring);
+ memset(&context->metric,
+ 0,
+ sizeof(struct cmt_decode_prometheus_context_metric));
+ cfl_list_init(&context->metric.samples);
+}
+
+
+int cmt_decode_prometheus_create(
+ struct cmt **out_cmt,
+ const char *in_buf,
+ size_t in_size,
+ struct cmt_decode_prometheus_parse_opts *opts)
+{
+ yyscan_t scanner;
+ YY_BUFFER_STATE buf;
+ struct cmt *cmt;
+ struct cmt_decode_prometheus_context context;
+ int result;
+
+ cmt = cmt_create();
+
+ if (cmt == NULL) {
+ return CMT_DECODE_PROMETHEUS_ALLOCATION_ERROR;
+ }
+
+ memset(&context, 0, sizeof(context));
+ context.cmt = cmt;
+ if (opts) {
+ context.opts = *opts;
+ }
+ cfl_list_init(&(context.metric.samples));
+ cmt_decode_prometheus_lex_init(&scanner);
+ if (!in_size) {
+ in_size = strlen(in_buf);
+ }
+ buf = cmt_decode_prometheus__scan_bytes((char *)in_buf, in_size, scanner);
+ if (!buf) {
+ cmt_destroy(cmt);
+ return CMT_DECODE_PROMETHEUS_ALLOCATION_ERROR;
+ }
+
+ result = cmt_decode_prometheus_parse(scanner, &context);
+
+ if (result == 0) {
+ *out_cmt = cmt;
+ }
+ else {
+ cmt_destroy(cmt);
+ if (context.errcode) {
+ result = context.errcode;
+ }
+ reset_context(&context, true);
+ }
+
+ cmt_decode_prometheus__delete_buffer(buf, scanner);
+ cmt_decode_prometheus_lex_destroy(scanner);
+
+ return result;
+}
+
+void cmt_decode_prometheus_destroy(struct cmt *cmt)
+{
+ cmt_destroy(cmt);
+}
+
+static int report_error(struct cmt_decode_prometheus_context *context,
+ int errcode,
+ const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ context->errcode = errcode;
+ if (context->opts.errbuf && context->opts.errbuf_size) {
+ vsnprintf(context->opts.errbuf, context->opts.errbuf_size - 1, format, args);
+ }
+ va_end(args);
+ return errcode;
+}
+
+static int split_metric_name(struct cmt_decode_prometheus_context *context,
+ cfl_sds_t metric_name, char **ns,
+ char **subsystem, char **name)
+{
+ /* split the name */
+ *ns = strdup(metric_name);
+ if (!*ns) {
+ return report_error(context,
+ CMT_DECODE_PROMETHEUS_ALLOCATION_ERROR,
+ "memory allocation failed");
+ }
+ *subsystem = strchr(*ns, '_');
+ if (!(*subsystem)) {
+ *name = *ns;
+ *ns = "";
+ }
+ else {
+ **subsystem = 0; /* split */
+ (*subsystem)++;
+ *name = strchr(*subsystem, '_');
+ if (!(*name)) {
+ *name = *subsystem;
+ *subsystem = "";
+ }
+ else {
+ **name = 0;
+ (*name)++;
+ }
+ }
+ return 0;
+}
+
+/* Use this helper function to return a stub value for docstring when it is not
+ * available. This is necessary for now because the metric constructors require
+ * a docstring, even though it is not required by prometheus spec. */
+static char *get_docstring(struct cmt_decode_prometheus_context *context)
+{
+ return context->metric.docstring && strlen(context->metric.docstring) ?
+ context->metric.docstring : " ";
+}
+
+static int parse_uint64(const char *in, uint64_t *out)
+{
+ char *end;
+ int64_t val;
+
+ errno = 0;
+ val = strtol(in, &end, 10);
+ if (end == in || *end != 0 || errno) {
+ return -1;
+ }
+
+ /* Even though prometheus text format supports negative numbers, cmetrics
+ * doesn't, so we truncate to 0 */
+ if (val < 0) {
+ val = 0;
+ }
+ *out = val;
+ return 0;
+}
+
+static int parse_double(const char *in, double *out)
+{
+ char *end;
+ double val;
+ errno = 0;
+ val = strtod(in, &end);
+ if (end == in || *end != 0 || errno) {
+ return -1;
+ }
+ *out = val;
+ return 0;
+}
+
+static int parse_timestamp(struct cmt_decode_prometheus_context *context,
+ char *data_source, uint64_t *timestamp)
+{
+ int result;
+
+ result = CMT_DECODE_PROMETHEUS_SUCCESS;
+
+ if (data_source != NULL && strlen(data_source) > 0) {
+ result = parse_uint64(data_source, timestamp);
+
+ if (result) {
+ result = report_error(context,
+ CMT_DECODE_PROMETHEUS_PARSE_TIMESTAMP_FAILED,
+ "failed to parse sample: \"%s\" is not a valid "
+ "timestamp", data_source);
+ }
+ else {
+ /* prometheus text format timestamps are expressed in milliseconds,
+ * while cmetrics expresses them in nanoseconds, so multiply by 10e5
+ */
+
+ *timestamp *= 10e5;
+ }
+ }
+
+ return result;
+}
+
+static int parse_value_timestamp(
+ struct cmt_decode_prometheus_context *context,
+ struct cmt_decode_prometheus_context_sample *sample,
+ double *value,
+ uint64_t *timestamp)
+{
+
+ if (parse_double(sample->value1, value)) {
+ return report_error(context,
+ CMT_DECODE_PROMETHEUS_PARSE_VALUE_FAILED,
+ "failed to parse sample: \"%s\" is not a valid "
+ "value", sample->value1);
+ }
+
+ if (context->opts.override_timestamp) {
+ *timestamp = context->opts.override_timestamp;
+ }
+ else if (!strlen(sample->value2)) {
+ /* No timestamp was specified, use default value */
+ *timestamp = context->opts.default_timestamp;
+ return 0;
+ }
+ else if (parse_uint64(sample->value2, timestamp)) {
+ return report_error(context,
+ CMT_DECODE_PROMETHEUS_PARSE_TIMESTAMP_FAILED,
+ "failed to parse sample: \"%s\" is not a valid "
+ "timestamp", sample->value2);
+ }
+
+ /* prometheus text format timestamps are in milliseconds, while cmetrics is in
+ * nanoseconds, so multiply by 10e5 */
+ *timestamp = *timestamp * 10e5;
+
+ return 0;
+}
+
+static int add_metric_counter(struct cmt_decode_prometheus_context *context)
+{
+ int ret;
+ size_t label_count;
+ struct cmt_counter *c;
+ struct cfl_list *head;
+ struct cfl_list *tmp;
+ struct cmt_decode_prometheus_context_sample *sample;
+ double value;
+ uint64_t timestamp;
+
+ c = cmt_counter_create(context->cmt,
+ context->metric.ns,
+ context->metric.subsystem,
+ context->metric.name,
+ get_docstring(context),
+ context->metric.label_count,
+ context->metric.labels);
+
+ if (!c) {
+ return report_error(context,
+ CMT_DECODE_PROMETHEUS_CMT_CREATE_ERROR,
+ "cmt_counter_create failed");
+ }
+
+ cfl_list_foreach_safe(head, tmp, &context->metric.samples) {
+ sample = cfl_list_entry(head, struct cmt_decode_prometheus_context_sample, _head);
+ label_count = context->metric.label_count;
+ ret = parse_value_timestamp(context, sample, &value, &timestamp);
+ if (ret) {
+ return ret;
+ }
+ if (cmt_counter_set(c,
+ timestamp,
+ value,
+ label_count,
+ label_count ? sample->label_values : NULL)) {
+ return report_error(context,
+ CMT_DECODE_PROMETHEUS_CMT_SET_ERROR,
+ "cmt_counter_set failed");
+ }
+ }
+
+ return 0;
+}
+
+static int add_metric_gauge(struct cmt_decode_prometheus_context *context)
+{
+ int ret;
+ size_t label_count;
+ struct cmt_gauge *c;
+ struct cfl_list *head;
+ struct cfl_list *tmp;
+ struct cmt_decode_prometheus_context_sample *sample;
+ double value;
+ uint64_t timestamp;
+
+ c = cmt_gauge_create(context->cmt,
+ context->metric.ns,
+ context->metric.subsystem,
+ context->metric.name,
+ get_docstring(context),
+ context->metric.label_count,
+ context->metric.labels);
+
+ if (!c) {
+ return report_error(context,
+ CMT_DECODE_PROMETHEUS_CMT_CREATE_ERROR,
+ "cmt_gauge_create failed");
+ }
+
+ cfl_list_foreach_safe(head, tmp, &context->metric.samples) {
+ sample = cfl_list_entry(head, struct cmt_decode_prometheus_context_sample, _head);
+ label_count = context->metric.label_count;
+ ret = parse_value_timestamp(context, sample, &value, &timestamp);
+ if (ret) {
+ return ret;
+ }
+ if (cmt_gauge_set(c,
+ timestamp,
+ value,
+ label_count,
+ label_count ? sample->label_values : NULL)) {
+ return report_error(context,
+ CMT_DECODE_PROMETHEUS_CMT_SET_ERROR,
+ "cmt_gauge_set failed");
+ }
+ }
+
+ return 0;
+}
+
+static int add_metric_untyped(struct cmt_decode_prometheus_context *context)
+{
+ int ret;
+ size_t label_count;
+ struct cmt_untyped *c;
+ struct cfl_list *head;
+ struct cfl_list *tmp;
+ struct cmt_decode_prometheus_context_sample *sample;
+ double value;
+ uint64_t timestamp;
+
+ c = cmt_untyped_create(context->cmt,
+ context->metric.ns,
+ context->metric.subsystem,
+ context->metric.name,
+ get_docstring(context),
+ context->metric.label_count,
+ context->metric.labels);
+
+ if (!c) {
+ return report_error(context,
+ CMT_DECODE_PROMETHEUS_CMT_CREATE_ERROR,
+ "cmt_untyped_create failed");
+ }
+
+ cfl_list_foreach_safe(head, tmp, &context->metric.samples) {
+ sample = cfl_list_entry(head, struct cmt_decode_prometheus_context_sample, _head);
+ label_count = context->metric.label_count;
+ ret = parse_value_timestamp(context, sample, &value, &timestamp);
+ if (ret) {
+ return ret;
+ }
+ if (cmt_untyped_set(c,
+ timestamp,
+ value,
+ label_count,
+ label_count ? sample->label_values : NULL)) {
+ return report_error(context,
+ CMT_DECODE_PROMETHEUS_CMT_SET_ERROR,
+ "cmt_untyped_set failed");
+ }
+ }
+
+ return 0;
+}
+
+static int add_metric_histogram(struct cmt_decode_prometheus_context *context)
+{
+ int ret = 0;
+ int i;
+ size_t bucket_count;
+ size_t bucket_index;
+ double *buckets = NULL;
+ uint64_t *bucket_defaults = NULL;
+ double sum;
+ uint64_t count = 0;
+ double count_dbl;
+ struct cfl_list *head;
+ struct cfl_list *tmp;
+ struct cmt_decode_prometheus_context_sample *sample;
+ size_t le_label_index = 0;
+ struct cmt_histogram *h;
+ struct cmt_histogram_buckets *cmt_buckets;
+ cfl_sds_t *labels_without_le = NULL;
+ cfl_sds_t *values_without_le = NULL;
+ int label_i;
+ uint64_t timestamp;
+
+ /* bucket_count = sample count - 3:
+ * - "Inf" bucket
+ * - sum
+ * - count */
+ bucket_count = cfl_list_size(&context->metric.samples) - 3;
+ timestamp = context->opts.override_timestamp;
+
+ bucket_defaults = calloc(bucket_count + 1, sizeof(*bucket_defaults));
+ if (!bucket_defaults) {
+ ret = report_error(context,
+ CMT_DECODE_PROMETHEUS_CMT_CREATE_ERROR,
+ "failed to allocate bucket defaults");
+ goto end;
+ }
+ buckets = calloc(bucket_count, sizeof(*buckets));
+ if (!buckets) {
+ ret = report_error(context,
+ CMT_DECODE_PROMETHEUS_CMT_CREATE_ERROR,
+ "failed to allocate buckets");
+ goto end;
+ }
+ labels_without_le = calloc(context->metric.label_count - 1, sizeof(*labels_without_le));
+ if (!labels_without_le) {
+ ret = report_error(context,
+ CMT_DECODE_PROMETHEUS_CMT_CREATE_ERROR,
+ "failed to allocate labels_without_le");
+ goto end;
+ }
+ values_without_le = calloc(context->metric.label_count - 1, sizeof(*labels_without_le));
+ if (!values_without_le) {
+ ret = report_error(context,
+ CMT_DECODE_PROMETHEUS_CMT_CREATE_ERROR,
+ "failed to allocate values_without_le");
+ goto end;
+ }
+
+
+ label_i = 0;
+ sample = cfl_list_entry_first(&context->metric.samples, struct cmt_decode_prometheus_context_sample, _head);
+ for (i = 0; i < context->metric.label_count; i++) {
+ if (!strcmp(context->metric.labels[i], "le")) {
+ le_label_index = i;
+ } else {
+ labels_without_le[label_i] = context->metric.labels[i];
+ values_without_le[label_i] = sample->label_values[i];
+ label_i++;
+ }
+ }
+
+ bucket_index = 0;
+ cfl_list_foreach_safe(head, tmp, &context->metric.samples) {
+ sample = cfl_list_entry(head, struct cmt_decode_prometheus_context_sample, _head);
+ switch (sample->type) {
+ case CMT_DECODE_PROMETHEUS_CONTEXT_SAMPLE_TYPE_BUCKET:
+ if (bucket_index == bucket_count) {
+ /* probably last bucket, which has "Inf" */
+ break;
+ }
+ if (parse_double(sample->label_values[le_label_index],
+ buckets + bucket_index)) {
+ ret = report_error(context,
+ CMT_DECODE_PROMETHEUS_CMT_CREATE_ERROR,
+ "failed to parse bucket");
+ goto end;
+ }
+ if (parse_uint64(sample->value1,
+ bucket_defaults + bucket_index)) {
+ /* Count is supposed to be integer, but apparently
+ * some tools can generate count in a floating format.
+ * Try to parse as a double and then cast to uint64_t */
+ if (parse_double(sample->value1, &count_dbl) || count_dbl < 0) {
+ ret = report_error(context,
+ CMT_DECODE_PROMETHEUS_CMT_CREATE_ERROR,
+ "failed to parse count");
+ goto end;
+ } else {
+ *(bucket_defaults + bucket_index) = (uint64_t)count_dbl;
+ }
+ }
+ bucket_index++;
+
+ if (!timestamp) {
+ ret = parse_timestamp(context, sample->value2, &timestamp);
+
+ if (ret) {
+ goto end;
+ }
+ }
+
+ break;
+ case CMT_DECODE_PROMETHEUS_CONTEXT_SAMPLE_TYPE_SUM:
+ if (parse_double(sample->value1, &sum)) {
+ ret = report_error(context,
+ CMT_DECODE_PROMETHEUS_CMT_CREATE_ERROR,
+ "failed to parse sum");
+ goto end;
+ }
+
+ if (!timestamp) {
+ ret = parse_timestamp(context, sample->value2, &timestamp);
+
+ if (ret) {
+ goto end;
+ }
+ }
+
+ break;
+ case CMT_DECODE_PROMETHEUS_CONTEXT_SAMPLE_TYPE_COUNT:
+ if (parse_uint64(sample->value1, &count)) {
+ /* Count is supposed to be integer, but apparently
+ * some tools can generate count in a floating format.
+ * Try to parse as a double and then cast to uint64_t */
+ if (parse_double(sample->value1, &count_dbl) || count_dbl < 0) {
+ ret = report_error(context,
+ CMT_DECODE_PROMETHEUS_CMT_CREATE_ERROR,
+ "failed to parse count");
+ goto end;
+ } else {
+ count = (uint64_t)count_dbl;
+ }
+ }
+ bucket_defaults[bucket_index] = count;
+
+ if (!timestamp) {
+ ret = parse_timestamp(context, sample->value2, &timestamp);
+
+ if (ret) {
+ goto end;
+ }
+ }
+
+ break;
+ }
+ }
+
+ if (!timestamp) {
+ /* No timestamp was specified, use default value */
+ timestamp = context->opts.default_timestamp;
+ }
+
+ h = context->current.histogram;
+ if (!h || label_i != h->map->label_count) {
+ cmt_buckets = cmt_histogram_buckets_create_size(buckets, bucket_count);
+ if (!cmt_buckets) {
+ ret = report_error(context,
+ CMT_DECODE_PROMETHEUS_CMT_CREATE_ERROR,
+ "cmt_histogram_buckets_create_size failed");
+ goto end;
+ }
+
+ h = cmt_histogram_create(context->cmt,
+ context->metric.ns,
+ context->metric.subsystem,
+ context->metric.name,
+ get_docstring(context),
+ cmt_buckets,
+ label_i,
+ label_i ? labels_without_le : NULL);
+
+ if (!h) {
+ ret = report_error(context,
+ CMT_DECODE_PROMETHEUS_CMT_CREATE_ERROR,
+ "cmt_histogram_create failed");
+ goto end;
+ }
+
+ context->current.histogram = h;
+ }
+
+ if (cmt_histogram_set_default(h, timestamp, bucket_defaults, sum, count,
+ label_i,
+ label_i ? values_without_le : NULL)) {
+ ret = report_error(context,
+ CMT_DECODE_PROMETHEUS_CMT_CREATE_ERROR,
+ "cmt_histogram_set_default failed");
+ }
+
+
+end:
+ if (buckets) {
+ free(buckets);
+ }
+ if (bucket_defaults) {
+ free(bucket_defaults);
+ }
+ if (labels_without_le) {
+ free(labels_without_le);
+ }
+ if (values_without_le) {
+ free(values_without_le);
+ }
+
+ return ret;
+}
+
+static int add_metric_summary(struct cmt_decode_prometheus_context *context)
+{
+ int ret = 0;
+ int i;
+ size_t quantile_count;
+ size_t quantile_index;
+ double *quantiles = NULL;
+ double *quantile_defaults = NULL;
+ double sum;
+ double count_dbl;
+ size_t label_count;
+ uint64_t count = 0;
+ struct cfl_list *head;
+ struct cfl_list *tmp;
+ struct cmt_decode_prometheus_context_sample *sample;
+ size_t quantile_label_index = 0;
+ struct cmt_summary *s;
+ cfl_sds_t *labels_without_quantile = NULL;
+ cfl_sds_t *values_without_quantile = NULL;
+ int label_i;
+ uint64_t timestamp;
+
+ /* quantile_count = sample count - 2:
+ * - sum
+ * - count */
+ quantile_count = cfl_list_size(&context->metric.samples) - 2;
+ timestamp = context->opts.override_timestamp;
+
+ quantile_defaults = calloc(quantile_count, sizeof(*quantile_defaults));
+ if (!quantile_defaults) {
+ ret = report_error(context,
+ CMT_DECODE_PROMETHEUS_CMT_CREATE_ERROR,
+ "failed to allocate quantile defaults");
+ goto end;
+ }
+ quantiles = calloc(quantile_count, sizeof(*quantiles));
+ if (!quantiles) {
+ ret = report_error(context,
+ CMT_DECODE_PROMETHEUS_CMT_CREATE_ERROR,
+ "failed to allocate quantiles");
+ goto end;
+ }
+
+ label_count = 0;
+ for (i = 0; i < context->metric.label_count; i++) {
+ if (strcmp(context->metric.labels[i], "quantile")) {
+ /* quantile is not a label */
+ label_count++;
+ }
+ }
+
+ labels_without_quantile = calloc(label_count, sizeof(*labels_without_quantile));
+ if (!labels_without_quantile) {
+ ret = report_error(context,
+ CMT_DECODE_PROMETHEUS_CMT_CREATE_ERROR,
+ "failed to allocate labels_without_quantile");
+ goto end;
+ }
+ values_without_quantile = calloc(label_count, sizeof(*labels_without_quantile));
+ if (!values_without_quantile) {
+ ret = report_error(context,
+ CMT_DECODE_PROMETHEUS_CMT_CREATE_ERROR,
+ "failed to allocate values_without_quantile");
+ goto end;
+ }
+
+ label_i = 0;
+ sample = cfl_list_entry_first(&context->metric.samples,
+ struct cmt_decode_prometheus_context_sample, _head);
+ for (i = 0; i < context->metric.label_count; i++) {
+ if (!strcmp(context->metric.labels[i], "quantile")) {
+ quantile_label_index = i;
+ break;
+ } else {
+ labels_without_quantile[label_i] = context->metric.labels[i];
+ values_without_quantile[label_i] = sample->label_values[i];
+ label_i++;
+ }
+ }
+
+ quantile_index = 0;
+ cfl_list_foreach_safe(head, tmp, &context->metric.samples) {
+ sample = cfl_list_entry(head, struct cmt_decode_prometheus_context_sample, _head);
+ switch (sample->type) {
+ case CMT_DECODE_PROMETHEUS_CONTEXT_SAMPLE_TYPE_NORMAL:
+ if (parse_double(sample->label_values[quantile_label_index],
+ quantiles + quantile_index)) {
+ ret = report_error(context,
+ CMT_DECODE_PROMETHEUS_CMT_CREATE_ERROR,
+ "failed to parse bucket");
+ goto end;
+ }
+ if (parse_double(sample->value1,
+ quantile_defaults + quantile_index)) {
+ ret = report_error(context,
+ CMT_DECODE_PROMETHEUS_CMT_CREATE_ERROR,
+ "failed to parse quantile value");
+ goto end;
+ }
+ quantile_index++;
+
+ if (!timestamp) {
+ ret = parse_timestamp(context, sample->value2, &timestamp);
+
+ if (ret) {
+ goto end;
+ }
+ }
+
+ break;
+ case CMT_DECODE_PROMETHEUS_CONTEXT_SAMPLE_TYPE_SUM:
+ if (parse_double(sample->value1, &sum)) {
+ ret = report_error(context,
+ CMT_DECODE_PROMETHEUS_CMT_CREATE_ERROR,
+ "failed to parse summary sum");
+ goto end;
+ }
+
+ if (!timestamp) {
+ ret = parse_timestamp(context, sample->value2, &timestamp);
+
+ if (ret) {
+ goto end;
+ }
+ }
+
+ break;
+
+ case CMT_DECODE_PROMETHEUS_CONTEXT_SAMPLE_TYPE_COUNT:
+ if (parse_uint64(sample->value1, &count)) {
+ /* Count is supposed to be integer, but apparently
+ * some tools can generate count in a floating format.
+ * Try to parse as a double and then cast to uint64_t */
+ if (parse_double(sample->value1, &count_dbl) || count_dbl < 0) {
+ ret = report_error(context,
+ CMT_DECODE_PROMETHEUS_CMT_CREATE_ERROR,
+ "failed to parse count");
+ goto end;
+ } else {
+ count = (uint64_t)count_dbl;
+ }
+ }
+
+ if (!timestamp) {
+ ret = parse_timestamp(context, sample->value2, &timestamp);
+
+ if (ret) {
+ goto end;
+ }
+ }
+
+ break;
+ }
+ }
+
+ if (!timestamp) {
+ /* No timestamp was specified, use default value */
+ timestamp = context->opts.default_timestamp;
+ }
+
+ s = context->current.summary;
+ if (!s || label_i != s->map->label_count) {
+ s = cmt_summary_create(context->cmt,
+ context->metric.ns,
+ context->metric.subsystem,
+ context->metric.name,
+ get_docstring(context),
+ quantile_count,
+ quantiles,
+ label_i,
+ label_i ? labels_without_quantile : NULL);
+
+ if (!s) {
+ ret = report_error(context,
+ CMT_DECODE_PROMETHEUS_CMT_CREATE_ERROR,
+ "cmt_summary_create failed");
+ goto end;
+ }
+
+ context->current.summary = s;
+ }
+
+ if (cmt_summary_set_default(s, timestamp, quantile_defaults, sum, count,
+ label_i,
+ label_i ? values_without_quantile : NULL)) {
+ ret = report_error(context,
+ CMT_DECODE_PROMETHEUS_CMT_CREATE_ERROR,
+ "cmt_summary_set_default failed");
+ }
+
+
+end:
+ if (quantile_defaults) {
+ free(quantile_defaults);
+ }
+ if (quantiles) {
+ free(quantiles);
+ }
+ if (labels_without_quantile) {
+ free(labels_without_quantile);
+ }
+ if (values_without_quantile) {
+ free(values_without_quantile);
+ }
+
+ return ret;
+}
+
+static int finish_metric(struct cmt_decode_prometheus_context *context,
+ bool reset_summary,
+ cfl_sds_t current_metric_name)
+{
+ int rv = 0;
+
+ if (cfl_list_is_empty(&context->metric.samples)) {
+ goto end;
+ }
+
+ switch (context->metric.type) {
+ case COUNTER:
+ rv = add_metric_counter(context);
+ break;
+ case GAUGE:
+ rv = add_metric_gauge(context);
+ break;
+ case HISTOGRAM:
+ rv = add_metric_histogram(context);
+ break;
+ case SUMMARY:
+ rv = add_metric_summary(context);
+ break;
+ default:
+ rv = add_metric_untyped(context);
+ break;
+ }
+
+end:
+ reset_context(context, reset_summary);
+
+ if (current_metric_name) {
+ context->metric.name_orig = current_metric_name;
+ rv = split_metric_name(context,
+ current_metric_name,
+ &(context->metric.ns),
+ &(context->metric.subsystem),
+ &(context->metric.name));
+ }
+ return rv;
+}
+
+/* special case for summary */
+static int finish_duplicate_histogram_summary_sum_count(
+ struct cmt_decode_prometheus_context *context,
+ cfl_sds_t metric_name,
+ int type)
+{
+ int rv;
+ int current_metric_type;
+ cfl_sds_t current_metric_docstring;
+
+ if (type == CMT_DECODE_PROMETHEUS_CONTEXT_SAMPLE_TYPE_COUNT) {
+ cfl_sds_set_len(metric_name, cfl_sds_len(metric_name) - 6);
+ } else if (type == CMT_DECODE_PROMETHEUS_CONTEXT_SAMPLE_TYPE_SUM) {
+ cfl_sds_set_len(metric_name, cfl_sds_len(metric_name) - 4);
+ } else if (type == CMT_DECODE_PROMETHEUS_CONTEXT_SAMPLE_TYPE_BUCKET) {
+ cfl_sds_set_len(metric_name, cfl_sds_len(metric_name) - 7);
+ }
+ metric_name[cfl_sds_len(metric_name)] = 0;
+
+ current_metric_type = context->metric.type;
+ current_metric_docstring = cfl_sds_create(context->metric.docstring);
+
+ rv = finish_metric(context, false, metric_name);
+ if (rv) {
+ cfl_sds_destroy(current_metric_docstring);
+ return rv;
+ }
+
+ context->metric.type = current_metric_type;
+ context->metric.docstring = current_metric_docstring;
+ context->metric.current_sample_type = type;
+
+ return 0;
+}
+
+static int parse_histogram_summary_name(
+ struct cmt_decode_prometheus_context *context,
+ cfl_sds_t metric_name)
+{
+ bool sum_found;
+ bool count_found;
+ bool has_buckets;
+ bool is_previous_sum_or_count;
+ bool name_matched = false;
+ struct cfl_list *head;
+ struct cfl_list *tmp;
+ size_t current_name_len;
+ size_t parsed_name_len;
+ struct cmt_decode_prometheus_context_sample *sample;
+
+ current_name_len = strlen(metric_name);
+ parsed_name_len = strlen(context->metric.name_orig);
+ if (current_name_len < parsed_name_len) {
+ /* current name length cannot be less than the length already parsed. That means
+ * another metric has started */
+ return finish_metric(context, true, metric_name);
+ }
+
+ if (strncmp(context->metric.name_orig, metric_name, parsed_name_len)) {
+ /* the name prefix must be the same or we are starting a new metric */
+ return finish_metric(context, true, metric_name);
+ }
+ else if (parsed_name_len == current_name_len) {
+ name_matched = true;
+ }
+
+ sum_found = false;
+ count_found = false;
+ has_buckets = false;
+
+ cfl_list_foreach_safe(head, tmp, &context->metric.samples) {
+ sample = cfl_list_entry(head, struct cmt_decode_prometheus_context_sample, _head);
+
+ switch (sample->type) {
+ case CMT_DECODE_PROMETHEUS_CONTEXT_SAMPLE_TYPE_SUM:
+ sum_found = true;
+ break;
+ case CMT_DECODE_PROMETHEUS_CONTEXT_SAMPLE_TYPE_COUNT:
+ count_found = true;
+ break;
+ default:
+ has_buckets = true;
+ break;
+ }
+ }
+
+ sample = cfl_list_entry_last(&context->metric.samples,
+ struct cmt_decode_prometheus_context_sample, _head);
+ is_previous_sum_or_count = sample->type == CMT_DECODE_PROMETHEUS_CONTEXT_SAMPLE_TYPE_SUM ||
+ sample->type == CMT_DECODE_PROMETHEUS_CONTEXT_SAMPLE_TYPE_COUNT;
+
+ if (name_matched) {
+ if (sum_found && count_found) {
+ /* finish instance of the summary/histogram */
+ return finish_duplicate_histogram_summary_sum_count(context, metric_name, -1);
+ }
+ else {
+ /* parsing HELP after TYPE */
+ cfl_sds_destroy(metric_name);
+ return 0;
+ }
+ }
+
+ /* invalid histogram/summary suffix, treat it as a different metric */
+ if (!strcmp(metric_name + parsed_name_len, "_bucket")) {
+ if (sum_found && count_found && has_buckets && is_previous_sum_or_count) {
+ /* already found both sum and count, so this is a new metric */
+ return finish_duplicate_histogram_summary_sum_count(
+ context,
+ metric_name,
+ CMT_DECODE_PROMETHEUS_CONTEXT_SAMPLE_TYPE_BUCKET);
+ }
+ context->metric.current_sample_type = CMT_DECODE_PROMETHEUS_CONTEXT_SAMPLE_TYPE_BUCKET;
+ }
+ else if (!strcmp(metric_name + parsed_name_len, "_sum")) {
+ if (sum_found) {
+ /* already found a `_sum` for this metric, so this must necessarily be a new
+ * one */
+ return finish_duplicate_histogram_summary_sum_count(
+ context,
+ metric_name,
+ CMT_DECODE_PROMETHEUS_CONTEXT_SAMPLE_TYPE_SUM);
+ }
+ context->metric.current_sample_type = CMT_DECODE_PROMETHEUS_CONTEXT_SAMPLE_TYPE_SUM;
+ sum_found = true;
+ }
+ else if (!strcmp(metric_name + parsed_name_len, "_count")) {
+ if (count_found) {
+ /* already found a `_count` for this metric, so this must necessarily be a new
+ * one */
+ return finish_duplicate_histogram_summary_sum_count(
+ context,
+ metric_name,
+ CMT_DECODE_PROMETHEUS_CONTEXT_SAMPLE_TYPE_COUNT);
+ }
+ context->metric.current_sample_type = CMT_DECODE_PROMETHEUS_CONTEXT_SAMPLE_TYPE_COUNT;
+ count_found = true;
+ }
+ else {
+ /* invalid histogram/summary suffix, treat it as a different metric */
+ return finish_metric(context, true, metric_name);
+ }
+
+ /* still in the same metric */
+ cfl_sds_destroy(metric_name);
+ return 0;
+}
+
+static int parse_metric_name(
+ struct cmt_decode_prometheus_context *context,
+ cfl_sds_t metric_name)
+{
+ int ret = 0;
+
+ if (context->metric.name_orig) {
+ if (context->metric.type == HISTOGRAM || context->metric.type == SUMMARY) {
+ ret = parse_histogram_summary_name(context, metric_name);
+ if (!ret) {
+ /* bucket/sum/count parsed */
+ return ret;
+ }
+ }
+ else if (strcmp(context->metric.name_orig, metric_name)) {
+ /* new metric name means the current metric is finished */
+ return finish_metric(context, true, metric_name);
+ }
+ else {
+ /* same metric with name already allocated, destroy and return */
+ cfl_sds_destroy(metric_name);
+ return ret;
+ }
+ }
+
+ if (!ret) {
+ context->metric.name_orig = metric_name;
+ ret = split_metric_name(context, metric_name,
+ &(context->metric.ns),
+ &(context->metric.subsystem),
+ &(context->metric.name));
+ }
+ else {
+ cfl_sds_destroy(metric_name);
+ }
+
+ return ret;
+}
+
+static int parse_label(
+ struct cmt_decode_prometheus_context *context,
+ cfl_sds_t name, cfl_sds_t value)
+{
+ int i;
+ struct cmt_decode_prometheus_context_sample *sample;
+
+ if (context->metric.label_count >= CMT_DECODE_PROMETHEUS_MAX_LABEL_COUNT) {
+ cfl_sds_destroy(name);
+ cfl_sds_destroy(value);
+ return report_error(context,
+ CMT_DECODE_PROMETHEUS_MAX_LABEL_COUNT_EXCEEDED,
+ "maximum number of labels exceeded");
+ }
+
+ /* check if the label is already registered */
+ for (i = 0; i < context->metric.label_count; i++) {
+ if (!strcmp(name, context->metric.labels[i])) {
+ /* found, free the name memory and use the existing one */
+ cfl_sds_destroy(name);
+ name = context->metric.labels[i];
+ break;
+ }
+ }
+ if (i == context->metric.label_count) {
+ /* didn't found the label, add it now */
+ context->metric.labels[i] = name;
+ context->metric.label_count++;
+ }
+
+ sample = cfl_list_entry_last(&context->metric.samples,
+ struct cmt_decode_prometheus_context_sample, _head);
+ sample->label_values[i] = value;
+ return 0;
+}
+
+static int sample_start(struct cmt_decode_prometheus_context *context)
+{
+ struct cmt_decode_prometheus_context_sample *sample;
+
+ sample = malloc(sizeof(*sample));
+ if (!sample) {
+ return report_error(context,
+ CMT_DECODE_PROMETHEUS_ALLOCATION_ERROR,
+ "memory allocation failed");
+ }
+
+ memset(sample, 0, sizeof(*sample));
+ sample->type = context->metric.current_sample_type;
+ cfl_list_add(&sample->_head, &context->metric.samples);
+ return 0;
+}
+
+static int parse_sample(
+ struct cmt_decode_prometheus_context *context,
+ const char *value1,
+ const char *value2)
+{
+ struct cmt_decode_prometheus_context_sample *sample;
+ sample = cfl_list_entry_last(&context->metric.samples,
+ struct cmt_decode_prometheus_context_sample, _head);
+
+ strcpy(sample->value1, value1);
+ strcpy(sample->value2, value2);
+ return 0;
+}
+
+/* called automatically by the generated parser code on error */
+static int cmt_decode_prometheus_error(void *yyscanner,
+ struct cmt_decode_prometheus_context *context,
+ const char *msg)
+{
+ report_error(context, CMT_DECODE_PROMETHEUS_SYNTAX_ERROR, msg);
+ return 0;
+}
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_decode_prometheus.l b/src/fluent-bit/lib/cmetrics/src/cmt_decode_prometheus.l
new file mode 100644
index 000000000..54a7c79d8
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_decode_prometheus.l
@@ -0,0 +1,195 @@
+%option prefix="cmt_decode_prometheus_"
+
+%option reentrant bison-bridge
+%option noyywrap nounput noinput
+%option nodefault
+
+%{
+
+#include <cmetrics/cmt_decode_prometheus.h>
+
+#define STRBUF_RET \
+ yylval->str = context->strbuf; \
+ context->strbuf = NULL
+
+%}
+
+/* here we define some states that allow us to create rules only
+ matched in certain situations */
+
+%x INQUOTE HELPTAG INHELPTAG TYPETAG INTYPETAG COMMENT COMMENT_START
+
+%%
+
+%{
+ if (context->opts.start_token) {
+ int t = context->opts.start_token;
+ context->opts.start_token = 0;
+ return t;
+ }
+%}
+
+<*>\r\n|\n {
+ int top_state = YYSTATE;
+ // We always return to the INITIAL state on a linefeed, no matter which
+ // state we are on (the "<*>" means this rule is applied on every state)
+ BEGIN(INITIAL);
+ if (top_state == INHELPTAG) {
+ // But if we were on the INHELPTAG state, we return everything collected
+ // in strbuf
+ STRBUF_RET;
+ return METRIC_DOC;
+ }
+}
+
+^[ ]*#[ ]* {
+ // Lines with "#" as the first non-whitespace character begin a comment
+ // unless the first token is either HELP or TYPE. To handle this ambiguity,
+ // we enter the COMMENT_START state, which contains rules for selecting
+ // if this is a HELP/TYPE tag or just a normal comment
+ BEGIN(COMMENT_START);
+}
+
+<COMMENT_START>HELP[ \t]+ {
+ // Begin a help tag
+ BEGIN(HELPTAG);
+}
+
+<COMMENT_START>TYPE[ \t]+ {
+ // Begin a type tag
+ BEGIN(TYPETAG);
+}
+
+<COMMENT_START>[^\n] {
+ // Any character that is not a newline begins the COMMENT state where
+ // everything is ignored until the next linefeed. This works because flex
+ // will prioritize the two rules above this one since they have longer
+ // matches.
+ BEGIN(COMMENT);
+}
+
+<COMMENT>[^\n]+ {
+ // ignore
+}
+
+<HELPTAG,TYPETAG>[^ \t]+ {
+ // The next token will be the metric name
+ yylval->str = cfl_sds_create(yytext);
+ return YYSTATE == HELPTAG ? HELP : TYPE;
+}
+
+<HELPTAG,TYPETAG>[ \t]* {
+ // Every whitespace after the metric name is ignored
+ if (YYSTATE == HELPTAG) {
+ // For HELPTAG we enter the INHELPTAG start condition which we will use to
+ // read everything until the end of line into context->strbuf. We enter a
+ // separate start condition for this to handle "\\" and "\n" escapes
+ // more easily.
+ BEGIN(INHELPTAG);
+ context->strbuf = sds_alloc(256);
+ }
+ else {
+ // For TYPETAG we enter INTYPETAG start condition to check only valid
+ // metric types are accepted. This prevents us from having to do
+ // manual validation later.
+ BEGIN(INTYPETAG);
+ }
+}
+
+<INHELPTAG><<EOF>> {
+ // Handle EOF when in the INHELPTAG state by returning the buffered docstring.
+ // While this is not strictly necessary, it makes easier unit testing the
+ // lexer
+ BEGIN(INITIAL);
+ STRBUF_RET;
+ return METRIC_DOC;
+}
+
+<INHELPTAG>\\n {
+ // Process linefeed escape sequence
+ context->strbuf = cfl_sds_cat(context->strbuf, "\n", 1);
+}
+
+<INHELPTAG>\\\\ {
+ // Process backslack escape sequence
+ context->strbuf = cfl_sds_cat(context->strbuf, "\\", 1);
+}
+
+<INHELPTAG>[^\r\n\\]+ {
+ // Put everything that is not a backslash or a line feed into strbuf
+ context->strbuf = cfl_sds_cat(context->strbuf, yytext, yyleng);
+}
+
+<INTYPETAG>counter {
+ return COUNTER;
+}
+
+<INTYPETAG>gauge {
+ return GAUGE;
+}
+
+<INTYPETAG>summary {
+ return SUMMARY;
+}
+
+<INTYPETAG>untyped {
+ return UNTYPED;
+}
+
+<INTYPETAG>histogram {
+ return HISTOGRAM;
+}
+
+<INTYPETAG,INITIAL>[ \t]+ {
+ /* ignore whitespace */
+}
+
+["] {
+ BEGIN(INQUOTE);
+ context->strbuf = sds_alloc(256);
+}
+
+<INQUOTE>[\\]["] {
+ context->strbuf = cfl_sds_cat(context->strbuf, "\"", 1);
+}
+
+<INQUOTE>\\n {
+ context->strbuf = cfl_sds_cat(context->strbuf, "\n", 1);
+}
+
+<INQUOTE>\\\\ {
+ context->strbuf = cfl_sds_cat(context->strbuf, "\\", 1);
+}
+
+<INQUOTE>[^\r\n\\"]+ {
+ context->strbuf = cfl_sds_cat(context->strbuf, yytext, yyleng);
+}
+
+<INQUOTE>["] {
+ BEGIN(INITIAL);
+ STRBUF_RET;
+ return QUOTED;
+}
+
+[+-]?(?i:(INF|NAN)) {
+ strncpy(yylval->numstr, yytext, sizeof(yylval->numstr) - 1);
+ return INFNAN;
+}
+
+[a-zA-Z_][a-zA-Z_0-9]* {
+ yylval->str = cfl_sds_create(yytext);
+ return IDENTIFIER;
+}
+
+[0-9.eE+-]+ {
+ strncpy(yylval->numstr, yytext, sizeof(yylval->numstr) - 1);
+ return NUMSTR;
+}
+
+. {
+ // Catch all workaround to avoid having to define token types for every
+ // possible delimiter. We simply return the character to the parser.
+ return *yytext;
+}
+
+%%
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_decode_prometheus.y b/src/fluent-bit/lib/cmetrics/src/cmt_decode_prometheus.y
new file mode 100644
index 000000000..d4396b9e2
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_decode_prometheus.y
@@ -0,0 +1,151 @@
+%define api.pure true
+%name-prefix "cmt_decode_prometheus_"
+%define parse.error verbose
+
+%param {void *yyscanner}
+%param {struct cmt_decode_prometheus_context *context}
+
+%{
+// we inline cmt_decode_prometheus.c which contains all the actions to avoid
+// having to export a bunch of symbols that are only used by the generated
+// parser code
+#include "cmt_decode_prometheus.c"
+%}
+
+%union {
+ cfl_sds_t str;
+ char numstr[64];
+ int integer;
+}
+
+%token '=' '{' '}' ','
+%token <str> IDENTIFIER QUOTED HELP TYPE METRIC_DOC
+%token COUNTER GAUGE SUMMARY UNTYPED HISTOGRAM
+%token START_HEADER START_LABELS START_SAMPLES
+%token <numstr> NUMSTR INFNAN
+
+%type <integer> metric_type
+%type <numstr> value
+
+%destructor {
+ cfl_sds_destroy($$);
+} <str>
+
+%start start;
+
+%%
+
+start:
+ START_HEADER header
+ | START_LABELS labels
+ | START_SAMPLES samples
+ | metrics {
+ if (finish_metric(context, true, NULL)) {
+ YYABORT;
+ }
+ }
+;
+
+metrics:
+ metrics metric
+ | metric
+;
+
+metric:
+ header samples
+ | samples
+ | header
+;
+
+header:
+ help type
+ | help
+ | type help
+ | type
+;
+
+help:
+ HELP METRIC_DOC {
+ if (parse_metric_name(context, $1)) {
+ YYABORT;
+ }
+ context->metric.docstring = $2;
+ }
+;
+
+type:
+ TYPE metric_type {
+ if (parse_metric_name(context, $1)) {
+ YYABORT;
+ }
+ context->metric.type = $2;
+ }
+;
+
+metric_type:
+ COUNTER { $$ = COUNTER; }
+ | GAUGE { $$ = GAUGE; }
+ | SUMMARY { $$ = SUMMARY; }
+ | UNTYPED { $$ = UNTYPED; }
+ | HISTOGRAM { $$ = HISTOGRAM; }
+;
+
+samples:
+ samples sample
+ | sample
+;
+
+sample:
+ IDENTIFIER {
+ if (parse_metric_name(context, $1)) {
+ YYABORT;
+ }
+ $1 = NULL;
+ if (sample_start(context)) {
+ YYABORT;
+ }
+ } sample_data
+;
+
+sample_data:
+ '{' '}' values
+ | '{' labels '}' values
+ | values
+;
+
+labels:
+ labellist ','
+ | labellist
+;
+
+labellist:
+ labellist ',' label
+ | label
+;
+
+label:
+ IDENTIFIER '=' QUOTED {
+ if (parse_label(context, $1, $3)) {
+ YYABORT;
+ }
+ }
+;
+
+values:
+ value value {
+ if (parse_sample(context, $1, $2)) {
+ YYABORT;
+ }
+ }
+ | value {
+ if (parse_sample(context, $1, "")) {
+ YYABORT;
+ }
+ }
+;
+
+value:
+ NUMSTR | INFNAN
+;
+
+%%
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_encode_influx.c b/src/fluent-bit/lib/cmetrics/src/cmt_encode_influx.c
new file mode 100644
index 000000000..1dfe3797d
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_encode_influx.c
@@ -0,0 +1,374 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_metric.h>
+#include <cmetrics/cmt_map.h>
+#include <cmetrics/cmt_counter.h>
+#include <cmetrics/cmt_gauge.h>
+#include <cmetrics/cmt_untyped.h>
+#include <cmetrics/cmt_summary.h>
+#include <cmetrics/cmt_histogram.h>
+#include <cmetrics/cmt_compat.h>
+
+#include <ctype.h>
+
+/*
+ * Influx wire protocol
+ * --------------------
+ * https://docs.influxdata.com/influxdb/cloud/reference/syntax/line-protocol/
+ *
+ * Format used by influxdb when ingesting prometheus metrics
+ * ---------------------------------------------------------
+ * https://docs.influxdata.com/influxdb/v2.1/reference/prometheus-metrics/
+ */
+
+
+/* Histograms and Summaries :
+ * Just to get started I'll use version 1 which is what I think we have been
+ * following so far, if we were to use version 2 format_metric would need to be
+ * converted to call this function multiple times with a single limit on each line.
+ */
+
+static void append_histogram_metric_value(struct cmt_map *map,
+ cfl_sds_t *buf,
+ struct cmt_metric *metric)
+{
+ size_t entry_buffer_length;
+ size_t entry_buffer_index;
+ char entry_buffer[256];
+ struct cmt_histogram *histogram;
+ struct cmt_histogram_buckets *buckets;
+ size_t index;
+
+ histogram = (struct cmt_histogram *) map->parent;
+ buckets = histogram->buckets;
+
+ for (index = 0 ; index <= buckets->count ; index++) {
+ if (index < buckets->count) {
+ entry_buffer_index = snprintf(entry_buffer,
+ sizeof(entry_buffer) - 1,
+ "%g",
+ buckets->upper_bounds[index]);
+ }
+ else {
+ entry_buffer_index = snprintf(entry_buffer,
+ sizeof(entry_buffer) - 1,
+ "+Inf");
+ }
+
+ entry_buffer_length = entry_buffer_index;
+
+ entry_buffer_length += snprintf(&entry_buffer[entry_buffer_index],
+ sizeof(entry_buffer) - 1 -
+ entry_buffer_index,
+ "=%" PRIu64 ",",
+ cmt_metric_hist_get_value(metric,
+ index));
+
+ cfl_sds_cat_safe(buf, entry_buffer, entry_buffer_length);
+ }
+
+ entry_buffer_length = snprintf(entry_buffer,
+ sizeof(entry_buffer) - 1 ,
+ "sum=%.17g,",
+ cmt_metric_hist_get_sum_value(metric));
+
+ cfl_sds_cat_safe(buf, entry_buffer, entry_buffer_length);
+
+ entry_buffer_length = snprintf(entry_buffer,
+ sizeof(entry_buffer) - 1 ,
+ "count=%" PRIu64 " ",
+ cmt_metric_hist_get_count_value(metric));
+
+ cfl_sds_cat_safe(buf, entry_buffer, entry_buffer_length);
+
+ entry_buffer_length = snprintf(entry_buffer,
+ sizeof(entry_buffer) - 1 ,
+ "%" PRIu64 "\n",
+ cmt_metric_get_timestamp(metric));
+
+ cfl_sds_cat_safe(buf, entry_buffer, entry_buffer_length);
+}
+
+static void append_summary_metric_value(struct cmt_map *map,
+ cfl_sds_t *buf,
+ struct cmt_metric *metric)
+{
+ size_t entry_buffer_length;
+ char entry_buffer[256];
+ struct cmt_summary *summary;
+ size_t index;
+
+ summary = (struct cmt_summary *) map->parent;
+
+ for (index = 0 ; index < summary->quantiles_count ; index++) {
+ entry_buffer_length = snprintf(entry_buffer,
+ sizeof(entry_buffer) - 1,
+ "%g=%.17g,",
+ summary->quantiles[index],
+ cmt_summary_quantile_get_value(metric,
+ index));
+
+ cfl_sds_cat_safe(buf, entry_buffer, entry_buffer_length);
+ }
+
+ entry_buffer_length = snprintf(entry_buffer,
+ sizeof(entry_buffer) - 1 ,
+ "sum=%.17g,",
+ cmt_summary_get_sum_value(metric));
+
+ cfl_sds_cat_safe(buf, entry_buffer, entry_buffer_length);
+
+ entry_buffer_length = snprintf(entry_buffer,
+ sizeof(entry_buffer) - 1 ,
+ "count=%" PRIu64 " ",
+ cmt_summary_get_count_value(metric));
+
+ cfl_sds_cat_safe(buf, entry_buffer, entry_buffer_length);
+
+ entry_buffer_length = snprintf(entry_buffer,
+ sizeof(entry_buffer) - 1 ,
+ "%" PRIu64 "\n",
+ cmt_metric_get_timestamp(metric));
+
+ cfl_sds_cat_safe(buf, entry_buffer, entry_buffer_length);
+}
+
+static void append_metric_value(struct cmt_map *map,
+ cfl_sds_t *buf, struct cmt_metric *metric)
+{
+ int len;
+ uint64_t ts;
+ double val;
+ char tmp[256];
+ struct cmt_opts *opts;
+
+ if (map->type == CMT_HISTOGRAM) {
+ return append_histogram_metric_value(map, buf, metric);
+ }
+ else if (map->type == CMT_SUMMARY) {
+ return append_summary_metric_value(map, buf, metric);
+ }
+
+ opts = map->opts;
+
+ /* Retrieve metric value */
+ val = cmt_metric_get_value(metric);
+
+ ts = cmt_metric_get_timestamp(metric);
+ len = snprintf(tmp, sizeof(tmp) - 1, "=%.17g %" PRIu64 "\n", val, ts);
+
+ cfl_sds_cat_safe(buf, opts->name, cfl_sds_len(opts->name));
+ cfl_sds_cat_safe(buf, tmp, len);
+
+}
+
+static int line_protocol_escape(const char *str_in, int size_in,
+ char *str_out, int quote)
+{
+ int i;
+ int size_out = 0;
+ char ch;
+
+ for (i = 0; i < size_in; ++i) {
+ ch = str_in[i];
+ if (quote ? (ch == '"' || ch == '\\') : (isspace(ch) || ch == ',' || ch == '=')) {
+ str_out[size_out++] = '\\';
+ }
+ else if (ch == '\\') {
+ str_out[size_out++] = '\\';
+ }
+ str_out[size_out++] = ch;
+ }
+
+ return size_out;
+}
+
+static int append_string(cfl_sds_t *buf, cfl_sds_t str)
+{
+ int len;
+ int size;
+ char *esc_buf;
+
+ len = cfl_sds_len(str);
+ esc_buf = malloc(len * 2);
+ if (!esc_buf) {
+ cmt_errno();
+ return -1;
+ }
+
+ size = line_protocol_escape(str, len, esc_buf, 0);
+ cfl_sds_cat_safe(buf, esc_buf, size);
+
+ free(esc_buf);
+ return 0;
+}
+
+static void format_metric(struct cmt *cmt, cfl_sds_t *buf, struct cmt_map *map,
+ struct cmt_metric *metric)
+{
+ int i;
+ int n;
+ int count = 0;
+ int static_labels = 0;
+ struct cmt_map_label *label_k;
+ struct cmt_map_label *label_v;
+ struct cfl_list *head;
+ struct cmt_opts *opts;
+ struct cmt_label *slabel;
+
+ if (map->type == CMT_SUMMARY && !metric->sum_quantiles_set) {
+ return;
+ }
+
+ opts = map->opts;
+
+ /* Measurement */
+ cfl_sds_cat_safe(buf, opts->ns, cfl_sds_len(opts->ns));
+
+ if (cfl_sds_len(opts->subsystem) > 0) {
+ cfl_sds_cat_safe(buf, "_", 1);
+ cfl_sds_cat_safe(buf, opts->subsystem, cfl_sds_len(opts->subsystem));
+ }
+
+ /* Static labels (tags) */
+ static_labels = cmt_labels_count(cmt->static_labels);
+ if (static_labels > 0) {
+ cfl_sds_cat_safe(buf, ",", 1);
+ cfl_list_foreach(head, &cmt->static_labels->list) {
+ count++;
+ slabel = cfl_list_entry(head, struct cmt_label, _head);
+
+ /* key */
+ append_string(buf, slabel->key);
+
+ /* = */
+ cfl_sds_cat_safe(buf, "=", 1);
+
+ /* val */
+ append_string(buf, slabel->val);
+
+ if (count < static_labels) {
+ cfl_sds_cat_safe(buf, ",", 1);
+ }
+ }
+ }
+
+ /* Labels / Tags */
+ n = cfl_list_size(&metric->labels);
+ if (n > 0) {
+ cfl_sds_cat_safe(buf, ",", 1);
+
+ label_k = cfl_list_entry_first(&map->label_keys, struct cmt_map_label, _head);
+
+ i = 1;
+ cfl_list_foreach(head, &metric->labels) {
+ label_v = cfl_list_entry(head, struct cmt_map_label, _head);
+
+ /* key */
+ append_string(buf, label_k->name);
+ cfl_sds_cat_safe(buf, "=", 1);
+ append_string(buf, label_v->name);
+
+ if (i < n) {
+ cfl_sds_cat_safe(buf, ",", 1);
+ }
+ i++;
+
+ label_k = cfl_list_entry_next(&label_k->_head, struct cmt_map_label,
+ _head, &map->label_keys);
+ }
+ }
+
+ cfl_sds_cat_safe(buf, " ", 1);
+ append_metric_value(map, buf, metric);
+}
+
+static void format_metrics(struct cmt *cmt,
+ cfl_sds_t *buf, struct cmt_map *map)
+{
+ struct cfl_list *head;
+ struct cmt_metric *metric;
+
+ /* Simple metric, no labels */
+ if (map->metric_static_set == 1) {
+ format_metric(cmt, buf, map, &map->metric);
+ }
+
+ cfl_list_foreach(head, &map->metrics) {
+ metric = cfl_list_entry(head, struct cmt_metric, _head);
+ format_metric(cmt, buf, map, metric);
+ }
+}
+
+/* Format all the registered metrics in Prometheus Text format */
+cfl_sds_t cmt_encode_influx_create(struct cmt *cmt)
+{
+ cfl_sds_t buf;
+ struct cfl_list *head;
+ struct cmt_counter *counter;
+ struct cmt_gauge *gauge;
+ struct cmt_untyped *untyped;
+ struct cmt_summary *summary;
+ struct cmt_histogram *histogram;
+
+ /* Allocate a 1KB of buffer */
+ buf = cfl_sds_create_size(1024);
+ if (!buf) {
+ return NULL;
+ }
+
+ /* Counters */
+ cfl_list_foreach(head, &cmt->counters) {
+ counter = cfl_list_entry(head, struct cmt_counter, _head);
+ format_metrics(cmt, &buf, counter->map);
+ }
+
+ /* Gauges */
+ cfl_list_foreach(head, &cmt->gauges) {
+ gauge = cfl_list_entry(head, struct cmt_gauge, _head);
+ format_metrics(cmt, &buf, gauge->map);
+ }
+
+ /* Summaries */
+ cfl_list_foreach(head, &cmt->summaries) {
+ summary = cfl_list_entry(head, struct cmt_summary, _head);
+ format_metrics(cmt, &buf, summary->map);
+ }
+
+ /* Histograms */
+ cfl_list_foreach(head, &cmt->histograms) {
+ histogram = cfl_list_entry(head, struct cmt_histogram, _head);
+ format_metrics(cmt, &buf, histogram->map);
+ }
+
+ /* Untyped */
+ cfl_list_foreach(head, &cmt->untypeds) {
+ untyped = cfl_list_entry(head, struct cmt_untyped, _head);
+ format_metrics(cmt, &buf, untyped->map);
+ }
+
+ return buf;
+}
+
+void cmt_encode_influx_destroy(cfl_sds_t text)
+{
+ cfl_sds_destroy(text);
+}
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_encode_msgpack.c b/src/fluent-bit/lib/cmetrics/src/cmt_encode_msgpack.c
new file mode 100644
index 000000000..b31a78e3b
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_encode_msgpack.c
@@ -0,0 +1,552 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_metric.h>
+#include <cmetrics/cmt_map.h>
+#include <cmetrics/cmt_histogram.h>
+#include <cmetrics/cmt_summary.h>
+#include <cmetrics/cmt_counter.h>
+#include <cmetrics/cmt_gauge.h>
+#include <cmetrics/cmt_untyped.h>
+#include <cmetrics/cmt_compat.h>
+#include <cmetrics/cmt_encode_msgpack.h>
+#include <cmetrics/cmt_variant_utils.h>
+
+struct cmt_map_label *create_label(char *label_text)
+{
+ struct cmt_map_label *new_label;
+
+ new_label = calloc(1, sizeof(struct cmt_map_label));
+
+ if (NULL != new_label) {
+ new_label->name = cfl_sds_create(label_text);
+
+ if (NULL == new_label->name) {
+ free(new_label);
+
+ new_label = NULL;
+ }
+ }
+
+ return new_label;
+}
+
+static void pack_header(mpack_writer_t *writer, struct cmt *cmt, struct cmt_map *map)
+{
+ struct cmt_opts *opts;
+ struct cfl_list *head;
+ struct cmt_map_label *label;
+ size_t index;
+ struct cmt_summary *summary = NULL;
+ struct cmt_histogram *histogram = NULL;
+ struct cmt_counter *counter = NULL;
+ size_t meta_field_count;
+
+ opts = map->opts;
+ meta_field_count = 4;
+
+ if (map->type == CMT_HISTOGRAM) {
+ histogram = (struct cmt_histogram *) map->parent;
+
+ meta_field_count++;
+ }
+ else if (map->type == CMT_SUMMARY) {
+ summary = (struct cmt_summary *) map->parent;
+
+ meta_field_count++;
+ }
+ else if (map->type == CMT_COUNTER){
+ counter = (struct cmt_counter *) map->parent;
+
+ meta_field_count++;
+ }
+
+ /* 'meta' */
+ mpack_write_cstr(writer, "meta");
+ mpack_start_map(writer, meta_field_count);
+
+ /* 'ver' */
+ mpack_write_cstr(writer, "ver");
+ mpack_write_uint(writer, MSGPACK_ENCODER_VERSION);
+
+ /* 'type' */
+ mpack_write_cstr(writer, "type");
+ mpack_write_uint(writer, map->type);
+
+ /* 'opts' */
+ mpack_write_cstr(writer, "opts");
+ mpack_start_map(writer, 4);
+
+ /* opts['ns'] */
+ mpack_write_cstr(writer, "ns");
+ mpack_write_cstr(writer, opts->ns);
+
+ /* opts['subsystem'] */
+ mpack_write_cstr(writer, "ss");
+ mpack_write_cstr(writer, opts->subsystem);
+
+ /* opts['name'] */
+ mpack_write_cstr(writer, "name");
+ mpack_write_cstr(writer, opts->name);
+
+ /* opts['description'] */
+ mpack_write_cstr(writer, "desc");
+ mpack_write_cstr(writer, opts->description);
+
+ mpack_finish_map(writer); /* 'opts' */
+
+ /* 'labels' (label keys) */
+ mpack_write_cstr(writer, "labels");
+ mpack_start_array(writer, map->label_count);
+ cfl_list_foreach(head, &map->label_keys) {
+ label = cfl_list_entry(head, struct cmt_map_label, _head);
+
+ mpack_write_cstr(writer, label->name);
+ }
+ mpack_finish_array(writer);
+
+ if (map->type == CMT_HISTOGRAM) {
+ /* 'buckets' (histogram buckets) */
+ mpack_write_cstr(writer, "buckets");
+
+ if (histogram->buckets != NULL) {
+ mpack_start_array(writer, histogram->buckets->count);
+
+ for (index = 0 ;
+ index < histogram->buckets->count ;
+ index++) {
+ mpack_write_double(writer, histogram->buckets->upper_bounds[index]);
+ }
+ }
+ else {
+ mpack_start_array(writer, 0);
+ }
+
+ mpack_finish_array(writer);
+ }
+ else if (map->type == CMT_SUMMARY) {
+ /* 'quantiles' (summary quantiles) */
+ mpack_write_cstr(writer, "quantiles");
+
+ mpack_start_array(writer, summary->quantiles_count);
+
+ for (index = 0 ;
+ index < summary->quantiles_count ;
+ index++) {
+ mpack_write_double(writer, summary->quantiles[index]);
+ }
+
+ mpack_finish_array(writer);
+ }
+ else if (map->type == CMT_COUNTER){
+ /* aggregation_type */
+ mpack_write_cstr(writer, "aggregation_type");
+ mpack_write_int(writer, counter->aggregation_type);
+ }
+
+ mpack_finish_map(writer); /* 'meta' */
+}
+
+static int pack_metric(mpack_writer_t *writer, struct cmt_map *map, struct cmt_metric *metric)
+{
+ int c_labels;
+ int s;
+ double val;
+ size_t index;
+ struct cfl_list *head;
+ struct cmt_map_label *label;
+ struct cmt_summary *summary;
+ struct cmt_histogram *histogram;
+
+ c_labels = cfl_list_size(&metric->labels);
+
+ s = 3;
+
+ if (c_labels > 0) {
+ s++;
+ }
+
+ mpack_start_map(writer, s);
+
+ mpack_write_cstr(writer, "ts");
+ mpack_write_uint(writer, metric->timestamp);
+
+ if (map->type == CMT_HISTOGRAM) {
+ histogram = (struct cmt_histogram *) map->parent;
+
+ mpack_write_cstr(writer, "histogram");
+ mpack_start_map(writer, 3);
+
+ mpack_write_cstr(writer, "buckets");
+ mpack_start_array(writer, histogram->buckets->count + 1);
+ for (index = 0 ;
+ index <= histogram->buckets->count ;
+ index++) {
+ mpack_write_uint(writer, cmt_metric_hist_get_value(metric, index));
+ }
+
+ mpack_finish_array(writer);
+
+ mpack_write_cstr(writer, "sum");
+ mpack_write_double(writer, cmt_metric_hist_get_sum_value(metric));
+
+ mpack_write_cstr(writer, "count");
+ mpack_write_uint(writer, cmt_metric_hist_get_count_value(metric));
+
+ mpack_finish_map(writer); /* 'histogram' */
+ }
+ else if (map->type == CMT_SUMMARY) {
+ summary = (struct cmt_summary *) map->parent;
+
+ mpack_write_cstr(writer, "summary");
+ mpack_start_map(writer, 4);
+
+ mpack_write_cstr(writer, "quantiles_set");
+ mpack_write_uint(writer, metric->sum_quantiles_set);
+
+ mpack_write_cstr(writer, "quantiles");
+ mpack_start_array(writer, summary->quantiles_count);
+
+ for (index = 0 ; index < summary->quantiles_count ; index++) {
+ mpack_write_uint(writer, metric->sum_quantiles[index]);
+ }
+
+ mpack_finish_array(writer);
+
+ mpack_write_cstr(writer, "count");
+ mpack_write_uint(writer, cmt_summary_get_count_value(metric));
+
+ mpack_write_cstr(writer, "sum");
+ mpack_write_uint(writer, metric->sum_sum);
+
+ mpack_finish_map(writer); /* 'summary' */
+ }
+ else {
+ mpack_write_cstr(writer, "value");
+ val = cmt_metric_get_value(metric);
+ mpack_write_double(writer, val);
+ }
+
+ s = cfl_list_size(&metric->labels);
+ if (s > 0) {
+ mpack_write_cstr(writer, "labels");
+ mpack_start_array(writer, c_labels);
+
+ cfl_list_foreach(head, &metric->labels) {
+ label = cfl_list_entry(head, struct cmt_map_label, _head);
+
+ if (label->name != NULL) {
+ mpack_write_cstr(writer, label->name);
+ }
+ else {
+ mpack_write_nil(writer);
+ }
+ }
+
+ mpack_finish_array(writer);
+ }
+
+ mpack_write_cstr(writer, "hash");
+ mpack_write_uint(writer, metric->hash);
+
+ mpack_finish_map(writer);
+
+ return 0;
+}
+
+static int pack_basic_type(mpack_writer_t *writer, struct cmt *cmt, struct cmt_map *map)
+{
+ int values_size = 0;
+ struct cfl_list *head;
+ struct cmt_metric *metric;
+
+ /* metric scope dictionary that holds meta and values*/
+ mpack_start_map(writer, 2);
+
+ pack_header(writer, cmt, map);
+
+ if (map->metric_static_set) {
+ values_size++;
+ }
+ values_size += cfl_list_size(&map->metrics);
+
+ mpack_write_cstr(writer, "values");
+ mpack_start_array(writer, values_size);
+
+ if (map->metric_static_set) {
+ pack_metric(writer, map, &map->metric);
+ }
+
+ cfl_list_foreach(head, &map->metrics) {
+ metric = cfl_list_entry(head, struct cmt_metric, _head);
+ pack_metric(writer, map, metric);
+ }
+ mpack_finish_array(writer);
+
+ mpack_finish_map(writer);
+
+ return 0;
+}
+
+static void pack_static_labels(mpack_writer_t *writer, struct cmt *cmt)
+{
+ struct cmt_label *static_label;
+ struct cfl_list *head;
+
+ /* 'static_labels' (static labels) */
+ mpack_write_cstr(writer, "static_labels");
+
+ mpack_start_array(writer, cfl_list_size(&cmt->static_labels->list));
+
+ cfl_list_foreach(head, &cmt->static_labels->list) {
+ static_label = cfl_list_entry(head, struct cmt_label, _head);
+
+ mpack_start_array(writer, 2);
+
+ mpack_write_cstr(writer, static_label->key);
+ mpack_write_cstr(writer, static_label->val);
+
+ mpack_finish_array(writer);
+ }
+
+ mpack_finish_array(writer);
+}
+
+static int pack_static_processing_section(mpack_writer_t *writer, struct cmt *cmt)
+{
+ mpack_write_cstr(writer, "processing");
+
+ mpack_start_map(writer, 1);
+
+ pack_static_labels(writer, cmt);
+
+ mpack_finish_map(writer); /* 'processing' */
+
+ return 0;
+}
+
+static int pack_context_header(mpack_writer_t *writer, struct cmt *cmt)
+{
+ int result;
+
+ mpack_write_cstr(writer, "meta");
+ mpack_start_map(writer, 3);
+
+ mpack_write_cstr(writer, "cmetrics");
+ result = pack_cfl_variant_kvlist(writer, cmt->internal_metadata);
+
+ if (result != 0) {
+ return -1;
+ }
+
+ mpack_write_cstr(writer, "external");
+ result = pack_cfl_variant_kvlist(writer, cmt->external_metadata);
+
+ if (result != 0) {
+ return -2;
+ }
+
+ pack_static_processing_section(writer, cmt);
+
+ mpack_finish_map(writer); /* 'context_header' */
+
+ return 0;
+}
+
+static int pack_context_metrics(mpack_writer_t *writer, struct cmt *cmt)
+{
+ size_t metric_count;
+ struct cmt_histogram *histogram;
+ struct cmt_summary *summary;
+ struct cmt_untyped *untyped;
+ struct cmt_counter *counter;
+ struct cmt_gauge *gauge;
+ struct cfl_list *head;
+
+ metric_count = 0;
+ metric_count += cfl_list_size(&cmt->counters);
+ metric_count += cfl_list_size(&cmt->gauges);
+ metric_count += cfl_list_size(&cmt->untypeds);
+ metric_count += cfl_list_size(&cmt->summaries);
+ metric_count += cfl_list_size(&cmt->histograms);
+
+ mpack_write_cstr(writer, "metrics");
+ mpack_start_array(writer, metric_count);
+
+ /* Counters */
+ cfl_list_foreach(head, &cmt->counters) {
+ counter = cfl_list_entry(head, struct cmt_counter, _head);
+ pack_basic_type(writer, cmt, counter->map);
+ }
+
+ /* Gauges */
+ cfl_list_foreach(head, &cmt->gauges) {
+ gauge = cfl_list_entry(head, struct cmt_gauge, _head);
+ pack_basic_type(writer, cmt, gauge->map);
+ }
+
+ /* Untyped */
+ cfl_list_foreach(head, &cmt->untypeds) {
+ untyped = cfl_list_entry(head, struct cmt_untyped, _head);
+ pack_basic_type(writer, cmt, untyped->map);
+ }
+
+ /* Summary */
+ cfl_list_foreach(head, &cmt->summaries) {
+ summary = cfl_list_entry(head, struct cmt_summary, _head);
+ pack_basic_type(writer, cmt, summary->map);
+ }
+
+ /* Histogram */
+ cfl_list_foreach(head, &cmt->histograms) {
+ histogram = cfl_list_entry(head, struct cmt_histogram, _head);
+ pack_basic_type(writer, cmt, histogram->map);
+ }
+
+ mpack_finish_array(writer);
+
+ return 0;
+}
+
+static int pack_context(mpack_writer_t *writer, struct cmt *cmt)
+{
+ int result;
+
+ mpack_start_map(writer, 2);
+
+ result = pack_context_header(writer, cmt);
+
+ if (result != 0) {
+ return -1;
+ }
+
+ result = pack_context_metrics(writer, cmt);
+
+ if (result != 0) {
+ return -2;
+ }
+
+ mpack_finish_map(writer); /* outermost context scope */
+
+ return 0;
+}
+
+/* Takes a cmetrics context and serialize it using msgpack */
+int cmt_encode_msgpack_create(struct cmt *cmt, char **out_buf, size_t *out_size)
+{
+ char *data;
+ size_t size;
+ mpack_writer_t writer;
+ int result;
+
+ /*
+ * CMetrics data schema
+
+ {
+ 'meta' => {
+ 'cmetrics' => {
+ 'producer': STRING
+ },
+ 'external' => { ... },
+ 'processing' => {
+ 'static_labels' => [
+ [STRING, STRING], ...
+ ]
+ }
+ },
+ 'metrics' => [
+ {
+ 'meta' => {
+ 'ver' => INTEGER
+ 'type' => INTEGER
+ '0' = counter
+ '1' = gauge
+ '2' = histogram (WIP)
+ 'opts' => {
+ 'ns' => ns
+ 'subsystem' => subsystem
+ 'name' => name
+ 'description' => description
+ },
+ 'label_keys' => [STRING, ...],
+ 'buckets' => [n, ...]
+ },
+ 'values' => [
+ {
+ 'ts' : nanosec timestamp,
+ 'value': float64 value,
+ 'label_values': [STRING, ...],
+ 'histogram':{
+ 'sum': float64,
+ 'count': uint64,
+ 'buckets': [n, ...]
+ },
+ 'summary': {
+ 'sum': float64,
+ 'count': uint64,
+ 'quantiles': [n, ...],
+ 'quantiles_set': uint64
+ },
+ 'hash': uint64 value
+ }
+ ]
+ }, ...
+ ]
+ }
+ *
+ *
+ * The following fields are metric type specific and are only
+ * included for histograms :
+ * meta->buckets
+ * values[n]->buckets
+ * values[n]->count
+ * values[n]->sum
+ */
+
+ if (cmt == NULL) {
+ return -1;
+ }
+
+ mpack_writer_init_growable(&writer, &data, &size);
+
+ result = pack_context(&writer, cmt);
+
+ if (mpack_writer_destroy(&writer) != mpack_ok) {
+ fprintf(stderr, "An error occurred encoding the data!\n");
+
+ return -1;
+ }
+
+ if (result != 0) {
+ return result;
+ }
+
+ *out_buf = data;
+ *out_size = size;
+
+ return 0;
+}
+
+void cmt_encode_msgpack_destroy(char *out_buf)
+{
+ if (NULL != out_buf) {
+ MPACK_FREE(out_buf);
+ }
+}
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_encode_opentelemetry.c b/src/fluent-bit/lib/cmetrics/src/cmt_encode_opentelemetry.c
new file mode 100644
index 000000000..8c9341863
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_encode_opentelemetry.c
@@ -0,0 +1,2592 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_metric.h>
+#include <cmetrics/cmt_map.h>
+#include <cmetrics/cmt_gauge.h>
+#include <cmetrics/cmt_counter.h>
+#include <cmetrics/cmt_untyped.h>
+#include <cmetrics/cmt_summary.h>
+#include <cmetrics/cmt_histogram.h>
+#include <cmetrics/cmt_encode_opentelemetry.h>
+
+static Opentelemetry__Proto__Metrics__V1__ScopeMetrics **
+ initialize_scope_metrics_list(
+ size_t element_count);
+
+static void destroy_scope_metric_list(
+ Opentelemetry__Proto__Metrics__V1__ScopeMetrics **metric_list);
+
+struct cfl_kvlist *fetch_metadata_kvlist_key(
+ struct cfl_kvlist *kvlist, char *key)
+{
+ struct cfl_variant *entry_variant;
+ struct cfl_kvlist *entry_kvlist;
+
+ if (kvlist == NULL) {
+ return NULL;
+ }
+
+ entry_variant = cfl_kvlist_fetch(kvlist, key);
+
+ if (entry_variant != NULL) {
+ entry_kvlist = entry_variant->data.as_kvlist;
+ }
+ else {
+ entry_kvlist = NULL;
+ }
+
+ return entry_kvlist;
+}
+
+
+static int is_string_releaseable(char *address);
+
+static int is_metric_empty(struct cmt_map *map);
+
+static size_t get_metric_count(struct cmt *cmt);
+
+static Opentelemetry__Proto__Metrics__V1__SummaryDataPoint__ValueAtQuantile *
+ initialize_summary_value_at_quantile(
+ double quantile, double value);
+
+static void destroy_summary_value_at_quantile(
+ Opentelemetry__Proto__Metrics__V1__SummaryDataPoint__ValueAtQuantile *value);
+
+static Opentelemetry__Proto__Metrics__V1__SummaryDataPoint__ValueAtQuantile **
+ initialize_summary_value_at_quantile_list(
+ size_t element_count);
+
+static void destroy_summary_value_at_quantile_list(
+ Opentelemetry__Proto__Metrics__V1__SummaryDataPoint__ValueAtQuantile **list);
+
+
+static void destroy_metrics_data(
+ Opentelemetry__Proto__Metrics__V1__MetricsData *metrics_data);
+
+static Opentelemetry__Proto__Metrics__V1__MetricsData *
+ initialize_metrics_data(size_t resource_metrics_count);
+
+static void destroy_resource(
+ Opentelemetry__Proto__Resource__V1__Resource *resource);
+
+static void destroy_resource_metrics(
+ Opentelemetry__Proto__Metrics__V1__ResourceMetrics *resource_metrics);
+
+static Opentelemetry__Proto__Metrics__V1__ResourceMetrics *
+ initialize_resource_metrics(
+ struct cfl_kvlist *resource_metrics_root,
+ Opentelemetry__Proto__Resource__V1__Resource *resource,
+ size_t instrumentation_library_metrics_element_count);
+
+static void destroy_resource_metrics_list(
+ Opentelemetry__Proto__Metrics__V1__ResourceMetrics **resource_metrics_list);
+
+static Opentelemetry__Proto__Metrics__V1__ResourceMetrics **
+ initialize_resource_metrics_list(
+ size_t element_count);
+
+static Opentelemetry__Proto__Common__V1__InstrumentationScope *
+ initialize_instrumentation_scope(
+ struct cfl_kvlist *scope_root,
+ int *error_detection_flag);
+
+static void destroy_scope_metrics(
+ Opentelemetry__Proto__Metrics__V1__ScopeMetrics *metric);
+
+static Opentelemetry__Proto__Metrics__V1__ScopeMetrics *
+ initialize_scope_metrics(
+ struct cfl_kvlist *scope_metrics_root,
+ size_t metric_element_count);
+
+static int append_metric_to_scope_metrics(
+ Opentelemetry__Proto__Metrics__V1__ScopeMetrics *instrumentation_scope_metrics,
+ Opentelemetry__Proto__Metrics__V1__Metric *metric,
+ size_t metric_slot_hint);
+
+static void destroy_scope_metric_list(
+ Opentelemetry__Proto__Metrics__V1__ScopeMetrics **metric_list);
+
+static void destroy_attribute(
+ Opentelemetry__Proto__Common__V1__KeyValue *attribute);
+
+static Opentelemetry__Proto__Common__V1__KeyValue *
+ initialize_string_attribute(char *key, char *value);
+
+static void destroy_attribute_list(
+ Opentelemetry__Proto__Common__V1__KeyValue **attribute_list);
+
+static Opentelemetry__Proto__Common__V1__KeyValue **
+ initialize_attribute_list(
+ size_t element_count);
+
+static void destroy_numerical_data_point(
+ Opentelemetry__Proto__Metrics__V1__NumberDataPoint *data_point);
+
+static void destroy_summary_data_point(
+ Opentelemetry__Proto__Metrics__V1__SummaryDataPoint *data_point);
+
+static void destroy_histogram_data_point(
+ Opentelemetry__Proto__Metrics__V1__HistogramDataPoint *data_point);
+
+static void destroy_data_point(
+ void *data_point,
+ int data_point_type);
+
+static void destroy_numerical_data_point_list(
+ Opentelemetry__Proto__Metrics__V1__NumberDataPoint **data_point_list);
+
+static void destroy_summary_data_point_list(
+ Opentelemetry__Proto__Metrics__V1__SummaryDataPoint **data_point_list);
+
+static void destroy_histogram_data_point_list(
+ Opentelemetry__Proto__Metrics__V1__HistogramDataPoint **data_point_list);
+
+static Opentelemetry__Proto__Metrics__V1__NumberDataPoint *
+ initialize_numerical_data_point(
+ uint64_t start_time,
+ uint64_t timestamp,
+ double value,
+ Opentelemetry__Proto__Common__V1__KeyValue **attribute_list,
+ size_t attribute_count);
+
+static Opentelemetry__Proto__Metrics__V1__SummaryDataPoint *
+ initialize_summary_data_point(
+ uint64_t start_time,
+ uint64_t timestamp,
+ uint64_t count,
+ double sum,
+ size_t quantile_count,
+ double *quantile_list,
+ size_t value_count,
+ uint64_t *value_list,
+ Opentelemetry__Proto__Common__V1__KeyValue **attribute_list,
+ size_t attribute_count);
+
+static Opentelemetry__Proto__Metrics__V1__HistogramDataPoint *
+ initialize_histogram_data_point(
+ uint64_t start_time,
+ uint64_t timestamp,
+ uint64_t count,
+ double sum,
+ size_t bucket_count,
+ uint64_t *bucket_list,
+ size_t boundary_count,
+ double *boundary_list,
+ Opentelemetry__Proto__Common__V1__KeyValue **attribute_list,
+ size_t attribute_count);
+
+static int append_attribute_to_numerical_data_point(
+ Opentelemetry__Proto__Metrics__V1__NumberDataPoint *data_point,
+ Opentelemetry__Proto__Common__V1__KeyValue *attribute,
+ size_t attribute_slot_hint);
+
+static int append_attribute_to_summary_data_point(
+ Opentelemetry__Proto__Metrics__V1__SummaryDataPoint *data_point,
+ Opentelemetry__Proto__Common__V1__KeyValue *attribute,
+ size_t attribute_slot_hint);
+
+static int append_attribute_to_histogram_data_point(
+ Opentelemetry__Proto__Metrics__V1__HistogramDataPoint *data_point,
+ Opentelemetry__Proto__Common__V1__KeyValue *attribute,
+ size_t attribute_slot_hint);
+
+static int append_attribute_to_data_point(
+ void *data_point,
+ int data_point_type,
+ Opentelemetry__Proto__Common__V1__KeyValue *attribute,
+ size_t attribute_slot_hint);
+
+static int append_attribute_to_data_point(
+ void *data_point,
+ int data_point_type,
+ Opentelemetry__Proto__Common__V1__KeyValue *attribute,
+ size_t attribute_slot_hint);
+
+static Opentelemetry__Proto__Metrics__V1__NumberDataPoint **
+ initialize_numerical_data_point_list(
+ size_t element_count);
+
+static Opentelemetry__Proto__Metrics__V1__HistogramDataPoint **
+ initialize_histogram_data_point_list(
+ size_t element_count);
+
+static void destroy_metric(
+ Opentelemetry__Proto__Metrics__V1__Metric *metric);
+
+static Opentelemetry__Proto__Metrics__V1__Metric *
+ initialize_metric(int type,
+ char *name,
+ char *description,
+ char *unit,
+ int monotonism_flag,
+ int aggregation_temporality_type,
+ size_t data_point_count);
+
+static int append_data_point_to_metric(
+ Opentelemetry__Proto__Metrics__V1__Metric *metric,
+ void *data_point,
+ size_t data_point_slot_hint);
+
+static void destroy_metric_list(
+ Opentelemetry__Proto__Metrics__V1__Metric **metric_list);
+
+static Opentelemetry__Proto__Metrics__V1__Metric **
+ initialize_metric_list(
+ size_t element_count);
+
+void cmt_encode_opentelemetry_destroy(cfl_sds_t text);
+
+static void destroy_opentelemetry_context(
+ struct cmt_opentelemetry_context *context);
+
+static struct cmt_opentelemetry_context *initialize_opentelemetry_context(
+ struct cmt *cmt);
+
+static inline Opentelemetry__Proto__Common__V1__AnyValue *cfl_variant_to_otlp_any_value(struct cfl_variant *value);
+static inline Opentelemetry__Proto__Common__V1__KeyValue *cfl_variant_kvpair_to_otlp_kvpair(struct cfl_kvpair *input_pair);
+static inline Opentelemetry__Proto__Common__V1__AnyValue *cfl_variant_kvlist_to_otlp_any_value(struct cfl_variant *value);
+
+static inline void otlp_any_value_destroy(Opentelemetry__Proto__Common__V1__AnyValue *value);
+static inline void otlp_kvpair_destroy(Opentelemetry__Proto__Common__V1__KeyValue *kvpair);
+static inline void otlp_kvlist_destroy(Opentelemetry__Proto__Common__V1__KeyValueList *kvlist);
+static inline void otlp_array_destroy(Opentelemetry__Proto__Common__V1__ArrayValue *array);
+
+static inline void otlp_kvpair_list_destroy(Opentelemetry__Proto__Common__V1__KeyValue **pair_list, size_t entry_count);
+
+static inline void otlp_kvpair_destroy(Opentelemetry__Proto__Common__V1__KeyValue *kvpair)
+{
+ if (kvpair != NULL) {
+ if (kvpair->key != NULL) {
+ free(kvpair->key);
+ }
+
+ if (kvpair->value != NULL) {
+ otlp_any_value_destroy(kvpair->value);
+ }
+
+ free(kvpair);
+ }
+}
+
+static inline void otlp_kvlist_destroy(Opentelemetry__Proto__Common__V1__KeyValueList *kvlist)
+{
+ size_t index;
+
+ if (kvlist != NULL) {
+ if (kvlist->values != NULL) {
+ for (index = 0 ; index < kvlist->n_values ; index++) {
+ otlp_kvpair_destroy(kvlist->values[index]);
+ }
+
+ free(kvlist->values);
+ }
+
+ free(kvlist);
+ }
+}
+
+static inline void otlp_array_destroy(Opentelemetry__Proto__Common__V1__ArrayValue *array)
+{
+ size_t index;
+
+ if (array != NULL) {
+ if (array->values != NULL) {
+ for (index = 0 ; index < array->n_values ; index++) {
+ otlp_any_value_destroy(array->values[index]);
+ }
+
+ free(array->values);
+ }
+
+ free(array);
+ }
+}
+
+static inline void otlp_any_value_destroy(Opentelemetry__Proto__Common__V1__AnyValue *value)
+{
+ if (value != NULL) {
+ if (value->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE) {
+ if (value->string_value != NULL) {
+ free(value->string_value);
+ }
+ }
+ else if (value->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_ARRAY_VALUE) {
+ if (value->array_value != NULL) {
+ otlp_array_destroy(value->array_value);
+ }
+ }
+ else if (value->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_KVLIST_VALUE) {
+ if (value->kvlist_value != NULL) {
+ otlp_kvlist_destroy(value->kvlist_value);
+ }
+ }
+ else if (value->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_BYTES_VALUE) {
+ if (value->bytes_value.data != NULL) {
+ free(value->bytes_value.data);
+ }
+ }
+
+ free(value);
+ }
+}
+
+static inline Opentelemetry__Proto__Common__V1__KeyValue **otlp_kvpair_list_initialize(size_t entry_count)
+{
+ Opentelemetry__Proto__Common__V1__KeyValue **result;
+
+ result = \
+ calloc(entry_count, sizeof(Opentelemetry__Proto__Common__V1__KeyValue *));
+
+ return result;
+}
+
+
+static Opentelemetry__Proto__Common__V1__ArrayValue *otlp_array_value_initialize(size_t entry_count)
+{
+ Opentelemetry__Proto__Common__V1__ArrayValue *value;
+
+ value = calloc(1, sizeof(Opentelemetry__Proto__Common__V1__ArrayValue));
+
+ if (value != NULL) {
+ opentelemetry__proto__common__v1__array_value__init(value);
+
+ if (entry_count > 0) {
+ value->values = \
+ calloc(entry_count,
+ sizeof(Opentelemetry__Proto__Common__V1__AnyValue *));
+
+ if (value->values == NULL) {
+ free(value);
+
+ value = NULL;
+ }
+ else {
+ value->n_values = entry_count;
+ }
+ }
+ }
+
+ return value;
+}
+
+static Opentelemetry__Proto__Common__V1__KeyValue *otlp_kvpair_value_initialize()
+{
+ Opentelemetry__Proto__Common__V1__KeyValue *value;
+
+ value = calloc(1, sizeof(Opentelemetry__Proto__Common__V1__KeyValue));
+
+ if (value != NULL) {
+ opentelemetry__proto__common__v1__key_value__init(value);
+ }
+
+ return value;
+}
+
+static Opentelemetry__Proto__Common__V1__KeyValueList *otlp_kvlist_value_initialize(size_t entry_count)
+{
+ Opentelemetry__Proto__Common__V1__KeyValueList *value;
+
+ value = calloc(1, sizeof(Opentelemetry__Proto__Common__V1__KeyValueList));
+
+ if (value != NULL) {
+ opentelemetry__proto__common__v1__key_value_list__init(value);
+
+ if (entry_count > 0) {
+ value->values = \
+ calloc(entry_count,
+ sizeof(Opentelemetry__Proto__Common__V1__KeyValue *));
+
+ if (value->values == NULL) {
+ free(value);
+
+ value = NULL;
+ }
+ else {
+ value->n_values = entry_count;
+ }
+ }
+ }
+
+ return value;
+}
+
+static Opentelemetry__Proto__Common__V1__AnyValue *otlp_any_value_initialize(int data_type, size_t entry_count)
+{
+ Opentelemetry__Proto__Common__V1__AnyValue *value;
+
+ value = calloc(1, sizeof(Opentelemetry__Proto__Common__V1__AnyValue));
+
+ if (value == NULL) {
+ return NULL;
+ }
+
+ opentelemetry__proto__common__v1__any_value__init(value);
+
+ if (data_type == CFL_VARIANT_STRING) {
+ value->value_case = OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE;
+ }
+ else if (data_type == CFL_VARIANT_BOOL) {
+ value->value_case = OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_BOOL_VALUE;
+ }
+ else if (data_type == CFL_VARIANT_INT) {
+ value->value_case = OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_INT_VALUE;
+ }
+ else if (data_type == CFL_VARIANT_DOUBLE) {
+ value->value_case = OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_DOUBLE_VALUE;
+ }
+ else if (data_type == CFL_VARIANT_ARRAY) {
+ value->value_case = OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_ARRAY_VALUE;
+
+ value->array_value = otlp_array_value_initialize(entry_count);
+
+ if (value->array_value == NULL) {
+ free(value);
+
+ value = NULL;
+ }
+ }
+ else if (data_type == CFL_VARIANT_KVLIST) {
+ value->value_case = OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_KVLIST_VALUE;
+
+ value->kvlist_value = otlp_kvlist_value_initialize(entry_count);
+
+ if (value->kvlist_value == NULL) {
+ free(value);
+
+ value = NULL;
+ }
+ }
+ else if (data_type == CFL_VARIANT_BYTES) {
+ value->value_case = OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_BYTES_VALUE;
+ }
+ else if (data_type == CFL_VARIANT_REFERENCE) {
+ value->value_case = OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE;
+ }
+ else {
+ free(value);
+
+ value = NULL;
+ }
+
+ return value;
+}
+
+static inline Opentelemetry__Proto__Common__V1__KeyValue *cfl_variant_kvpair_to_otlp_kvpair(struct cfl_kvpair *input_pair)
+{
+ Opentelemetry__Proto__Common__V1__KeyValue *pair;
+
+ pair = otlp_kvpair_value_initialize();
+
+ if (pair != NULL) {
+ pair->key = strdup(input_pair->key);
+
+ if (pair->key != NULL) {
+ pair->value = cfl_variant_to_otlp_any_value(input_pair->val);
+
+ if (pair->value == NULL) {
+ free(pair->key);
+
+ pair->key = NULL;
+ }
+ }
+
+ if (pair->key == NULL) {
+ free(pair);
+
+ pair = NULL;
+ }
+ }
+
+ return pair;
+}
+
+static inline void otlp_kvpair_list_destroy(Opentelemetry__Proto__Common__V1__KeyValue **pair_list, size_t entry_count)
+{
+ size_t index;
+
+ if (pair_list != NULL) {
+ for (index = 0 ; index < entry_count ; index++) {
+ otlp_kvpair_destroy(pair_list[index]);
+ }
+
+ free(pair_list);
+ }
+}
+
+static inline Opentelemetry__Proto__Common__V1__KeyValue **cfl_kvlist_to_otlp_kvpair_list(struct cfl_kvlist *kvlist)
+{
+ size_t entry_count;
+ Opentelemetry__Proto__Common__V1__KeyValue *keyvalue;
+ struct cfl_list *iterator;
+ Opentelemetry__Proto__Common__V1__KeyValue **result;
+ struct cfl_kvpair *kvpair;
+ size_t index;
+
+ entry_count = cfl_kvlist_count(kvlist);
+
+ result = otlp_kvpair_list_initialize(entry_count + 1);
+
+ if (result != NULL) {
+ index = 0;
+
+ cfl_list_foreach(iterator, &kvlist->list) {
+ kvpair = cfl_list_entry(iterator, struct cfl_kvpair, _head);
+
+ keyvalue = cfl_variant_kvpair_to_otlp_kvpair(kvpair);
+
+ if (keyvalue == NULL) {
+ otlp_kvpair_list_destroy(result, entry_count);
+
+ result = NULL;
+
+ break;
+ }
+
+ result[index++] = keyvalue;
+ }
+ }
+
+ return result;
+}
+
+
+static inline Opentelemetry__Proto__Common__V1__AnyValue *cfl_variant_kvlist_to_otlp_any_value(struct cfl_variant *value)
+{
+ size_t entry_count;
+ Opentelemetry__Proto__Common__V1__KeyValue *keyvalue;
+ struct cfl_list *iterator;
+ Opentelemetry__Proto__Common__V1__AnyValue *result;
+ struct cfl_kvpair *kvpair;
+ struct cfl_kvlist *kvlist;
+ size_t index;
+
+
+ kvlist = value->data.as_kvlist;
+
+ entry_count = cfl_kvlist_count(kvlist);
+
+ result = otlp_any_value_initialize(CFL_VARIANT_KVLIST, entry_count);
+
+ if (result != NULL) {
+ index = 0;
+
+ cfl_list_foreach(iterator, &kvlist->list) {
+ kvpair = cfl_list_entry(iterator, struct cfl_kvpair, _head);
+
+ keyvalue = cfl_variant_kvpair_to_otlp_kvpair(kvpair);
+
+ if (keyvalue == NULL) {
+ otlp_any_value_destroy(result);
+
+ result = NULL;
+
+ break;
+ }
+
+ result->kvlist_value->values[index++] = keyvalue;
+ }
+ }
+
+ return result;
+}
+
+
+static inline Opentelemetry__Proto__Common__V1__AnyValue *cfl_variant_array_to_otlp_any_value(struct cfl_variant *value)
+{
+ size_t entry_count;
+ Opentelemetry__Proto__Common__V1__AnyValue *entry_value;
+ Opentelemetry__Proto__Common__V1__AnyValue *result;
+ struct cfl_array *array;
+ size_t index;
+
+ array = value->data.as_array;
+
+ entry_count = array->entry_count;
+
+ result = otlp_any_value_initialize(CFL_VARIANT_ARRAY, entry_count);
+
+ if (result != NULL) {
+ index = 0;
+
+ for (index = 0 ; index < entry_count ; index++) {
+ entry_value = cfl_variant_to_otlp_any_value(cfl_array_fetch_by_index(array, index));
+
+ if (entry_value == NULL) {
+ otlp_any_value_destroy(result);
+
+ result = NULL;
+
+ break;
+ }
+
+ result->array_value->values[index] = entry_value;
+ }
+ }
+
+ return result;
+}
+
+static inline Opentelemetry__Proto__Common__V1__AnyValue *cfl_variant_string_to_otlp_any_value(struct cfl_variant *value)
+{
+ Opentelemetry__Proto__Common__V1__AnyValue *result;
+
+ result = otlp_any_value_initialize(CFL_VARIANT_STRING, 0);
+
+ if (result != NULL) {
+ result->string_value = strdup(value->data.as_string);
+
+ if (result->string_value == NULL) {
+ otlp_any_value_destroy(result);
+
+ result = NULL;
+ }
+ }
+
+ return result;
+}
+
+static inline Opentelemetry__Proto__Common__V1__AnyValue *cfl_variant_boolean_to_otlp_any_value(struct cfl_variant *value)
+{
+ Opentelemetry__Proto__Common__V1__AnyValue *result;
+
+ result = otlp_any_value_initialize(CFL_VARIANT_BOOL, 0);
+
+ if (result != NULL) {
+ result->bool_value = value->data.as_bool;
+ }
+
+ return result;
+}
+
+static inline Opentelemetry__Proto__Common__V1__AnyValue *cfl_variant_int64_to_otlp_any_value(struct cfl_variant *value)
+{
+ Opentelemetry__Proto__Common__V1__AnyValue *result;
+
+ result = otlp_any_value_initialize(CFL_VARIANT_INT, 0);
+
+ if (result != NULL) {
+ result->int_value = value->data.as_int64;
+ }
+
+ return result;
+}
+
+static inline Opentelemetry__Proto__Common__V1__AnyValue *cfl_variant_double_to_otlp_any_value(struct cfl_variant *value)
+{
+ Opentelemetry__Proto__Common__V1__AnyValue *result;
+
+ result = otlp_any_value_initialize(CFL_VARIANT_DOUBLE, 0);
+
+ if (result != NULL) {
+ result->double_value = value->data.as_double;
+ }
+
+ return result;
+}
+
+static inline Opentelemetry__Proto__Common__V1__AnyValue *cfl_variant_binary_to_otlp_any_value(struct cfl_variant *value)
+{
+ Opentelemetry__Proto__Common__V1__AnyValue *result;
+
+ result = otlp_any_value_initialize(CFL_VARIANT_BYTES, 0);
+
+ if (result != NULL) {
+ result->bytes_value.len = cfl_sds_len(value->data.as_bytes);
+ result->bytes_value.data = calloc(result->bytes_value.len, sizeof(char));
+
+ if (result->bytes_value.data == NULL) {
+ otlp_any_value_destroy(result);
+
+ result = NULL;
+ }
+
+ memcpy(result->bytes_value.data, value->data.as_bytes, result->bytes_value.len);
+ }
+
+ return result;
+}
+
+static inline Opentelemetry__Proto__Common__V1__AnyValue *cfl_variant_to_otlp_any_value(struct cfl_variant *value)
+{
+ Opentelemetry__Proto__Common__V1__AnyValue *result;
+
+ if (value->type == CFL_VARIANT_STRING) {
+ result = cfl_variant_string_to_otlp_any_value(value);
+ }
+ else if (value->type == CFL_VARIANT_BOOL) {
+ result = cfl_variant_boolean_to_otlp_any_value(value);
+ }
+ else if (value->type == CFL_VARIANT_INT) {
+ result = cfl_variant_int64_to_otlp_any_value(value);
+ }
+ else if (value->type == CFL_VARIANT_DOUBLE) {
+ result = cfl_variant_double_to_otlp_any_value(value);
+ }
+ else if (value->type == CFL_VARIANT_ARRAY) {
+ result = cfl_variant_array_to_otlp_any_value(value);
+ }
+ else if (value->type == CFL_VARIANT_KVLIST) {
+ result = cfl_variant_kvlist_to_otlp_any_value(value);
+ }
+ else if (value->type == CFL_VARIANT_BYTES) {
+ result = cfl_variant_binary_to_otlp_any_value(value);
+ }
+ else if (value->type == CFL_VARIANT_REFERENCE) {
+ result = cfl_variant_string_to_otlp_any_value(value);
+ }
+ else {
+ result = NULL;
+ }
+
+ return result;
+}
+
+static char *fetch_metadata_string_key(struct cfl_kvlist *metadata, char *key_name, int *error_flag)
+{
+ struct cfl_variant *value;
+
+ *error_flag = CMT_FALSE;
+
+ value = cfl_kvlist_fetch(metadata, key_name);
+
+ if (value == NULL) {
+ return NULL;
+ }
+
+ if (value->type != CFL_VARIANT_STRING) {
+ *error_flag = CMT_TRUE;
+
+ return NULL;
+ }
+
+ return cfl_sds_create(value->data.as_string);
+}
+
+static int64_t fetch_metadata_int64_key(struct cfl_kvlist *metadata, char *key_name, int *error_flag)
+{
+ struct cfl_variant *value;
+
+ *error_flag = CMT_FALSE;
+
+ value = cfl_kvlist_fetch(metadata, key_name);
+
+ if (value == NULL) {
+ return 0;
+ }
+
+ if (value->type != CFL_VARIANT_INT) {
+ *error_flag = CMT_TRUE;
+
+ return 0;
+ }
+
+ return value->data.as_int64;
+}
+
+static int is_string_releaseable(char *address)
+ {
+ return (address != NULL &&
+ address != protobuf_c_empty_string);
+}
+
+static int is_metric_empty(struct cmt_map *map)
+{
+ size_t sample_count;
+
+ sample_count = cfl_list_size(&map->metrics);
+
+ if (map->metric_static_set) {
+ sample_count++;
+ }
+
+ return (sample_count == 0);
+}
+
+static size_t get_metric_count(struct cmt *cmt)
+{
+ size_t metric_count;
+ struct cmt_histogram *histogram;
+ struct cmt_summary *summary;
+ struct cmt_untyped *untyped;
+ struct cmt_counter *counter;
+ struct cmt_gauge *gauge;
+ struct cfl_list *head;
+
+ metric_count = 0;
+
+ cfl_list_foreach(head, &cmt->counters) {
+ counter = cfl_list_entry(head, struct cmt_counter, _head);
+
+ metric_count += !is_metric_empty(counter->map);
+ }
+
+ cfl_list_foreach(head, &cmt->gauges) {
+ gauge = cfl_list_entry(head, struct cmt_gauge, _head);
+
+ metric_count += !is_metric_empty(gauge->map);
+ }
+
+ cfl_list_foreach(head, &cmt->untypeds) {
+ untyped = cfl_list_entry(head, struct cmt_untyped, _head);
+
+ metric_count += !is_metric_empty(untyped->map);
+ }
+
+ cfl_list_foreach(head, &cmt->summaries) {
+ summary = cfl_list_entry(head, struct cmt_summary, _head);
+
+ metric_count += !is_metric_empty(summary->map);
+ }
+
+ cfl_list_foreach(head, &cmt->histograms) {
+ histogram = cfl_list_entry(head, struct cmt_histogram, _head);
+
+ metric_count += !is_metric_empty(histogram->map);
+ }
+
+ return metric_count;
+}
+
+static void destroy_metrics_data(
+ Opentelemetry__Proto__Metrics__V1__MetricsData *metrics_data)
+{
+ if (metrics_data != NULL) {
+ destroy_resource_metrics_list(metrics_data->resource_metrics);
+
+ free(metrics_data);
+ }
+}
+
+static Opentelemetry__Proto__Metrics__V1__MetricsData *
+ initialize_metrics_data(size_t resource_metrics_count)
+{
+ Opentelemetry__Proto__Metrics__V1__MetricsData *metrics_data;
+
+ metrics_data = calloc(1, sizeof(Opentelemetry__Proto__Metrics__V1__MetricsData));
+
+ if (metrics_data == NULL) {
+ return NULL;
+ }
+
+ opentelemetry__proto__metrics__v1__metrics_data__init(metrics_data);
+
+ metrics_data->resource_metrics = initialize_resource_metrics_list(resource_metrics_count);
+
+ if (metrics_data->resource_metrics == NULL) {
+ destroy_metrics_data(metrics_data);
+
+ return NULL;
+ }
+
+ metrics_data->n_resource_metrics = resource_metrics_count;
+
+ return metrics_data;
+}
+
+static void destroy_resource(
+ Opentelemetry__Proto__Resource__V1__Resource *resource)
+{
+ if (resource != NULL) {
+ if (resource->attributes != NULL) {
+ otlp_kvpair_list_destroy(resource->attributes, resource->n_attributes);
+ }
+
+ free(resource);
+ }
+}
+
+static void destroy_resource_metrics(
+ Opentelemetry__Proto__Metrics__V1__ResourceMetrics *resource_metrics)
+{
+ if (resource_metrics != NULL) {
+ if (is_string_releaseable(resource_metrics->schema_url)) {
+ cfl_sds_destroy(resource_metrics->schema_url);
+ }
+
+ if (resource_metrics->resource != NULL) {
+ destroy_resource(resource_metrics->resource);
+ }
+
+ if (resource_metrics->scope_metrics != NULL) {
+ destroy_scope_metric_list(resource_metrics->scope_metrics);
+ }
+
+ free(resource_metrics);
+ }
+}
+
+static Opentelemetry__Proto__Metrics__V1__ResourceMetrics *
+ initialize_resource_metrics(
+ struct cfl_kvlist *resource_metrics_root,
+ Opentelemetry__Proto__Resource__V1__Resource *resource,
+ size_t scope_metrics_element_count)
+{
+ int error_detection_flag;
+ Opentelemetry__Proto__Metrics__V1__ResourceMetrics *resource_metrics;
+ struct cfl_kvlist *metadata;
+
+ metadata = fetch_metadata_kvlist_key(resource_metrics_root, "metadata");
+
+ resource_metrics = \
+ calloc(1, sizeof(Opentelemetry__Proto__Metrics__V1__ResourceMetrics));
+
+ if (resource_metrics == NULL) {
+ return NULL;
+ }
+
+ opentelemetry__proto__metrics__v1__resource_metrics__init(
+ resource_metrics);
+
+ if (metadata != NULL) {
+ resource_metrics->schema_url = fetch_metadata_string_key(metadata, "schema_url", &error_detection_flag);
+ }
+ else {
+ error_detection_flag = CMT_FALSE;
+ }
+
+ if (error_detection_flag) {
+ destroy_resource_metrics(resource_metrics);
+
+ return NULL;
+ }
+
+ resource_metrics->scope_metrics = \
+ initialize_scope_metrics_list(
+ scope_metrics_element_count);
+
+ if (resource_metrics->scope_metrics == NULL) {
+ destroy_resource_metrics(resource_metrics);
+
+ return NULL;
+ }
+
+ resource_metrics->n_scope_metrics = \
+ scope_metrics_element_count;
+
+ resource_metrics->resource = resource;
+
+ return resource_metrics;
+}
+
+static void destroy_resource_metrics_list(
+ Opentelemetry__Proto__Metrics__V1__ResourceMetrics **metric_list)
+{
+ size_t element_index;
+
+ if (metric_list != NULL) {
+ for (element_index = 0 ;
+ metric_list[element_index] != NULL ;
+ element_index++) {
+ destroy_resource_metrics(metric_list[element_index]);
+
+ metric_list[element_index] = NULL;
+ }
+
+ free(metric_list);
+ }
+}
+
+static Opentelemetry__Proto__Metrics__V1__ResourceMetrics **
+ initialize_resource_metrics_list(
+ size_t element_count)
+{
+ Opentelemetry__Proto__Metrics__V1__ResourceMetrics **metric_list;
+
+ metric_list = calloc(element_count + 1,
+ sizeof(Opentelemetry__Proto__Metrics__V1__ResourceMetrics *));
+
+ return metric_list;
+}
+
+static void destroy_scope_metrics(
+ Opentelemetry__Proto__Metrics__V1__ScopeMetrics *metric)
+{
+ if (metric != NULL) {
+ destroy_metric_list(metric->metrics);
+
+ free(metric);
+ }
+}
+
+void destroy_instrumentation_scope(Opentelemetry__Proto__Common__V1__InstrumentationScope *scope)
+{
+ if (scope->name != NULL) {
+ cfl_sds_destroy(scope->name);
+ }
+
+ if (scope->version != NULL) {
+ cfl_sds_destroy(scope->version);
+ }
+
+ if (scope->attributes != NULL) {
+ destroy_attribute_list(scope->attributes);
+ }
+
+ free(scope);
+}
+
+static Opentelemetry__Proto__Common__V1__InstrumentationScope *
+ initialize_instrumentation_scope(
+ struct cfl_kvlist *scope_root,
+ int *error_detection_flag)
+{
+ struct cfl_kvlist *attributes;
+ struct cfl_kvlist *metadata;
+ Opentelemetry__Proto__Common__V1__InstrumentationScope *scope;
+
+ *error_detection_flag = CMT_FALSE;
+
+ if (scope_root == NULL) {
+ return NULL;
+ }
+
+ attributes = fetch_metadata_kvlist_key(scope_root, "attributes");
+ metadata = fetch_metadata_kvlist_key(scope_root, "metadata");
+
+ if (cfl_kvlist_count(attributes) == 0 &&
+ cfl_kvlist_count(metadata) == 0) {
+ return NULL;
+ }
+
+ scope = \
+ calloc(1, sizeof(Opentelemetry__Proto__Common__V1__InstrumentationScope));
+
+ if (scope == NULL) {
+ *error_detection_flag = CMT_TRUE;
+
+ return NULL;
+ }
+
+ opentelemetry__proto__common__v1__instrumentation_scope__init(scope);
+
+ scope->attributes = cfl_kvlist_to_otlp_kvpair_list(attributes);
+
+ if (scope->attributes == NULL) {
+ *error_detection_flag = CMT_TRUE;
+ }
+
+ scope->n_attributes = cfl_kvlist_count(attributes);
+
+ if (!(*error_detection_flag)) {
+ scope->dropped_attributes_count = (uint32_t) fetch_metadata_int64_key(
+ metadata,
+ "dropped_attributes_count",
+ error_detection_flag);
+ }
+
+ if (!(*error_detection_flag)) {
+ scope->name = fetch_metadata_string_key(metadata, "name", error_detection_flag);
+ }
+
+ if (!(*error_detection_flag)) {
+ scope->version = fetch_metadata_string_key(metadata, "version", error_detection_flag);
+ }
+
+ if (*error_detection_flag &&
+ scope != NULL) {
+ destroy_instrumentation_scope(scope);
+
+ scope = NULL;
+ }
+
+ return scope;
+}
+
+static Opentelemetry__Proto__Metrics__V1__ScopeMetrics *
+ initialize_scope_metrics(
+ struct cfl_kvlist *scope_metrics_root,
+ size_t metric_element_count)
+{
+ int error_detection_flag;
+ Opentelemetry__Proto__Metrics__V1__ScopeMetrics *scope_metrics;
+ struct cfl_kvlist *metadata;
+
+ metadata = fetch_metadata_kvlist_key(scope_metrics_root, "metadata");
+
+ scope_metrics = \
+ calloc(1, sizeof(Opentelemetry__Proto__Metrics__V1__ScopeMetrics));
+
+ if (scope_metrics == NULL) {
+ return NULL;
+ }
+
+ opentelemetry__proto__metrics__v1__scope_metrics__init(scope_metrics);
+
+ error_detection_flag = CMT_FALSE;
+
+ if (metric_element_count > 0) {
+ scope_metrics->metrics = \
+ initialize_metric_list(metric_element_count);
+
+ if (scope_metrics->metrics == NULL) {
+ error_detection_flag = CMT_TRUE;
+ }
+ else {
+ scope_metrics->n_metrics = metric_element_count;
+ }
+ }
+
+ if (!error_detection_flag && metadata != NULL) {
+ scope_metrics->schema_url = fetch_metadata_string_key(metadata, "schema_url", &error_detection_flag);
+ }
+
+ if (error_detection_flag &&
+ scope_metrics != NULL) {
+ destroy_scope_metrics(scope_metrics);
+
+ scope_metrics = NULL;
+ }
+
+ return scope_metrics;
+}
+
+static int append_metric_to_scope_metrics(
+ Opentelemetry__Proto__Metrics__V1__ScopeMetrics *metrics,
+ Opentelemetry__Proto__Metrics__V1__Metric *metric,
+ size_t metric_slot_hint)
+{
+ size_t metric_slot_index;
+
+ for (metric_slot_index = metric_slot_hint ;
+ metric_slot_index < metrics->n_metrics;
+ metric_slot_index++) {
+ if (metrics->metrics[metric_slot_index] == NULL) {
+ metrics->metrics[metric_slot_index] = metric;
+
+ return CMT_ENCODE_OPENTELEMETRY_SUCCESS;
+ }
+ }
+
+ return CMT_ENCODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR;
+}
+
+static void destroy_scope_metric_list(
+ Opentelemetry__Proto__Metrics__V1__ScopeMetrics **metric_list)
+{
+ size_t element_index;
+
+ if (metric_list != NULL) {
+ for (element_index = 0 ;
+ metric_list[element_index] != NULL ;
+ element_index++) {
+ destroy_scope_metrics(metric_list[element_index]);
+
+ metric_list[element_index] = NULL;
+ }
+
+ free(metric_list);
+ }
+}
+
+static Opentelemetry__Proto__Metrics__V1__ScopeMetrics **
+ initialize_scope_metrics_list(
+ size_t element_count)
+{
+ Opentelemetry__Proto__Metrics__V1__ScopeMetrics **metric_list;
+
+ metric_list = calloc(element_count + 1,
+ sizeof(Opentelemetry__Proto__Metrics__V1__ScopeMetrics *));
+
+ return metric_list;
+}
+
+static void destroy_attribute(
+ Opentelemetry__Proto__Common__V1__KeyValue *attribute)
+{
+ if (attribute != NULL) {
+ if (attribute->value != NULL) {
+ if (attribute->value->value_case == \
+ OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE) {
+ if (is_string_releaseable(attribute->value->string_value)) {
+ cfl_sds_destroy(attribute->value->string_value);
+ }
+ }
+
+ free(attribute->value);
+ }
+
+ if (is_string_releaseable(attribute->key)) {
+ cfl_sds_destroy(attribute->key);
+ }
+
+ free(attribute);
+ }
+}
+
+static Opentelemetry__Proto__Common__V1__KeyValue *
+ initialize_string_attribute(char *key, char *value)
+{
+ Opentelemetry__Proto__Common__V1__KeyValue *attribute;
+
+ attribute = calloc(1,
+ sizeof(Opentelemetry__Proto__Common__V1__KeyValue));
+
+ if (attribute == NULL) {
+ return NULL;
+ }
+
+ opentelemetry__proto__common__v1__key_value__init(attribute);
+
+ attribute->value = calloc(1,
+ sizeof(Opentelemetry__Proto__Common__V1__AnyValue));
+
+ if (attribute->value == NULL) {
+ destroy_attribute(attribute);
+
+ return NULL;
+ }
+
+ opentelemetry__proto__common__v1__any_value__init(attribute->value);
+
+ attribute->value->string_value = cfl_sds_create(value);
+
+ if (attribute->value->string_value == NULL) {
+ destroy_attribute(attribute);
+
+ return NULL;
+ }
+
+ attribute->value->value_case = OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE;
+
+ attribute->key = cfl_sds_create(key);
+
+ if (attribute->key == NULL) {
+ destroy_attribute(attribute);
+
+ return NULL;
+ }
+
+ return attribute;
+}
+
+static void destroy_attribute_list(
+ Opentelemetry__Proto__Common__V1__KeyValue **attribute_list)
+{
+ size_t element_index;
+
+ if (attribute_list != NULL) {
+ for (element_index = 0 ;
+ attribute_list[element_index] != NULL ;
+ element_index++) {
+ destroy_attribute(attribute_list[element_index]);
+
+ attribute_list[element_index] = NULL;
+ }
+
+ free(attribute_list);
+ }
+}
+
+static Opentelemetry__Proto__Common__V1__KeyValue **
+ initialize_attribute_list(
+ size_t element_count)
+{
+ Opentelemetry__Proto__Common__V1__KeyValue **attribute_list;
+
+ attribute_list = calloc(element_count + 1,
+ sizeof(Opentelemetry__Proto__Common__V1__KeyValue *));
+
+ return attribute_list;
+}
+
+static void destroy_numerical_data_point(
+ Opentelemetry__Proto__Metrics__V1__NumberDataPoint *data_point)
+{
+ if (data_point != NULL) {
+ destroy_attribute_list(data_point->attributes);
+
+ free(data_point);
+ }
+}
+
+static void destroy_summary_data_point(
+ Opentelemetry__Proto__Metrics__V1__SummaryDataPoint *data_point)
+{
+ if (data_point != NULL) {
+ destroy_attribute_list(data_point->attributes);
+
+ if (data_point->quantile_values != NULL) {
+ destroy_summary_value_at_quantile_list(data_point->quantile_values);
+ }
+
+ free(data_point);
+ }
+}
+
+static void destroy_histogram_data_point(
+ Opentelemetry__Proto__Metrics__V1__HistogramDataPoint *data_point)
+{
+ if (data_point != NULL) {
+ destroy_attribute_list(data_point->attributes);
+
+ if (data_point->bucket_counts != NULL) {
+ free(data_point->bucket_counts);
+ }
+
+ if (data_point->explicit_bounds != NULL) {
+ free(data_point->explicit_bounds);
+ }
+
+ free(data_point);
+ }
+}
+
+static void destroy_data_point(
+ void *data_point,
+ int data_point_type)
+{
+ switch (data_point_type) {
+ case CMT_COUNTER:
+ case CMT_GAUGE:
+ case CMT_UNTYPED:
+ return destroy_numerical_data_point(data_point);
+ case CMT_SUMMARY:
+ return destroy_summary_data_point(data_point);
+ case CMT_HISTOGRAM:
+ return destroy_histogram_data_point(data_point);
+ }
+}
+
+static void destroy_numerical_data_point_list(
+ Opentelemetry__Proto__Metrics__V1__NumberDataPoint **data_point_list)
+{
+ size_t element_index;
+
+ if (data_point_list != NULL) {
+ for (element_index = 0 ;
+ data_point_list[element_index] != NULL ;
+ element_index++) {
+ destroy_numerical_data_point(data_point_list[element_index]);
+
+ data_point_list[element_index] = NULL;
+ }
+
+ free(data_point_list);
+ }
+}
+
+static void destroy_summary_data_point_list(
+ Opentelemetry__Proto__Metrics__V1__SummaryDataPoint **data_point_list)
+{
+ size_t element_index;
+
+ if (data_point_list != NULL) {
+ for (element_index = 0 ;
+ data_point_list[element_index] != NULL ;
+ element_index++) {
+ destroy_summary_data_point(data_point_list[element_index]);
+
+ data_point_list[element_index] = NULL;
+ }
+
+ free(data_point_list);
+ }
+}
+
+static void destroy_histogram_data_point_list(
+ Opentelemetry__Proto__Metrics__V1__HistogramDataPoint **data_point_list)
+{
+ size_t element_index;
+
+ if (data_point_list != NULL) {
+ for (element_index = 0 ;
+ data_point_list[element_index] != NULL ;
+ element_index++) {
+ destroy_histogram_data_point(data_point_list[element_index]);
+
+ data_point_list[element_index] = NULL;
+ }
+
+ free(data_point_list);
+ }
+}
+
+static Opentelemetry__Proto__Metrics__V1__NumberDataPoint *
+ initialize_numerical_data_point(
+ uint64_t start_time,
+ uint64_t timestamp,
+ double value,
+ Opentelemetry__Proto__Common__V1__KeyValue **attribute_list,
+ size_t attribute_count)
+{
+ Opentelemetry__Proto__Metrics__V1__NumberDataPoint *data_point;
+
+ data_point = calloc(1,
+ sizeof(Opentelemetry__Proto__Metrics__V1__NumberDataPoint));
+
+ if (data_point == NULL) {
+ return NULL;
+ }
+
+ opentelemetry__proto__metrics__v1__number_data_point__init(data_point);
+
+ data_point->start_time_unix_nano = start_time;
+ data_point->time_unix_nano = timestamp;
+ data_point->value_case = OPENTELEMETRY__PROTO__METRICS__V1__NUMBER_DATA_POINT__VALUE_AS_DOUBLE;
+ data_point->as_double = value;
+ data_point->attributes = attribute_list;
+ data_point->n_attributes = attribute_count;
+
+ return data_point;
+}
+
+static Opentelemetry__Proto__Metrics__V1__SummaryDataPoint__ValueAtQuantile *
+ initialize_summary_value_at_quantile(
+ double quantile, double value)
+{
+ Opentelemetry__Proto__Metrics__V1__SummaryDataPoint__ValueAtQuantile *instance;
+
+ instance = calloc(1, sizeof(Opentelemetry__Proto__Metrics__V1__SummaryDataPoint__ValueAtQuantile));
+
+ if (instance != NULL) {
+ opentelemetry__proto__metrics__v1__summary_data_point__value_at_quantile__init(instance);
+
+ instance->quantile = quantile;
+ instance->value = value;
+ }
+
+ return instance;
+}
+
+static void destroy_summary_value_at_quantile(
+ Opentelemetry__Proto__Metrics__V1__SummaryDataPoint__ValueAtQuantile *value)
+{
+ if (value != NULL) {
+ free(value);
+ }
+}
+
+static Opentelemetry__Proto__Metrics__V1__SummaryDataPoint__ValueAtQuantile **
+ initialize_summary_value_at_quantile_list(
+ size_t element_count)
+{
+ Opentelemetry__Proto__Metrics__V1__SummaryDataPoint__ValueAtQuantile **list;
+
+ list = calloc(element_count + 1,
+ sizeof(Opentelemetry__Proto__Metrics__V1__SummaryDataPoint__ValueAtQuantile *));
+
+ return list;
+}
+
+static void destroy_summary_value_at_quantile_list(
+ Opentelemetry__Proto__Metrics__V1__SummaryDataPoint__ValueAtQuantile **list)
+{
+ size_t index;
+
+ if (list != NULL) {
+ for (index = 0 ;
+ list[index] != NULL ;
+ index++) {
+ destroy_summary_value_at_quantile(list[index]);
+
+ list[index] = NULL;
+ }
+
+ free(list);
+ }
+}
+
+
+
+static Opentelemetry__Proto__Metrics__V1__SummaryDataPoint *
+ initialize_summary_data_point(
+ uint64_t start_time,
+ uint64_t timestamp,
+ uint64_t count,
+ double sum,
+ size_t quantile_count,
+ double *quantile_list,
+ size_t value_count,
+ uint64_t *value_list,
+ Opentelemetry__Proto__Common__V1__KeyValue **attribute_list,
+ size_t attribute_count)
+{
+ Opentelemetry__Proto__Metrics__V1__SummaryDataPoint *data_point;
+ size_t index;
+
+ data_point = calloc(1,
+ sizeof(Opentelemetry__Proto__Metrics__V1__SummaryDataPoint));
+
+ if (data_point == NULL) {
+ return NULL;
+ }
+
+ opentelemetry__proto__metrics__v1__summary_data_point__init(data_point);
+
+ data_point->start_time_unix_nano = start_time;
+ data_point->time_unix_nano = timestamp;
+
+ data_point->count = count;
+ data_point->sum = sum;
+ data_point->n_quantile_values = quantile_count;
+
+ data_point->quantile_values = initialize_summary_value_at_quantile_list(quantile_count);
+
+ if (data_point->quantile_values == NULL) {
+ cmt_errno();
+
+ free(data_point);
+
+ return NULL;
+ }
+
+ if (quantile_count > 0) {
+ if (value_list != NULL) {
+ for (index = 0 ; index < quantile_count ; index++) {
+ data_point->quantile_values[index] =
+ initialize_summary_value_at_quantile(quantile_list[index],
+ cmt_math_uint64_to_d64(value_list[index]));
+
+ if (data_point->quantile_values[index] == NULL) {
+ destroy_summary_value_at_quantile_list(data_point->quantile_values);
+
+ free(data_point);
+
+ return NULL;
+ }
+ }
+ }
+ }
+
+ data_point->attributes = attribute_list;
+ data_point->n_attributes = attribute_count;
+
+ return data_point;
+}
+
+static Opentelemetry__Proto__Metrics__V1__HistogramDataPoint *
+ initialize_histogram_data_point(
+ uint64_t start_time,
+ uint64_t timestamp,
+ uint64_t count,
+ double sum,
+ size_t bucket_count,
+ uint64_t *bucket_list,
+ size_t boundary_count,
+ double *boundary_list,
+ Opentelemetry__Proto__Common__V1__KeyValue **attribute_list,
+ size_t attribute_count)
+{
+ Opentelemetry__Proto__Metrics__V1__HistogramDataPoint *data_point;
+ size_t index;
+
+ data_point = calloc(1,
+ sizeof(Opentelemetry__Proto__Metrics__V1__HistogramDataPoint));
+
+ if (data_point == NULL) {
+ return NULL;
+ }
+
+ opentelemetry__proto__metrics__v1__histogram_data_point__init(data_point);
+
+ data_point->start_time_unix_nano = start_time;
+ data_point->time_unix_nano = timestamp;
+
+ data_point->count = count;
+ data_point->n_bucket_counts = bucket_count;
+
+ data_point->sum = sum;
+
+ if (bucket_count > 0) {
+ data_point->bucket_counts = calloc(bucket_count, sizeof(uint64_t));
+
+ if (data_point->bucket_counts == NULL) {
+ cmt_errno();
+
+ free(data_point);
+
+ return NULL;
+ }
+
+ if (bucket_list != NULL) {
+ for (index = 0 ; index < bucket_count ; index++) {
+ data_point->bucket_counts[index] = bucket_list[index];
+ }
+ }
+ }
+
+ data_point->n_explicit_bounds = boundary_count;
+
+ if (boundary_count > 0) {
+ data_point->explicit_bounds = calloc(boundary_count, sizeof(uint64_t));
+
+ if (data_point->explicit_bounds == NULL) {
+ cmt_errno();
+
+ if (data_point->bucket_counts != NULL) {
+ free(data_point->bucket_counts);
+ }
+
+ free(data_point);
+
+ return NULL;
+ }
+
+ if (boundary_list != NULL) {
+ for (index = 0 ; index < boundary_count ; index++) {
+ data_point->explicit_bounds[index] = boundary_list[index];
+ }
+ }
+ }
+
+ data_point->attributes = attribute_list;
+ data_point->n_attributes = attribute_count;
+
+ return data_point;
+}
+
+static int append_attribute_to_numerical_data_point(
+ Opentelemetry__Proto__Metrics__V1__NumberDataPoint *data_point,
+ Opentelemetry__Proto__Common__V1__KeyValue *attribute,
+ size_t attribute_slot_hint)
+{
+ size_t attribute_slot_index;
+
+ for (attribute_slot_index = attribute_slot_hint ;
+ attribute_slot_index < data_point->n_attributes;
+ attribute_slot_index++) {
+ if (data_point->attributes[attribute_slot_index] == NULL) {
+ data_point->attributes[attribute_slot_index] = attribute;
+
+ return CMT_ENCODE_OPENTELEMETRY_SUCCESS;
+ }
+ }
+
+ return CMT_ENCODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR;
+}
+
+static int append_attribute_to_summary_data_point(
+ Opentelemetry__Proto__Metrics__V1__SummaryDataPoint *data_point,
+ Opentelemetry__Proto__Common__V1__KeyValue *attribute,
+ size_t attribute_slot_hint)
+{
+ size_t attribute_slot_index;
+
+ for (attribute_slot_index = attribute_slot_hint ;
+ attribute_slot_index < data_point->n_attributes;
+ attribute_slot_index++) {
+ if (data_point->attributes[attribute_slot_index] == NULL) {
+ data_point->attributes[attribute_slot_index] = attribute;
+
+ return CMT_ENCODE_OPENTELEMETRY_SUCCESS;
+ }
+ }
+
+ return CMT_ENCODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR;
+}
+
+static int append_attribute_to_histogram_data_point(
+ Opentelemetry__Proto__Metrics__V1__HistogramDataPoint *data_point,
+ Opentelemetry__Proto__Common__V1__KeyValue *attribute,
+ size_t attribute_slot_hint)
+{
+ size_t attribute_slot_index;
+
+ for (attribute_slot_index = attribute_slot_hint ;
+ attribute_slot_index < data_point->n_attributes;
+ attribute_slot_index++) {
+ if (data_point->attributes[attribute_slot_index] == NULL) {
+ data_point->attributes[attribute_slot_index] = attribute;
+
+ return CMT_ENCODE_OPENTELEMETRY_SUCCESS;
+ }
+ }
+
+ return CMT_ENCODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR;
+}
+
+static int append_attribute_to_data_point(
+ void *data_point,
+ int data_point_type,
+ Opentelemetry__Proto__Common__V1__KeyValue *attribute,
+ size_t attribute_slot_hint)
+{
+ switch (data_point_type) {
+ case CMT_COUNTER:
+ case CMT_GAUGE:
+ case CMT_UNTYPED:
+ return append_attribute_to_numerical_data_point(data_point,
+ attribute,
+ attribute_slot_hint);
+ case CMT_SUMMARY:
+ return append_attribute_to_summary_data_point(data_point,
+ attribute,
+ attribute_slot_hint);
+ case CMT_HISTOGRAM:
+ return append_attribute_to_histogram_data_point(data_point,
+ attribute,
+ attribute_slot_hint);
+ }
+
+ return CMT_ENCODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR;
+}
+
+static Opentelemetry__Proto__Metrics__V1__NumberDataPoint **
+ initialize_numerical_data_point_list(
+ size_t element_count)
+{
+ Opentelemetry__Proto__Metrics__V1__NumberDataPoint **data_point_list;
+
+ data_point_list = calloc(element_count + 1,
+ sizeof(Opentelemetry__Proto__Metrics__V1__NumberDataPoint *));
+
+ return data_point_list;
+}
+
+static Opentelemetry__Proto__Metrics__V1__SummaryDataPoint **
+ initialize_summary_data_point_list(
+ size_t element_count)
+{
+ Opentelemetry__Proto__Metrics__V1__SummaryDataPoint **data_point_list;
+
+ data_point_list = calloc(element_count + 1,
+ sizeof(Opentelemetry__Proto__Metrics__V1__SummaryDataPoint *));
+
+ return data_point_list;
+}
+
+static Opentelemetry__Proto__Metrics__V1__HistogramDataPoint **
+ initialize_histogram_data_point_list(
+ size_t element_count)
+{
+ Opentelemetry__Proto__Metrics__V1__HistogramDataPoint **data_point_list;
+
+ data_point_list = calloc(element_count + 1,
+ sizeof(Opentelemetry__Proto__Metrics__V1__HistogramDataPoint *));
+
+ if (data_point_list == NULL) {
+ return NULL;
+ }
+
+ return data_point_list;
+}
+
+static void destroy_metric(
+ Opentelemetry__Proto__Metrics__V1__Metric *metric)
+{
+ if (metric != NULL) {
+ if (is_string_releaseable(metric->name)) {
+ cfl_sds_destroy(metric->name);
+ metric->name = NULL;
+ }
+
+ if (is_string_releaseable(metric->description)) {
+ cfl_sds_destroy(metric->description);
+ metric->description = NULL;
+ }
+
+ if (is_string_releaseable(metric->unit)) {
+ cfl_sds_destroy(metric->unit);
+ metric->unit = NULL;
+ }
+
+ if (metric->data_case == OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_SUM) {
+ destroy_numerical_data_point_list(metric->sum->data_points);
+
+ free(metric->sum);
+ }
+ else if (metric->data_case == OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_GAUGE) {
+ destroy_numerical_data_point_list(metric->gauge->data_points);
+
+ free(metric->gauge);
+ }
+ else if (metric->data_case == OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_SUMMARY) {
+ destroy_summary_data_point_list(metric->summary->data_points);
+
+ free(metric->histogram);
+ }
+ else if (metric->data_case == OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_HISTOGRAM) {
+ destroy_histogram_data_point_list(metric->histogram->data_points);
+
+ free(metric->histogram);
+ }
+
+ free(metric);
+ }
+}
+
+static Opentelemetry__Proto__Metrics__V1__Metric *
+ initialize_metric(int type,
+ char *name,
+ char *description,
+ char *unit,
+ int monotonism_flag,
+ int aggregation_temporality_type,
+ size_t data_point_count)
+{
+ Opentelemetry__Proto__Metrics__V1__Metric *metric;
+
+ metric = calloc(1,
+ sizeof(Opentelemetry__Proto__Metrics__V1__Metric));
+
+ if (metric == NULL) {
+ return NULL;
+ }
+
+ opentelemetry__proto__metrics__v1__metric__init(metric);
+
+ metric->name = cfl_sds_create(name);
+
+ if (metric->name == NULL) {
+ destroy_metric(metric);
+
+ return NULL;
+ }
+
+ if (description != NULL) {
+ metric->description = cfl_sds_create(description);
+
+ if (metric->description == NULL) {
+ destroy_metric(metric);
+
+ return NULL;
+ }
+ }
+
+ if (unit != NULL) {
+ metric->unit = cfl_sds_create(unit);
+
+ if (metric->unit == NULL) {
+ destroy_metric(metric);
+
+ return NULL;
+ }
+ }
+
+ if (type == CMT_COUNTER) {
+ metric->sum = calloc(1, sizeof(Opentelemetry__Proto__Metrics__V1__Sum));
+
+ if (metric->sum == NULL) {
+ destroy_metric(metric);
+
+ return NULL;
+ }
+
+ opentelemetry__proto__metrics__v1__sum__init(metric->sum);
+
+ metric->data_case = OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_SUM;
+ metric->sum->data_points = initialize_numerical_data_point_list(data_point_count);
+
+ if (metric->sum->data_points == NULL) {
+ destroy_metric(metric);
+
+ return NULL;
+ }
+
+
+ metric->sum->aggregation_temporality = aggregation_temporality_type;
+ metric->sum->is_monotonic = monotonism_flag;
+ metric->sum->n_data_points = data_point_count;
+ }
+ else if (type == CMT_UNTYPED) {
+ metric->gauge = calloc(1, sizeof(Opentelemetry__Proto__Metrics__V1__Gauge));
+
+ if (metric->gauge == NULL) {
+ destroy_metric(metric);
+
+ return NULL;
+ }
+
+ opentelemetry__proto__metrics__v1__gauge__init(metric->gauge);
+
+ metric->data_case = OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_GAUGE;
+ metric->gauge->data_points = initialize_numerical_data_point_list(data_point_count);
+
+ if (metric->gauge->data_points == NULL) {
+ destroy_metric(metric);
+
+ return NULL;
+ }
+
+ metric->gauge->n_data_points = data_point_count;
+ }
+ else if (type == CMT_GAUGE) {
+ metric->gauge = calloc(1, sizeof(Opentelemetry__Proto__Metrics__V1__Gauge));
+
+ if (metric->gauge == NULL) {
+ destroy_metric(metric);
+
+ return NULL;
+ }
+
+ opentelemetry__proto__metrics__v1__gauge__init(metric->gauge);
+
+ metric->data_case = OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_GAUGE;
+ metric->gauge->data_points = initialize_numerical_data_point_list(data_point_count);
+
+ if (metric->gauge->data_points == NULL) {
+ destroy_metric(metric);
+
+ return NULL;
+ }
+
+ metric->gauge->n_data_points = data_point_count;
+ }
+ else if (type == CMT_SUMMARY) {
+ metric->summary = calloc(1, sizeof(Opentelemetry__Proto__Metrics__V1__Summary));
+
+ if (metric->summary == NULL) {
+ destroy_metric(metric);
+
+ return NULL;
+ }
+
+ opentelemetry__proto__metrics__v1__summary__init(metric->summary);
+
+ metric->data_case = OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_SUMMARY;
+ metric->summary->data_points = initialize_summary_data_point_list(data_point_count);
+
+ if (metric->summary->data_points == NULL) {
+ destroy_metric(metric);
+
+ return NULL;
+ }
+
+ metric->summary->n_data_points = data_point_count;
+ }
+ else if (type == CMT_HISTOGRAM) {
+ metric->histogram = calloc(1, sizeof(Opentelemetry__Proto__Metrics__V1__Histogram));
+
+ if (metric->histogram == NULL) {
+ destroy_metric(metric);
+
+ return NULL;
+ }
+
+ opentelemetry__proto__metrics__v1__histogram__init(metric->histogram);
+
+ metric->data_case = OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_HISTOGRAM;
+ metric->histogram->data_points = initialize_histogram_data_point_list(data_point_count);
+
+ if (metric->histogram->data_points == NULL) {
+ destroy_metric(metric);
+
+ return NULL;
+ }
+
+ metric->histogram->n_data_points = data_point_count;
+ metric->histogram->aggregation_temporality = aggregation_temporality_type;
+ }
+
+ return metric;
+}
+
+static int append_data_point_to_metric(
+ Opentelemetry__Proto__Metrics__V1__Metric *metric,
+ void *data_point,
+ size_t data_point_slot_hint)
+{
+ void **data_point_list;
+ size_t data_point_slot_index;
+ size_t data_point_slot_count;
+
+ data_point_list = NULL;
+ data_point_slot_count = 0;
+
+ if (metric != NULL) {
+ if (metric->data_case == OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_SUM) {
+ data_point_list = (void **) metric->sum->data_points;
+ data_point_slot_count = metric->sum->n_data_points;
+ }
+ else if (metric->data_case == OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_GAUGE) {
+ data_point_list = (void **) metric->gauge->data_points;
+ data_point_slot_count = metric->gauge->n_data_points;
+ }
+ else if (metric->data_case == OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_SUMMARY) {
+ data_point_list = (void **) metric->summary->data_points;
+ data_point_slot_count = metric->summary->n_data_points;
+ }
+ else if (metric->data_case == OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_HISTOGRAM) {
+ data_point_list = (void **) metric->histogram->data_points;
+ data_point_slot_count = metric->histogram->n_data_points;
+ }
+ }
+
+ for (data_point_slot_index = data_point_slot_hint ;
+ data_point_slot_index < data_point_slot_count;
+ data_point_slot_index++) {
+ if (data_point_list[data_point_slot_index] == NULL) {
+ data_point_list[data_point_slot_index] = data_point;
+
+ return CMT_ENCODE_OPENTELEMETRY_SUCCESS;
+ }
+ }
+
+ return CMT_ENCODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR;
+}
+
+static void destroy_metric_list(
+ Opentelemetry__Proto__Metrics__V1__Metric **metric_list)
+{
+ size_t element_index;
+
+ if (metric_list != NULL) {
+ for (element_index = 0 ;
+ metric_list[element_index] != NULL ;
+ element_index++) {
+ destroy_metric(metric_list[element_index]);
+
+ metric_list[element_index] = NULL;
+ }
+
+ free(metric_list);
+ }
+}
+
+static Opentelemetry__Proto__Metrics__V1__Metric **
+ initialize_metric_list(
+ size_t element_count)
+{
+ Opentelemetry__Proto__Metrics__V1__Metric **metric_list;
+
+ metric_list = calloc(element_count + 1,
+ sizeof(Opentelemetry__Proto__Metrics__V1__Metric *));
+
+ if (metric_list == NULL) {
+ return NULL;
+ }
+
+ return metric_list;
+}
+
+static void destroy_opentelemetry_context(
+ struct cmt_opentelemetry_context *context)
+{
+ if (context != NULL) {
+ if (context->metrics_data != NULL) {
+ destroy_metrics_data(context->metrics_data);
+ }
+
+ free(context);
+ }
+}
+
+static Opentelemetry__Proto__Resource__V1__Resource *
+ initialize_resource(
+ struct cfl_kvlist *resource_root,
+ int *error_detection_flag)
+{
+ struct cfl_kvlist *attributes;
+ struct cfl_kvlist *metadata;
+ Opentelemetry__Proto__Resource__V1__Resource *resource;
+
+ *error_detection_flag = CMT_FALSE;
+
+ if (resource_root == NULL) {
+ return NULL;
+ }
+
+ attributes = fetch_metadata_kvlist_key(resource_root, "attributes");
+ metadata = fetch_metadata_kvlist_key(resource_root, "metadata");
+
+ if (cfl_kvlist_count(attributes) == 0 &&
+ cfl_kvlist_count(metadata) == 0) {
+ return NULL;
+ }
+
+ resource = \
+ calloc(1, sizeof(Opentelemetry__Proto__Resource__V1__Resource));
+
+ if (resource == NULL) {
+ *error_detection_flag = CMT_TRUE;
+
+ return NULL;
+ }
+
+ opentelemetry__proto__resource__v1__resource__init(resource);
+
+ resource->attributes = cfl_kvlist_to_otlp_kvpair_list(attributes);
+
+ if (resource->attributes == NULL) {
+ *error_detection_flag = CMT_TRUE;
+ }
+
+ resource->n_attributes = cfl_kvlist_count(attributes);
+
+ if (!(*error_detection_flag)) {
+ resource->dropped_attributes_count = (uint32_t) fetch_metadata_int64_key(
+ metadata,
+ "dropped_attributes_count",
+ error_detection_flag);
+ }
+
+ if (*error_detection_flag &&
+ resource != NULL) {
+ destroy_resource(resource);
+
+ resource = NULL;
+ }
+
+ return resource;
+}
+
+static struct cmt_opentelemetry_context *initialize_opentelemetry_context(
+ struct cmt *cmt)
+{
+ struct cfl_kvlist *resource_metrics_root;
+ struct cfl_kvlist *scope_metrics_root;
+ struct cfl_kvlist *resource_root;
+ struct cfl_kvlist *scope_root;
+ Opentelemetry__Proto__Resource__V1__Resource *resource;
+ struct cmt_opentelemetry_context *context;
+ int result;
+
+ resource_metrics_root = fetch_metadata_kvlist_key(cmt->external_metadata, "resource_metrics");
+ resource_root = fetch_metadata_kvlist_key(cmt->external_metadata, "resource");
+ scope_metrics_root = fetch_metadata_kvlist_key(cmt->external_metadata, "scope_metrics");
+ scope_root = fetch_metadata_kvlist_key(cmt->external_metadata, "scope");
+
+ result = CMT_ENCODE_OPENTELEMETRY_SUCCESS;
+
+ context = calloc(1, sizeof(struct cmt_opentelemetry_context));
+
+ if (context == NULL) {
+ result = CMT_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR;
+
+ goto cleanup;
+ }
+
+ memset(context, 0, sizeof(struct cmt_opentelemetry_context));
+
+ context->cmt = cmt;
+
+ resource = initialize_resource(resource_root, &result);
+
+ if (resource == NULL) {
+ if (result) {
+ result = CMT_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR;
+
+ goto cleanup;
+ }
+ }
+
+ context->metrics_data = initialize_metrics_data(1);
+
+ if (context->metrics_data == NULL) {
+ result = CMT_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR;
+
+ goto cleanup;
+ }
+
+ context->metrics_data->resource_metrics[0] = \
+ initialize_resource_metrics(resource_metrics_root, resource, 1);
+
+ if (context->metrics_data->resource_metrics[0] == NULL) {
+ result = CMT_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR;
+
+ goto cleanup;
+ }
+
+ context->metrics_data->resource_metrics[0]->scope_metrics[0] = \
+ initialize_scope_metrics(scope_metrics_root,
+ get_metric_count(cmt));
+
+ if (context->metrics_data->resource_metrics[0]->scope_metrics[0] == NULL) {
+ result = CMT_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR;
+
+ goto cleanup;
+ }
+
+ context->metrics_data->\
+ resource_metrics[0]->\
+ scope_metrics[0]->\
+ scope = \
+ initialize_instrumentation_scope(
+ scope_root, &result);
+
+ if (result) {
+ result = CMT_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR;
+
+ goto cleanup;
+ }
+
+cleanup:
+ if (result != CMT_ENCODE_OPENTELEMETRY_SUCCESS) {
+ if (context != NULL) {
+ destroy_opentelemetry_context(context);
+
+ context = NULL;
+ }
+ }
+
+ return context;
+}
+
+int append_sample_to_metric(struct cmt_opentelemetry_context *context,
+ Opentelemetry__Proto__Metrics__V1__Metric *metric,
+ struct cmt_map *map,
+ struct cmt_metric *sample,
+ size_t sample_index)
+{
+ size_t attribute_index;
+ size_t attribute_count;
+ Opentelemetry__Proto__Common__V1__KeyValue **attribute_list;
+ struct cmt_label *static_label;
+ struct cmt_map_label *label_value;
+ struct cmt_map_label *label_name;
+ void *data_point = NULL;
+ Opentelemetry__Proto__Common__V1__KeyValue *attribute;
+ struct cmt_histogram *histogram;
+ struct cmt_summary *summary;
+ int result;
+ struct cfl_list *head;
+
+ attribute_count = cfl_list_size(&context->cmt->static_labels->list) +
+ cfl_list_size(&sample->labels);
+
+ attribute_list = initialize_attribute_list(attribute_count);
+
+ if (attribute_list == NULL) {
+ return CMT_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+
+ if (map->type == CMT_COUNTER ||
+ map->type == CMT_GAUGE ||
+ map->type == CMT_UNTYPED ) {
+ data_point = initialize_numerical_data_point(0,
+ cmt_metric_get_timestamp(sample),
+ cmt_metric_get_value(sample),
+ attribute_list,
+ attribute_count);
+ }
+ else if (map->type == CMT_SUMMARY) {
+ summary = (struct cmt_summary *) map->parent;
+
+ data_point = initialize_summary_data_point(0,
+ cmt_metric_get_timestamp(sample),
+ cmt_summary_get_count_value(sample),
+ cmt_summary_get_sum_value(sample),
+ summary->quantiles_count,
+ summary->quantiles,
+ summary->quantiles_count,
+ sample->sum_quantiles,
+ attribute_list,
+ attribute_count);
+ }
+ else if (map->type == CMT_HISTOGRAM) {
+ histogram = (struct cmt_histogram *) map->parent;
+
+ data_point = initialize_histogram_data_point(0,
+ cmt_metric_get_timestamp(sample),
+ cmt_metric_hist_get_count_value(sample),
+ cmt_metric_hist_get_sum_value(sample),
+ histogram->buckets->count + 1,
+ sample->hist_buckets,
+ histogram->buckets->count,
+ histogram->buckets->upper_bounds,
+ attribute_list,
+ attribute_count);
+ }
+
+ if (data_point == NULL) {
+ destroy_attribute_list(attribute_list);
+
+ return CMT_ENCODE_OPENTELEMETRY_DATA_POINT_INIT_ERROR;
+ }
+
+ attribute_index = 0;
+
+ cfl_list_foreach(head, &context->cmt->static_labels->list) {
+ static_label = cfl_list_entry(head, struct cmt_label, _head);
+
+ attribute = initialize_string_attribute(static_label->key,
+ static_label->val);
+
+ if (attribute == NULL) {
+ destroy_data_point(data_point, map->type);
+
+ return CMT_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+
+ result = append_attribute_to_data_point(data_point,
+ map->type,
+ attribute,
+ attribute_index++);
+
+ if (result != CMT_ENCODE_OPENTELEMETRY_SUCCESS)
+ {
+ destroy_data_point(data_point, map->type);
+
+ return result;
+ }
+ }
+
+ label_name = cfl_list_entry_first(&map->label_keys, struct cmt_map_label, _head);
+
+ cfl_list_foreach(head, &sample->labels) {
+ label_value = cfl_list_entry(head, struct cmt_map_label, _head);
+
+ attribute = initialize_string_attribute(label_name->name,
+ label_value->name);
+
+ if (attribute == NULL) {
+ destroy_data_point(data_point, map->type);
+
+ return CMT_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+
+ result = append_attribute_to_data_point(data_point,
+ map->type,
+ attribute,
+ attribute_index++);
+
+ if (result != CMT_ENCODE_OPENTELEMETRY_SUCCESS)
+ {
+ destroy_data_point(data_point, map->type);
+
+ return result;
+ }
+
+ label_name = cfl_list_entry_next(&label_name->_head, struct cmt_map_label,
+ _head, &map->label_keys);
+ }
+
+ result = append_data_point_to_metric(metric, (void *) data_point, sample_index);
+
+ if (result != CMT_ENCODE_OPENTELEMETRY_SUCCESS) {
+ destroy_data_point(data_point, map->type);
+
+ return result;
+ }
+
+ return result;
+}
+
+int pack_basic_type(struct cmt_opentelemetry_context *context,
+ struct cmt_map *map,
+ size_t *metric_index)
+{
+ int aggregation_temporality_type;
+ int monotonism_flag;
+ size_t sample_index;
+ size_t sample_count;
+ struct cmt_counter *counter;
+ struct cmt_metric *sample;
+ Opentelemetry__Proto__Metrics__V1__Metric *metric;
+ int result;
+ struct cfl_list *head;
+
+ sample_count = 0;
+
+ if (map->metric_static_set) {
+ sample_count++;
+ }
+
+ sample_count += cfl_list_size(&map->metrics);
+
+ if (sample_count == 0) {
+ return CMT_ENCODE_OPENTELEMETRY_SUCCESS;
+ }
+
+ aggregation_temporality_type = OPENTELEMETRY__PROTO__METRICS__V1__AGGREGATION_TEMPORALITY__AGGREGATION_TEMPORALITY_UNSPECIFIED;
+ monotonism_flag = CMT_FALSE;
+
+ if (map->type == CMT_COUNTER) {
+ if (map->parent != NULL) {
+ counter = (struct cmt_counter *) map->parent;
+
+ if (counter->aggregation_type == CMT_AGGREGATION_TYPE_DELTA) {
+ aggregation_temporality_type = OPENTELEMETRY__PROTO__METRICS__V1__AGGREGATION_TEMPORALITY__AGGREGATION_TEMPORALITY_DELTA;
+ }
+ else if (counter->aggregation_type == CMT_AGGREGATION_TYPE_CUMULATIVE) {
+ aggregation_temporality_type = OPENTELEMETRY__PROTO__METRICS__V1__AGGREGATION_TEMPORALITY__AGGREGATION_TEMPORALITY_CUMULATIVE;
+ }
+
+ monotonism_flag = !counter->allow_reset;
+ }
+ }
+
+ metric = initialize_metric(map->type,
+ map->opts->fqname,
+ map->opts->description,
+ map->unit,
+ monotonism_flag,
+ aggregation_temporality_type,
+ sample_count);
+
+ if (metric == NULL) {
+ return CMT_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR;
+ }
+
+ sample_index = 0;
+
+ if (map->metric_static_set) {
+ result = append_sample_to_metric(context,
+ metric,
+ map,
+ &map->metric,
+ sample_index++);
+
+ if (result != CMT_ENCODE_OPENTELEMETRY_SUCCESS) {
+ destroy_metric(metric);
+
+ return result;
+ }
+ }
+
+ cfl_list_foreach(head, &map->metrics) {
+ sample = cfl_list_entry(head, struct cmt_metric, _head);
+
+ result = append_sample_to_metric(context,
+ metric,
+ map,
+ sample,
+ sample_index++);
+
+ if (result != CMT_ENCODE_OPENTELEMETRY_SUCCESS) {
+ destroy_metric(metric);
+
+ return result;
+ }
+ }
+
+ result = append_metric_to_scope_metrics(
+ context->\
+ metrics_data->\
+ resource_metrics[0]->\
+ scope_metrics[0],
+ metric,
+ *metric_index);
+
+ if (result != CMT_ENCODE_OPENTELEMETRY_SUCCESS) {
+ destroy_metric(metric);
+
+ return result;
+ }
+
+ (*metric_index)++;
+
+ return result;
+}
+
+static cfl_sds_t render_opentelemetry_context_to_sds(
+ struct cmt_opentelemetry_context *context)
+{
+ cfl_sds_t result_buffer;
+ size_t result_size;
+
+ result_size = opentelemetry__proto__metrics__v1__metrics_data__get_packed_size(
+ context->metrics_data);
+
+ result_buffer = cfl_sds_create_size(result_size);
+
+ if(result_buffer != NULL) {
+ opentelemetry__proto__metrics__v1__metrics_data__pack(
+ context->metrics_data,
+ (uint8_t *) result_buffer);
+
+ cfl_sds_set_len(result_buffer, result_size);
+ }
+
+ return result_buffer;
+}
+
+cfl_sds_t cmt_encode_opentelemetry_create(struct cmt *cmt)
+{
+ size_t metric_index;
+ struct cmt_opentelemetry_context *context;
+ struct cmt_histogram *histogram;
+ struct cmt_summary *summary;
+ struct cmt_untyped *untyped;
+ struct cmt_counter *counter;
+ int result;
+ struct cmt_gauge *gauge;
+ struct cfl_list *head;
+ cfl_sds_t buf;
+
+ buf = NULL;
+ result = 0;
+
+ context = initialize_opentelemetry_context(cmt);
+
+ if (context == NULL) {
+ return NULL;
+ }
+
+ metric_index = 0;
+
+ if (result == CMT_ENCODE_OPENTELEMETRY_SUCCESS) {
+ cfl_list_foreach(head, &cmt->counters) {
+ counter = cfl_list_entry(head, struct cmt_counter, _head);
+ result = pack_basic_type(context, counter->map, &metric_index);
+
+ if (result != CMT_ENCODE_OPENTELEMETRY_SUCCESS) {
+ break;
+ }
+ }
+ }
+
+ if (result == CMT_ENCODE_OPENTELEMETRY_SUCCESS) {
+ cfl_list_foreach(head, &cmt->gauges) {
+ gauge = cfl_list_entry(head, struct cmt_gauge, _head);
+ result = pack_basic_type(context, gauge->map, &metric_index);
+
+ if (result != CMT_ENCODE_OPENTELEMETRY_SUCCESS) {
+ break;
+ }
+ }
+ }
+
+ if (result == CMT_ENCODE_OPENTELEMETRY_SUCCESS) {
+ cfl_list_foreach(head, &cmt->untypeds) {
+ untyped = cfl_list_entry(head, struct cmt_untyped, _head);
+ result = pack_basic_type(context, untyped->map, &metric_index);
+
+ if (result != CMT_ENCODE_OPENTELEMETRY_SUCCESS) {
+ break;
+ }
+ }
+ }
+
+ if (result == CMT_ENCODE_OPENTELEMETRY_SUCCESS) {
+ cfl_list_foreach(head, &cmt->summaries) {
+ summary = cfl_list_entry(head, struct cmt_summary, _head);
+ result = pack_basic_type(context, summary->map, &metric_index);
+
+ if (result != CMT_ENCODE_OPENTELEMETRY_SUCCESS) {
+ break;
+ }
+ }
+ }
+
+ if (result == CMT_ENCODE_OPENTELEMETRY_SUCCESS) {
+ cfl_list_foreach(head, &cmt->histograms) {
+ histogram = cfl_list_entry(head, struct cmt_histogram, _head);
+ result = pack_basic_type(context, histogram->map, &metric_index);
+
+ if (result != CMT_ENCODE_OPENTELEMETRY_SUCCESS) {
+ break;
+ }
+ }
+ }
+
+ if (result == CMT_ENCODE_OPENTELEMETRY_SUCCESS) {
+ buf = render_opentelemetry_context_to_sds(context);
+ }
+
+ destroy_opentelemetry_context(context);
+
+ return buf;
+}
+
+void cmt_encode_opentelemetry_destroy(cfl_sds_t text)
+{
+ cfl_sds_destroy(text);
+}
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_encode_prometheus.c b/src/fluent-bit/lib/cmetrics/src/cmt_encode_prometheus.c
new file mode 100644
index 000000000..287d701bc
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_encode_prometheus.c
@@ -0,0 +1,543 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <stdbool.h>
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_metric.h>
+#include <cmetrics/cmt_map.h>
+#include <cmetrics/cmt_counter.h>
+#include <cmetrics/cmt_gauge.h>
+#include <cmetrics/cmt_summary.h>
+#include <cmetrics/cmt_histogram.h>
+
+#include <cmetrics/cmt_untyped.h>
+#include <cmetrics/cmt_compat.h>
+
+#define PROM_FMT_VAL_FROM_VAL 0
+#define PROM_FMT_VAL_FROM_BUCKET_ID 1
+#define PROM_FMT_VAL_FROM_QUANTILE 2
+#define PROM_FMT_VAL_FROM_SUM 3
+#define PROM_FMT_VAL_FROM_COUNT 4
+
+struct prom_fmt {
+ int metric_name; /* metric name already set ? */
+ int brace_open; /* first brace open ? */
+ int labels_count; /* number of labels aready added */
+ int value_from;
+
+ /*
+ * For value_from 'PROM_FMT_VAL_FROM_BUCKET_ID', the 'id' belongs to a bucket
+ * id position, if is 'PROM_FMT_VAL_FROM_QUANTILE' the value represents a
+ * sum_quantiles position.
+ */
+ int id;
+};
+
+static void prom_fmt_init(struct prom_fmt *fmt)
+{
+ fmt->metric_name = CMT_FALSE;
+ fmt->brace_open = CMT_FALSE;
+ fmt->labels_count = 0;
+ fmt->value_from = PROM_FMT_VAL_FROM_VAL;
+ fmt->id = -1;
+}
+
+/*
+ * Prometheus Exposition Format
+ * ----------------------------
+ * https://github.com/prometheus/docs/blob/master/content/docs/instrumenting/exposition_formats.md
+ */
+
+static void metric_escape(cfl_sds_t *buf, cfl_sds_t description, bool escape_quote)
+{
+ int i;
+ size_t len;
+
+ len = cfl_sds_len(description);
+
+ for (i = 0; i < len; i++) {
+ switch (description[i]) {
+ case '\\':
+ cfl_sds_cat_safe(buf, "\\\\", 2);
+ break;
+ case '\n':
+ cfl_sds_cat_safe(buf, "\\n", 2);
+ break;
+ case '"':
+ if (escape_quote) {
+ cfl_sds_cat_safe(buf, "\\\"", 2);
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ cfl_sds_cat_safe(buf, description + i, 1);
+ break;
+ }
+ }
+}
+
+static void metric_banner(cfl_sds_t *buf, struct cmt_map *map,
+ struct cmt_metric *metric)
+{
+ struct cmt_opts *opts;
+
+ opts = map->opts;
+
+ /* HELP */
+ cfl_sds_cat_safe(buf, "# HELP ", 7);
+ cfl_sds_cat_safe(buf, opts->fqname, cfl_sds_len(opts->fqname));
+
+ if (cfl_sds_len(opts->description) > 1 || opts->description[0] != ' ') {
+ /* only append description if it is not empty. the parser uses a single whitespace
+ * string to signal that no HELP was provided */
+ cfl_sds_cat_safe(buf, " ", 1);
+ metric_escape(buf, opts->description, false);
+ }
+ cfl_sds_cat_safe(buf, "\n", 1);
+
+ /* TYPE */
+ cfl_sds_cat_safe(buf, "# TYPE ", 7);
+ cfl_sds_cat_safe(buf, opts->fqname, cfl_sds_len(opts->fqname));
+
+ if (map->type == CMT_COUNTER) {
+ cfl_sds_cat_safe(buf, " counter\n", 9);
+ }
+ else if (map->type == CMT_GAUGE) {
+ cfl_sds_cat_safe(buf, " gauge\n", 7);
+ }
+ else if (map->type == CMT_SUMMARY) {
+ cfl_sds_cat_safe(buf, " summary\n", 9);
+ }
+ else if (map->type == CMT_HISTOGRAM) {
+ cfl_sds_cat_safe(buf, " histogram\n", 11);
+ }
+ else if (map->type == CMT_UNTYPED) {
+ cfl_sds_cat_safe(buf, " untyped\n", 9);
+ }
+}
+
+static void append_metric_value(cfl_sds_t *buf,
+ struct cmt_map *map,
+ struct cmt_metric *metric,
+ struct prom_fmt *fmt, int add_timestamp)
+{
+ int len;
+ double val;
+ uint64_t ts;
+ char tmp[128];
+
+ /*
+ * Retrieve metric value
+ * ---------------------
+ * the formatter 'fmt->value_from' specifies from 'where' the value must
+ * be retrieved from, note the 'metric' structure contains one generic
+ * value field plus others associated to histograms.
+ */
+ if (fmt->value_from == PROM_FMT_VAL_FROM_VAL) {
+ /* get 'normal' metric value */
+ val = cmt_metric_get_value(metric);
+ }
+ else if (fmt->value_from == PROM_FMT_VAL_FROM_BUCKET_ID) {
+ /* retrieve the value from a bucket */
+ val = cmt_metric_hist_get_value(metric, fmt->id);
+ }
+ else if (fmt->value_from == PROM_FMT_VAL_FROM_QUANTILE) {
+ /* retrieve the value from a bucket */
+ val = cmt_summary_quantile_get_value(metric, fmt->id);
+ }
+ else {
+ if (map->type == CMT_HISTOGRAM) {
+ if (fmt->value_from == PROM_FMT_VAL_FROM_SUM) {
+ val = cmt_metric_hist_get_sum_value(metric);
+ }
+ else if (fmt->value_from == PROM_FMT_VAL_FROM_COUNT) {
+ val = cmt_metric_hist_get_count_value(metric);
+ }
+ }
+ else if (map->type == CMT_SUMMARY) {
+ if (fmt->value_from == PROM_FMT_VAL_FROM_SUM) {
+ val = cmt_summary_get_sum_value(metric);
+ }
+ else if (fmt->value_from == PROM_FMT_VAL_FROM_COUNT) {
+ val = cmt_summary_get_count_value(metric);
+ }
+ }
+ }
+
+ if (add_timestamp) {
+ ts = cmt_metric_get_timestamp(metric);
+
+ /* convert from nanoseconds to milliseconds */
+ ts /= 1000000;
+
+ len = snprintf(tmp, sizeof(tmp) - 1, " %.17g %" PRIu64 "\n", val, ts);
+ }
+ else {
+ len = snprintf(tmp, sizeof(tmp) - 1, " %.17g\n", val);
+ }
+ cfl_sds_cat_safe(buf, tmp, len);
+}
+
+static int add_label(cfl_sds_t *buf, cfl_sds_t key, cfl_sds_t val)
+{
+ cfl_sds_cat_safe(buf, key, cfl_sds_len(key));
+ cfl_sds_cat_safe(buf, "=\"", 2);
+ metric_escape(buf, val, true);
+ cfl_sds_cat_safe(buf, "\"", 1);
+
+ return 1;
+}
+
+static int add_static_labels(struct cmt *cmt, cfl_sds_t *buf)
+{
+ int count = 0;
+ int total = 0;
+ struct cfl_list *head;
+ struct cmt_label *label;
+
+ total = cfl_list_size(&cmt->static_labels->list);
+ cfl_list_foreach(head, &cmt->static_labels->list) {
+ label = cfl_list_entry(head, struct cmt_label, _head);
+
+ count += add_label(buf, label->key, label->val);
+ if (count < total) {
+ cfl_sds_cat_safe(buf, ",", 1);
+ }
+ }
+
+ return count;
+}
+
+static void format_metric(struct cmt *cmt,
+ cfl_sds_t *buf, struct cmt_map *map,
+ struct cmt_metric *metric, int add_timestamp,
+ struct prom_fmt *fmt)
+{
+ int i;
+ int static_labels = 0;
+ int defined_labels = 0;
+ struct cmt_map_label *label_k;
+ struct cmt_map_label *label_v;
+ struct cfl_list *head;
+ struct cmt_opts *opts;
+
+ opts = map->opts;
+
+ /* Metric info */
+ if (!fmt->metric_name) {
+ cfl_sds_cat_safe(buf, opts->fqname, cfl_sds_len(opts->fqname));
+ }
+
+ /* Static labels */
+ static_labels = cmt_labels_count(cmt->static_labels);
+ cfl_list_foreach(head, &metric->labels) {
+ label_v = cfl_list_entry(head, struct cmt_map_label, _head);
+ if (strlen(label_v->name)) {
+ defined_labels++;
+ }
+ }
+
+ if (!fmt->brace_open && (static_labels + defined_labels > 0)) {
+ cfl_sds_cat_safe(buf, "{", 1);
+ }
+
+ if (static_labels > 0) {
+ /* if some labels were added before, add the separator */
+ if (fmt->labels_count > 0) {
+ cfl_sds_cat_safe(buf, ",", 1);
+ }
+ fmt->labels_count += add_static_labels(cmt, buf);
+ }
+
+ /* Append api defined labels */
+ if (defined_labels > 0) {
+ if (fmt->labels_count > 0) {
+ cfl_sds_cat_safe(buf, ",", 1);
+ }
+
+ i = 1;
+ label_k = cfl_list_entry_first(&map->label_keys, struct cmt_map_label, _head);
+ cfl_list_foreach(head, &metric->labels) {
+ label_v = cfl_list_entry(head, struct cmt_map_label, _head);
+
+ if (strlen(label_v->name)) {
+ fmt->labels_count += add_label(buf, label_k->name, label_v->name);
+ if (i < defined_labels) {
+ cfl_sds_cat_safe(buf, ",", 1);
+ }
+
+ i++;
+ }
+
+ label_k = cfl_list_entry_next(&label_k->_head, struct cmt_map_label,
+ _head, &map->label_keys);
+ }
+ }
+
+ if (fmt->labels_count > 0) {
+ cfl_sds_cat_safe(buf, "}", 1);
+ }
+
+ append_metric_value(buf, map, metric, fmt, add_timestamp);
+}
+
+static cfl_sds_t bucket_value_to_string(double val)
+{
+ int len;
+ cfl_sds_t str;
+
+ str = cfl_sds_create_size(64);
+ if (!str) {
+ return NULL;
+ }
+
+ len = snprintf(str, 64, "%g", val);
+ cfl_sds_len_set(str, len);
+
+ if (!strchr(str, '.')) {
+ cfl_sds_cat_safe(&str, ".0", 2);
+ }
+
+ return str;
+}
+
+static void format_histogram_bucket(struct cmt *cmt,
+ cfl_sds_t *buf, struct cmt_map *map,
+ struct cmt_metric *metric, int add_timestamp)
+{
+ int i;
+ cfl_sds_t val;
+ struct cmt_histogram *histogram;
+ struct cmt_histogram_buckets *bucket;
+ struct cmt_opts *opts;
+ struct prom_fmt fmt = {0};
+
+ histogram = (struct cmt_histogram *) map->parent;
+ bucket = histogram->buckets;
+ opts = map->opts;
+
+ for (i = 0; i <= bucket->count; i++) {
+ /* metric name */
+ cfl_sds_cat_safe(buf, opts->fqname, cfl_sds_len(opts->fqname));
+ cfl_sds_cat_safe(buf, "_bucket", 7);
+
+ /* upper bound */
+ cfl_sds_cat_safe(buf, "{le=\"", 5);
+
+ if (i < bucket->count) {
+ val = bucket_value_to_string(bucket->upper_bounds[i]);
+ cfl_sds_cat_safe(buf, val, cfl_sds_len(val));
+ cfl_sds_destroy(val);
+ }
+ else {
+ cfl_sds_cat_safe(buf, "+Inf", 4);
+ }
+ cfl_sds_cat_safe(buf, "\"", 1);
+
+ /* configure formatter */
+ fmt.metric_name = CMT_TRUE;
+ fmt.brace_open = CMT_TRUE;
+ fmt.labels_count = 1;
+ fmt.value_from = PROM_FMT_VAL_FROM_BUCKET_ID;
+ fmt.id = i;
+
+ /* append metric labels, value and timestamp */
+ format_metric(cmt, buf, map, metric, add_timestamp, &fmt);
+ }
+
+ /* sum */
+ prom_fmt_init(&fmt);
+ fmt.metric_name = CMT_TRUE;
+ fmt.value_from = PROM_FMT_VAL_FROM_SUM;
+
+ cfl_sds_cat_safe(buf, opts->fqname, cfl_sds_len(opts->fqname));
+ cfl_sds_cat_safe(buf, "_sum", 4);
+ format_metric(cmt, buf, map, metric, add_timestamp, &fmt);
+
+ /* count */
+ fmt.labels_count = 0;
+ fmt.value_from = PROM_FMT_VAL_FROM_COUNT;
+
+ cfl_sds_cat_safe(buf, opts->fqname, cfl_sds_len(opts->fqname));
+ cfl_sds_cat_safe(buf, "_count", 6);
+ format_metric(cmt, buf, map, metric, add_timestamp, &fmt);
+}
+
+static void format_summary_quantiles(struct cmt *cmt,
+ cfl_sds_t *buf, struct cmt_map *map,
+ struct cmt_metric *metric, int add_timestamp)
+{
+ int i;
+ cfl_sds_t val;
+ struct cmt_summary *summary;
+ struct cmt_opts *opts;
+ struct prom_fmt fmt = {0};
+
+ summary = (struct cmt_summary *) map->parent;
+ opts = map->opts;
+
+ if (metric->sum_quantiles_set) {
+ for (i = 0; i < summary->quantiles_count; i++) {
+ /* metric name */
+ cfl_sds_cat_safe(buf, opts->fqname, cfl_sds_len(opts->fqname));
+
+ /* quantiles */
+ cfl_sds_cat_safe(buf, "{quantile=\"", 11);
+ val = bucket_value_to_string(summary->quantiles[i]);
+ cfl_sds_cat_safe(buf, val, cfl_sds_len(val));
+ cfl_sds_destroy(val);
+ cfl_sds_cat_safe(buf, "\"", 1);
+
+ /* configure formatter */
+ fmt.metric_name = CMT_TRUE;
+ fmt.brace_open = CMT_TRUE;
+ fmt.labels_count = 1;
+ fmt.value_from = PROM_FMT_VAL_FROM_QUANTILE;
+ fmt.id = i;
+
+ /* append metric labels, value and timestamp */
+ format_metric(cmt, buf, map, metric, add_timestamp, &fmt);
+ }
+ }
+
+ /* sum */
+ prom_fmt_init(&fmt);
+ fmt.metric_name = CMT_TRUE;
+ fmt.value_from = PROM_FMT_VAL_FROM_SUM;
+
+ cfl_sds_cat_safe(buf, opts->fqname, cfl_sds_len(opts->fqname));
+ cfl_sds_cat_safe(buf, "_sum", 4);
+ format_metric(cmt, buf, map, metric, add_timestamp, &fmt);
+
+ /* count */
+ fmt.labels_count = 0;
+ fmt.value_from = PROM_FMT_VAL_FROM_COUNT;
+
+ cfl_sds_cat_safe(buf, opts->fqname, cfl_sds_len(opts->fqname));
+ cfl_sds_cat_safe(buf, "_count", 6);
+ format_metric(cmt, buf, map, metric, add_timestamp, &fmt);
+}
+
+static void format_metrics(struct cmt *cmt, cfl_sds_t *buf, struct cmt_map *map,
+ int add_timestamp)
+{
+ int banner_set = CMT_FALSE;
+ struct cfl_list *head;
+ struct cmt_metric *metric;
+ struct prom_fmt fmt = {0};
+
+ /* Simple metric, no labels */
+ if (map->metric_static_set) {
+ metric_banner(buf, map, &map->metric);
+ banner_set = CMT_TRUE;
+
+ if (map->type == CMT_HISTOGRAM) {
+ /* Histogram needs to format the buckets, one line per bucket */
+ format_histogram_bucket(cmt, buf, map, &map->metric, add_timestamp);
+ }
+ else if (map->type == CMT_SUMMARY) {
+ /* Histogram needs to format the buckets, one line per bucket */
+ format_summary_quantiles(cmt, buf, map, &map->metric, add_timestamp);
+ }
+ else {
+ prom_fmt_init(&fmt);
+ format_metric(cmt, buf, map, &map->metric, add_timestamp, &fmt);
+ }
+ }
+
+ if (cfl_list_size(&map->metrics) > 0) {
+ metric = cfl_list_entry_first(&map->metrics, struct cmt_metric, _head);
+ if (!banner_set) {
+ metric_banner(buf, map, metric);
+ }
+ }
+
+ cfl_list_foreach(head, &map->metrics) {
+ metric = cfl_list_entry(head, struct cmt_metric, _head);
+
+ /* Format the metric based on its type */
+ if (map->type == CMT_HISTOGRAM) {
+ /* Histogram needs to format the buckets, one line per bucket */
+ format_histogram_bucket(cmt, buf, map, metric, add_timestamp);
+ }
+ else if (map->type == CMT_SUMMARY) {
+ format_summary_quantiles(cmt, buf, map, metric, add_timestamp);
+ }
+ else {
+ prom_fmt_init(&fmt);
+ format_metric(cmt, buf, map, metric, add_timestamp, &fmt);
+ }
+ }
+}
+
+/* Format all the registered metrics in Prometheus Text format */
+cfl_sds_t cmt_encode_prometheus_create(struct cmt *cmt, int add_timestamp)
+{
+ cfl_sds_t buf;
+ struct cfl_list *head;
+ struct cmt_counter *counter;
+ struct cmt_gauge *gauge;
+ struct cmt_summary *summary;
+ struct cmt_histogram *histogram;
+ struct cmt_untyped *untyped;
+
+ /* Allocate a 1KB of buffer */
+ buf = cfl_sds_create_size(1024);
+ if (!buf) {
+ return NULL;
+ }
+
+ /* Counters */
+ cfl_list_foreach(head, &cmt->counters) {
+ counter = cfl_list_entry(head, struct cmt_counter, _head);
+ format_metrics(cmt, &buf, counter->map, add_timestamp);
+ }
+
+ /* Gauges */
+ cfl_list_foreach(head, &cmt->gauges) {
+ gauge = cfl_list_entry(head, struct cmt_gauge, _head);
+ format_metrics(cmt, &buf, gauge->map, add_timestamp);
+ }
+
+ /* Summaries */
+ cfl_list_foreach(head, &cmt->summaries) {
+ summary = cfl_list_entry(head, struct cmt_summary, _head);
+ format_metrics(cmt, &buf, summary->map, add_timestamp);
+ }
+
+ /* Histograms */
+ cfl_list_foreach(head, &cmt->histograms) {
+ histogram = cfl_list_entry(head, struct cmt_histogram, _head);
+ format_metrics(cmt, &buf, histogram->map, add_timestamp);
+ }
+
+ /* Untyped */
+ cfl_list_foreach(head, &cmt->untypeds) {
+ untyped = cfl_list_entry(head, struct cmt_untyped, _head);
+ format_metrics(cmt, &buf, untyped->map, add_timestamp);
+ }
+
+ return buf;
+}
+
+void cmt_encode_prometheus_destroy(cfl_sds_t text)
+{
+ cfl_sds_destroy(text);
+}
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_encode_prometheus_remote_write.c b/src/fluent-bit/lib/cmetrics/src/cmt_encode_prometheus_remote_write.c
new file mode 100644
index 000000000..2aede25e7
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_encode_prometheus_remote_write.c
@@ -0,0 +1,1131 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_metric.h>
+#include <cmetrics/cmt_map.h>
+#include <cmetrics/cmt_counter.h>
+#include <cmetrics/cmt_summary.h>
+#include <cmetrics/cmt_histogram.h>
+#include <cmetrics/cmt_gauge.h>
+#include <cmetrics/cmt_untyped.h>
+#include <cmetrics/cmt_encode_prometheus_remote_write.h>
+
+#define SYNTHETIC_METRIC_SUMMARY_COUNT_SEQUENCE_DELTA 10000000
+#define SYNTHETIC_METRIC_SUMMARY_SUM_SEQUENCE_DELTA 100000000
+
+#define SYNTHETIC_METRIC_HISTOGRAM_COUNT_SEQUENCE_DELTA 10000000
+#define SYNTHETIC_METRIC_HISTOGRAM_SUM_SEQUENCE_DELTA 100000000
+
+static cfl_sds_t render_remote_write_context_to_sds(
+ struct cmt_prometheus_remote_write_context *context);
+
+static void destroy_prometheus_label_list(Prometheus__Label **label_list,
+ size_t entry_count);
+
+static void destroy_prometheus_sample_list(Prometheus__Sample **sample_list,
+ size_t entry_count);
+
+static void cmt_destroy_prometheus_remote_write_context(
+ struct cmt_prometheus_remote_write_context *context);
+
+static uint64_t calculate_label_set_hash(struct cfl_list *label_values, uint64_t seed);
+
+static size_t count_metrics_with_matching_label_set(struct cfl_list *metrics,
+ uint64_t sequence_number,
+ uint64_t desired_hash);
+
+static int append_entry_to_prometheus_label_list(Prometheus__Label **label_list,
+ size_t *index,
+ char *name,
+ char *value);
+
+static int set_up_time_series_for_label_set(
+ struct cmt_prometheus_remote_write_context *context,
+ struct cmt_map *map,
+ struct cmt_metric *metric,
+ struct cmt_prometheus_time_series **time_series);
+
+static int pack_metric_metadata(struct cmt_prometheus_remote_write_context *context,
+ struct cmt_map *map,
+ struct cmt_metric *metric);
+
+static int append_metric_to_timeseries(struct cmt_prometheus_time_series *time_series,
+ struct cmt_metric *metric);
+
+static int pack_basic_type(struct cmt_prometheus_remote_write_context *context,
+ struct cmt_map *map);
+
+static void destroy_label(struct cmt_map_label *instance)
+{
+ if (instance != NULL) {
+ if (instance->name != NULL) {
+ cfl_sds_destroy(instance->name);
+ }
+
+ free(instance);
+ }
+}
+
+static struct cmt_map_label *create_label(char *caption)
+{
+ struct cmt_map_label *instance;
+
+ instance = calloc(1, sizeof(struct cmt_map_label));
+
+ if (instance != NULL) {
+ if (caption != NULL) {
+ instance->name = cfl_sds_create(caption);
+
+ if (instance->name == NULL) {
+ cmt_errno();
+
+ free(instance);
+
+ instance = NULL;
+ }
+ }
+ }
+
+ return instance;
+}
+
+cfl_sds_t render_remote_write_context_to_sds(
+ struct cmt_prometheus_remote_write_context *context)
+{
+ size_t write_request_size;
+ struct cmt_prometheus_time_series *time_series_entry;
+ struct cmt_prometheus_metric_metadata *metadata_entry;
+ cfl_sds_t result_buffer;
+ size_t entry_index;
+ struct cfl_list *head;
+
+ context->write_request.n_timeseries = cfl_list_size(&context->time_series_entries);
+ context->write_request.n_metadata = cfl_list_size(&context->metadata_entries);
+
+ context->write_request.timeseries = calloc(context->write_request.n_timeseries,
+ sizeof(Prometheus__TimeSeries *));
+
+ if (context->write_request.timeseries == NULL) {
+ cmt_errno();
+
+ return NULL;
+ }
+
+ context->write_request.metadata = calloc(context->write_request.n_metadata,
+ sizeof(Prometheus__TimeSeries *));
+
+ if (context->write_request.metadata == NULL) {
+ cmt_errno();
+
+ free(context->write_request.timeseries);
+
+ return NULL;
+ }
+
+ entry_index = 0;
+
+ cfl_list_foreach(head, &context->time_series_entries) {
+ time_series_entry = cfl_list_entry(head, struct cmt_prometheus_time_series, _head);
+
+ context->write_request.timeseries[entry_index++] = &time_series_entry->data;
+ }
+
+ entry_index = 0;
+
+ cfl_list_foreach(head, &context->metadata_entries) {
+ metadata_entry = cfl_list_entry(head, struct cmt_prometheus_metric_metadata, _head);
+
+ context->write_request.metadata[entry_index++] = &metadata_entry->data;
+ }
+
+ write_request_size = prometheus__write_request__get_packed_size(&context->write_request);
+
+ result_buffer = cfl_sds_create_size(write_request_size);
+
+ if(result_buffer != NULL) {
+ prometheus__write_request__pack(&context->write_request, (uint8_t *) result_buffer);
+
+ cfl_sds_set_len(result_buffer, write_request_size);
+ }
+
+ free(context->write_request.timeseries);
+
+ free(context->write_request.metadata);
+
+ return result_buffer;
+}
+
+void cmt_destroy_prometheus_remote_write_context(
+ struct cmt_prometheus_remote_write_context *context)
+{
+ struct cmt_prometheus_time_series *time_series_entry;
+ struct cmt_prometheus_metric_metadata *metadata_entry;
+ struct cfl_list *head;
+ struct cfl_list *tmp;
+
+ cfl_list_foreach_safe(head, tmp, &context->time_series_entries) {
+ time_series_entry = cfl_list_entry(head, struct cmt_prometheus_time_series, _head);
+
+ if (time_series_entry->data.labels != NULL) {
+ destroy_prometheus_label_list(time_series_entry->data.labels,
+ time_series_entry->data.n_labels);
+
+ time_series_entry->data.labels = NULL;
+ }
+
+ if (time_series_entry->data.samples != NULL) {
+ destroy_prometheus_sample_list(time_series_entry->data.samples,
+ time_series_entry->data.n_samples);
+
+ time_series_entry->data.samples = NULL;
+ }
+
+ cfl_list_del(&time_series_entry->_head);
+
+ free(time_series_entry);
+ }
+
+ cfl_list_foreach_safe(head, tmp, &context->metadata_entries) {
+ metadata_entry = cfl_list_entry(head, struct cmt_prometheus_metric_metadata, _head);
+
+ if (metadata_entry->data.metric_family_name != NULL) {
+ cfl_sds_destroy(metadata_entry->data.metric_family_name);
+ }
+
+ if (metadata_entry->data.help != NULL) {
+ cfl_sds_destroy(metadata_entry->data.help);
+ }
+
+ if (metadata_entry->data.unit != NULL) {
+ cfl_sds_destroy(metadata_entry->data.unit);
+ }
+
+ cfl_list_del(&metadata_entry->_head);
+
+ free(metadata_entry);
+ }
+}
+
+uint64_t calculate_label_set_hash(struct cfl_list *label_values, uint64_t seed)
+{
+ struct cmt_map_label *label_value;
+ cfl_hash_state_t state;
+ struct cfl_list *head;
+
+ cfl_hash_64bits_reset(&state);
+ cfl_hash_64bits_update(&state, &seed, sizeof(uint64_t));
+
+ cfl_list_foreach(head, label_values) {
+ label_value = cfl_list_entry(head, struct cmt_map_label, _head);
+
+ if (label_value->name == NULL) {
+ cfl_hash_64bits_update(&state, "_NULL_", 6);
+ }
+ else {
+ cfl_hash_64bits_update(&state, label_value->name, cfl_sds_len(label_value->name));
+ }
+ }
+
+ return cfl_hash_64bits_digest(&state);
+}
+
+size_t count_metrics_with_matching_label_set(struct cfl_list *metrics,
+ uint64_t sequence_number,
+ uint64_t desired_hash)
+{
+ uint64_t label_set_hash;
+ size_t matches;
+ struct cmt_metric *metric;
+ struct cfl_list *head;
+
+ matches = 0;
+
+ cfl_list_foreach(head, metrics) {
+ metric = cfl_list_entry(head, struct cmt_metric, _head);
+
+ label_set_hash = calculate_label_set_hash(&metric->labels, sequence_number);
+
+ if (label_set_hash == desired_hash) {
+ matches++;
+ }
+ }
+
+ return matches;
+}
+
+int append_entry_to_prometheus_label_list(Prometheus__Label **label_list,
+ size_t *index,
+ char *name,
+ char *value)
+{
+ label_list[*index] = calloc(1, sizeof(Prometheus__Label));
+
+ if (label_list[*index] == NULL) {
+ cmt_errno();
+
+ return CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_ALLOCATION_ERROR;
+ }
+
+ prometheus__label__init(label_list[*index]);
+
+ label_list[*index]->name = cfl_sds_create(name);
+
+ if (label_list[*index]->name == NULL) {
+ free(label_list[*index]);
+
+ return CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_ALLOCATION_ERROR;
+ }
+
+ label_list[*index]->value = cfl_sds_create(value);
+
+ if (label_list[*index]->value == NULL) {
+ cfl_sds_destroy(label_list[*index]->name);
+ free(label_list[*index]);
+
+ return CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_ALLOCATION_ERROR;
+ }
+
+ (*index)++;
+
+ return CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS;
+}
+
+void destroy_prometheus_sample_list(Prometheus__Sample **sample_list,
+ size_t entry_count)
+{
+ size_t index;
+
+ if (sample_list != NULL) {
+ for (index = 0 ; index < entry_count ; index++) {
+ if (sample_list[index] != NULL) {
+ free(sample_list[index]);
+ sample_list[index] = NULL;
+ }
+ }
+
+ free(sample_list);
+ }
+}
+
+void destroy_prometheus_label_list(Prometheus__Label **label_list,
+ size_t entry_count)
+{
+ size_t index;
+
+ if (label_list != NULL) {
+ for (index = 0 ; index < entry_count ; index++) {
+ if (label_list[index] != NULL) {
+ if (label_list[index]->name != NULL) {
+ cfl_sds_destroy(label_list[index]->name);
+ label_list[index]->name = NULL;
+ }
+
+ if (label_list[index]->value != NULL) {
+ cfl_sds_destroy(label_list[index]->value);
+ label_list[index]->value = NULL;
+ }
+
+ free(label_list[index]);
+ label_list[index] = NULL;
+ }
+ }
+
+ free(label_list);
+ }
+}
+
+int set_up_time_series_for_label_set(struct cmt_prometheus_remote_write_context *context,
+ struct cmt_map *map,
+ struct cmt_metric *metric,
+ struct cmt_prometheus_time_series **time_series)
+{
+ uint8_t time_series_match_found;
+ size_t label_set_hash_matches;
+ struct cmt_prometheus_time_series *time_series_entry;
+ uint64_t label_set_hash;
+ struct cmt_label *static_label;
+ size_t label_index;
+ size_t label_count;
+ struct cmt_map_label *label_value;
+ struct cmt_map_label *label_name;
+ Prometheus__Label **label_list;
+ Prometheus__Sample **value_list;
+ int result;
+ struct cfl_list *head;
+
+ label_set_hash = calculate_label_set_hash(&metric->labels, context->sequence_number);
+
+ /* Determine if there is an existing time series for this label set */
+ time_series_match_found = CMT_FALSE;
+
+ cfl_list_foreach(head, &context->time_series_entries) {
+ time_series_entry = cfl_list_entry(head, struct cmt_prometheus_time_series, _head);
+
+ if (time_series_entry->label_set_hash == label_set_hash) {
+ time_series_match_found = CMT_TRUE;
+
+ break;
+ }
+ }
+
+ if (time_series_match_found == CMT_TRUE) {
+ *time_series = time_series_entry;
+
+ return CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS;
+ }
+
+ /* Find out how many samples share these label values */
+ label_set_hash_matches = count_metrics_with_matching_label_set(&map->metrics,
+ context->sequence_number,
+ label_set_hash);
+
+ if (label_set_hash_matches == 0)
+ {
+ label_set_hash_matches++;
+ }
+
+ /* Allocate the memory required for the label and value lists, we need to add
+ * one for the fixed __name__ label
+ */
+ label_count = cfl_list_size(&context->cmt->static_labels->list) +
+ cfl_list_size(&metric->labels) +
+ 1;
+
+
+ time_series_entry = calloc(1, sizeof(struct cmt_prometheus_time_series));
+
+ if (time_series_entry == NULL) {
+ cmt_errno();
+
+ return CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_ALLOCATION_ERROR;
+ }
+
+ label_list = calloc(label_count, sizeof(Prometheus__Label *));
+
+ if (label_list == NULL) {
+ cmt_errno();
+
+ free(time_series_entry);
+
+ return CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_ALLOCATION_ERROR;
+ }
+
+ value_list = calloc(label_set_hash_matches, sizeof(Prometheus__Sample *));
+
+ if (value_list == NULL) {
+ cmt_errno();
+
+ free(time_series_entry);
+ free(label_list);
+
+ return CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_ALLOCATION_ERROR;
+ }
+
+ /* Initialize the time series */
+ prometheus__time_series__init(&time_series_entry->data);
+
+ time_series_entry->data.n_labels = label_count;
+ time_series_entry->data.labels = label_list;
+ time_series_entry->data.n_samples = label_set_hash_matches;
+ time_series_entry->data.samples = value_list;
+
+ time_series_entry->label_set_hash = label_set_hash;
+ time_series_entry->entries_set = 0;
+
+ /* Initialize the label list */
+ label_index = 0;
+
+ /* Add the __name__ label */
+ result = append_entry_to_prometheus_label_list(label_list,
+ &label_index,
+ "__name__",
+ map->opts->fqname);
+
+ if (result != CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS)
+ {
+ free(time_series_entry);
+ free(label_list);
+ free(value_list);
+
+ return result;
+ }
+
+ /* Add the static labels */
+ result = CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS;
+
+ cfl_list_foreach(head, &context->cmt->static_labels->list) {
+ static_label = cfl_list_entry(head, struct cmt_label, _head);
+
+ result = append_entry_to_prometheus_label_list(label_list,
+ &label_index,
+ static_label->key,
+ static_label->val);
+
+ if (result != CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS)
+ {
+ break;
+ }
+ }
+
+ /* Add the specific labels */
+ if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS && label_count > 0) {
+ label_name = cfl_list_entry_first(&map->label_keys, struct cmt_map_label, _head);
+
+ cfl_list_foreach(head, &metric->labels) {
+ label_value = cfl_list_entry(head, struct cmt_map_label, _head);
+
+ result = append_entry_to_prometheus_label_list(label_list,
+ &label_index,
+ label_name->name,
+ label_value->name);
+
+ if (result != CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS)
+ {
+ break;
+ }
+
+ label_name = cfl_list_entry_next(&label_name->_head, struct cmt_map_label,
+ _head, &map->label_keys);
+ }
+ }
+
+ if (result != CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ destroy_prometheus_label_list(label_list, label_index);
+ free(time_series_entry);
+ free(value_list);
+
+ return result;
+ }
+
+ /* Add the time series to the context so we can find it when we try to format
+ * a metric with these same labels;
+ */
+ cfl_list_add(&time_series_entry->_head, &context->time_series_entries);
+
+ *time_series = time_series_entry;
+
+ return CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS;
+}
+
+
+int pack_metric_metadata(struct cmt_prometheus_remote_write_context *context,
+ struct cmt_map *map,
+ struct cmt_metric *metric)
+{
+ struct cmt_prometheus_metric_metadata *metadata_entry;
+
+ metadata_entry = calloc(1, sizeof(struct cmt_prometheus_metric_metadata));
+
+ if (metadata_entry == NULL) {
+ cmt_errno();
+
+ return CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_ALLOCATION_ERROR;
+ }
+
+ prometheus__metric_metadata__init(&metadata_entry->data);
+
+ if (map->type == CMT_COUNTER) {
+ metadata_entry->data.type = PROMETHEUS__METRIC_METADATA__METRIC_TYPE__COUNTER;
+ }
+ else if (map->type == CMT_GAUGE) {
+ metadata_entry->data.type = PROMETHEUS__METRIC_METADATA__METRIC_TYPE__GAUGE;
+ }
+ else if (map->type == CMT_UNTYPED) {
+ metadata_entry->data.type = PROMETHEUS__METRIC_METADATA__METRIC_TYPE__UNKNOWN;
+ }
+ else if (map->type == CMT_SUMMARY) {
+ metadata_entry->data.type = PROMETHEUS__METRIC_METADATA__METRIC_TYPE__SUMMARY;
+ }
+ else {
+ return CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_UNEXPECTED_METRIC_TYPE;
+ }
+
+ if (map->opts->fqname == NULL) {
+ metadata_entry->data.metric_family_name = cfl_sds_create("");
+ }
+ else {
+ metadata_entry->data.metric_family_name = cfl_sds_create(map->opts->fqname);
+ }
+
+ if (metadata_entry->data.metric_family_name == NULL) {
+ return CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_ALLOCATION_ERROR;
+ }
+
+ if (map->opts->description == NULL) {
+ metadata_entry->data.help = cfl_sds_create("");
+ }
+ else {
+ metadata_entry->data.help = cfl_sds_create(map->opts->description);
+ }
+
+ if (metadata_entry->data.help == NULL) {
+ return CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_ALLOCATION_ERROR;
+ }
+
+ if (map->unit == NULL) {
+ metadata_entry->data.unit = cfl_sds_create("");
+ }
+ else {
+ metadata_entry->data.unit = cfl_sds_create(map->unit);
+ }
+
+ if (metadata_entry->data.unit == NULL) {
+ return CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_ALLOCATION_ERROR;
+ }
+
+ cfl_list_add(&metadata_entry->_head, &context->metadata_entries);
+
+ return 0;
+}
+
+int append_metric_to_timeseries(struct cmt_prometheus_time_series *time_series,
+ struct cmt_metric *metric)
+{
+ uint64_t ts;
+ Prometheus__Sample *sample;
+
+ sample = calloc(1, sizeof(Prometheus__Sample));
+
+ if (sample == NULL) {
+ cmt_errno();
+
+ return CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_ALLOCATION_ERROR;
+ }
+
+ prometheus__sample__init(sample);
+
+ sample->value = cmt_metric_get_value(metric);
+
+ ts = cmt_metric_get_timestamp(metric);
+ sample->timestamp = ts / 1000000;
+ time_series->data.samples[time_series->entries_set++] = sample;
+
+ return CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS;
+}
+
+int pack_basic_metric_sample(struct cmt_prometheus_remote_write_context *context,
+ struct cmt_map *map,
+ struct cmt_metric *metric,
+ int add_metadata)
+{
+ struct cmt_prometheus_time_series *time_series;
+ int result;
+
+ result = set_up_time_series_for_label_set(context, map, metric, &time_series);
+
+ if (result != CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ return result;
+ }
+
+ if (add_metadata == CMT_TRUE) {
+ result = pack_metric_metadata(context, map, metric);
+
+ if (result != CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ return result;
+ }
+ }
+
+ return append_metric_to_timeseries(time_series, metric);
+}
+
+int pack_basic_type(struct cmt_prometheus_remote_write_context *context,
+ struct cmt_map *map)
+{
+ int add_metadata;
+ struct cmt_metric *metric;
+ int result;
+ struct cfl_list *head;
+
+ context->sequence_number++;
+ add_metadata = CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_ADD_METADATA;
+
+ if (map->metric_static_set == CMT_TRUE) {
+ result = pack_basic_metric_sample(context, map, &map->metric, add_metadata);
+
+ if (result != CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ return result;
+ }
+ }
+
+ cfl_list_foreach(head, &map->metrics) {
+ metric = cfl_list_entry(head, struct cmt_metric, _head);
+
+ result = pack_basic_metric_sample(context, map, metric, add_metadata);
+
+ if (result != CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ return result;
+ }
+
+ if (add_metadata == CMT_TRUE) {
+ add_metadata = CMT_FALSE;
+ }
+ }
+
+ return CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS;
+}
+
+int pack_complex_metric_sample(struct cmt_prometheus_remote_write_context *context,
+ struct cmt_map *map,
+ struct cmt_metric *metric,
+ int add_metadata)
+{
+ size_t original_label_value_count = 0;
+ cfl_sds_t synthetized_metric_name;
+ cfl_sds_t original_metric_name;
+ size_t label_value_count = 0;
+ cfl_sds_t additional_label_caption;
+ size_t label_key_count;
+ struct cmt_map_label *additional_label;
+ struct cmt_metric dummy_metric;
+ struct cmt_prometheus_time_series *time_series;
+ struct cmt_map_label *dummy_label;
+ struct cmt_histogram *histogram;
+ struct cmt_summary *summary;
+ int result;
+ size_t index;
+
+ additional_label_caption = cfl_sds_create_len(NULL, 128);
+
+ if (additional_label_caption == NULL) {
+ return CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_ALLOCATION_ERROR;
+ }
+
+ synthetized_metric_name = cfl_sds_create_len(NULL,
+ cfl_sds_alloc(map->opts->fqname) + 16);
+
+ if (synthetized_metric_name == NULL) {
+ cfl_sds_destroy(additional_label_caption);
+
+ return CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_ALLOCATION_ERROR;
+ }
+
+ original_metric_name = map->opts->fqname;
+
+ result = CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS;
+
+ memset(&dummy_metric, 0, sizeof(struct cmt_metric));
+ memcpy(&dummy_metric.labels, &metric->labels, sizeof(struct cfl_list));
+
+ dummy_metric.timestamp = metric->timestamp;
+
+ if (map->type == CMT_SUMMARY) {
+ summary = (struct cmt_summary *) map->parent;
+
+ context->sequence_number += SYNTHETIC_METRIC_SUMMARY_COUNT_SEQUENCE_DELTA;
+
+ map->opts->fqname = synthetized_metric_name;
+
+ cfl_sds_len_set(synthetized_metric_name,
+ snprintf(synthetized_metric_name,
+ cfl_sds_alloc(synthetized_metric_name) -1,
+ "%s_count",
+ original_metric_name));
+
+ cmt_metric_set(&dummy_metric,
+ dummy_metric.timestamp,
+ cmt_summary_get_count_value(metric));
+
+ result = set_up_time_series_for_label_set(context, map, metric, &time_series);
+
+ if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ if (add_metadata == CMT_TRUE) {
+ result = pack_metric_metadata(context, map, &dummy_metric);
+ }
+
+ if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ result = append_metric_to_timeseries(time_series, &dummy_metric);
+ }
+ }
+
+ context->sequence_number -= SYNTHETIC_METRIC_SUMMARY_COUNT_SEQUENCE_DELTA;
+
+ if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ context->sequence_number += SYNTHETIC_METRIC_SUMMARY_SUM_SEQUENCE_DELTA;
+ cfl_sds_len_set(synthetized_metric_name,
+ snprintf(synthetized_metric_name,
+ cfl_sds_alloc(synthetized_metric_name) -1,
+ "%s_sum",
+ original_metric_name));
+
+ cmt_metric_set(&dummy_metric,
+ dummy_metric.timestamp,
+ cmt_summary_get_sum_value(metric));
+
+ result = set_up_time_series_for_label_set(context, map, metric, &time_series);
+
+ if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ if (add_metadata == CMT_TRUE) {
+ result = pack_metric_metadata(context, map, &dummy_metric);
+ }
+
+ if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ result = append_metric_to_timeseries(time_series, &dummy_metric);
+ }
+ }
+
+ context->sequence_number -= SYNTHETIC_METRIC_SUMMARY_SUM_SEQUENCE_DELTA;
+ }
+
+ map->opts->fqname = original_metric_name;
+
+ if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ label_key_count = cfl_list_size(&map->label_keys);
+ original_label_value_count = cfl_list_size(&metric->labels);
+
+ for (label_value_count = original_label_value_count ;
+ result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS &&
+ label_value_count < label_key_count;
+ label_value_count++) {
+ dummy_label = create_label(NULL);
+
+ if (dummy_label == NULL) {
+ result = CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_ALLOCATION_ERROR;
+ }
+
+ cfl_list_add(&dummy_label->_head, &metric->labels);
+ }
+
+ if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ additional_label = cfl_list_entry_last(&metric->labels, struct cmt_map_label, _head);
+
+ if (additional_label == NULL) {
+ result = CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_ALLOCATION_ERROR;
+ }
+
+ if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ additional_label->name = (cfl_sds_t) additional_label_caption;
+
+ for(index = 0 ;
+ result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS &&
+ index < summary->quantiles_count ;
+ index++) {
+ cfl_sds_len_set(additional_label_caption,
+ snprintf(additional_label_caption,
+ cfl_sds_alloc(additional_label_caption) - 1,
+ "%.17g", summary->quantiles[index]));
+
+ dummy_metric.val = cmt_math_d64_to_uint64(cmt_summary_quantile_get_value(metric, index));
+
+ result = set_up_time_series_for_label_set(context, map, metric, &time_series);
+
+ if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ if (add_metadata == CMT_TRUE) {
+ result = pack_metric_metadata(context, map, &dummy_metric);
+ }
+
+ if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ result = append_metric_to_timeseries(time_series, &dummy_metric);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (map->type == CMT_HISTOGRAM) {
+ histogram = (struct cmt_histogram *) map->parent;
+
+ context->sequence_number += SYNTHETIC_METRIC_HISTOGRAM_COUNT_SEQUENCE_DELTA;
+
+ map->opts->fqname = synthetized_metric_name;
+
+ cfl_sds_len_set(synthetized_metric_name,
+ snprintf(synthetized_metric_name,
+ cfl_sds_alloc(synthetized_metric_name) -1,
+ "%s_count",
+ original_metric_name));
+
+ cmt_metric_set(&dummy_metric,
+ dummy_metric.timestamp,
+ cmt_metric_hist_get_count_value(metric));
+
+ result = set_up_time_series_for_label_set(context, map, metric, &time_series);
+
+ if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ if (add_metadata == CMT_TRUE) {
+ result = pack_metric_metadata(context, map, &dummy_metric);
+ }
+
+ if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ result = append_metric_to_timeseries(time_series, &dummy_metric);
+ }
+ }
+
+ context->sequence_number -= SYNTHETIC_METRIC_HISTOGRAM_COUNT_SEQUENCE_DELTA;
+
+ if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ context->sequence_number += SYNTHETIC_METRIC_HISTOGRAM_SUM_SEQUENCE_DELTA;
+
+ cfl_sds_len_set(synthetized_metric_name,
+ snprintf(synthetized_metric_name,
+ cfl_sds_alloc(synthetized_metric_name) -1,
+ "%s_sum",
+ original_metric_name));
+
+ cmt_metric_set(&dummy_metric,
+ dummy_metric.timestamp,
+ cmt_metric_hist_get_sum_value(metric));
+
+ result = set_up_time_series_for_label_set(context, map, metric, &time_series);
+
+ if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ if (add_metadata == CMT_TRUE) {
+ result = pack_metric_metadata(context, map, &dummy_metric);
+ }
+
+ if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ result = append_metric_to_timeseries(time_series, &dummy_metric);
+ }
+ }
+
+ context->sequence_number -= SYNTHETIC_METRIC_HISTOGRAM_SUM_SEQUENCE_DELTA;
+ }
+
+ if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ cfl_sds_len_set(synthetized_metric_name,
+ snprintf(synthetized_metric_name,
+ cfl_sds_alloc(synthetized_metric_name) - 1,
+ "%s_bucket",
+ original_metric_name));
+
+ label_key_count = cfl_list_size(&map->label_keys);
+ original_label_value_count = cfl_list_size(&metric->labels);
+
+ for (label_value_count = original_label_value_count ;
+ result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS &&
+ label_value_count < label_key_count;
+ label_value_count++) {
+ dummy_label = create_label(NULL);
+
+ if (dummy_label == NULL) {
+ result = CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_ALLOCATION_ERROR;
+ }
+
+ cfl_list_add(&dummy_label->_head, &metric->labels);
+ }
+
+ if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ additional_label = cfl_list_entry_last(&metric->labels, struct cmt_map_label, _head);
+
+ additional_label->name = (cfl_sds_t) additional_label_caption;
+
+ for(index = 0 ;
+ result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS &&
+ index <= histogram->buckets->count ;
+ index++) {
+ if (index < histogram->buckets->count) {
+ cfl_sds_len_set(additional_label_caption,
+ snprintf(additional_label_caption,
+ cfl_sds_alloc(additional_label_caption) - 1,
+ "%.17g", histogram->buckets->upper_bounds[index]));
+ }
+ else {
+ cfl_sds_len_set(additional_label_caption,
+ snprintf(additional_label_caption,
+ cfl_sds_alloc(additional_label_caption) - 1,
+ "+Inf"));
+ }
+
+
+ dummy_metric.val = cmt_math_d64_to_uint64(cmt_metric_hist_get_value(metric, index));
+
+ result = set_up_time_series_for_label_set(context, map, metric, &time_series);
+
+ if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ if (add_metadata == CMT_TRUE) {
+ result = pack_metric_metadata(context, map, &dummy_metric);
+ }
+
+ if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ result = append_metric_to_timeseries(time_series, &dummy_metric);
+ }
+ }
+ }
+ }
+ }
+
+ map->opts->fqname = original_metric_name;
+ }
+
+ for ( ;
+ label_value_count > original_label_value_count;
+ label_value_count--) {
+ additional_label = cfl_list_entry_last(&metric->labels, struct cmt_map_label, _head);
+
+ if (additional_label != NULL) {
+ cfl_list_del(&additional_label->_head);
+
+ if (additional_label->name == additional_label_caption) {
+ additional_label_caption = NULL;
+ }
+
+ destroy_label(additional_label);
+ }
+ }
+
+ if (additional_label_caption != NULL) {
+ cfl_sds_destroy(additional_label_caption);
+ }
+
+ cfl_sds_destroy(synthetized_metric_name);
+
+ return result;
+}
+
+int pack_complex_type(struct cmt_prometheus_remote_write_context *context,
+ struct cmt_map *map)
+{
+ struct cmt_map_label additional_label;
+ int add_metadata;
+ struct cmt_metric *metric;
+ int result;
+ struct cfl_list *head;
+
+ context->sequence_number++;
+
+ add_metadata = CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_ADD_METADATA;
+ result = CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS;
+
+ if (map->type == CMT_SUMMARY) {
+ additional_label.name = (cfl_sds_t) "quantile";
+ }
+ else if (map->type == CMT_HISTOGRAM) {
+ additional_label.name = (cfl_sds_t) "le";
+ }
+
+ cfl_list_add(&additional_label._head, &map->label_keys);
+
+ if (map->metric_static_set == CMT_TRUE) {
+ result = pack_complex_metric_sample(context, map, &map->metric, add_metadata);
+ }
+
+ if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ cfl_list_foreach(head, &map->metrics) {
+ metric = cfl_list_entry(head, struct cmt_metric, _head);
+
+ result = pack_complex_metric_sample(context, map, metric, add_metadata);
+
+ if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ if (add_metadata == CMT_TRUE) {
+ add_metadata = CMT_FALSE;
+ }
+ }
+ }
+ }
+
+ if (map->type == CMT_SUMMARY ||
+ map->type == CMT_HISTOGRAM) {
+ cfl_list_del(&additional_label._head);
+ }
+
+ return CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS;
+}
+
+/* Format all the registered metrics in Prometheus Text format */
+cfl_sds_t cmt_encode_prometheus_remote_write_create(struct cmt *cmt)
+{
+ struct cmt_histogram *histogram;
+ struct cmt_prometheus_remote_write_context context;
+ struct cmt_untyped *untyped;
+ struct cmt_counter *counter;
+ struct cmt_summary *summary;
+ int result;
+ struct cmt_gauge *gauge;
+ struct cfl_list *head;
+ cfl_sds_t buf;
+
+ result = CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS;
+ buf = NULL;
+
+ memset(&context, 0, sizeof(struct cmt_prometheus_remote_write_context));
+
+ prometheus__write_request__init(&context.write_request);
+
+ context.cmt = cmt;
+
+ cfl_list_init(&context.time_series_entries);
+ cfl_list_init(&context.metadata_entries);
+
+ /* Counters */
+ cfl_list_foreach(head, &cmt->counters) {
+ counter = cfl_list_entry(head, struct cmt_counter, _head);
+ result = pack_basic_type(&context, counter->map);
+
+ if (result != CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ break;
+ }
+ }
+
+ if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ /* Gauges */
+ cfl_list_foreach(head, &cmt->gauges) {
+ gauge = cfl_list_entry(head, struct cmt_gauge, _head);
+ result = pack_basic_type(&context, gauge->map);
+
+ if (result != CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ break;
+ }
+ }
+ }
+
+ if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ /* Untyped */
+ cfl_list_foreach(head, &cmt->untypeds) {
+ untyped = cfl_list_entry(head, struct cmt_untyped, _head);
+ pack_basic_type(&context, untyped->map);
+ }
+ }
+
+ if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ /* Summaries */
+ cfl_list_foreach(head, &cmt->summaries) {
+ summary = cfl_list_entry(head, struct cmt_summary, _head);
+ result = pack_complex_type(&context, summary->map);
+
+ if (result != CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ break;
+ }
+ }
+ }
+
+ if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ /* Histograms */
+ cfl_list_foreach(head, &cmt->histograms) {
+ histogram = cfl_list_entry(head, struct cmt_histogram, _head);
+ result = pack_complex_type(&context, histogram->map);
+
+ if (result != CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ break;
+ }
+ }
+ }
+
+ if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) {
+ buf = render_remote_write_context_to_sds(&context);
+ }
+
+ cmt_destroy_prometheus_remote_write_context(&context);
+
+ return buf;
+}
+
+void cmt_encode_prometheus_remote_write_destroy(cfl_sds_t text)
+{
+ cfl_sds_destroy(text);
+}
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_encode_splunk_hec.c b/src/fluent-bit/lib/cmetrics/src/cmt_encode_splunk_hec.c
new file mode 100644
index 000000000..a2db9ae83
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_encode_splunk_hec.c
@@ -0,0 +1,710 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_metric.h>
+#include <cmetrics/cmt_map.h>
+#include <cmetrics/cmt_counter.h>
+#include <cmetrics/cmt_gauge.h>
+#include <cmetrics/cmt_untyped.h>
+#include <cmetrics/cmt_histogram.h>
+#include <cmetrics/cmt_summary.h>
+#include <cmetrics/cmt_time.h>
+#include <cmetrics/cmt_compat.h>
+#include <cmetrics/cmt_encode_splunk_hec.h>
+
+static cfl_sds_t double_to_string(double val)
+{
+ int len;
+ cfl_sds_t str;
+
+ str = cfl_sds_create_size(64);
+ if (!str) {
+ return NULL;
+ }
+
+ len = snprintf(str, 64, "%g", val);
+ if (strstr(str, "e+")) {
+ len = snprintf(str, 64, "%e", val);
+ cfl_sds_len_set(str, len);
+ } else {
+ cfl_sds_len_set(str, len);
+ }
+
+ if (!strchr(str, '.')) {
+ cfl_sds_cat_safe(&str, ".0", 2);
+ }
+
+ return str;
+}
+
+static void format_metric_name(cfl_sds_t *buf, struct cmt_map *map, const char *suffix)
+{
+ int mlen = 0;
+ int slen = 0;
+ cfl_sds_t metric_name = NULL;
+ struct cmt_opts *opts;
+
+ opts = map->opts;
+
+ if (cfl_sds_len(opts->subsystem) > 0) {
+ /* Calculate length for "metric_name:subsystem.name": */
+ mlen = 13 + cfl_sds_len(opts->subsystem) + 1 + cfl_sds_len(opts->name) + 2;
+ metric_name = cfl_sds_create_size(mlen);
+ cfl_sds_cat_safe(&metric_name, "\"metric_name:", 13);
+ cfl_sds_cat_safe(&metric_name, opts->subsystem, cfl_sds_len(opts->subsystem));
+ cfl_sds_cat_safe(&metric_name, ".", 1);
+ cfl_sds_cat_safe(&metric_name, opts->name, cfl_sds_len(opts->name));
+ }
+ else {
+ /* Calculate length for "metric_name:subsystem.name": */
+ mlen = 13 + cfl_sds_len(opts->name) + 2;
+ metric_name = cfl_sds_create_size(mlen);
+ cfl_sds_cat_safe(&metric_name, "\"metric_name:", 13);
+ cfl_sds_cat_safe(&metric_name, opts->name, cfl_sds_len(opts->name));
+ }
+ if (suffix != NULL) {
+ slen = strlen(suffix);
+ mlen += slen;
+ cfl_sds_cat_safe(&metric_name, suffix, slen);
+ }
+ cfl_sds_cat_safe(&metric_name, "\":", 2);
+ cfl_sds_cat_safe(buf, metric_name, mlen);
+ cfl_sds_destroy(metric_name);
+}
+
+static void format_metric_type(cfl_sds_t *buf, const char *metric_type_name)
+{
+ int len = 0;
+ char tmp[32];
+
+ len = snprintf(tmp, sizeof(tmp) - 1, ",\"metric_type\":\"%s\"", metric_type_name);
+ cfl_sds_cat_safe(buf, tmp, len);
+}
+
+static void append_metric_value(cfl_sds_t *buf, struct cmt_map *map,
+ struct cmt_metric *metric)
+{
+ int len;
+ double val;
+ char tmp[128];
+ cfl_sds_t metric_val;
+
+ /* Retreive metric name */
+ format_metric_name(buf, map, NULL);
+
+ /* Retrieve metric value */
+ val = cmt_metric_get_value(metric);
+ metric_val = double_to_string(val);
+
+ len = snprintf(tmp, sizeof(tmp) - 1, "%s", metric_val);
+ cfl_sds_cat_safe(buf, tmp, len);
+ cfl_sds_destroy(metric_val);
+}
+
+static void format_context_common(struct cmt_splunk_hec_context *context, cfl_sds_t *buf, struct cmt_map *map,
+ struct cmt_metric *metric)
+{
+ int len, tlen;
+ char hostname[256], timestamp[128];
+ char *index = NULL, *source = NULL, *source_type = NULL;
+ struct timespec tms;
+ uint64_t ts;
+ int result = CMT_ENCODE_SPLUNK_HEC_ALLOCATION_ERROR;
+
+ /* Open parenthesis */
+ cfl_sds_cat_safe(buf, "{", 1);
+
+ /* host */
+ len = snprintf(hostname, sizeof(hostname) - 1, "\"host\":\"%s\",", context->host);
+ cfl_sds_cat_safe(buf, hostname, len);
+
+ /* timestamp (RFC3339Nano) */
+ ts = cmt_metric_get_timestamp(metric);
+ cmt_time_from_ns(&tms, ts);
+
+ /* timestamp (floting point) */
+ len = snprintf(timestamp, sizeof(timestamp) - 1, "\"time\":%09lu.%09lu,", tms.tv_sec, tms.tv_nsec);
+ cfl_sds_cat_safe(buf, timestamp, len);
+
+ /* event type: metric */
+ cfl_sds_cat_safe(buf, "\"event\":\"metric\",", 17);
+
+ /* index */
+ if (context->index != NULL) {
+ tlen = strlen(context->index) + 12; /* adding snprintf template character length */
+ index = malloc(tlen);
+ if (index == NULL) {
+ cmt_errno();
+ result = CMT_ENCODE_SPLUNK_HEC_ALLOCATION_ERROR;
+
+ goto cleanup;
+ }
+ len = snprintf(index, tlen, "\"index\":\"%s\",", context->index);
+ cfl_sds_cat_safe(buf, index, len);
+ free(index);
+ }
+
+ /* source */
+ if (context->source != NULL) {
+ tlen = strlen(context->source) + 13; /* adding snprintf template character length */
+ source = malloc(tlen);
+ if (source == NULL) {
+ cmt_errno();
+ result = CMT_ENCODE_SPLUNK_HEC_ALLOCATION_ERROR;
+
+ goto cleanup;
+ }
+ len = snprintf(source, tlen, "\"source\":\"%s\",", context->source);
+ cfl_sds_cat_safe(buf, source, len);
+ free(source);
+ }
+
+ /* sourcetype */
+ if (context->source_type != NULL) {
+ tlen = strlen(context->source_type) + 18; /* adding snprintf template character length */
+ source_type = malloc(tlen);
+ if (source == NULL) {
+ cmt_errno();
+ result = CMT_ENCODE_SPLUNK_HEC_ALLOCATION_ERROR;
+
+ goto cleanup;
+ }
+ len = snprintf(source_type, tlen, "\"sourcetype\":\"%s\",", context->source_type);
+ cfl_sds_cat_safe(buf, source_type, len);
+ free(source_type);
+ }
+
+ return;
+
+cleanup:
+ if (result != CMT_ENCODE_SPLUNK_HEC_SUCCESS) {
+ if (index != NULL) {
+ free(index);
+ }
+ if (source != NULL) {
+ free(source);
+ }
+ if (source_type != NULL) {
+ free(source_type);
+ }
+ }
+}
+
+static void format_metric_labels(struct cmt_splunk_hec_context *context, cfl_sds_t *buf, struct cmt_map *map,
+ struct cmt_metric *metric)
+{
+ int i;
+ int n;
+ int count = 0;
+ int static_labels = 0;
+
+ struct cmt_map_label *label_k;
+ struct cmt_map_label *label_v;
+ struct cfl_list *head;
+ struct cmt_label *slabel;
+
+ /* Static labels */
+ static_labels = cmt_labels_count(context->cmt->static_labels);
+ if (static_labels > 0) {
+ cfl_sds_cat_safe(buf, ",", 1);
+ cfl_list_foreach(head, &context->cmt->static_labels->list) {
+ count++;
+ cfl_sds_cat_safe(buf, "\"", 1);
+ slabel = cfl_list_entry(head, struct cmt_label, _head);
+ cfl_sds_cat_safe(buf, slabel->key, cfl_sds_len(slabel->key));
+ cfl_sds_cat_safe(buf, "\":\"", 3);
+ cfl_sds_cat_safe(buf, slabel->val, cfl_sds_len(slabel->val));
+ cfl_sds_cat_safe(buf, "\"", 1);
+
+ if (count < static_labels) {
+ cfl_sds_cat_safe(buf, ",", 1);
+ }
+ }
+ }
+
+ n = cfl_list_size(&metric->labels);
+ if (n > 0) {
+ cfl_sds_cat_safe(buf, ",", 1);
+ label_k = cfl_list_entry_first(&map->label_keys, struct cmt_map_label, _head);
+
+ i = 0;
+ cfl_list_foreach(head, &metric->labels) {
+ label_v = cfl_list_entry(head, struct cmt_map_label, _head);
+
+ cfl_sds_cat_safe(buf, "\"", 1);
+ cfl_sds_cat_safe(buf, label_k->name, cfl_sds_len(label_k->name));
+ cfl_sds_cat_safe(buf, "\":\"", 3);
+ cfl_sds_cat_safe(buf, label_v->name, cfl_sds_len(label_v->name));
+ cfl_sds_cat_safe(buf, "\"", 1);
+ i++;
+
+ label_k = cfl_list_entry_next(&label_k->_head, struct cmt_map_label,
+ _head, &map->label_keys);
+ if (i < n) {
+ cfl_sds_cat_safe(buf, ",", 1);
+ }
+ }
+ }
+}
+
+static void append_bucket_metric(cfl_sds_t *buf, struct cmt_map *map,
+ struct cmt_metric *metric, int index)
+{
+ int len = 0;
+ double val;
+ char tmp[128];
+ cfl_sds_t metric_val;
+
+ /* metric name for bucket */
+ format_metric_name(buf, map, "_bucket");
+
+ /* Retrieve metric value */
+ val = cmt_metric_hist_get_value(metric, index);
+ metric_val = double_to_string(val);
+
+ len = snprintf(tmp, sizeof(tmp) - 1, "%s", metric_val);
+ cfl_sds_cat_safe(buf, tmp, len);
+ cfl_sds_destroy(metric_val);
+}
+
+static void format_histogram_bucket(struct cmt_splunk_hec_context *context, cfl_sds_t *buf, struct cmt_map *map,
+ struct cmt_metric *metric)
+{
+ int index;
+ int len = 0;
+ char tmp[128];
+ cfl_sds_t val;
+ uint64_t metric_val;
+ struct cmt_histogram *histogram;
+ struct cmt_histogram_buckets *buckets;
+ cfl_sds_t metric_str;
+
+ histogram = (struct cmt_histogram *) map->parent;
+ buckets = histogram->buckets;
+
+ for (index = 0; index <= buckets->count; index++) {
+ /* Common fields */
+ format_context_common(context, buf, map, metric);
+
+ /* Other fields */
+ cfl_sds_cat_safe(buf, "\"fields\":{", 10);
+
+ /* bucket metric */
+ append_bucket_metric(buf, map, metric, index);
+
+ /* upper bound */
+ cfl_sds_cat_safe(buf, ",\"le\":", 6);
+
+ if (index < buckets->count) {
+ cfl_sds_cat_safe(buf, "\"", 1);
+ val = double_to_string(buckets->upper_bounds[index]);
+ cfl_sds_cat_safe(buf, val, cfl_sds_len(val));
+ cfl_sds_destroy(val);
+ cfl_sds_cat_safe(buf, "\"", 1);
+ }
+ else {
+ cfl_sds_cat_safe(buf, "\"+Inf\"", 6);
+ }
+
+ /* Format labels */
+ format_metric_labels(context, buf, map, metric);
+
+ /* Format metric type */
+ format_metric_type(buf, "Histogram");
+
+ /* Close parenthesis for fields */
+ cfl_sds_cat_safe(buf, "}", 1);
+
+ /* Close parenthesis */
+ cfl_sds_cat_safe(buf, "}", 1);
+ }
+
+ /* Format histogram sum */
+ {
+ /* Common fields */
+ format_context_common(context, buf, map, metric);
+
+ /* Other fields */
+ cfl_sds_cat_safe(buf, "\"fields\":{", 10);
+
+ /* metric name for bucket */
+ format_metric_name(buf, map, "_sum");
+
+ /* Retrieve metric value */
+ metric_val = cmt_metric_hist_get_sum_value(metric);
+ metric_str = double_to_string(metric_val);
+
+ len = snprintf(tmp, sizeof(tmp) - 1, "%s", metric_str);
+ cfl_sds_cat_safe(buf, tmp, len);
+ cfl_sds_destroy(metric_str);
+
+ /* Format labels */
+ format_metric_labels(context, buf, map, metric);
+
+ /* Format metric type */
+ format_metric_type(buf, "Histogram");
+
+ /* Close parenthesis for fields */
+ cfl_sds_cat_safe(buf, "}", 1);
+
+ /* Close parenthesis */
+ cfl_sds_cat_safe(buf, "}", 1);
+ }
+
+ /* Format histogram sum */
+ {
+ /* Common fields */
+ format_context_common(context, buf, map, metric);
+
+ /* Other fields */
+ cfl_sds_cat_safe(buf, "\"fields\":{", 10);
+
+ /* metric name for bucket */
+ format_metric_name(buf, map, "_count");
+
+ /* Retrieve metric value */
+ metric_val = cmt_metric_hist_get_count_value(metric);
+ metric_str = double_to_string(metric_val);
+
+ len = snprintf(tmp, sizeof(tmp) - 1, "%s", metric_str);
+ cfl_sds_cat_safe(buf, tmp, len);
+ cfl_sds_destroy(metric_str);
+
+ /* Format labels */
+ format_metric_labels(context, buf, map, metric);
+
+ /* Format metric type */
+ format_metric_type(buf, "Histogram");
+
+ /* Close parenthesis for fields */
+ cfl_sds_cat_safe(buf, "}", 1);
+
+ /* Close parenthesis */
+ cfl_sds_cat_safe(buf, "}", 1);
+ }
+}
+
+static void append_quantiles_metric(cfl_sds_t *buf, struct cmt_map *map,
+ struct cmt_metric *metric, int index)
+{
+ int len = 0;
+ double val;
+ char tmp[128];
+ cfl_sds_t metric_val;
+
+ /* metric name for bucket */
+ format_metric_name(buf, map, NULL);
+
+ /* Retrieve metric value */
+ val = cmt_summary_quantile_get_value(metric, index);
+ metric_val = double_to_string(val);
+
+ len = snprintf(tmp, sizeof(tmp) - 1, "%s", metric_val);
+ cfl_sds_cat_safe(buf, tmp, len);
+ cfl_sds_destroy(metric_val);
+}
+
+static void format_summary_metric(struct cmt_splunk_hec_context *context, cfl_sds_t *buf, struct cmt_map *map,
+ struct cmt_metric *metric)
+{
+ int index;
+ int len = 0;
+ char tmp[128];
+ cfl_sds_t val;
+ uint64_t metric_val;
+ struct cmt_summary *summary;
+ cfl_sds_t metric_str;
+
+ summary = (struct cmt_summary *) map->parent;
+
+ if (metric->sum_quantiles_set) {
+ for (index = 0; index < summary->quantiles_count; index++) {
+ /* Common fields */
+ format_context_common(context, buf, map, metric);
+
+ /* Other fields */
+ cfl_sds_cat_safe(buf, "\"fields\":{", 10);
+
+ /* bucket metric */
+ append_quantiles_metric(buf, map, metric, index);
+
+ /* quantiles */
+ cfl_sds_cat_safe(buf, ",\"qt\":\"", 7);
+ val = double_to_string(summary->quantiles[index]);
+ cfl_sds_cat_safe(buf, val, cfl_sds_len(val));
+ cfl_sds_destroy(val);
+ cfl_sds_cat_safe(buf, "\"", 1);
+
+ /* Format labels */
+ format_metric_labels(context, buf, map, metric);
+
+ /* Format metric type */
+ format_metric_type(buf, "Summary");
+
+ /* Close parenthesis for fields */
+ cfl_sds_cat_safe(buf, "}", 1);
+
+ /* Close parenthesis */
+ cfl_sds_cat_safe(buf, "}", 1);
+ }
+ }
+
+ /* Format Summary sum */
+ {
+ /* Common fields */
+ format_context_common(context, buf, map, metric);
+
+ /* Other fields */
+ cfl_sds_cat_safe(buf, "\"fields\":{", 10);
+
+ /* metric name for bucket */
+ format_metric_name(buf, map, "_sum");
+
+ /* Retrieve metric value */
+ metric_val = cmt_summary_get_sum_value(metric);
+ metric_str = double_to_string(metric_val);
+
+ len = snprintf(tmp, sizeof(tmp) - 1, "%s", metric_str);
+ cfl_sds_cat_safe(buf, tmp, len);
+ cfl_sds_destroy(metric_str);
+
+ /* Format labels */
+ format_metric_labels(context, buf, map, metric);
+
+ /* Format metric type */
+ format_metric_type(buf, "Summary");
+
+ /* Close parenthesis for fields */
+ cfl_sds_cat_safe(buf, "}", 1);
+
+ /* Close parenthesis */
+ cfl_sds_cat_safe(buf, "}", 1);
+ }
+
+ /* Format summary count */
+ {
+ /* Common fields */
+ format_context_common(context, buf, map, metric);
+
+ /* Other fields */
+ cfl_sds_cat_safe(buf, "\"fields\":{", 10);
+
+ /* metric name for bucket */
+ format_metric_name(buf, map, "_count");
+
+ /* Retrieve metric value */
+ metric_val = cmt_summary_get_count_value(metric);
+ metric_str = double_to_string(metric_val);
+
+ len = snprintf(tmp, sizeof(tmp) - 1, "%s", metric_str);
+ cfl_sds_cat_safe(buf, tmp, len);
+ cfl_sds_destroy(metric_str);
+
+ /* Format labels */
+ format_metric_labels(context, buf, map, metric);
+
+ /* Format metric type */
+ format_metric_type(buf, "Summary");
+
+ /* Close parenthesis for fields */
+ cfl_sds_cat_safe(buf, "}", 1);
+
+ /* Close parenthesis */
+ cfl_sds_cat_safe(buf, "}", 1);
+ }
+}
+
+static void format_metric_data_points(struct cmt_splunk_hec_context *context, cfl_sds_t *buf, struct cmt_map *map,
+ struct cmt_metric *metric)
+{
+ /* Common fields */
+ format_context_common(context, buf, map, metric);
+
+ /* Other fields */
+ cfl_sds_cat_safe(buf, "\"fields\":{", 10);
+
+ /* Metric name and value */
+ append_metric_value(buf, map, metric);
+
+ /* Format labels */
+ format_metric_labels(context, buf, map, metric);
+
+ /* Close parenthesis for fields */
+ cfl_sds_cat_safe(buf, "}", 1);
+
+ /* Close parenthesis */
+ cfl_sds_cat_safe(buf, "}", 1);
+}
+
+static void format_metric(struct cmt_splunk_hec_context *context, cfl_sds_t *buf, struct cmt_map *map,
+ struct cmt_metric *metric)
+{
+ if (map->type == CMT_HISTOGRAM) {
+ return format_histogram_bucket(context, buf, map, metric);
+ }
+ else if (map->type == CMT_SUMMARY) {
+ return format_summary_metric(context, buf, map, metric);
+ }
+ else {
+ /* For Counter, Gauge, and Untyped types */
+ return format_metric_data_points(context, buf, map, metric);
+ }
+}
+
+static void format_metrics(struct cmt_splunk_hec_context *context, cfl_sds_t *buf, struct cmt_map *map)
+{
+ struct cfl_list *head;
+ struct cmt_metric *metric;
+
+ /* Simple metric, no labels */
+ if (map->metric_static_set == 1) {
+ format_metric(context, buf, map, &map->metric);
+ }
+
+ cfl_list_foreach(head, &map->metrics) {
+ metric = cfl_list_entry(head, struct cmt_metric, _head);
+ format_metric(context, buf, map, metric);
+ }
+}
+
+static void destroy_splunk_hec_context(struct cmt_splunk_hec_context *context)
+{
+ if (context != NULL) {
+ free(context);
+ }
+}
+
+static struct cmt_splunk_hec_context
+*initialize_splunk_hec_context(struct cmt *cmt, const char *host,
+ const char *index, const char *source, const char *source_type)
+{
+ int result = CMT_ENCODE_SPLUNK_HEC_SUCCESS;
+ struct cmt_splunk_hec_context *context = NULL;
+
+ context = calloc(1, sizeof(struct cmt_splunk_hec_context));
+ if (context == NULL) {
+ result = CMT_ENCODE_SPLUNK_HEC_ALLOCATION_ERROR;
+
+ goto cleanup;
+ }
+
+ /* host parameter is mandatory. */
+ if (host == NULL) {
+ result = CMT_ENCODE_SPLUNK_HEC_INVALID_ARGUMENT_ERROR;
+
+ goto cleanup;
+ }
+
+ memset(context, 0, sizeof(struct cmt_splunk_hec_context));
+ context->cmt = cmt;
+ context->host = host;
+ context->index = NULL;
+ context->source = NULL;
+ context->source_type = NULL;
+
+ /* Setting up optional members. */
+ if (index != NULL) {
+ context->index = index;
+ }
+ if (source != NULL) {
+ context->source = source;
+ }
+ if (source_type != NULL) {
+ context->source_type = source_type;
+ }
+
+cleanup:
+ if (result != CMT_ENCODE_SPLUNK_HEC_SUCCESS) {
+ if (context != NULL) {
+ destroy_splunk_hec_context(context);
+
+ context = NULL;
+ }
+ }
+
+ return context;
+}
+
+/* Format all the registered metrics in Splunk HEC JSON format */
+cfl_sds_t cmt_encode_splunk_hec_create(struct cmt *cmt, const char *host,
+ const char *index, const char *source, const char *source_type)
+{
+ cfl_sds_t buf;
+ struct cfl_list *head;
+ struct cmt_counter *counter;
+ struct cmt_gauge *gauge;
+ struct cmt_untyped *untyped;
+ struct cmt_summary *summary;
+ struct cmt_histogram *histogram;
+ struct cmt_splunk_hec_context *context;
+
+ context = initialize_splunk_hec_context(cmt, host, index, source, source_type);
+
+ if (context == NULL) {
+ return NULL;
+ }
+
+ /* Allocate a 1KB of buffer */
+ buf = cfl_sds_create_size(1024);
+ if (!buf) {
+ return NULL;
+ }
+
+ /* Counters */
+ cfl_list_foreach(head, &cmt->counters) {
+ counter = cfl_list_entry(head, struct cmt_counter, _head);
+ format_metrics(context, &buf, counter->map);
+ }
+
+ /* Gauges */
+ cfl_list_foreach(head, &cmt->gauges) {
+ gauge = cfl_list_entry(head, struct cmt_gauge, _head);
+ format_metrics(context, &buf, gauge->map);
+ }
+
+ /* Summaries */
+ cfl_list_foreach(head, &cmt->summaries) {
+ summary = cfl_list_entry(head, struct cmt_summary, _head);
+ format_metrics(context, &buf, summary->map);
+ }
+
+ /* Histograms */
+ cfl_list_foreach(head, &cmt->histograms) {
+ histogram = cfl_list_entry(head, struct cmt_histogram, _head);
+ format_metrics(context, &buf, histogram->map);
+ }
+
+ /* Untyped */
+ cfl_list_foreach(head, &cmt->untypeds) {
+ untyped = cfl_list_entry(head, struct cmt_untyped, _head);
+ format_metrics(context, &buf, untyped->map);
+ }
+
+ if (context != NULL) {
+ destroy_splunk_hec_context(context);
+ }
+
+ return buf;
+}
+
+void cmt_encode_splunk_hec_destroy(cfl_sds_t text)
+{
+ cfl_sds_destroy(text);
+}
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_encode_text.c b/src/fluent-bit/lib/cmetrics/src/cmt_encode_text.c
new file mode 100644
index 000000000..ffdfe314c
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_encode_text.c
@@ -0,0 +1,332 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_metric.h>
+#include <cmetrics/cmt_map.h>
+#include <cmetrics/cmt_counter.h>
+#include <cmetrics/cmt_gauge.h>
+#include <cmetrics/cmt_untyped.h>
+#include <cmetrics/cmt_histogram.h>
+#include <cmetrics/cmt_summary.h>
+#include <cmetrics/cmt_time.h>
+#include <cmetrics/cmt_compat.h>
+
+static void append_histogram_metric_value(cfl_sds_t *buf,
+ struct cmt_map *map,
+ struct cmt_metric *metric)
+{
+ char *bucket_value_format_string;
+ size_t entry_buffer_length;
+ size_t entry_buffer_index;
+ char entry_buffer[256];
+ struct cmt_histogram *histogram;
+ struct cmt_histogram_buckets *buckets;
+ size_t index;
+
+ histogram = (struct cmt_histogram *) map->parent;
+ buckets = histogram->buckets;
+
+ cfl_sds_cat_safe(buf, " = { buckets = { ", 17);
+
+ for (index = 0 ; index <= buckets->count ; index++) {
+ if (index < buckets->count) {
+ entry_buffer_index = snprintf(entry_buffer,
+ sizeof(entry_buffer) - 1,
+ "%g",
+ buckets->upper_bounds[index]);
+
+ bucket_value_format_string = "=%" PRIu64 ", ";
+ }
+ else {
+ entry_buffer_index = snprintf(entry_buffer,
+ sizeof(entry_buffer) - 1,
+ "+Inf");
+
+ bucket_value_format_string = "=%" PRIu64 " ";
+ }
+
+ entry_buffer_length = entry_buffer_index;
+
+ entry_buffer_length += snprintf(&entry_buffer[entry_buffer_index],
+ sizeof(entry_buffer) - 1 -
+ entry_buffer_index,
+ bucket_value_format_string,
+ cmt_metric_hist_get_value(metric,
+ index));
+
+ cfl_sds_cat_safe(buf, entry_buffer, entry_buffer_length);
+ }
+
+ cfl_sds_cat_safe(buf, "}, ", 3);
+
+ entry_buffer_length = snprintf(entry_buffer,
+ sizeof(entry_buffer) - 1 ,
+ "sum=%g, ",
+ cmt_metric_hist_get_sum_value(metric));
+
+ cfl_sds_cat_safe(buf, entry_buffer, entry_buffer_length);
+
+ entry_buffer_length = snprintf(entry_buffer,
+ sizeof(entry_buffer) - 1 ,
+ "count=%" PRIu64 ,
+ cmt_metric_hist_get_count_value(metric));
+
+ cfl_sds_cat_safe(buf, entry_buffer, entry_buffer_length);
+
+ cfl_sds_cat_safe(buf, " }\n", 3);
+}
+
+static void append_summary_metric_value(cfl_sds_t *buf,
+ struct cmt_map *map,
+ struct cmt_metric *metric)
+{
+ char *quantile_pair_format_string;
+ size_t entry_buffer_length;
+ char entry_buffer[256];
+ struct cmt_summary *summary;
+ size_t index;
+
+ summary = (struct cmt_summary *) map->parent;
+
+ cfl_sds_cat_safe(buf, " = { quantiles = { ", 19);
+
+ for (index = 0 ; index < summary->quantiles_count ; index++) {
+ if (index < summary->quantiles_count - 1) {
+ quantile_pair_format_string = "%g=%g, ";
+ }
+ else {
+ quantile_pair_format_string = "%g=%g ";
+ }
+
+ entry_buffer_length = snprintf(entry_buffer,
+ sizeof(entry_buffer) - 1,
+ quantile_pair_format_string,
+ summary->quantiles[index],
+ cmt_summary_quantile_get_value(metric,
+ index));
+
+ cfl_sds_cat_safe(buf, entry_buffer, entry_buffer_length);
+ }
+
+ cfl_sds_cat_safe(buf, "}, ", 3);
+
+ entry_buffer_length = snprintf(entry_buffer,
+ sizeof(entry_buffer) - 1 ,
+ "sum=%g, ",
+ cmt_summary_get_sum_value(metric));
+
+ cfl_sds_cat_safe(buf, entry_buffer, entry_buffer_length);
+
+ entry_buffer_length = snprintf(entry_buffer,
+ sizeof(entry_buffer) - 1 ,
+ "count=%" PRIu64,
+ cmt_summary_get_count_value(metric));
+
+ cfl_sds_cat_safe(buf, entry_buffer, entry_buffer_length);
+
+ cfl_sds_cat_safe(buf, " }\n", 3);
+}
+
+static void append_metric_value(cfl_sds_t *buf, struct cmt_map *map,
+ struct cmt_metric *metric)
+{
+ int len;
+ double val;
+ char tmp[128];
+
+ if (map->type == CMT_HISTOGRAM) {
+ return append_histogram_metric_value(buf, map, metric);
+ }
+ else if (map->type == CMT_SUMMARY) {
+ return append_summary_metric_value(buf, map, metric);
+ }
+
+ /* Retrieve metric value */
+ val = cmt_metric_get_value(metric);
+
+ len = snprintf(tmp, sizeof(tmp) - 1, " = %.17g\n", val);
+ cfl_sds_cat_safe(buf, tmp, len);
+}
+
+static void format_metric(struct cmt *cmt, cfl_sds_t *buf, struct cmt_map *map,
+ struct cmt_metric *metric)
+{
+ int i;
+ int n;
+ int len;
+ int count = 0;
+ int static_labels = 0;
+ char tmp[128];
+ uint64_t ts;
+ struct tm tm;
+ struct timespec tms;
+ struct cmt_map_label *label_k;
+ struct cmt_map_label *label_v;
+ struct cfl_list *head;
+ struct cmt_opts *opts;
+ struct cmt_label *slabel;
+
+ opts = map->opts;
+
+ /* timestamp (RFC3339Nano) */
+ ts = cmt_metric_get_timestamp(metric);
+
+ cmt_time_from_ns(&tms, ts);
+
+ cmt_platform_gmtime_r(&tms.tv_sec, &tm);
+ len = strftime(tmp, sizeof(tmp) - 1, "%Y-%m-%dT%H:%M:%S.", &tm);
+ cfl_sds_cat_safe(buf, tmp, len);
+
+ len = snprintf(tmp, sizeof(tmp) - 1, "%09luZ ", tms.tv_nsec);
+ cfl_sds_cat_safe(buf, tmp, len);
+
+ /* Metric info */
+ cfl_sds_cat_safe(buf, opts->fqname, cfl_sds_len(opts->fqname));
+
+ /* Static labels */
+ static_labels = cmt_labels_count(cmt->static_labels);
+ if (static_labels > 0) {
+ cfl_sds_cat_safe(buf, "{", 1);
+ cfl_list_foreach(head, &cmt->static_labels->list) {
+ count++;
+ slabel = cfl_list_entry(head, struct cmt_label, _head);
+ cfl_sds_cat_safe(buf, slabel->key, cfl_sds_len(slabel->key));
+ cfl_sds_cat_safe(buf, "=\"", 2);
+ cfl_sds_cat_safe(buf, slabel->val, cfl_sds_len(slabel->val));
+ cfl_sds_cat_safe(buf, "\"", 1);
+
+ if (count < static_labels) {
+ cfl_sds_cat_safe(buf, ",", 1);
+ }
+ }
+ }
+
+ n = cfl_list_size(&metric->labels);
+ if (n > 0) {
+ if (static_labels > 0) {
+ cfl_sds_cat_safe(buf, ",", 1);
+ }
+ else {
+ cfl_sds_cat_safe(buf, "{", 1);
+ }
+
+ label_k = cfl_list_entry_first(&map->label_keys, struct cmt_map_label, _head);
+
+ i = 1;
+ cfl_list_foreach(head, &metric->labels) {
+ label_v = cfl_list_entry(head, struct cmt_map_label, _head);
+
+ cfl_sds_cat_safe(buf, label_k->name, cfl_sds_len(label_k->name));
+ cfl_sds_cat_safe(buf, "=\"", 2);
+ cfl_sds_cat_safe(buf, label_v->name, cfl_sds_len(label_v->name));
+
+ if (i < n) {
+ cfl_sds_cat_safe(buf, "\",", 2);
+ }
+ else {
+ cfl_sds_cat_safe(buf, "\"", 1);
+ }
+ i++;
+
+ label_k = cfl_list_entry_next(&label_k->_head, struct cmt_map_label,
+ _head, &map->label_keys);
+ }
+ cfl_sds_cat_safe(buf, "}", 1);
+
+ append_metric_value(buf, map, metric);
+ }
+ else {
+ if (static_labels > 0) {
+ cfl_sds_cat_safe(buf, "}", 1);
+ }
+ append_metric_value(buf, map, metric);
+ }
+}
+
+static void format_metrics(struct cmt *cmt, cfl_sds_t *buf, struct cmt_map *map)
+{
+ struct cfl_list *head;
+ struct cmt_metric *metric;
+
+ /* Simple metric, no labels */
+ if (map->metric_static_set == 1) {
+ format_metric(cmt, buf, map, &map->metric);
+ }
+
+ cfl_list_foreach(head, &map->metrics) {
+ metric = cfl_list_entry(head, struct cmt_metric, _head);
+ format_metric(cmt, buf, map, metric);
+ }
+}
+
+/* Format all the registered metrics in Prometheus Text format */
+cfl_sds_t cmt_encode_text_create(struct cmt *cmt)
+{
+ cfl_sds_t buf;
+ struct cfl_list *head;
+ struct cmt_counter *counter;
+ struct cmt_gauge *gauge;
+ struct cmt_untyped *untyped;
+ struct cmt_summary *summary;
+ struct cmt_histogram *histogram;
+
+ /* Allocate a 1KB of buffer */
+ buf = cfl_sds_create_size(1024);
+ if (!buf) {
+ return NULL;
+ }
+
+ /* Counters */
+ cfl_list_foreach(head, &cmt->counters) {
+ counter = cfl_list_entry(head, struct cmt_counter, _head);
+ format_metrics(cmt, &buf, counter->map);
+ }
+
+ /* Gauges */
+ cfl_list_foreach(head, &cmt->gauges) {
+ gauge = cfl_list_entry(head, struct cmt_gauge, _head);
+ format_metrics(cmt, &buf, gauge->map);
+ }
+
+ /* Summaries */
+ cfl_list_foreach(head, &cmt->summaries) {
+ summary = cfl_list_entry(head, struct cmt_summary, _head);
+ format_metrics(cmt, &buf, summary->map);
+ }
+
+ /* Histograms */
+ cfl_list_foreach(head, &cmt->histograms) {
+ histogram = cfl_list_entry(head, struct cmt_histogram, _head);
+ format_metrics(cmt, &buf, histogram->map);
+ }
+
+ /* Untyped */
+ cfl_list_foreach(head, &cmt->untypeds) {
+ untyped = cfl_list_entry(head, struct cmt_untyped, _head);
+ format_metrics(cmt, &buf, untyped->map);
+ }
+
+ return buf;
+}
+
+void cmt_encode_text_destroy(cfl_sds_t text)
+{
+ cfl_sds_destroy(text);
+}
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_gauge.c b/src/fluent-bit/lib/cmetrics/src/cmt_gauge.c
new file mode 100644
index 000000000..16c8f48b4
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_gauge.c
@@ -0,0 +1,198 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_log.h>
+#include <cmetrics/cmt_math.h>
+#include <cmetrics/cmt_map.h>
+#include <cmetrics/cmt_metric.h>
+#include <cmetrics/cmt_gauge.h>
+
+struct cmt_gauge *cmt_gauge_create(struct cmt *cmt,
+ char *ns, char *subsystem, char *name,
+ char *help, int label_count, char **label_keys)
+{
+ int ret;
+ struct cmt_gauge *gauge;
+
+ if (!ns) {
+ cmt_log_error(cmt, "null ns not allowed");
+ return NULL;
+ }
+
+ if (!subsystem) {
+ cmt_log_error(cmt, "null subsystem not allowed");
+ return NULL;
+ }
+
+ if (!name || strlen(name) == 0) {
+ cmt_log_error(cmt, "undefined name");
+ return NULL;
+ }
+
+ if (!help || strlen(help) == 0) {
+ cmt_log_error(cmt, "undefined help");
+ return NULL;
+ }
+
+ gauge = calloc(1, sizeof(struct cmt_gauge));
+ if (!gauge) {
+ cmt_errno();
+ return NULL;
+ }
+ cfl_list_add(&gauge->_head, &cmt->gauges);
+
+ /* Initialize options */
+ ret = cmt_opts_init(&gauge->opts, ns, subsystem, name, help);
+ if (ret == -1) {
+ cmt_log_error(cmt, "unable to initialize options for gauge");
+ cmt_gauge_destroy(gauge);
+ return NULL;
+ }
+
+ /* Create the map */
+ gauge->map = cmt_map_create(CMT_GAUGE, &gauge->opts, label_count, label_keys,
+ (void *) gauge);
+ if (!gauge->map) {
+ cmt_log_error(cmt, "unable to allocate map for gauge");
+ cmt_gauge_destroy(gauge);
+ return NULL;
+ }
+
+ gauge->cmt = cmt;
+
+ return gauge;
+}
+
+int cmt_gauge_destroy(struct cmt_gauge *gauge)
+{
+ cfl_list_del(&gauge->_head);
+ cmt_opts_exit(&gauge->opts);
+ if (gauge->map) {
+ cmt_map_destroy(gauge->map);
+ }
+ free(gauge);
+ return 0;
+}
+
+int cmt_gauge_set(struct cmt_gauge *gauge, uint64_t timestamp, double val,
+ int labels_count, char **label_vals)
+{
+ struct cmt_metric *metric;
+
+ metric = cmt_map_metric_get(&gauge->opts, gauge->map, labels_count, label_vals,
+ CMT_TRUE);
+ if (!metric) {
+ cmt_log_error(gauge->cmt, "unable to retrieve metric: %s for gauge %s_%s_%s",
+ gauge->map, gauge->opts.ns, gauge->opts.subsystem,
+ gauge->opts.name);
+ return -1;
+ }
+ cmt_metric_set(metric, timestamp, val);
+ return 0;
+}
+
+int cmt_gauge_inc(struct cmt_gauge *gauge, uint64_t timestamp,
+ int labels_count, char **label_vals)
+
+{
+ struct cmt_metric *metric;
+
+ metric = cmt_map_metric_get(&gauge->opts, gauge->map, labels_count, label_vals,
+ CMT_TRUE);
+ if (!metric) {
+ cmt_log_error(gauge->cmt, "unable to retrieve metric: %s for gauge %s_%s_%s",
+ gauge->map, gauge->opts.ns, gauge->opts.subsystem,
+ gauge->opts.name);
+ return -1;
+ }
+ cmt_metric_inc(metric, timestamp);
+ return 0;
+}
+
+int cmt_gauge_dec(struct cmt_gauge *gauge, uint64_t timestamp,
+ int labels_count, char **label_vals)
+{
+ struct cmt_metric *metric;
+
+ metric = cmt_map_metric_get(&gauge->opts, gauge->map, labels_count, label_vals,
+ CMT_TRUE);
+ if (!metric) {
+ cmt_log_error(gauge->cmt, "unable to retrieve metric: %s for gauge %s_%s_%s",
+ gauge->map, gauge->opts.ns, gauge->opts.subsystem,
+ gauge->opts.name);
+ return -1;
+ }
+ cmt_metric_dec(metric, timestamp);
+ return 0;
+}
+
+int cmt_gauge_add(struct cmt_gauge *gauge, uint64_t timestamp, double val,
+ int labels_count, char **label_vals)
+{
+ struct cmt_metric *metric;
+
+ metric = cmt_map_metric_get(&gauge->opts, gauge->map, labels_count, label_vals,
+ CMT_TRUE);
+ if (!metric) {
+ cmt_log_error(gauge->cmt, "unable to retrieve metric: %s for gauge %s_%s_%s",
+ gauge->map, gauge->opts.ns, gauge->opts.subsystem,
+ gauge->opts.name);
+ return -1;
+ }
+ cmt_metric_add(metric, timestamp, val);
+ return 0;
+}
+
+int cmt_gauge_sub(struct cmt_gauge *gauge, uint64_t timestamp, double val,
+ int labels_count, char **label_vals)
+{
+ struct cmt_metric *metric;
+
+ metric = cmt_map_metric_get(&gauge->opts, gauge->map, labels_count, label_vals,
+ CMT_TRUE);
+ if (!metric) {
+ cmt_log_error(gauge->cmt, "unable to retrieve metric: %s for gauge %s_%s_%s",
+ gauge->map, gauge->opts.ns, gauge->opts.subsystem,
+ gauge->opts.name);
+ return -1;
+ }
+ cmt_metric_sub(metric, timestamp, val);
+ return 0;
+}
+
+int cmt_gauge_get_val(struct cmt_gauge *gauge,
+ int labels_count, char **label_vals, double *out_val)
+{
+ int ret;
+ double val = 0;
+
+ ret = cmt_map_metric_get_val(&gauge->opts,
+ gauge->map, labels_count, label_vals,
+ &val);
+ if (ret == -1) {
+ cmt_log_error(gauge->cmt,
+ "unable to retrieve metric value: %s for gauge %s_%s_%s",
+ gauge->map, gauge->opts.ns, gauge->opts.subsystem,
+ gauge->opts.name);
+ return -1;
+ }
+ *out_val = val;
+ return 0;
+}
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_histogram.c b/src/fluent-bit/lib/cmetrics/src/cmt_histogram.c
new file mode 100644
index 000000000..ae895f3ad
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_histogram.c
@@ -0,0 +1,403 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_log.h>
+#include <cmetrics/cmt_map.h>
+#include <cmetrics/cmt_metric.h>
+#include <cmetrics/cmt_histogram.h>
+
+#include <stdarg.h>
+
+struct cmt_histogram_buckets *cmt_histogram_buckets_create_size(double *bkts, size_t count)
+{
+ int i;
+ double *upper_bounds;
+ struct cmt_histogram_buckets *buckets;
+
+ if (count < 1) {
+ return NULL;
+ }
+
+ /* besides buckets set by the user, we add an implicit bucket for +inf */
+ upper_bounds = calloc(1, sizeof(double) * count + 1);
+ if (!upper_bounds) {
+ cmt_errno();
+ return NULL;
+ }
+
+ buckets = calloc(1, sizeof(struct cmt_histogram_buckets));
+ if (!buckets) {
+ cmt_errno();
+ free(upper_bounds);
+ return NULL;
+ }
+
+ buckets->count = count;
+ buckets->upper_bounds = upper_bounds;
+
+ if (bkts != NULL) {
+ for (i = 0; i < count; i++) {
+ upper_bounds[i] = bkts[i];
+ }
+ }
+
+ return buckets;
+}
+
+struct cmt_histogram_buckets *cmt_histogram_buckets_create(size_t count, ...)
+{
+ int i;
+ double *bucket_array;
+ struct cmt_histogram_buckets *buckets;
+ va_list va;
+
+ bucket_array = calloc(count, sizeof(double));
+ if (!bucket_array) {
+ return NULL;
+ }
+
+ va_start(va, count);
+ for (i = 0; i < count; i++) {
+ bucket_array[i] = va_arg(va, double);
+ }
+ va_end(va);
+
+ buckets = cmt_histogram_buckets_create_size(bucket_array, count);
+ free(bucket_array);
+
+ return buckets;
+}
+
+/* Create default buckets */
+struct cmt_histogram_buckets *cmt_histogram_buckets_default_create()
+{
+ return cmt_histogram_buckets_create_size((double[]) {
+ 0.005, 0.01, 0.025, 0.05,
+ 0.1, 0.25, 0.5, 1.0, 2.5,
+ 5.0, 10.0 }, 11);
+}
+
+/* Linear bucket creation */
+struct cmt_histogram_buckets *cmt_histogram_buckets_linear_create(double start,
+ double width,
+ size_t count)
+{
+ int i;
+ double *upper_bounds;
+ struct cmt_histogram_buckets *buckets;
+
+ if (count <= 1) {
+ return NULL;
+ }
+
+ upper_bounds = calloc(1, sizeof(double) * count);
+ if (!upper_bounds) {
+ cmt_errno();
+ return NULL;
+ }
+
+ buckets = calloc(1, sizeof(struct cmt_histogram_buckets));
+ if (!buckets) {
+ cmt_errno();
+ free(upper_bounds);
+ return NULL;
+ }
+
+ buckets->count = count;
+ buckets->upper_bounds = upper_bounds;
+
+ /* initialize first bucket */
+ upper_bounds[0] = start;
+
+ for (i = 1; i < count; i++) {
+ upper_bounds[i] = upper_bounds[i - 1] + width;
+ }
+
+ return buckets;
+}
+
+/* Exponential bucket creation */
+struct cmt_histogram_buckets *cmt_histogram_buckets_exponential_create(double start,
+ double factor,
+ size_t count)
+{
+ int i;
+ double *upper_bounds;
+ struct cmt_histogram_buckets *buckets;
+
+ if (start <= 0) {
+ return NULL;
+ }
+
+ if (factor <= 1) {
+ return NULL;
+ }
+
+ if (count < 1) {
+ return NULL;
+ }
+
+ upper_bounds = calloc(1, sizeof(double) * count);
+ if (!upper_bounds) {
+ cmt_errno();
+ return NULL;
+ }
+
+ buckets = calloc(1, sizeof(struct cmt_histogram_buckets));
+ if (!buckets) {
+ cmt_errno();
+ free(upper_bounds);
+ return NULL;
+ }
+
+ buckets->count = count;
+ buckets->upper_bounds = upper_bounds;
+
+ /* initialize first bucket */
+ upper_bounds[0] = start;
+
+ for (i = 1; i < count; i++) {
+ upper_bounds[i] = upper_bounds[i - 1] * factor;
+ }
+
+ return buckets;
+}
+
+void cmt_histogram_buckets_destroy(struct cmt_histogram_buckets *buckets)
+{
+ if (!buckets) {
+ return;
+ }
+
+ if (buckets->upper_bounds) {
+ free(buckets->upper_bounds);
+ }
+
+ free(buckets);
+}
+
+static int check_buckets(struct cmt_histogram_buckets *buckets)
+{
+ int i;
+
+ for (i = 1; i < buckets->count; i++) {
+ if (buckets->upper_bounds[i - 1] > buckets->upper_bounds[i]) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+struct cmt_histogram *cmt_histogram_create(struct cmt *cmt,
+ char *ns, char *subsystem,
+ char *name, char *help,
+ struct cmt_histogram_buckets *buckets,
+ int label_count, char **label_keys)
+{
+ int ret;
+ struct cmt_histogram *h;
+
+ if (!ns) {
+ cmt_log_error(cmt, "null ns not allowed");
+ return NULL;
+ }
+
+ if (!subsystem) {
+ cmt_log_error(cmt, "null subsystem not allowed");
+ return NULL;
+ }
+
+ if (!name || strlen(name) == 0) {
+ cmt_log_error(cmt, "undefined name");
+ return NULL;
+ }
+
+ if (!help || strlen(help) == 0) {
+ cmt_log_error(cmt, "undefined help");
+ return NULL;
+ }
+
+ h = calloc(1, sizeof(struct cmt_histogram));
+ if (!h) {
+ cmt_errno();
+ return NULL;
+ }
+ cfl_list_add(&h->_head, &cmt->histograms);
+
+ /* set buckets */
+ if (buckets) {
+ h->buckets = buckets;
+ }
+ else {
+ /* set 'default' buckets */
+ h->buckets = cmt_histogram_buckets_default_create();
+ if (!h->buckets) {
+ cmt_histogram_destroy(h);
+ return NULL;
+ }
+ }
+
+ /* Validate buckets order */
+ ret = check_buckets(h->buckets);
+ if (ret != 0) {
+ cmt_histogram_destroy(h);
+ return NULL;
+ }
+
+ /* initialize options */
+ ret = cmt_opts_init(&h->opts, ns, subsystem, name, help);
+ if (ret == -1) {
+ cmt_log_error(cmt, "unable to initialize options for histogram");
+ cmt_histogram_destroy(h);
+ return NULL;
+ }
+
+ /* Create the map */
+ h->map = cmt_map_create(CMT_HISTOGRAM, &h->opts, label_count, label_keys,
+ (void *) h);
+ if (!h->map) {
+ cmt_log_error(cmt, "unable to allocate map for histogram");
+ cmt_histogram_destroy(h);
+ return NULL;
+ }
+
+ return h;
+}
+
+int cmt_histogram_destroy(struct cmt_histogram *h)
+{
+ cfl_list_del(&h->_head);
+ cmt_opts_exit(&h->opts);
+
+ if (h->buckets) {
+ cmt_histogram_buckets_destroy(h->buckets);
+ }
+
+ if (h->map) {
+ cmt_map_destroy(h->map);
+ }
+
+ free(h);
+ return 0;
+}
+
+static struct cmt_metric *histogram_get_metric(struct cmt_histogram *histogram,
+ int labels_count, char **label_vals)
+{
+ struct cmt_metric *metric;
+ struct cmt_histogram_buckets *buckets;
+
+ metric = cmt_map_metric_get(&histogram->opts, histogram->map,
+ labels_count, label_vals, CMT_TRUE);
+ if (!metric) {
+ cmt_log_error(histogram->cmt,
+ "unable to retrieve metric: %s for histogram %s_%s_%s",
+ histogram->map, histogram->opts.ns, histogram->opts.subsystem,
+ histogram->opts.name);
+ return NULL;
+ }
+
+ /* ref buckets */
+ buckets = histogram->buckets;
+
+ /* make sure buckets has been initialized */
+ if (!metric->hist_buckets) {
+ metric->hist_buckets = calloc(1, sizeof(uint64_t) * (buckets->count + 1));
+ if (!metric->hist_buckets) {
+ cmt_errno();
+ return NULL;
+ }
+ }
+
+ return metric;
+}
+
+/* Observe the value and put it in the right bucket */
+int cmt_histogram_observe(struct cmt_histogram *histogram, uint64_t timestamp,
+ double val, int labels_count, char **label_vals)
+{
+ int i;
+ struct cmt_metric *metric;
+ struct cmt_histogram_buckets *buckets;
+
+ metric = histogram_get_metric(histogram, labels_count, label_vals);
+ if (!metric) {
+ cmt_log_error(histogram->cmt,
+ "unable to retrieve metric: %s for histogram %s_%s_%s",
+ histogram->map, histogram->opts.ns, histogram->opts.subsystem,
+ histogram->opts.name);
+ return -1;
+ }
+
+ /* increment buckets */
+ buckets = histogram->buckets;
+ for (i = buckets->count - 1; i >= 0; i--) {
+ if (val > buckets->upper_bounds[i]) {
+ break;
+ }
+ cmt_metric_hist_inc(metric, timestamp, i);
+ }
+
+ /* increment bucket +Inf */
+ cmt_metric_hist_inc(metric, timestamp, buckets->count);
+
+ /* increment bucket _count */
+ cmt_metric_hist_count_inc(metric, timestamp);
+
+ /* add observed value to _sum */
+ cmt_metric_hist_sum_add(metric, timestamp, val);
+ return 0;
+}
+
+int cmt_histogram_set_default(struct cmt_histogram *histogram,
+ uint64_t timestamp,
+ uint64_t *bucket_defaults,
+ double sum,
+ uint64_t count,
+ int labels_count, char **label_vals)
+{
+ int i;
+ struct cmt_metric *metric;
+ struct cmt_histogram_buckets *buckets;
+
+ metric = histogram_get_metric(histogram, labels_count, label_vals);
+ if (!metric) {
+ cmt_log_error(histogram->cmt,
+ "unable to retrieve metric: %s for histogram %s_%s_%s",
+ histogram->map, histogram->opts.ns, histogram->opts.subsystem,
+ histogram->opts.name);
+ return -1;
+ }
+
+ /*
+ * For every bucket, set the default value set in 'defaults', note that no
+ * size check is performed and we trust the caller set the proper array size
+ */
+ buckets = histogram->buckets;
+ for (i = 0; i <= buckets->count; i++) {
+ cmt_metric_hist_set(metric, timestamp, i, bucket_defaults[i]);
+ }
+
+ cmt_metric_hist_sum_set(metric, timestamp, sum);
+ cmt_metric_hist_count_set(metric, timestamp, count);
+
+ return 0;
+}
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_label.c b/src/fluent-bit/lib/cmetrics/src/cmt_label.c
new file mode 100644
index 000000000..911ebb59a
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_label.c
@@ -0,0 +1,100 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_label.h>
+
+/*
+ * This interface file provide helper functions to compose a dynamic list
+ * of custom labels with specific keys and values. Note that this is not
+ * about labels defined by metrics upon creation, but label lists to be
+ * used by the encoders when formatting the data.
+ */
+struct cmt_labels *cmt_labels_create()
+{
+ struct cmt_labels *l;
+
+ l = malloc(sizeof(struct cmt_labels));
+ if (!l) {
+ cmt_errno();
+ return NULL;
+ }
+ cfl_list_init(&l->list);
+ return l;
+}
+
+void cmt_labels_destroy(struct cmt_labels *labels)
+{
+ struct cfl_list *tmp;
+ struct cfl_list *head;
+ struct cmt_label *l;
+
+ cfl_list_foreach_safe(head, tmp, &labels->list) {
+ l = cfl_list_entry(head, struct cmt_label, _head);
+ if (l->key) {
+ cfl_sds_destroy(l->key);
+ }
+ if (l->val) {
+ cfl_sds_destroy(l->val);
+ }
+ cfl_list_del(&l->_head);
+ free(l);
+ }
+
+ free(labels);
+}
+
+int cmt_labels_add_kv(struct cmt_labels *labels, char *key, char *val)
+{
+ struct cmt_label *l;
+
+ l = malloc(sizeof(struct cmt_label));
+ if (!l) {
+ cmt_errno();
+ return -1;
+ }
+
+ l->key = cfl_sds_create(key);
+ if (!l->key) {
+ free(l);
+ return -1;
+ }
+
+ l->val = cfl_sds_create(val);
+ if (!l->val) {
+ cfl_sds_destroy(l->key);
+ free(l);
+ return -1;
+ }
+
+ cfl_list_add(&l->_head, &labels->list);
+ return 0;
+}
+
+int cmt_labels_count(struct cmt_labels *labels)
+{
+ int c = 0;
+ struct cfl_list *head;
+
+ cfl_list_foreach(head, &labels->list) {
+ c++;
+ }
+
+ return c;
+}
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_log.c b/src/fluent-bit/lib/cmetrics/src/cmt_log.c
new file mode 100644
index 000000000..7711c5661
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_log.c
@@ -0,0 +1,91 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_log.h>
+#include <cmetrics/cmt_compat.h>
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+
+#ifdef _WIN32
+ #define strerror_r(errnum, buf, buf_size) strerror_s(buf, buf_size, errnum)
+#endif
+
+void cmt_log_print(void *ctx, int level, const char *file, int line,
+ const char *fmt, ...)
+{
+ int ret;
+ char buf[CMT_LOG_BUF_SIZE];
+ va_list args;
+ struct cmt *cmt = ctx;
+
+ if (!cmt->log_cb) {
+ return;
+ }
+
+ if (level > cmt->log_level) {
+ return;
+ }
+
+ va_start(args, fmt);
+ ret = vsnprintf(buf, CMT_LOG_BUF_SIZE - 1, fmt, args);
+
+ if (ret >= 0) {
+ buf[ret] = '\0';
+ }
+ va_end(args);
+
+ cmt->log_cb(ctx, level, file, line, buf);
+}
+
+int cmt_errno_print(int errnum, const char *file, int line)
+{
+ char buf[256];
+
+ strerror_r(errnum, buf, sizeof(buf) - 1);
+ fprintf(stderr, "[%s:%i errno=%i] %s\n",
+ file, line, errnum, buf);
+ return 0;
+}
+
+#ifdef _WIN32
+void cmt_winapi_error_print(const char *func, int line)
+{
+ int error = GetLastError();
+ char buf[256];
+ int success;
+
+ success = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ error,
+ LANG_SYSTEM_DEFAULT,
+ buf,
+ sizeof(buf),
+ NULL);
+ if (success) {
+ fprintf(stderr, "[%s() line=%i error=%i] %s\n", func, line, error, buf);
+ }
+ else {
+ fprintf(stderr, "[%s() line=%i error=%i] Win32 API failed\n", func, line, error);
+ }
+}
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_map.c b/src/fluent-bit/lib/cmetrics/src/cmt_map.c
new file mode 100644
index 000000000..ab25bffb8
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_map.c
@@ -0,0 +1,318 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_map.h>
+#include <cmetrics/cmt_log.h>
+#include <cmetrics/cmt_metric.h>
+#include <cmetrics/cmt_compat.h>
+
+struct cmt_map *cmt_map_create(int type, struct cmt_opts *opts, int count, char **labels,
+ void *parent)
+{
+ int i;
+ char *name;
+ struct cmt_map *map;
+ struct cmt_map_label *label;
+
+ if (count < 0) {
+ return NULL;
+ }
+
+ map = calloc(1, sizeof(struct cmt_map));
+ if (!map) {
+ cmt_errno();
+ return NULL;
+ }
+ map->type = type;
+ map->opts = opts;
+ map->parent = parent;
+ map->label_count = count;
+ cfl_list_init(&map->label_keys);
+ cfl_list_init(&map->metrics);
+ cfl_list_init(&map->metric.labels);
+
+ if (count == 0) {
+ map->metric_static_set = 1;
+ }
+
+ for (i = 0; i < count; i++) {
+ label = malloc(sizeof(struct cmt_map_label));
+ if (!label) {
+ cmt_errno();
+ goto error;
+ }
+
+ name = labels[i];
+ label->name = cfl_sds_create(name);
+ if (!label->name) {
+ cmt_errno();
+ free(label);
+ goto error;
+ }
+ cfl_list_add(&label->_head, &map->label_keys);
+ }
+
+ return map;
+
+ error:
+ cmt_map_destroy(map);
+ return NULL;
+}
+
+static struct cmt_metric *metric_hash_lookup(struct cmt_map *map, uint64_t hash)
+{
+ struct cfl_list *head;
+ struct cmt_metric *metric;
+
+ if (hash == 0) {
+ return &map->metric;
+ }
+
+ cfl_list_foreach(head, &map->metrics) {
+ metric = cfl_list_entry(head, struct cmt_metric, _head);
+ if (metric->hash == hash) {
+ return metric;
+ }
+ }
+
+ return NULL;
+}
+
+static struct cmt_metric *map_metric_create(uint64_t hash,
+ int labels_count, char **labels_val)
+{
+ int i;
+ char *name;
+ struct cmt_metric *metric;
+ struct cmt_map_label *label;
+
+ metric = calloc(1, sizeof(struct cmt_metric));
+ if (!metric) {
+ cmt_errno();
+ return NULL;
+ }
+ cfl_list_init(&metric->labels);
+ metric->val = 0.0;
+ metric->hash = hash;
+
+ for (i = 0; i < labels_count; i++) {
+ label = malloc(sizeof(struct cmt_map_label));
+ if (!label) {
+ cmt_errno();
+ goto error;
+ }
+
+ name = labels_val[i];
+ label->name = cfl_sds_create(name);
+ if (!label->name) {
+ cmt_errno();
+ free(label);
+ goto error;
+ }
+ cfl_list_add(&label->_head, &metric->labels);
+ }
+
+ return metric;
+
+ error:
+ free(metric);
+ return NULL;
+}
+
+static void map_metric_destroy(struct cmt_metric *metric)
+{
+ struct cfl_list *tmp;
+ struct cfl_list *head;
+ struct cmt_map_label *label;
+
+ cfl_list_foreach_safe(head, tmp, &metric->labels) {
+ label = cfl_list_entry(head, struct cmt_map_label, _head);
+ cfl_sds_destroy(label->name);
+ cfl_list_del(&label->_head);
+ free(label);
+ }
+
+ if (metric->hist_buckets) {
+ free(metric->hist_buckets);
+ }
+ if (metric->sum_quantiles) {
+ free(metric->sum_quantiles);
+ }
+
+ cfl_list_del(&metric->_head);
+ free(metric);
+}
+
+struct cmt_metric *cmt_map_metric_get(struct cmt_opts *opts, struct cmt_map *map,
+ int labels_count, char **labels_val,
+ int write_op)
+{
+ int i;
+ int len;
+ char *ptr;
+ uint64_t hash;
+ cfl_hash_state_t state;
+ struct cmt_metric *metric = NULL;
+
+ /* Enforce zero or exact labels */
+ if (labels_count > 0 && labels_count != map->label_count) {
+ return NULL;
+ }
+
+ /*
+ * If the caller wants the no-labeled metric (metric_static_set) make sure
+ * it was already pre-defined.
+ */
+ if (labels_count == 0) {
+ /*
+ * if an upcoming 'write operation' will be performed for a default
+ * static metric, just initialize it and return it.
+ */
+ if (map->metric_static_set) {
+ metric = &map->metric;
+ }
+ else if (write_op) {
+ metric = &map->metric;
+ if (!map->metric_static_set) {
+ map->metric_static_set = 1;
+ }
+ }
+
+ /* return the proper context or NULL */
+ return metric;
+ }
+
+ /* Lookup the metric */
+ cfl_hash_64bits_reset(&state);
+ cfl_hash_64bits_update(&state, opts->fqname, cfl_sds_len(opts->fqname));
+ for (i = 0; i < labels_count; i++) {
+ ptr = labels_val[i];
+ if (!ptr) {
+ cfl_hash_64bits_update(&state, "_NULL_", 6);
+ }
+ else {
+ len = strlen(ptr);
+ cfl_hash_64bits_update(&state, ptr, len);
+ }
+ }
+
+ hash = cfl_hash_64bits_digest(&state);
+ metric = metric_hash_lookup(map, hash);
+
+ if (metric) {
+ return metric;
+ }
+
+ /*
+ * If the metric was not found and the caller will not write a value, just
+ * return NULL.
+ */
+ if (!write_op) {
+ return NULL;
+ }
+
+ /* If the metric has not been found, just create it */
+ metric = map_metric_create(hash, labels_count, labels_val);
+ if (!metric) {
+ return NULL;
+ }
+ cfl_list_add(&metric->_head, &map->metrics);
+ return metric;
+}
+
+int cmt_map_metric_get_val(struct cmt_opts *opts, struct cmt_map *map,
+ int labels_count, char **labels_val,
+ double *out_val)
+{
+ double val = 0;
+ struct cmt_metric *metric;
+
+ metric = cmt_map_metric_get(opts, map, labels_count, labels_val, CMT_FALSE);
+ if (!metric) {
+ return -1;
+ }
+
+ val = cmt_metric_get_value(metric);
+ *out_val = val;
+ return 0;
+}
+
+void cmt_map_destroy(struct cmt_map *map)
+{
+ struct cfl_list *tmp;
+ struct cfl_list *head;
+ struct cmt_map_label *label;
+ struct cmt_metric *metric;
+
+ cfl_list_foreach_safe(head, tmp, &map->label_keys) {
+ label = cfl_list_entry(head, struct cmt_map_label, _head);
+ cfl_sds_destroy(label->name);
+ cfl_list_del(&label->_head);
+ free(label);
+ }
+
+ cfl_list_foreach_safe(head, tmp, &map->metrics) {
+ metric = cfl_list_entry(head, struct cmt_metric, _head);
+ map_metric_destroy(metric);
+ }
+
+ /* histogram and quantile allocation for static metric */
+ if (map->metric_static_set) {
+ metric = &map->metric;
+
+ if (map->type == CMT_HISTOGRAM) {
+ if (metric->hist_buckets) {
+ free(metric->hist_buckets);
+ }
+ }
+ else if (map->type == CMT_SUMMARY) {
+ if (metric->sum_quantiles) {
+ free(metric->sum_quantiles);
+ }
+ }
+ }
+
+ free(map);
+}
+
+/* I don't know if we should leave this or promote the label type so it has its own
+ * header and source files with their own constructor / destructor and an agnostic name.
+ * That last bit comes from the fact that we are using the cmt_map_label type both in the
+ * dimension definition list held by the map structure and the dimension value list held
+ * by the metric structure.
+ */
+
+void destroy_label_list(struct cfl_list *label_list)
+{
+ struct cfl_list *tmp;
+ struct cfl_list *head;
+ struct cmt_map_label *label;
+
+ cfl_list_foreach_safe(head, tmp, label_list) {
+ label = cfl_list_entry(head, struct cmt_map_label, _head);
+
+ cfl_sds_destroy(label->name);
+
+ cfl_list_del(&label->_head);
+
+ free(label);
+ }
+}
+
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_metric.c b/src/fluent-bit/lib/cmetrics/src/cmt_metric.c
new file mode 100644
index 000000000..dc58d26f8
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_metric.c
@@ -0,0 +1,142 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_metric.h>
+#include <cmetrics/cmt_math.h>
+#include <cmetrics/cmt_atomic.h>
+
+static inline int metric_exchange(struct cmt_metric *metric, uint64_t timestamp,
+ double new_value, double old_value)
+{
+ uint64_t tmp_new;
+ uint64_t tmp_old;
+ int result;
+
+ tmp_new = cmt_math_d64_to_uint64(new_value);
+ tmp_old = cmt_math_d64_to_uint64(old_value);
+
+ result = cmt_atomic_compare_exchange(&metric->val, tmp_old, tmp_new);
+
+ if(0 == result) {
+ return 0;
+ }
+
+ cmt_atomic_store(&metric->timestamp, timestamp);
+
+ return 1;
+}
+
+static inline void add(struct cmt_metric *metric, uint64_t timestamp, double val)
+{
+ double old;
+ double new;
+ int result;
+
+ do {
+ old = cmt_metric_get_value(metric);
+ new = old + val;
+
+ result = metric_exchange(metric, timestamp, new, old);
+ }
+ while(0 == result);
+}
+
+void cmt_metric_set(struct cmt_metric *metric, uint64_t timestamp, double val)
+{
+ uint64_t tmp;
+
+ tmp = cmt_math_d64_to_uint64(val);
+
+ cmt_atomic_store(&metric->val, tmp);
+ cmt_atomic_store(&metric->timestamp, timestamp);
+}
+
+static inline int metric_hist_exchange(struct cmt_metric *metric,
+ uint64_t timestamp,
+ int bucket_id,
+ uint64_t new, uint64_t old)
+{
+ int result;
+
+ result = cmt_atomic_compare_exchange(&metric->hist_buckets[bucket_id],
+ old, new);
+ if (result == 0) {
+ return 0;
+ }
+
+ cmt_atomic_store(&metric->timestamp, timestamp);
+ return 1;
+}
+
+void cmt_metric_hist_bucket_inc(struct cmt_metric *metric, uint64_t timestamp,
+ int bucket_id)
+{
+ int result;
+ uint64_t old;
+ uint64_t new;
+
+ do {
+ old = cmt_atomic_load(&metric->hist_buckets[bucket_id]);
+ new = old + 1;
+ result = metric_hist_exchange(metric, timestamp, bucket_id, new, old);
+ }
+ while (result == 0);
+}
+
+
+void cmt_metric_inc(struct cmt_metric *metric, uint64_t timestamp)
+{
+ add(metric, timestamp, 1);
+}
+
+void cmt_metric_dec(struct cmt_metric *metric, uint64_t timestamp)
+{
+ double volatile val = 1.0;
+
+ add(metric, timestamp, val * -1);
+}
+
+void cmt_metric_add(struct cmt_metric *metric, uint64_t timestamp, double val)
+{
+ add(metric, timestamp, val);
+}
+
+void cmt_metric_sub(struct cmt_metric *metric, uint64_t timestamp, double val)
+{
+ add(metric, timestamp, (double volatile) val * -1);
+}
+
+double cmt_metric_get_value(struct cmt_metric *metric)
+{
+ uint64_t val;
+
+ val = cmt_atomic_load(&metric->val);
+
+ return cmt_math_uint64_to_d64(val);
+}
+
+uint64_t cmt_metric_get_timestamp(struct cmt_metric *metric)
+{
+ uint64_t val;
+
+ val = cmt_atomic_load(&metric->timestamp);
+
+ return val;
+}
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_metric_histogram.c b/src/fluent-bit/lib/cmetrics/src/cmt_metric_histogram.c
new file mode 100644
index 000000000..e2f7f0630
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_metric_histogram.c
@@ -0,0 +1,192 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_metric.h>
+#include <cmetrics/cmt_math.h>
+#include <cmetrics/cmt_atomic.h>
+
+static inline int metric_hist_exchange(struct cmt_metric *metric,
+ uint64_t timestamp,
+ int bucket_id,
+ uint64_t new, uint64_t old)
+{
+ int result;
+
+ result = cmt_atomic_compare_exchange(&metric->hist_buckets[bucket_id],
+ old, new);
+ if (result == 0) {
+ return 0;
+ }
+
+ cmt_atomic_store(&metric->timestamp, timestamp);
+ return 1;
+}
+
+static inline int metric_hist_count_exchange(struct cmt_metric *metric,
+ uint64_t timestamp,
+ uint64_t new, uint64_t old)
+{
+ int result;
+
+ result = cmt_atomic_compare_exchange(&metric->hist_count, old, new);
+ if (result == 0) {
+ return 0;
+ }
+
+ cmt_atomic_store(&metric->timestamp, timestamp);
+ return 1;
+}
+
+static inline int metric_sum_exchange(struct cmt_metric *metric,
+ uint64_t timestamp,
+ double new_value, double old_value)
+{
+ uint64_t tmp_new;
+ uint64_t tmp_old;
+ int result;
+
+ tmp_new = cmt_math_d64_to_uint64(new_value);
+ tmp_old = cmt_math_d64_to_uint64(old_value);
+
+ result = cmt_atomic_compare_exchange(&metric->hist_sum, tmp_old, tmp_new);
+
+ if (result == 0) {
+ return 0;
+ }
+
+ cmt_atomic_store(&metric->timestamp, timestamp);
+ return 1;
+}
+
+void cmt_metric_hist_inc(struct cmt_metric *metric, uint64_t timestamp,
+ int bucket_id)
+{
+ int result;
+ uint64_t old;
+ uint64_t new;
+
+ do {
+ old = cmt_atomic_load(&metric->hist_buckets[bucket_id]);
+ new = old + 1;
+ result = metric_hist_exchange(metric, timestamp, bucket_id, new, old);
+ }
+ while (result == 0);
+}
+
+void cmt_metric_hist_count_inc(struct cmt_metric *metric, uint64_t timestamp)
+{
+ int result;
+ uint64_t old;
+ uint64_t new;
+
+ do {
+ old = cmt_atomic_load(&metric->hist_count);
+ new = old + 1;
+
+ result = metric_hist_count_exchange(metric, timestamp, new, old);
+ }
+ while (result == 0);
+}
+
+void cmt_metric_hist_count_set(struct cmt_metric *metric, uint64_t timestamp,
+ uint64_t count)
+{
+ int result;
+ uint64_t old;
+ uint64_t new;
+
+ do {
+ old = cmt_atomic_load(&metric->hist_count);
+ new = count;
+
+ result = metric_hist_count_exchange(metric, timestamp, new, old);
+ }
+ while (result == 0);
+}
+
+void cmt_metric_hist_sum_add(struct cmt_metric *metric, uint64_t timestamp,
+ double val)
+{
+ double old;
+ double new;
+ int result;
+
+ do {
+ old = cmt_metric_hist_get_sum_value(metric);
+ new = old + val;
+ result = metric_sum_exchange(metric, timestamp, new, old);
+ }
+ while (0 == result);
+}
+
+void cmt_metric_hist_sum_set(struct cmt_metric *metric, uint64_t timestamp,
+ double val)
+{
+ double old;
+ double new;
+ int result;
+
+ do {
+ old = cmt_metric_hist_get_sum_value(metric);
+ new = val;
+ result = metric_sum_exchange(metric, timestamp, new, old);
+ }
+ while (0 == result);
+}
+
+void cmt_metric_hist_set(struct cmt_metric *metric, uint64_t timestamp,
+ int bucket_id, double val)
+{
+ int result;
+ uint64_t old;
+ uint64_t new;
+
+ do {
+ old = cmt_atomic_load(&metric->hist_buckets[bucket_id]);
+ new = val;
+
+ result = metric_hist_exchange(metric, timestamp, bucket_id, new, old);
+ }
+ while (result == 0);
+}
+
+uint64_t cmt_metric_hist_get_value(struct cmt_metric *metric, int bucket_id)
+{
+ uint64_t val;
+
+ val = cmt_atomic_load(&metric->hist_buckets[bucket_id]);
+ return val;
+}
+
+uint64_t cmt_metric_hist_get_count_value(struct cmt_metric *metric)
+{
+ uint64_t val;
+
+ val = cmt_atomic_load(&metric->hist_count);
+ return val;
+}
+
+double cmt_metric_hist_get_sum_value(struct cmt_metric *metric)
+{
+ uint64_t val;
+
+ val = cmt_atomic_load(&metric->hist_sum);
+ return cmt_math_uint64_to_d64(val);
+}
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_mpack_utils.c b/src/fluent-bit/lib/cmetrics/src/cmt_mpack_utils.c
new file mode 100644
index 000000000..e303554d8
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_mpack_utils.c
@@ -0,0 +1,286 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmt_mpack_utils.h>
+#include <mpack/mpack.h>
+
+int cmt_mpack_consume_double_tag(mpack_reader_t *reader, double *output_buffer)
+{
+ mpack_tag_t tag;
+
+ if (NULL == output_buffer) {
+ return CMT_MPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ if (NULL == reader) {
+ return CMT_MPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ tag = mpack_read_tag(reader);
+
+ if (mpack_ok != mpack_reader_error(reader)) {
+ return CMT_MPACK_ENGINE_ERROR;
+ }
+
+ if (mpack_type_double != mpack_tag_type(&tag)) {
+ return CMT_MPACK_UNEXPECTED_DATA_TYPE_ERROR;
+ }
+
+ *output_buffer = mpack_tag_double_value(&tag);
+
+ return CMT_MPACK_SUCCESS;
+}
+
+int cmt_mpack_consume_uint_tag(mpack_reader_t *reader, uint64_t *output_buffer)
+{
+ mpack_tag_t tag;
+
+ if (NULL == output_buffer) {
+ return CMT_MPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ if (NULL == reader) {
+ return CMT_MPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ tag = mpack_read_tag(reader);
+
+ if (mpack_ok != mpack_reader_error(reader)) {
+ return CMT_MPACK_ENGINE_ERROR;
+ }
+
+ if (mpack_type_uint != mpack_tag_type(&tag)) {
+ return CMT_MPACK_UNEXPECTED_DATA_TYPE_ERROR;
+ }
+
+ *output_buffer = mpack_tag_uint_value(&tag);
+
+ return CMT_MPACK_SUCCESS;
+}
+
+int cmt_mpack_consume_string_tag(mpack_reader_t *reader, cfl_sds_t *output_buffer)
+{
+ uint32_t string_length;
+ mpack_tag_t tag;
+
+ if (NULL == output_buffer) {
+ return CMT_MPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ if (NULL == reader) {
+ return CMT_MPACK_INVALID_ARGUMENT_ERROR;
+ }
+
+ tag = mpack_read_tag(reader);
+
+ if (mpack_ok != mpack_reader_error(reader)) {
+ return CMT_MPACK_ENGINE_ERROR;
+ }
+
+ if (mpack_type_str != mpack_tag_type(&tag)) {
+ return CMT_MPACK_UNEXPECTED_DATA_TYPE_ERROR;
+ }
+
+ string_length = mpack_tag_str_length(&tag);
+
+ /* This validation only applies to cmetrics and its use cases, we know
+ * for a fact that our label names and values are not supposed to be really
+ * long so a huge value here probably means that the data stream got corrupted.
+ */
+
+ if (CMT_MPACK_MAX_STRING_LENGTH < string_length) {
+ return CMT_MPACK_CORRUPT_INPUT_DATA_ERROR;
+ }
+
+ *output_buffer = cfl_sds_create_size(string_length + 1);
+
+ if (NULL == *output_buffer) {
+ return CMT_MPACK_ALLOCATION_ERROR;
+ }
+
+ cfl_sds_set_len(*output_buffer, string_length);
+
+ mpack_read_cstr(reader, *output_buffer, string_length + 1, string_length);
+
+ if (mpack_ok != mpack_reader_error(reader)) {
+ cfl_sds_destroy(*output_buffer);
+
+ *output_buffer = NULL;
+
+ return CMT_MPACK_ENGINE_ERROR;
+ }
+
+ mpack_done_str(reader);
+
+ if (mpack_ok != mpack_reader_error(reader)) {
+ cfl_sds_destroy(*output_buffer);
+
+ *output_buffer = NULL;
+
+ return CMT_MPACK_ENGINE_ERROR;
+ }
+
+ return CMT_MPACK_SUCCESS;
+}
+
+int cmt_mpack_unpack_map(mpack_reader_t *reader,
+ struct cmt_mpack_map_entry_callback_t *callback_list,
+ void *context)
+{
+ struct cmt_mpack_map_entry_callback_t *callback_entry;
+ uint32_t entry_index;
+ uint32_t entry_count;
+ cfl_sds_t key_name;
+ int result;
+ mpack_tag_t tag;
+
+ tag = mpack_read_tag(reader);
+
+ if (mpack_ok != mpack_reader_error(reader)) {
+ return CMT_MPACK_ENGINE_ERROR;
+ }
+
+ if (mpack_type_map != mpack_tag_type(&tag)) {
+ return CMT_MPACK_UNEXPECTED_DATA_TYPE_ERROR;
+ }
+
+ entry_count = mpack_tag_map_count(&tag);
+
+ /* This validation only applies to cmetrics and its use cases, we know
+ * how our schema looks and how many entries the different fields have and none
+ * of those exceed the number we set CMT_MPACK_MAX_MAP_ENTRY_COUNT to which is 10.
+ * Making these sanity checks optional or configurable in runtime might be worth
+ * the itme and complexity cost but that's something I don't know at the moment.
+ */
+
+ if (CMT_MPACK_MAX_MAP_ENTRY_COUNT < entry_count) {
+ return CMT_MPACK_CORRUPT_INPUT_DATA_ERROR;
+ }
+
+ result = 0;
+
+ for (entry_index = 0 ; 0 == result && entry_index < entry_count ; entry_index++) {
+ result = cmt_mpack_consume_string_tag(reader, &key_name);
+
+ if (CMT_MPACK_SUCCESS == result) {
+ callback_entry = callback_list;
+ result = CMT_MPACK_UNEXPECTED_KEY_ERROR;
+
+ while (CMT_MPACK_UNEXPECTED_KEY_ERROR == result &&
+ NULL != callback_entry->identifier) {
+
+ if (0 == strcmp(callback_entry->identifier, key_name)) {
+ result = callback_entry->handler(reader, entry_index, context);
+ }
+
+ callback_entry++;
+ }
+
+ cfl_sds_destroy(key_name);
+ }
+ }
+
+ if (CMT_MPACK_SUCCESS == result) {
+ mpack_done_map(reader);
+
+ if (mpack_ok != mpack_reader_error(reader))
+ {
+ return CMT_MPACK_PENDING_MAP_ENTRIES;
+ }
+ }
+
+ return result;
+}
+
+int cmt_mpack_unpack_array(mpack_reader_t *reader,
+ cmt_mpack_unpacker_entry_callback_fn_t entry_processor_callback,
+ void *context)
+{
+ uint32_t entry_index;
+ uint32_t entry_count;
+ mpack_tag_t tag;
+ int result;
+
+ tag = mpack_read_tag(reader);
+
+ if (mpack_ok != mpack_reader_error(reader))
+ {
+ return CMT_MPACK_ENGINE_ERROR;
+ }
+
+ if (mpack_type_array != mpack_tag_type(&tag)) {
+ return CMT_MPACK_UNEXPECTED_DATA_TYPE_ERROR;
+ }
+
+ entry_count = mpack_tag_array_count(&tag);
+
+ /* This validation only applies to cmetrics and its use cases, we know
+ * that in our schema we have the following arrays :
+ * label text dictionary (strings)
+ * dimension labels (indexes)
+ * metric values
+ * dimension values
+ *
+ * IMO none of these arrays should be huge so I think using 65535 as a limit
+ * gives us more than enough wiggle space (in reality I don't expect any of these
+ * arrays to hold more than 128 values but I could be wrong as that probably depends
+ * on the flush interval)
+ */
+
+ if (CMT_MPACK_MAX_ARRAY_ENTRY_COUNT < entry_count) {
+ return CMT_MPACK_CORRUPT_INPUT_DATA_ERROR;
+ }
+
+ result = CMT_MPACK_SUCCESS;
+
+ for (entry_index = 0 ;
+ CMT_MPACK_SUCCESS == result && entry_index < entry_count ;
+ entry_index++) {
+ result = entry_processor_callback(reader, entry_index, context);
+ }
+
+ if (CMT_MPACK_SUCCESS == result) {
+ mpack_done_array(reader);
+
+ if (mpack_ok != mpack_reader_error(reader))
+ {
+ return CMT_MPACK_PENDING_ARRAY_ENTRIES;
+ }
+ }
+
+ return result;
+}
+
+int cmt_mpack_peek_array_length(mpack_reader_t *reader)
+{
+ mpack_tag_t tag;
+
+ tag = mpack_peek_tag(reader);
+
+ if (mpack_ok != mpack_reader_error(reader))
+ {
+ return 0;
+ }
+
+ if (mpack_type_array != mpack_tag_type(&tag)) {
+ return 0;
+ }
+
+ return mpack_tag_array_count(&tag);
+}
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_opts.c b/src/fluent-bit/lib/cmetrics/src/cmt_opts.c
new file mode 100644
index 000000000..b4f4cd2a7
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_opts.c
@@ -0,0 +1,118 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_opts.h>
+
+/* Initialize an 'opts' context with given values */
+int cmt_opts_init(struct cmt_opts *opts,
+ char *ns, char *subsystem, char *name,
+ char *description)
+{
+ int len;
+ cfl_sds_t tmp;
+
+ if (!name) {
+ return -1;
+ }
+
+ if (ns) {
+ opts->ns = cfl_sds_create(ns);
+ if (!opts->ns) {
+ return -1;
+ }
+
+ opts->fqname = cfl_sds_create(ns);
+ if (!opts->fqname) {
+ return -1;
+ }
+
+ if (strlen(ns) > 0) {
+ tmp = cfl_sds_cat(opts->fqname, "_", 1);
+ if (!tmp) {
+ return -1;
+ }
+
+ opts->fqname = tmp;
+ }
+ }
+
+ if (subsystem) {
+ opts->subsystem = cfl_sds_create(subsystem);
+ if (!opts->subsystem) {
+ return -1;
+ }
+
+ if (strlen(opts->subsystem) > 0) {
+ tmp = cfl_sds_cat(opts->fqname,
+ opts->subsystem, cfl_sds_len(opts->subsystem));
+ if (!tmp) {
+ return -1;
+ }
+ opts->fqname = tmp;
+
+ len = cfl_sds_len(opts->fqname);
+ if (opts->fqname[len - 1] != '_') {
+ tmp = cfl_sds_cat(opts->fqname, "_", 1);
+ if (!tmp) {
+ return -1;
+ }
+ opts->fqname = tmp;
+ }
+ }
+ }
+
+ opts->name = cfl_sds_create(name);
+ opts->description = cfl_sds_create(description);
+
+ if (!opts->name || !opts->description) {
+ return -1;
+ }
+
+ tmp = cfl_sds_cat(opts->fqname, opts->name, cfl_sds_len(opts->name));
+ if (!tmp) {
+ return -1;
+ }
+ opts->fqname = tmp;
+
+ return 0;
+}
+
+void cmt_opts_exit(struct cmt_opts *opts)
+{
+ if (opts->ns) {
+ cfl_sds_destroy(opts->ns);
+ }
+
+ if (opts->subsystem) {
+ cfl_sds_destroy(opts->subsystem);
+ }
+
+ if (opts->name) {
+ cfl_sds_destroy(opts->name);
+ }
+
+ if (opts->description) {
+ cfl_sds_destroy(opts->description);
+ }
+
+ if (opts->fqname) {
+ cfl_sds_destroy(opts->fqname);
+ }
+}
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_summary.c b/src/fluent-bit/lib/cmetrics/src/cmt_summary.c
new file mode 100644
index 000000000..314fbfbef
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_summary.c
@@ -0,0 +1,306 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_log.h>
+#include <cmetrics/cmt_map.h>
+#include <cmetrics/cmt_math.h>
+#include <cmetrics/cmt_atomic.h>
+#include <cmetrics/cmt_metric.h>
+#include <cmetrics/cmt_summary.h>
+
+#include <stdarg.h>
+
+/*
+ * CMetrics 'Summary' metric type is only a container for values reported by a
+ * scrapper. We don't do data observation or calculate values for quantiles, we
+ * just compose a structure to keep the reported values.
+ *
+ * This metric type uses very similar 'Histogram' structures and interfaces.
+ */
+
+struct cmt_summary *cmt_summary_create(struct cmt *cmt,
+ char *ns, char *subsystem,
+ char *name, char *help,
+ size_t quantiles_count,
+ double *quantiles,
+ int label_count, char **label_keys)
+{
+ int i;
+ int ret;
+ struct cmt_summary *s;
+
+ if (!ns) {
+ cmt_log_error(cmt, "null ns not allowed");
+ return NULL;
+ }
+
+ if (!subsystem) {
+ cmt_log_error(cmt, "null subsystem not allowed");
+ return NULL;
+ }
+
+ if (!name || strlen(name) == 0) {
+ cmt_log_error(cmt, "undefined name");
+ return NULL;
+ }
+
+ if (!help || strlen(help) == 0) {
+ cmt_log_error(cmt, "undefined help");
+ return NULL;
+ }
+
+ s = calloc(1, sizeof(struct cmt_summary));
+ if (!s) {
+ cmt_errno();
+ return NULL;
+ }
+ cfl_list_add(&s->_head, &cmt->summaries);
+
+ /* initialize options */
+ ret = cmt_opts_init(&s->opts, ns, subsystem, name, help);
+ if (ret == -1) {
+ cmt_log_error(cmt, "unable to initialize options for summary");
+ cmt_summary_destroy(s);
+ return NULL;
+ }
+
+ /* Create the map */
+ s->map = cmt_map_create(CMT_SUMMARY, &s->opts, label_count, label_keys,
+ (void *) s);
+ if (!s->map) {
+ cmt_log_error(cmt, "unable to allocate map for summary");
+ cmt_summary_destroy(s);
+ return NULL;
+ }
+
+ /* create quantiles buffer */
+ if (quantiles_count > 0) {
+ s->quantiles_count = quantiles_count;
+ s->quantiles = calloc(1, sizeof(double) * quantiles_count);
+ if (!s->quantiles_count) {
+ cmt_errno();
+ cmt_summary_destroy(s);
+ return NULL;
+ }
+
+ /* set quantile */
+ for (i = 0; i < quantiles_count; i++) {
+ s->quantiles[i] = quantiles[i];
+ }
+ }
+
+ return s;
+}
+
+int cmt_summary_destroy(struct cmt_summary *summary)
+{
+ cfl_list_del(&summary->_head);
+ cmt_opts_exit(&summary->opts);
+
+ if (summary->map) {
+ cmt_map_destroy(summary->map);
+ }
+
+ if (summary->quantiles) {
+ free(summary->quantiles);
+ }
+
+ free(summary);
+ return 0;
+}
+
+double cmt_summary_quantile_get_value(struct cmt_metric *metric, int quantile_id)
+{
+ uint64_t val;
+
+ if (quantile_id < 0 /*|| quantile_id > metric->sum_quantiles_count*/) {
+ return 0;
+ }
+
+ val = cmt_atomic_load(&metric->sum_quantiles[quantile_id]);
+ return cmt_math_uint64_to_d64(val);
+}
+
+double cmt_summary_get_sum_value(struct cmt_metric *metric)
+{
+ uint64_t val;
+
+ val = cmt_atomic_load(&metric->sum_sum);
+ return cmt_math_uint64_to_d64(val);
+}
+
+uint64_t cmt_summary_get_count_value(struct cmt_metric *metric)
+{
+ uint64_t val;
+
+ val = cmt_atomic_load(&metric->sum_count);
+ return val;
+}
+
+static inline int summary_quantile_exchange(struct cmt_metric *metric,
+ uint64_t timestamp,
+ int quantile_id,
+ double new_value, double old_value)
+{
+ uint64_t tmp_new;
+ uint64_t tmp_old;
+ int result;
+
+ tmp_new = cmt_math_d64_to_uint64(new_value);
+ tmp_old = cmt_math_d64_to_uint64(old_value);
+
+ result = cmt_atomic_compare_exchange(&metric->sum_quantiles[quantile_id],
+ tmp_old, tmp_new);
+
+ if (result == 0) {
+ return 0;
+ }
+
+ cmt_atomic_store(&metric->timestamp, timestamp);
+ return 1;
+}
+
+static inline int summary_sum_exchange(struct cmt_metric *metric,
+ uint64_t timestamp,
+ double new_value, double old_value)
+{
+ uint64_t tmp_new;
+ uint64_t tmp_old;
+ int result;
+
+ tmp_new = cmt_math_d64_to_uint64(new_value);
+ tmp_old = cmt_math_d64_to_uint64(old_value);
+
+ result = cmt_atomic_compare_exchange(&metric->sum_sum, tmp_old, tmp_new);
+
+ if (result == 0) {
+ return 0;
+ }
+
+ cmt_atomic_store(&metric->timestamp, timestamp);
+ return 1;
+}
+
+static inline int summary_count_exchange(struct cmt_metric *metric,
+ uint64_t timestamp,
+ uint64_t new, uint64_t old)
+{
+ int result;
+
+ result = cmt_atomic_compare_exchange(&metric->sum_count, old, new);
+ if (result == 0) {
+ return 0;
+ }
+
+ cmt_atomic_store(&metric->timestamp, timestamp);
+ return 1;
+}
+
+void cmt_summary_quantile_set(struct cmt_metric *metric, uint64_t timestamp,
+ int quantile_id, double val)
+{
+ double old;
+ double new;
+ int result;
+
+ do {
+ old = cmt_summary_quantile_get_value(metric, quantile_id);
+ new = val;
+ result = summary_quantile_exchange(metric, timestamp, quantile_id, new, old);
+ }
+ while (0 == result);
+}
+
+void cmt_summary_sum_set(struct cmt_metric *metric, uint64_t timestamp, double val)
+{
+ double old;
+ double new;
+ int result;
+
+ do {
+ old = cmt_summary_get_sum_value(metric);
+ new = val;
+ result = summary_sum_exchange(metric, timestamp, new, old);
+ }
+ while (0 == result);
+}
+
+void cmt_summary_count_set(struct cmt_metric *metric, uint64_t timestamp,
+ uint64_t count)
+{
+ int result;
+ uint64_t old;
+ uint64_t new;
+
+ do {
+ old = cmt_atomic_load(&metric->sum_count);
+ new = count;
+
+ result = summary_count_exchange(metric, timestamp, new, old);
+ }
+ while (result == 0);
+}
+
+int cmt_summary_set_default(struct cmt_summary *summary,
+ uint64_t timestamp,
+ double *quantile_values,
+ double sum,
+ uint64_t count,
+ int labels_count, char **label_vars)
+{
+ int i;
+ struct cmt_metric *metric;
+
+ metric = cmt_map_metric_get(&summary->opts, summary->map,
+ labels_count, label_vars,
+ CMT_TRUE);
+ if (!metric) {
+ cmt_log_error(summary->cmt, "unable to retrieve metric: %s for summary %s_%s_%s",
+ summary->map, summary->opts.ns, summary->opts.subsystem,
+ summary->opts.name);
+ return -1;
+ }
+
+
+ if (!metric->sum_quantiles && summary->quantiles_count) {
+ metric->sum_quantiles = calloc(1, sizeof(uint64_t) * summary->quantiles_count);
+ if (!metric->sum_quantiles) {
+ cmt_errno();
+ return -1;
+ }
+ metric->sum_quantiles_count = summary->quantiles_count;
+ }
+
+ /* set quantile values */
+ if (quantile_values) {
+ /* yes, quantile values are set */
+ metric->sum_quantiles_set = CMT_TRUE;
+
+ /* populate each quantile */
+ for (i = 0; i < summary->quantiles_count; i++) {
+ cmt_summary_quantile_set(metric, timestamp, i, quantile_values[i]);
+ }
+ }
+
+ cmt_summary_sum_set(metric, timestamp, sum);
+ cmt_summary_count_set(metric, timestamp, count);
+
+ return 0;
+} \ No newline at end of file
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_time.c b/src/fluent-bit/lib/cmetrics/src/cmt_time.c
new file mode 100644
index 000000000..26bcfeb8f
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_time.c
@@ -0,0 +1,34 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmt_info.h>
+#include <inttypes.h>
+#include <time.h>
+
+void cmt_time_from_ns(struct timespec *tm, uint64_t ns)
+{
+ if (ns < 1000000000L) {
+ tm->tv_sec = 0;
+ tm->tv_nsec = ns;
+ }
+ else {
+ tm->tv_sec = ns / 1000000000L;
+ tm->tv_nsec = ns - (tm->tv_sec * 1000000000L);
+ }
+}
diff --git a/src/fluent-bit/lib/cmetrics/src/cmt_untyped.c b/src/fluent-bit/lib/cmetrics/src/cmt_untyped.c
new file mode 100644
index 000000000..24af3990a
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/cmt_untyped.c
@@ -0,0 +1,140 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_log.h>
+#include <cmetrics/cmt_map.h>
+#include <cmetrics/cmt_metric.h>
+#include <cmetrics/cmt_untyped.h>
+
+struct cmt_untyped *cmt_untyped_create(struct cmt *cmt,
+ char *ns, char *subsystem,
+ char *name, char *help,
+ int label_count, char **label_keys)
+{
+ int ret;
+ struct cmt_untyped *untyped;
+
+ if (!ns) {
+ cmt_log_error(cmt, "null ns not allowed");
+ return NULL;
+ }
+
+ if (!subsystem) {
+ cmt_log_error(cmt, "null subsystem not allowed");
+ return NULL;
+ }
+
+ if (!help || strlen(help) == 0) {
+ cmt_log_error(cmt, "undefined help");
+ return NULL;
+ }
+
+ if (!name || strlen(name) == 0) {
+ cmt_log_error(cmt, "undefined name");
+ return NULL;
+ }
+
+ if (!help || strlen(help) == 0) {
+ cmt_log_error(cmt, "undefined help");
+ return NULL;
+ }
+
+ untyped = calloc(1, sizeof(struct cmt_untyped));
+ if (!untyped) {
+ cmt_errno();
+ return NULL;
+ }
+ cfl_list_add(&untyped->_head, &cmt->untypeds);
+
+ ret = cmt_opts_init(&untyped->opts, ns, subsystem, name, help);
+ if (ret == -1) {
+ cmt_log_error(cmt, "unable to initialize options for untyped");
+ cmt_untyped_destroy(untyped);
+ return NULL;
+ }
+
+ /* Create the map */
+ untyped->map = cmt_map_create(CMT_UNTYPED, &untyped->opts, label_count, label_keys,
+ (void *) untyped);
+ if (!untyped->map) {
+ cmt_log_error(cmt, "unable to allocate map for untyped");
+ cmt_untyped_destroy(untyped);
+ return NULL;
+ }
+
+ untyped->cmt = cmt;
+
+ return untyped;
+}
+
+int cmt_untyped_destroy(struct cmt_untyped *untyped)
+{
+ cfl_list_del(&untyped->_head);
+ cmt_opts_exit(&untyped->opts);
+
+ if (untyped->map) {
+ cmt_map_destroy(untyped->map);
+ }
+ free(untyped);
+ return 0;
+}
+
+/* Set untyped value, new value cannot be smaller than current value */
+int cmt_untyped_set(struct cmt_untyped *untyped, uint64_t timestamp, double val,
+ int labels_count, char **label_vals)
+{
+ struct cmt_metric *metric;
+
+ metric = cmt_map_metric_get(&untyped->opts, untyped->map,
+ labels_count, label_vals,
+ CMT_TRUE);
+ if (!metric) {
+ cmt_log_error(untyped->cmt, "unable to retrieve metric: %s for untyped %s_%s_%s",
+ untyped->map, untyped->opts.ns, untyped->opts.subsystem,
+ untyped->opts.name);
+ return -1;
+ }
+
+ if (cmt_metric_get_value(metric) > val) {
+ return -1;
+ }
+ cmt_metric_set(metric, timestamp, val);
+ return 0;
+}
+
+int cmt_untyped_get_val(struct cmt_untyped *untyped,
+ int labels_count, char **label_vals, double *out_val)
+{
+ int ret;
+ double val = 0;
+
+ ret = cmt_map_metric_get_val(&untyped->opts,
+ untyped->map, labels_count, label_vals,
+ &val);
+ if (ret == -1) {
+ cmt_log_error(untyped->cmt,
+ "unable to retrieve metric value: %s for untyped %s_%s_%s",
+ untyped->map, untyped->opts.ns, untyped->opts.subsystem,
+ untyped->opts.name);
+ return -1;
+ }
+ *out_val = val;
+ return 0;
+}
diff --git a/src/fluent-bit/lib/cmetrics/src/external/LICENSE b/src/fluent-bit/lib/cmetrics/src/external/LICENSE
new file mode 100644
index 000000000..c1c6ac95f
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/external/LICENSE
@@ -0,0 +1,32 @@
+Copyright (c) 2008-2016, Dave Benson and the protobuf-c authors.
+All rights reserved.
+
+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.
+
+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.
+
+The code generated by the protoc-gen-c code generator and by the
+protoc-c compiler is owned by the owner of the input files used when
+generating it. This code is not standalone and requires a support
+library to be linked with it. This support library is covered by the
+above license.
diff --git a/src/fluent-bit/lib/cmetrics/src/external/opentelemetry_common.pb-c.c b/src/fluent-bit/lib/cmetrics/src/external/opentelemetry_common.pb-c.c
new file mode 100644
index 000000000..3eb1fd5bc
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/external/opentelemetry_common.pb-c.c
@@ -0,0 +1,624 @@
+/* Generated by the protocol buffer compiler. DO NOT EDIT! */
+/* Generated from: common.proto */
+
+/* Do not generate deprecated warnings for self */
+#ifndef PROTOBUF_C__NO_DEPRECATED
+#define PROTOBUF_C__NO_DEPRECATED
+#endif
+
+#include <opentelemetry/common.pb-c.h>
+void opentelemetry__proto__common__v1__any_value__init
+ (Opentelemetry__Proto__Common__V1__AnyValue *message)
+{
+ static const Opentelemetry__Proto__Common__V1__AnyValue init_value = OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__INIT;
+ *message = init_value;
+}
+size_t opentelemetry__proto__common__v1__any_value__get_packed_size
+ (const Opentelemetry__Proto__Common__V1__AnyValue *message)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__common__v1__any_value__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t opentelemetry__proto__common__v1__any_value__pack
+ (const Opentelemetry__Proto__Common__V1__AnyValue *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__common__v1__any_value__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t opentelemetry__proto__common__v1__any_value__pack_to_buffer
+ (const Opentelemetry__Proto__Common__V1__AnyValue *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__common__v1__any_value__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Opentelemetry__Proto__Common__V1__AnyValue *
+ opentelemetry__proto__common__v1__any_value__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Opentelemetry__Proto__Common__V1__AnyValue *)
+ protobuf_c_message_unpack (&opentelemetry__proto__common__v1__any_value__descriptor,
+ allocator, len, data);
+}
+void opentelemetry__proto__common__v1__any_value__free_unpacked
+ (Opentelemetry__Proto__Common__V1__AnyValue *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &opentelemetry__proto__common__v1__any_value__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void opentelemetry__proto__common__v1__array_value__init
+ (Opentelemetry__Proto__Common__V1__ArrayValue *message)
+{
+ static const Opentelemetry__Proto__Common__V1__ArrayValue init_value = OPENTELEMETRY__PROTO__COMMON__V1__ARRAY_VALUE__INIT;
+ *message = init_value;
+}
+size_t opentelemetry__proto__common__v1__array_value__get_packed_size
+ (const Opentelemetry__Proto__Common__V1__ArrayValue *message)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__common__v1__array_value__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t opentelemetry__proto__common__v1__array_value__pack
+ (const Opentelemetry__Proto__Common__V1__ArrayValue *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__common__v1__array_value__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t opentelemetry__proto__common__v1__array_value__pack_to_buffer
+ (const Opentelemetry__Proto__Common__V1__ArrayValue *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__common__v1__array_value__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Opentelemetry__Proto__Common__V1__ArrayValue *
+ opentelemetry__proto__common__v1__array_value__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Opentelemetry__Proto__Common__V1__ArrayValue *)
+ protobuf_c_message_unpack (&opentelemetry__proto__common__v1__array_value__descriptor,
+ allocator, len, data);
+}
+void opentelemetry__proto__common__v1__array_value__free_unpacked
+ (Opentelemetry__Proto__Common__V1__ArrayValue *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &opentelemetry__proto__common__v1__array_value__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void opentelemetry__proto__common__v1__key_value_list__init
+ (Opentelemetry__Proto__Common__V1__KeyValueList *message)
+{
+ static const Opentelemetry__Proto__Common__V1__KeyValueList init_value = OPENTELEMETRY__PROTO__COMMON__V1__KEY_VALUE_LIST__INIT;
+ *message = init_value;
+}
+size_t opentelemetry__proto__common__v1__key_value_list__get_packed_size
+ (const Opentelemetry__Proto__Common__V1__KeyValueList *message)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__common__v1__key_value_list__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t opentelemetry__proto__common__v1__key_value_list__pack
+ (const Opentelemetry__Proto__Common__V1__KeyValueList *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__common__v1__key_value_list__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t opentelemetry__proto__common__v1__key_value_list__pack_to_buffer
+ (const Opentelemetry__Proto__Common__V1__KeyValueList *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__common__v1__key_value_list__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Opentelemetry__Proto__Common__V1__KeyValueList *
+ opentelemetry__proto__common__v1__key_value_list__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Opentelemetry__Proto__Common__V1__KeyValueList *)
+ protobuf_c_message_unpack (&opentelemetry__proto__common__v1__key_value_list__descriptor,
+ allocator, len, data);
+}
+void opentelemetry__proto__common__v1__key_value_list__free_unpacked
+ (Opentelemetry__Proto__Common__V1__KeyValueList *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &opentelemetry__proto__common__v1__key_value_list__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void opentelemetry__proto__common__v1__key_value__init
+ (Opentelemetry__Proto__Common__V1__KeyValue *message)
+{
+ static const Opentelemetry__Proto__Common__V1__KeyValue init_value = OPENTELEMETRY__PROTO__COMMON__V1__KEY_VALUE__INIT;
+ *message = init_value;
+}
+size_t opentelemetry__proto__common__v1__key_value__get_packed_size
+ (const Opentelemetry__Proto__Common__V1__KeyValue *message)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__common__v1__key_value__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t opentelemetry__proto__common__v1__key_value__pack
+ (const Opentelemetry__Proto__Common__V1__KeyValue *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__common__v1__key_value__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t opentelemetry__proto__common__v1__key_value__pack_to_buffer
+ (const Opentelemetry__Proto__Common__V1__KeyValue *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__common__v1__key_value__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Opentelemetry__Proto__Common__V1__KeyValue *
+ opentelemetry__proto__common__v1__key_value__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Opentelemetry__Proto__Common__V1__KeyValue *)
+ protobuf_c_message_unpack (&opentelemetry__proto__common__v1__key_value__descriptor,
+ allocator, len, data);
+}
+void opentelemetry__proto__common__v1__key_value__free_unpacked
+ (Opentelemetry__Proto__Common__V1__KeyValue *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &opentelemetry__proto__common__v1__key_value__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void opentelemetry__proto__common__v1__string_key_value__init
+ (Opentelemetry__Proto__Common__V1__StringKeyValue *message)
+{
+ static const Opentelemetry__Proto__Common__V1__StringKeyValue init_value = OPENTELEMETRY__PROTO__COMMON__V1__STRING_KEY_VALUE__INIT;
+ *message = init_value;
+}
+size_t opentelemetry__proto__common__v1__string_key_value__get_packed_size
+ (const Opentelemetry__Proto__Common__V1__StringKeyValue *message)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__common__v1__string_key_value__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t opentelemetry__proto__common__v1__string_key_value__pack
+ (const Opentelemetry__Proto__Common__V1__StringKeyValue *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__common__v1__string_key_value__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t opentelemetry__proto__common__v1__string_key_value__pack_to_buffer
+ (const Opentelemetry__Proto__Common__V1__StringKeyValue *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__common__v1__string_key_value__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Opentelemetry__Proto__Common__V1__StringKeyValue *
+ opentelemetry__proto__common__v1__string_key_value__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Opentelemetry__Proto__Common__V1__StringKeyValue *)
+ protobuf_c_message_unpack (&opentelemetry__proto__common__v1__string_key_value__descriptor,
+ allocator, len, data);
+}
+void opentelemetry__proto__common__v1__string_key_value__free_unpacked
+ (Opentelemetry__Proto__Common__V1__StringKeyValue *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &opentelemetry__proto__common__v1__string_key_value__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void opentelemetry__proto__common__v1__instrumentation_library__init
+ (Opentelemetry__Proto__Common__V1__InstrumentationLibrary *message)
+{
+ static const Opentelemetry__Proto__Common__V1__InstrumentationLibrary init_value = OPENTELEMETRY__PROTO__COMMON__V1__INSTRUMENTATION_LIBRARY__INIT;
+ *message = init_value;
+}
+size_t opentelemetry__proto__common__v1__instrumentation_library__get_packed_size
+ (const Opentelemetry__Proto__Common__V1__InstrumentationLibrary *message)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__common__v1__instrumentation_library__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t opentelemetry__proto__common__v1__instrumentation_library__pack
+ (const Opentelemetry__Proto__Common__V1__InstrumentationLibrary *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__common__v1__instrumentation_library__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t opentelemetry__proto__common__v1__instrumentation_library__pack_to_buffer
+ (const Opentelemetry__Proto__Common__V1__InstrumentationLibrary *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__common__v1__instrumentation_library__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Opentelemetry__Proto__Common__V1__InstrumentationLibrary *
+ opentelemetry__proto__common__v1__instrumentation_library__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Opentelemetry__Proto__Common__V1__InstrumentationLibrary *)
+ protobuf_c_message_unpack (&opentelemetry__proto__common__v1__instrumentation_library__descriptor,
+ allocator, len, data);
+}
+void opentelemetry__proto__common__v1__instrumentation_library__free_unpacked
+ (Opentelemetry__Proto__Common__V1__InstrumentationLibrary *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &opentelemetry__proto__common__v1__instrumentation_library__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+static const ProtobufCFieldDescriptor opentelemetry__proto__common__v1__any_value__field_descriptors[7] =
+{
+ {
+ "string_value",
+ 1,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_STRING,
+ offsetof(Opentelemetry__Proto__Common__V1__AnyValue, value_case),
+ offsetof(Opentelemetry__Proto__Common__V1__AnyValue, string_value),
+ NULL,
+ &protobuf_c_empty_string,
+ 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "bool_value",
+ 2,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_BOOL,
+ offsetof(Opentelemetry__Proto__Common__V1__AnyValue, value_case),
+ offsetof(Opentelemetry__Proto__Common__V1__AnyValue, bool_value),
+ NULL,
+ NULL,
+ 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "int_value",
+ 3,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_INT64,
+ offsetof(Opentelemetry__Proto__Common__V1__AnyValue, value_case),
+ offsetof(Opentelemetry__Proto__Common__V1__AnyValue, int_value),
+ NULL,
+ NULL,
+ 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "double_value",
+ 4,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_DOUBLE,
+ offsetof(Opentelemetry__Proto__Common__V1__AnyValue, value_case),
+ offsetof(Opentelemetry__Proto__Common__V1__AnyValue, double_value),
+ NULL,
+ NULL,
+ 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "array_value",
+ 5,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Opentelemetry__Proto__Common__V1__AnyValue, value_case),
+ offsetof(Opentelemetry__Proto__Common__V1__AnyValue, array_value),
+ &opentelemetry__proto__common__v1__array_value__descriptor,
+ NULL,
+ 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "kvlist_value",
+ 6,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Opentelemetry__Proto__Common__V1__AnyValue, value_case),
+ offsetof(Opentelemetry__Proto__Common__V1__AnyValue, kvlist_value),
+ &opentelemetry__proto__common__v1__key_value_list__descriptor,
+ NULL,
+ 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "bytes_value",
+ 7,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_BYTES,
+ offsetof(Opentelemetry__Proto__Common__V1__AnyValue, value_case),
+ offsetof(Opentelemetry__Proto__Common__V1__AnyValue, bytes_value),
+ NULL,
+ NULL,
+ 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned opentelemetry__proto__common__v1__any_value__field_indices_by_name[] = {
+ 4, /* field[4] = array_value */
+ 1, /* field[1] = bool_value */
+ 6, /* field[6] = bytes_value */
+ 3, /* field[3] = double_value */
+ 2, /* field[2] = int_value */
+ 5, /* field[5] = kvlist_value */
+ 0, /* field[0] = string_value */
+};
+static const ProtobufCIntRange opentelemetry__proto__common__v1__any_value__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 7 }
+};
+const ProtobufCMessageDescriptor opentelemetry__proto__common__v1__any_value__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "opentelemetry.proto.common.v1.AnyValue",
+ "AnyValue",
+ "Opentelemetry__Proto__Common__V1__AnyValue",
+ "opentelemetry.proto.common.v1",
+ sizeof(Opentelemetry__Proto__Common__V1__AnyValue),
+ 7,
+ opentelemetry__proto__common__v1__any_value__field_descriptors,
+ opentelemetry__proto__common__v1__any_value__field_indices_by_name,
+ 1, opentelemetry__proto__common__v1__any_value__number_ranges,
+ (ProtobufCMessageInit) opentelemetry__proto__common__v1__any_value__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor opentelemetry__proto__common__v1__array_value__field_descriptors[1] =
+{
+ {
+ "values",
+ 1,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Opentelemetry__Proto__Common__V1__ArrayValue, n_values),
+ offsetof(Opentelemetry__Proto__Common__V1__ArrayValue, values),
+ &opentelemetry__proto__common__v1__any_value__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned opentelemetry__proto__common__v1__array_value__field_indices_by_name[] = {
+ 0, /* field[0] = values */
+};
+static const ProtobufCIntRange opentelemetry__proto__common__v1__array_value__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor opentelemetry__proto__common__v1__array_value__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "opentelemetry.proto.common.v1.ArrayValue",
+ "ArrayValue",
+ "Opentelemetry__Proto__Common__V1__ArrayValue",
+ "opentelemetry.proto.common.v1",
+ sizeof(Opentelemetry__Proto__Common__V1__ArrayValue),
+ 1,
+ opentelemetry__proto__common__v1__array_value__field_descriptors,
+ opentelemetry__proto__common__v1__array_value__field_indices_by_name,
+ 1, opentelemetry__proto__common__v1__array_value__number_ranges,
+ (ProtobufCMessageInit) opentelemetry__proto__common__v1__array_value__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor opentelemetry__proto__common__v1__key_value_list__field_descriptors[1] =
+{
+ {
+ "values",
+ 1,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Opentelemetry__Proto__Common__V1__KeyValueList, n_values),
+ offsetof(Opentelemetry__Proto__Common__V1__KeyValueList, values),
+ &opentelemetry__proto__common__v1__key_value__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned opentelemetry__proto__common__v1__key_value_list__field_indices_by_name[] = {
+ 0, /* field[0] = values */
+};
+static const ProtobufCIntRange opentelemetry__proto__common__v1__key_value_list__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor opentelemetry__proto__common__v1__key_value_list__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "opentelemetry.proto.common.v1.KeyValueList",
+ "KeyValueList",
+ "Opentelemetry__Proto__Common__V1__KeyValueList",
+ "opentelemetry.proto.common.v1",
+ sizeof(Opentelemetry__Proto__Common__V1__KeyValueList),
+ 1,
+ opentelemetry__proto__common__v1__key_value_list__field_descriptors,
+ opentelemetry__proto__common__v1__key_value_list__field_indices_by_name,
+ 1, opentelemetry__proto__common__v1__key_value_list__number_ranges,
+ (ProtobufCMessageInit) opentelemetry__proto__common__v1__key_value_list__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor opentelemetry__proto__common__v1__key_value__field_descriptors[2] =
+{
+ {
+ "key",
+ 1,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Common__V1__KeyValue, key),
+ NULL,
+ &protobuf_c_empty_string,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "value",
+ 2,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Common__V1__KeyValue, value),
+ &opentelemetry__proto__common__v1__any_value__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned opentelemetry__proto__common__v1__key_value__field_indices_by_name[] = {
+ 0, /* field[0] = key */
+ 1, /* field[1] = value */
+};
+static const ProtobufCIntRange opentelemetry__proto__common__v1__key_value__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor opentelemetry__proto__common__v1__key_value__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "opentelemetry.proto.common.v1.KeyValue",
+ "KeyValue",
+ "Opentelemetry__Proto__Common__V1__KeyValue",
+ "opentelemetry.proto.common.v1",
+ sizeof(Opentelemetry__Proto__Common__V1__KeyValue),
+ 2,
+ opentelemetry__proto__common__v1__key_value__field_descriptors,
+ opentelemetry__proto__common__v1__key_value__field_indices_by_name,
+ 1, opentelemetry__proto__common__v1__key_value__number_ranges,
+ (ProtobufCMessageInit) opentelemetry__proto__common__v1__key_value__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor opentelemetry__proto__common__v1__string_key_value__field_descriptors[2] =
+{
+ {
+ "key",
+ 1,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Common__V1__StringKeyValue, key),
+ NULL,
+ &protobuf_c_empty_string,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "value",
+ 2,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Common__V1__StringKeyValue, value),
+ NULL,
+ &protobuf_c_empty_string,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned opentelemetry__proto__common__v1__string_key_value__field_indices_by_name[] = {
+ 0, /* field[0] = key */
+ 1, /* field[1] = value */
+};
+static const ProtobufCIntRange opentelemetry__proto__common__v1__string_key_value__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor opentelemetry__proto__common__v1__string_key_value__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "opentelemetry.proto.common.v1.StringKeyValue",
+ "StringKeyValue",
+ "Opentelemetry__Proto__Common__V1__StringKeyValue",
+ "opentelemetry.proto.common.v1",
+ sizeof(Opentelemetry__Proto__Common__V1__StringKeyValue),
+ 2,
+ opentelemetry__proto__common__v1__string_key_value__field_descriptors,
+ opentelemetry__proto__common__v1__string_key_value__field_indices_by_name,
+ 1, opentelemetry__proto__common__v1__string_key_value__number_ranges,
+ (ProtobufCMessageInit) opentelemetry__proto__common__v1__string_key_value__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor opentelemetry__proto__common__v1__instrumentation_library__field_descriptors[2] =
+{
+ {
+ "name",
+ 1,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Common__V1__InstrumentationLibrary, name),
+ NULL,
+ &protobuf_c_empty_string,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "version",
+ 2,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Common__V1__InstrumentationLibrary, version),
+ NULL,
+ &protobuf_c_empty_string,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned opentelemetry__proto__common__v1__instrumentation_library__field_indices_by_name[] = {
+ 0, /* field[0] = name */
+ 1, /* field[1] = version */
+};
+static const ProtobufCIntRange opentelemetry__proto__common__v1__instrumentation_library__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor opentelemetry__proto__common__v1__instrumentation_library__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "opentelemetry.proto.common.v1.InstrumentationLibrary",
+ "InstrumentationLibrary",
+ "Opentelemetry__Proto__Common__V1__InstrumentationLibrary",
+ "opentelemetry.proto.common.v1",
+ sizeof(Opentelemetry__Proto__Common__V1__InstrumentationLibrary),
+ 2,
+ opentelemetry__proto__common__v1__instrumentation_library__field_descriptors,
+ opentelemetry__proto__common__v1__instrumentation_library__field_indices_by_name,
+ 1, opentelemetry__proto__common__v1__instrumentation_library__number_ranges,
+ (ProtobufCMessageInit) opentelemetry__proto__common__v1__instrumentation_library__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
diff --git a/src/fluent-bit/lib/cmetrics/src/external/opentelemetry_metrics.pb-c.c b/src/fluent-bit/lib/cmetrics/src/external/opentelemetry_metrics.pb-c.c
new file mode 100644
index 000000000..7452586c0
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/external/opentelemetry_metrics.pb-c.c
@@ -0,0 +1,1996 @@
+/* Generated by the protocol buffer compiler. DO NOT EDIT! */
+/* Generated from: otlp_metrics.proto */
+
+/* Do not generate deprecated warnings for self */
+#ifndef PROTOBUF_C__NO_DEPRECATED
+#define PROTOBUF_C__NO_DEPRECATED
+#endif
+
+#include <opentelemetry/metrics.pb-c.h>
+void opentelemetry__proto__metrics__v1__metrics_data__init
+ (Opentelemetry__Proto__Metrics__V1__MetricsData *message)
+{
+ static const Opentelemetry__Proto__Metrics__V1__MetricsData init_value = OPENTELEMETRY__PROTO__METRICS__V1__METRICS_DATA__INIT;
+ *message = init_value;
+}
+size_t opentelemetry__proto__metrics__v1__metrics_data__get_packed_size
+ (const Opentelemetry__Proto__Metrics__V1__MetricsData *message)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__metrics_data__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t opentelemetry__proto__metrics__v1__metrics_data__pack
+ (const Opentelemetry__Proto__Metrics__V1__MetricsData *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__metrics_data__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t opentelemetry__proto__metrics__v1__metrics_data__pack_to_buffer
+ (const Opentelemetry__Proto__Metrics__V1__MetricsData *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__metrics_data__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Opentelemetry__Proto__Metrics__V1__MetricsData *
+ opentelemetry__proto__metrics__v1__metrics_data__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Opentelemetry__Proto__Metrics__V1__MetricsData *)
+ protobuf_c_message_unpack (&opentelemetry__proto__metrics__v1__metrics_data__descriptor,
+ allocator, len, data);
+}
+void opentelemetry__proto__metrics__v1__metrics_data__free_unpacked
+ (Opentelemetry__Proto__Metrics__V1__MetricsData *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__metrics_data__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void opentelemetry__proto__metrics__v1__resource_metrics__init
+ (Opentelemetry__Proto__Metrics__V1__ResourceMetrics *message)
+{
+ static const Opentelemetry__Proto__Metrics__V1__ResourceMetrics init_value = OPENTELEMETRY__PROTO__METRICS__V1__RESOURCE_METRICS__INIT;
+ *message = init_value;
+}
+size_t opentelemetry__proto__metrics__v1__resource_metrics__get_packed_size
+ (const Opentelemetry__Proto__Metrics__V1__ResourceMetrics *message)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__resource_metrics__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t opentelemetry__proto__metrics__v1__resource_metrics__pack
+ (const Opentelemetry__Proto__Metrics__V1__ResourceMetrics *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__resource_metrics__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t opentelemetry__proto__metrics__v1__resource_metrics__pack_to_buffer
+ (const Opentelemetry__Proto__Metrics__V1__ResourceMetrics *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__resource_metrics__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Opentelemetry__Proto__Metrics__V1__ResourceMetrics *
+ opentelemetry__proto__metrics__v1__resource_metrics__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Opentelemetry__Proto__Metrics__V1__ResourceMetrics *)
+ protobuf_c_message_unpack (&opentelemetry__proto__metrics__v1__resource_metrics__descriptor,
+ allocator, len, data);
+}
+void opentelemetry__proto__metrics__v1__resource_metrics__free_unpacked
+ (Opentelemetry__Proto__Metrics__V1__ResourceMetrics *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__resource_metrics__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void opentelemetry__proto__metrics__v1__instrumentation_library_metrics__init
+ (Opentelemetry__Proto__Metrics__V1__InstrumentationLibraryMetrics *message)
+{
+ static const Opentelemetry__Proto__Metrics__V1__InstrumentationLibraryMetrics init_value = OPENTELEMETRY__PROTO__METRICS__V1__INSTRUMENTATION_LIBRARY_METRICS__INIT;
+ *message = init_value;
+}
+size_t opentelemetry__proto__metrics__v1__instrumentation_library_metrics__get_packed_size
+ (const Opentelemetry__Proto__Metrics__V1__InstrumentationLibraryMetrics *message)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__instrumentation_library_metrics__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t opentelemetry__proto__metrics__v1__instrumentation_library_metrics__pack
+ (const Opentelemetry__Proto__Metrics__V1__InstrumentationLibraryMetrics *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__instrumentation_library_metrics__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t opentelemetry__proto__metrics__v1__instrumentation_library_metrics__pack_to_buffer
+ (const Opentelemetry__Proto__Metrics__V1__InstrumentationLibraryMetrics *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__instrumentation_library_metrics__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Opentelemetry__Proto__Metrics__V1__InstrumentationLibraryMetrics *
+ opentelemetry__proto__metrics__v1__instrumentation_library_metrics__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Opentelemetry__Proto__Metrics__V1__InstrumentationLibraryMetrics *)
+ protobuf_c_message_unpack (&opentelemetry__proto__metrics__v1__instrumentation_library_metrics__descriptor,
+ allocator, len, data);
+}
+void opentelemetry__proto__metrics__v1__instrumentation_library_metrics__free_unpacked
+ (Opentelemetry__Proto__Metrics__V1__InstrumentationLibraryMetrics *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__instrumentation_library_metrics__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void opentelemetry__proto__metrics__v1__metric__init
+ (Opentelemetry__Proto__Metrics__V1__Metric *message)
+{
+ static const Opentelemetry__Proto__Metrics__V1__Metric init_value = OPENTELEMETRY__PROTO__METRICS__V1__METRIC__INIT;
+ *message = init_value;
+}
+size_t opentelemetry__proto__metrics__v1__metric__get_packed_size
+ (const Opentelemetry__Proto__Metrics__V1__Metric *message)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__metric__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t opentelemetry__proto__metrics__v1__metric__pack
+ (const Opentelemetry__Proto__Metrics__V1__Metric *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__metric__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t opentelemetry__proto__metrics__v1__metric__pack_to_buffer
+ (const Opentelemetry__Proto__Metrics__V1__Metric *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__metric__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Opentelemetry__Proto__Metrics__V1__Metric *
+ opentelemetry__proto__metrics__v1__metric__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Opentelemetry__Proto__Metrics__V1__Metric *)
+ protobuf_c_message_unpack (&opentelemetry__proto__metrics__v1__metric__descriptor,
+ allocator, len, data);
+}
+void opentelemetry__proto__metrics__v1__metric__free_unpacked
+ (Opentelemetry__Proto__Metrics__V1__Metric *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__metric__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void opentelemetry__proto__metrics__v1__gauge__init
+ (Opentelemetry__Proto__Metrics__V1__Gauge *message)
+{
+ static const Opentelemetry__Proto__Metrics__V1__Gauge init_value = OPENTELEMETRY__PROTO__METRICS__V1__GAUGE__INIT;
+ *message = init_value;
+}
+size_t opentelemetry__proto__metrics__v1__gauge__get_packed_size
+ (const Opentelemetry__Proto__Metrics__V1__Gauge *message)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__gauge__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t opentelemetry__proto__metrics__v1__gauge__pack
+ (const Opentelemetry__Proto__Metrics__V1__Gauge *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__gauge__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t opentelemetry__proto__metrics__v1__gauge__pack_to_buffer
+ (const Opentelemetry__Proto__Metrics__V1__Gauge *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__gauge__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Opentelemetry__Proto__Metrics__V1__Gauge *
+ opentelemetry__proto__metrics__v1__gauge__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Opentelemetry__Proto__Metrics__V1__Gauge *)
+ protobuf_c_message_unpack (&opentelemetry__proto__metrics__v1__gauge__descriptor,
+ allocator, len, data);
+}
+void opentelemetry__proto__metrics__v1__gauge__free_unpacked
+ (Opentelemetry__Proto__Metrics__V1__Gauge *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__gauge__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void opentelemetry__proto__metrics__v1__sum__init
+ (Opentelemetry__Proto__Metrics__V1__Sum *message)
+{
+ static const Opentelemetry__Proto__Metrics__V1__Sum init_value = OPENTELEMETRY__PROTO__METRICS__V1__SUM__INIT;
+ *message = init_value;
+}
+size_t opentelemetry__proto__metrics__v1__sum__get_packed_size
+ (const Opentelemetry__Proto__Metrics__V1__Sum *message)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__sum__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t opentelemetry__proto__metrics__v1__sum__pack
+ (const Opentelemetry__Proto__Metrics__V1__Sum *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__sum__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t opentelemetry__proto__metrics__v1__sum__pack_to_buffer
+ (const Opentelemetry__Proto__Metrics__V1__Sum *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__sum__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Opentelemetry__Proto__Metrics__V1__Sum *
+ opentelemetry__proto__metrics__v1__sum__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Opentelemetry__Proto__Metrics__V1__Sum *)
+ protobuf_c_message_unpack (&opentelemetry__proto__metrics__v1__sum__descriptor,
+ allocator, len, data);
+}
+void opentelemetry__proto__metrics__v1__sum__free_unpacked
+ (Opentelemetry__Proto__Metrics__V1__Sum *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__sum__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void opentelemetry__proto__metrics__v1__histogram__init
+ (Opentelemetry__Proto__Metrics__V1__Histogram *message)
+{
+ static const Opentelemetry__Proto__Metrics__V1__Histogram init_value = OPENTELEMETRY__PROTO__METRICS__V1__HISTOGRAM__INIT;
+ *message = init_value;
+}
+size_t opentelemetry__proto__metrics__v1__histogram__get_packed_size
+ (const Opentelemetry__Proto__Metrics__V1__Histogram *message)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__histogram__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t opentelemetry__proto__metrics__v1__histogram__pack
+ (const Opentelemetry__Proto__Metrics__V1__Histogram *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__histogram__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t opentelemetry__proto__metrics__v1__histogram__pack_to_buffer
+ (const Opentelemetry__Proto__Metrics__V1__Histogram *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__histogram__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Opentelemetry__Proto__Metrics__V1__Histogram *
+ opentelemetry__proto__metrics__v1__histogram__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Opentelemetry__Proto__Metrics__V1__Histogram *)
+ protobuf_c_message_unpack (&opentelemetry__proto__metrics__v1__histogram__descriptor,
+ allocator, len, data);
+}
+void opentelemetry__proto__metrics__v1__histogram__free_unpacked
+ (Opentelemetry__Proto__Metrics__V1__Histogram *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__histogram__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void opentelemetry__proto__metrics__v1__exponential_histogram__init
+ (Opentelemetry__Proto__Metrics__V1__ExponentialHistogram *message)
+{
+ static const Opentelemetry__Proto__Metrics__V1__ExponentialHistogram init_value = OPENTELEMETRY__PROTO__METRICS__V1__EXPONENTIAL_HISTOGRAM__INIT;
+ *message = init_value;
+}
+size_t opentelemetry__proto__metrics__v1__exponential_histogram__get_packed_size
+ (const Opentelemetry__Proto__Metrics__V1__ExponentialHistogram *message)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__exponential_histogram__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t opentelemetry__proto__metrics__v1__exponential_histogram__pack
+ (const Opentelemetry__Proto__Metrics__V1__ExponentialHistogram *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__exponential_histogram__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t opentelemetry__proto__metrics__v1__exponential_histogram__pack_to_buffer
+ (const Opentelemetry__Proto__Metrics__V1__ExponentialHistogram *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__exponential_histogram__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Opentelemetry__Proto__Metrics__V1__ExponentialHistogram *
+ opentelemetry__proto__metrics__v1__exponential_histogram__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Opentelemetry__Proto__Metrics__V1__ExponentialHistogram *)
+ protobuf_c_message_unpack (&opentelemetry__proto__metrics__v1__exponential_histogram__descriptor,
+ allocator, len, data);
+}
+void opentelemetry__proto__metrics__v1__exponential_histogram__free_unpacked
+ (Opentelemetry__Proto__Metrics__V1__ExponentialHistogram *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__exponential_histogram__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void opentelemetry__proto__metrics__v1__summary__init
+ (Opentelemetry__Proto__Metrics__V1__Summary *message)
+{
+ static const Opentelemetry__Proto__Metrics__V1__Summary init_value = OPENTELEMETRY__PROTO__METRICS__V1__SUMMARY__INIT;
+ *message = init_value;
+}
+size_t opentelemetry__proto__metrics__v1__summary__get_packed_size
+ (const Opentelemetry__Proto__Metrics__V1__Summary *message)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__summary__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t opentelemetry__proto__metrics__v1__summary__pack
+ (const Opentelemetry__Proto__Metrics__V1__Summary *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__summary__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t opentelemetry__proto__metrics__v1__summary__pack_to_buffer
+ (const Opentelemetry__Proto__Metrics__V1__Summary *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__summary__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Opentelemetry__Proto__Metrics__V1__Summary *
+ opentelemetry__proto__metrics__v1__summary__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Opentelemetry__Proto__Metrics__V1__Summary *)
+ protobuf_c_message_unpack (&opentelemetry__proto__metrics__v1__summary__descriptor,
+ allocator, len, data);
+}
+void opentelemetry__proto__metrics__v1__summary__free_unpacked
+ (Opentelemetry__Proto__Metrics__V1__Summary *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__summary__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void opentelemetry__proto__metrics__v1__number_data_point__init
+ (Opentelemetry__Proto__Metrics__V1__NumberDataPoint *message)
+{
+ static const Opentelemetry__Proto__Metrics__V1__NumberDataPoint init_value = OPENTELEMETRY__PROTO__METRICS__V1__NUMBER_DATA_POINT__INIT;
+ *message = init_value;
+}
+size_t opentelemetry__proto__metrics__v1__number_data_point__get_packed_size
+ (const Opentelemetry__Proto__Metrics__V1__NumberDataPoint *message)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__number_data_point__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t opentelemetry__proto__metrics__v1__number_data_point__pack
+ (const Opentelemetry__Proto__Metrics__V1__NumberDataPoint *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__number_data_point__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t opentelemetry__proto__metrics__v1__number_data_point__pack_to_buffer
+ (const Opentelemetry__Proto__Metrics__V1__NumberDataPoint *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__number_data_point__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Opentelemetry__Proto__Metrics__V1__NumberDataPoint *
+ opentelemetry__proto__metrics__v1__number_data_point__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Opentelemetry__Proto__Metrics__V1__NumberDataPoint *)
+ protobuf_c_message_unpack (&opentelemetry__proto__metrics__v1__number_data_point__descriptor,
+ allocator, len, data);
+}
+void opentelemetry__proto__metrics__v1__number_data_point__free_unpacked
+ (Opentelemetry__Proto__Metrics__V1__NumberDataPoint *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__number_data_point__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void opentelemetry__proto__metrics__v1__histogram_data_point__init
+ (Opentelemetry__Proto__Metrics__V1__HistogramDataPoint *message)
+{
+ static const Opentelemetry__Proto__Metrics__V1__HistogramDataPoint init_value = OPENTELEMETRY__PROTO__METRICS__V1__HISTOGRAM_DATA_POINT__INIT;
+ *message = init_value;
+}
+size_t opentelemetry__proto__metrics__v1__histogram_data_point__get_packed_size
+ (const Opentelemetry__Proto__Metrics__V1__HistogramDataPoint *message)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__histogram_data_point__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t opentelemetry__proto__metrics__v1__histogram_data_point__pack
+ (const Opentelemetry__Proto__Metrics__V1__HistogramDataPoint *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__histogram_data_point__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t opentelemetry__proto__metrics__v1__histogram_data_point__pack_to_buffer
+ (const Opentelemetry__Proto__Metrics__V1__HistogramDataPoint *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__histogram_data_point__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Opentelemetry__Proto__Metrics__V1__HistogramDataPoint *
+ opentelemetry__proto__metrics__v1__histogram_data_point__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Opentelemetry__Proto__Metrics__V1__HistogramDataPoint *)
+ protobuf_c_message_unpack (&opentelemetry__proto__metrics__v1__histogram_data_point__descriptor,
+ allocator, len, data);
+}
+void opentelemetry__proto__metrics__v1__histogram_data_point__free_unpacked
+ (Opentelemetry__Proto__Metrics__V1__HistogramDataPoint *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__histogram_data_point__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void opentelemetry__proto__metrics__v1__exponential_histogram_data_point__buckets__init
+ (Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint__Buckets *message)
+{
+ static const Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint__Buckets init_value = OPENTELEMETRY__PROTO__METRICS__V1__EXPONENTIAL_HISTOGRAM_DATA_POINT__BUCKETS__INIT;
+ *message = init_value;
+}
+void opentelemetry__proto__metrics__v1__exponential_histogram_data_point__init
+ (Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint *message)
+{
+ static const Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint init_value = OPENTELEMETRY__PROTO__METRICS__V1__EXPONENTIAL_HISTOGRAM_DATA_POINT__INIT;
+ *message = init_value;
+}
+size_t opentelemetry__proto__metrics__v1__exponential_histogram_data_point__get_packed_size
+ (const Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint *message)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__exponential_histogram_data_point__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t opentelemetry__proto__metrics__v1__exponential_histogram_data_point__pack
+ (const Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__exponential_histogram_data_point__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t opentelemetry__proto__metrics__v1__exponential_histogram_data_point__pack_to_buffer
+ (const Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__exponential_histogram_data_point__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint *
+ opentelemetry__proto__metrics__v1__exponential_histogram_data_point__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint *)
+ protobuf_c_message_unpack (&opentelemetry__proto__metrics__v1__exponential_histogram_data_point__descriptor,
+ allocator, len, data);
+}
+void opentelemetry__proto__metrics__v1__exponential_histogram_data_point__free_unpacked
+ (Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__exponential_histogram_data_point__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void opentelemetry__proto__metrics__v1__summary_data_point__value_at_quantile__init
+ (Opentelemetry__Proto__Metrics__V1__SummaryDataPoint__ValueAtQuantile *message)
+{
+ static const Opentelemetry__Proto__Metrics__V1__SummaryDataPoint__ValueAtQuantile init_value = OPENTELEMETRY__PROTO__METRICS__V1__SUMMARY_DATA_POINT__VALUE_AT_QUANTILE__INIT;
+ *message = init_value;
+}
+void opentelemetry__proto__metrics__v1__summary_data_point__init
+ (Opentelemetry__Proto__Metrics__V1__SummaryDataPoint *message)
+{
+ static const Opentelemetry__Proto__Metrics__V1__SummaryDataPoint init_value = OPENTELEMETRY__PROTO__METRICS__V1__SUMMARY_DATA_POINT__INIT;
+ *message = init_value;
+}
+size_t opentelemetry__proto__metrics__v1__summary_data_point__get_packed_size
+ (const Opentelemetry__Proto__Metrics__V1__SummaryDataPoint *message)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__summary_data_point__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t opentelemetry__proto__metrics__v1__summary_data_point__pack
+ (const Opentelemetry__Proto__Metrics__V1__SummaryDataPoint *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__summary_data_point__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t opentelemetry__proto__metrics__v1__summary_data_point__pack_to_buffer
+ (const Opentelemetry__Proto__Metrics__V1__SummaryDataPoint *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__summary_data_point__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Opentelemetry__Proto__Metrics__V1__SummaryDataPoint *
+ opentelemetry__proto__metrics__v1__summary_data_point__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Opentelemetry__Proto__Metrics__V1__SummaryDataPoint *)
+ protobuf_c_message_unpack (&opentelemetry__proto__metrics__v1__summary_data_point__descriptor,
+ allocator, len, data);
+}
+void opentelemetry__proto__metrics__v1__summary_data_point__free_unpacked
+ (Opentelemetry__Proto__Metrics__V1__SummaryDataPoint *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__summary_data_point__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void opentelemetry__proto__metrics__v1__exemplar__init
+ (Opentelemetry__Proto__Metrics__V1__Exemplar *message)
+{
+ static const Opentelemetry__Proto__Metrics__V1__Exemplar init_value = OPENTELEMETRY__PROTO__METRICS__V1__EXEMPLAR__INIT;
+ *message = init_value;
+}
+size_t opentelemetry__proto__metrics__v1__exemplar__get_packed_size
+ (const Opentelemetry__Proto__Metrics__V1__Exemplar *message)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__exemplar__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t opentelemetry__proto__metrics__v1__exemplar__pack
+ (const Opentelemetry__Proto__Metrics__V1__Exemplar *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__exemplar__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t opentelemetry__proto__metrics__v1__exemplar__pack_to_buffer
+ (const Opentelemetry__Proto__Metrics__V1__Exemplar *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__exemplar__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Opentelemetry__Proto__Metrics__V1__Exemplar *
+ opentelemetry__proto__metrics__v1__exemplar__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Opentelemetry__Proto__Metrics__V1__Exemplar *)
+ protobuf_c_message_unpack (&opentelemetry__proto__metrics__v1__exemplar__descriptor,
+ allocator, len, data);
+}
+void opentelemetry__proto__metrics__v1__exemplar__free_unpacked
+ (Opentelemetry__Proto__Metrics__V1__Exemplar *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &opentelemetry__proto__metrics__v1__exemplar__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+static const ProtobufCFieldDescriptor opentelemetry__proto__metrics__v1__metrics_data__field_descriptors[1] =
+{
+ {
+ "resource_metrics",
+ 1,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Opentelemetry__Proto__Metrics__V1__MetricsData, n_resource_metrics),
+ offsetof(Opentelemetry__Proto__Metrics__V1__MetricsData, resource_metrics),
+ &opentelemetry__proto__metrics__v1__resource_metrics__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned opentelemetry__proto__metrics__v1__metrics_data__field_indices_by_name[] = {
+ 0, /* field[0] = resource_metrics */
+};
+static const ProtobufCIntRange opentelemetry__proto__metrics__v1__metrics_data__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor opentelemetry__proto__metrics__v1__metrics_data__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "opentelemetry.proto.metrics.v1.MetricsData",
+ "MetricsData",
+ "Opentelemetry__Proto__Metrics__V1__MetricsData",
+ "opentelemetry.proto.metrics.v1",
+ sizeof(Opentelemetry__Proto__Metrics__V1__MetricsData),
+ 1,
+ opentelemetry__proto__metrics__v1__metrics_data__field_descriptors,
+ opentelemetry__proto__metrics__v1__metrics_data__field_indices_by_name,
+ 1, opentelemetry__proto__metrics__v1__metrics_data__number_ranges,
+ (ProtobufCMessageInit) opentelemetry__proto__metrics__v1__metrics_data__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor opentelemetry__proto__metrics__v1__resource_metrics__field_descriptors[3] =
+{
+ {
+ "resource",
+ 1,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__ResourceMetrics, resource),
+ &opentelemetry__proto__resource__v1__resource__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "instrumentation_library_metrics",
+ 2,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Opentelemetry__Proto__Metrics__V1__ResourceMetrics, n_instrumentation_library_metrics),
+ offsetof(Opentelemetry__Proto__Metrics__V1__ResourceMetrics, instrumentation_library_metrics),
+ &opentelemetry__proto__metrics__v1__instrumentation_library_metrics__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "schema_url",
+ 3,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__ResourceMetrics, schema_url),
+ NULL,
+ &protobuf_c_empty_string,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned opentelemetry__proto__metrics__v1__resource_metrics__field_indices_by_name[] = {
+ 1, /* field[1] = instrumentation_library_metrics */
+ 0, /* field[0] = resource */
+ 2, /* field[2] = schema_url */
+};
+static const ProtobufCIntRange opentelemetry__proto__metrics__v1__resource_metrics__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 3 }
+};
+const ProtobufCMessageDescriptor opentelemetry__proto__metrics__v1__resource_metrics__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "opentelemetry.proto.metrics.v1.ResourceMetrics",
+ "ResourceMetrics",
+ "Opentelemetry__Proto__Metrics__V1__ResourceMetrics",
+ "opentelemetry.proto.metrics.v1",
+ sizeof(Opentelemetry__Proto__Metrics__V1__ResourceMetrics),
+ 3,
+ opentelemetry__proto__metrics__v1__resource_metrics__field_descriptors,
+ opentelemetry__proto__metrics__v1__resource_metrics__field_indices_by_name,
+ 1, opentelemetry__proto__metrics__v1__resource_metrics__number_ranges,
+ (ProtobufCMessageInit) opentelemetry__proto__metrics__v1__resource_metrics__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor opentelemetry__proto__metrics__v1__instrumentation_library_metrics__field_descriptors[3] =
+{
+ {
+ "instrumentation_library",
+ 1,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__InstrumentationLibraryMetrics, instrumentation_library),
+ &opentelemetry__proto__common__v1__instrumentation_library__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "metrics",
+ 2,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Opentelemetry__Proto__Metrics__V1__InstrumentationLibraryMetrics, n_metrics),
+ offsetof(Opentelemetry__Proto__Metrics__V1__InstrumentationLibraryMetrics, metrics),
+ &opentelemetry__proto__metrics__v1__metric__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "schema_url",
+ 3,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__InstrumentationLibraryMetrics, schema_url),
+ NULL,
+ &protobuf_c_empty_string,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned opentelemetry__proto__metrics__v1__instrumentation_library_metrics__field_indices_by_name[] = {
+ 0, /* field[0] = instrumentation_library */
+ 1, /* field[1] = metrics */
+ 2, /* field[2] = schema_url */
+};
+static const ProtobufCIntRange opentelemetry__proto__metrics__v1__instrumentation_library_metrics__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 3 }
+};
+const ProtobufCMessageDescriptor opentelemetry__proto__metrics__v1__instrumentation_library_metrics__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "opentelemetry.proto.metrics.v1.InstrumentationLibraryMetrics",
+ "InstrumentationLibraryMetrics",
+ "Opentelemetry__Proto__Metrics__V1__InstrumentationLibraryMetrics",
+ "opentelemetry.proto.metrics.v1",
+ sizeof(Opentelemetry__Proto__Metrics__V1__InstrumentationLibraryMetrics),
+ 3,
+ opentelemetry__proto__metrics__v1__instrumentation_library_metrics__field_descriptors,
+ opentelemetry__proto__metrics__v1__instrumentation_library_metrics__field_indices_by_name,
+ 1, opentelemetry__proto__metrics__v1__instrumentation_library_metrics__number_ranges,
+ (ProtobufCMessageInit) opentelemetry__proto__metrics__v1__instrumentation_library_metrics__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor opentelemetry__proto__metrics__v1__metric__field_descriptors[8] =
+{
+ {
+ "name",
+ 1,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__Metric, name),
+ NULL,
+ &protobuf_c_empty_string,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "description",
+ 2,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__Metric, description),
+ NULL,
+ &protobuf_c_empty_string,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "unit",
+ 3,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__Metric, unit),
+ NULL,
+ &protobuf_c_empty_string,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "gauge",
+ 5,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Opentelemetry__Proto__Metrics__V1__Metric, data_case),
+ offsetof(Opentelemetry__Proto__Metrics__V1__Metric, gauge),
+ &opentelemetry__proto__metrics__v1__gauge__descriptor,
+ NULL,
+ 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "sum",
+ 7,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Opentelemetry__Proto__Metrics__V1__Metric, data_case),
+ offsetof(Opentelemetry__Proto__Metrics__V1__Metric, sum),
+ &opentelemetry__proto__metrics__v1__sum__descriptor,
+ NULL,
+ 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "histogram",
+ 9,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Opentelemetry__Proto__Metrics__V1__Metric, data_case),
+ offsetof(Opentelemetry__Proto__Metrics__V1__Metric, histogram),
+ &opentelemetry__proto__metrics__v1__histogram__descriptor,
+ NULL,
+ 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "exponential_histogram",
+ 10,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Opentelemetry__Proto__Metrics__V1__Metric, data_case),
+ offsetof(Opentelemetry__Proto__Metrics__V1__Metric, exponential_histogram),
+ &opentelemetry__proto__metrics__v1__exponential_histogram__descriptor,
+ NULL,
+ 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "summary",
+ 11,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Opentelemetry__Proto__Metrics__V1__Metric, data_case),
+ offsetof(Opentelemetry__Proto__Metrics__V1__Metric, summary),
+ &opentelemetry__proto__metrics__v1__summary__descriptor,
+ NULL,
+ 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned opentelemetry__proto__metrics__v1__metric__field_indices_by_name[] = {
+ 1, /* field[1] = description */
+ 6, /* field[6] = exponential_histogram */
+ 3, /* field[3] = gauge */
+ 5, /* field[5] = histogram */
+ 0, /* field[0] = name */
+ 4, /* field[4] = sum */
+ 7, /* field[7] = summary */
+ 2, /* field[2] = unit */
+};
+static const ProtobufCIntRange opentelemetry__proto__metrics__v1__metric__number_ranges[4 + 1] =
+{
+ { 1, 0 },
+ { 5, 3 },
+ { 7, 4 },
+ { 9, 5 },
+ { 0, 8 }
+};
+const ProtobufCMessageDescriptor opentelemetry__proto__metrics__v1__metric__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "opentelemetry.proto.metrics.v1.Metric",
+ "Metric",
+ "Opentelemetry__Proto__Metrics__V1__Metric",
+ "opentelemetry.proto.metrics.v1",
+ sizeof(Opentelemetry__Proto__Metrics__V1__Metric),
+ 8,
+ opentelemetry__proto__metrics__v1__metric__field_descriptors,
+ opentelemetry__proto__metrics__v1__metric__field_indices_by_name,
+ 4, opentelemetry__proto__metrics__v1__metric__number_ranges,
+ (ProtobufCMessageInit) opentelemetry__proto__metrics__v1__metric__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor opentelemetry__proto__metrics__v1__gauge__field_descriptors[1] =
+{
+ {
+ "data_points",
+ 1,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Opentelemetry__Proto__Metrics__V1__Gauge, n_data_points),
+ offsetof(Opentelemetry__Proto__Metrics__V1__Gauge, data_points),
+ &opentelemetry__proto__metrics__v1__number_data_point__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned opentelemetry__proto__metrics__v1__gauge__field_indices_by_name[] = {
+ 0, /* field[0] = data_points */
+};
+static const ProtobufCIntRange opentelemetry__proto__metrics__v1__gauge__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor opentelemetry__proto__metrics__v1__gauge__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "opentelemetry.proto.metrics.v1.Gauge",
+ "Gauge",
+ "Opentelemetry__Proto__Metrics__V1__Gauge",
+ "opentelemetry.proto.metrics.v1",
+ sizeof(Opentelemetry__Proto__Metrics__V1__Gauge),
+ 1,
+ opentelemetry__proto__metrics__v1__gauge__field_descriptors,
+ opentelemetry__proto__metrics__v1__gauge__field_indices_by_name,
+ 1, opentelemetry__proto__metrics__v1__gauge__number_ranges,
+ (ProtobufCMessageInit) opentelemetry__proto__metrics__v1__gauge__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor opentelemetry__proto__metrics__v1__sum__field_descriptors[3] =
+{
+ {
+ "data_points",
+ 1,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Opentelemetry__Proto__Metrics__V1__Sum, n_data_points),
+ offsetof(Opentelemetry__Proto__Metrics__V1__Sum, data_points),
+ &opentelemetry__proto__metrics__v1__number_data_point__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "aggregation_temporality",
+ 2,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_ENUM,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__Sum, aggregation_temporality),
+ &opentelemetry__proto__metrics__v1__aggregation_temporality__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "is_monotonic",
+ 3,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_BOOL,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__Sum, is_monotonic),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned opentelemetry__proto__metrics__v1__sum__field_indices_by_name[] = {
+ 1, /* field[1] = aggregation_temporality */
+ 0, /* field[0] = data_points */
+ 2, /* field[2] = is_monotonic */
+};
+static const ProtobufCIntRange opentelemetry__proto__metrics__v1__sum__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 3 }
+};
+const ProtobufCMessageDescriptor opentelemetry__proto__metrics__v1__sum__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "opentelemetry.proto.metrics.v1.Sum",
+ "Sum",
+ "Opentelemetry__Proto__Metrics__V1__Sum",
+ "opentelemetry.proto.metrics.v1",
+ sizeof(Opentelemetry__Proto__Metrics__V1__Sum),
+ 3,
+ opentelemetry__proto__metrics__v1__sum__field_descriptors,
+ opentelemetry__proto__metrics__v1__sum__field_indices_by_name,
+ 1, opentelemetry__proto__metrics__v1__sum__number_ranges,
+ (ProtobufCMessageInit) opentelemetry__proto__metrics__v1__sum__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor opentelemetry__proto__metrics__v1__histogram__field_descriptors[2] =
+{
+ {
+ "data_points",
+ 1,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Opentelemetry__Proto__Metrics__V1__Histogram, n_data_points),
+ offsetof(Opentelemetry__Proto__Metrics__V1__Histogram, data_points),
+ &opentelemetry__proto__metrics__v1__histogram_data_point__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "aggregation_temporality",
+ 2,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_ENUM,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__Histogram, aggregation_temporality),
+ &opentelemetry__proto__metrics__v1__aggregation_temporality__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned opentelemetry__proto__metrics__v1__histogram__field_indices_by_name[] = {
+ 1, /* field[1] = aggregation_temporality */
+ 0, /* field[0] = data_points */
+};
+static const ProtobufCIntRange opentelemetry__proto__metrics__v1__histogram__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor opentelemetry__proto__metrics__v1__histogram__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "opentelemetry.proto.metrics.v1.Histogram",
+ "Histogram",
+ "Opentelemetry__Proto__Metrics__V1__Histogram",
+ "opentelemetry.proto.metrics.v1",
+ sizeof(Opentelemetry__Proto__Metrics__V1__Histogram),
+ 2,
+ opentelemetry__proto__metrics__v1__histogram__field_descriptors,
+ opentelemetry__proto__metrics__v1__histogram__field_indices_by_name,
+ 1, opentelemetry__proto__metrics__v1__histogram__number_ranges,
+ (ProtobufCMessageInit) opentelemetry__proto__metrics__v1__histogram__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor opentelemetry__proto__metrics__v1__exponential_histogram__field_descriptors[2] =
+{
+ {
+ "data_points",
+ 1,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Opentelemetry__Proto__Metrics__V1__ExponentialHistogram, n_data_points),
+ offsetof(Opentelemetry__Proto__Metrics__V1__ExponentialHistogram, data_points),
+ &opentelemetry__proto__metrics__v1__exponential_histogram_data_point__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "aggregation_temporality",
+ 2,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_ENUM,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__ExponentialHistogram, aggregation_temporality),
+ &opentelemetry__proto__metrics__v1__aggregation_temporality__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned opentelemetry__proto__metrics__v1__exponential_histogram__field_indices_by_name[] = {
+ 1, /* field[1] = aggregation_temporality */
+ 0, /* field[0] = data_points */
+};
+static const ProtobufCIntRange opentelemetry__proto__metrics__v1__exponential_histogram__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor opentelemetry__proto__metrics__v1__exponential_histogram__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "opentelemetry.proto.metrics.v1.ExponentialHistogram",
+ "ExponentialHistogram",
+ "Opentelemetry__Proto__Metrics__V1__ExponentialHistogram",
+ "opentelemetry.proto.metrics.v1",
+ sizeof(Opentelemetry__Proto__Metrics__V1__ExponentialHistogram),
+ 2,
+ opentelemetry__proto__metrics__v1__exponential_histogram__field_descriptors,
+ opentelemetry__proto__metrics__v1__exponential_histogram__field_indices_by_name,
+ 1, opentelemetry__proto__metrics__v1__exponential_histogram__number_ranges,
+ (ProtobufCMessageInit) opentelemetry__proto__metrics__v1__exponential_histogram__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor opentelemetry__proto__metrics__v1__summary__field_descriptors[1] =
+{
+ {
+ "data_points",
+ 1,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Opentelemetry__Proto__Metrics__V1__Summary, n_data_points),
+ offsetof(Opentelemetry__Proto__Metrics__V1__Summary, data_points),
+ &opentelemetry__proto__metrics__v1__summary_data_point__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned opentelemetry__proto__metrics__v1__summary__field_indices_by_name[] = {
+ 0, /* field[0] = data_points */
+};
+static const ProtobufCIntRange opentelemetry__proto__metrics__v1__summary__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor opentelemetry__proto__metrics__v1__summary__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "opentelemetry.proto.metrics.v1.Summary",
+ "Summary",
+ "Opentelemetry__Proto__Metrics__V1__Summary",
+ "opentelemetry.proto.metrics.v1",
+ sizeof(Opentelemetry__Proto__Metrics__V1__Summary),
+ 1,
+ opentelemetry__proto__metrics__v1__summary__field_descriptors,
+ opentelemetry__proto__metrics__v1__summary__field_indices_by_name,
+ 1, opentelemetry__proto__metrics__v1__summary__number_ranges,
+ (ProtobufCMessageInit) opentelemetry__proto__metrics__v1__summary__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor opentelemetry__proto__metrics__v1__number_data_point__field_descriptors[7] =
+{
+ {
+ "start_time_unix_nano",
+ 2,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_FIXED64,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__NumberDataPoint, start_time_unix_nano),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "time_unix_nano",
+ 3,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_FIXED64,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__NumberDataPoint, time_unix_nano),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "as_double",
+ 4,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_DOUBLE,
+ offsetof(Opentelemetry__Proto__Metrics__V1__NumberDataPoint, value_case),
+ offsetof(Opentelemetry__Proto__Metrics__V1__NumberDataPoint, as_double),
+ NULL,
+ NULL,
+ 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "exemplars",
+ 5,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Opentelemetry__Proto__Metrics__V1__NumberDataPoint, n_exemplars),
+ offsetof(Opentelemetry__Proto__Metrics__V1__NumberDataPoint, exemplars),
+ &opentelemetry__proto__metrics__v1__exemplar__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "as_int",
+ 6,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_SFIXED64,
+ offsetof(Opentelemetry__Proto__Metrics__V1__NumberDataPoint, value_case),
+ offsetof(Opentelemetry__Proto__Metrics__V1__NumberDataPoint, as_int),
+ NULL,
+ NULL,
+ 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "attributes",
+ 7,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Opentelemetry__Proto__Metrics__V1__NumberDataPoint, n_attributes),
+ offsetof(Opentelemetry__Proto__Metrics__V1__NumberDataPoint, attributes),
+ &opentelemetry__proto__common__v1__key_value__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "flags",
+ 8,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_UINT32,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__NumberDataPoint, flags),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned opentelemetry__proto__metrics__v1__number_data_point__field_indices_by_name[] = {
+ 2, /* field[2] = as_double */
+ 4, /* field[4] = as_int */
+ 5, /* field[5] = attributes */
+ 3, /* field[3] = exemplars */
+ 6, /* field[6] = flags */
+ 0, /* field[0] = start_time_unix_nano */
+ 1, /* field[1] = time_unix_nano */
+};
+static const ProtobufCIntRange opentelemetry__proto__metrics__v1__number_data_point__number_ranges[1 + 1] =
+{
+ { 2, 0 },
+ { 0, 7 }
+};
+const ProtobufCMessageDescriptor opentelemetry__proto__metrics__v1__number_data_point__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "opentelemetry.proto.metrics.v1.NumberDataPoint",
+ "NumberDataPoint",
+ "Opentelemetry__Proto__Metrics__V1__NumberDataPoint",
+ "opentelemetry.proto.metrics.v1",
+ sizeof(Opentelemetry__Proto__Metrics__V1__NumberDataPoint),
+ 7,
+ opentelemetry__proto__metrics__v1__number_data_point__field_descriptors,
+ opentelemetry__proto__metrics__v1__number_data_point__field_indices_by_name,
+ 1, opentelemetry__proto__metrics__v1__number_data_point__number_ranges,
+ (ProtobufCMessageInit) opentelemetry__proto__metrics__v1__number_data_point__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor opentelemetry__proto__metrics__v1__histogram_data_point__field_descriptors[9] =
+{
+ {
+ "start_time_unix_nano",
+ 2,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_FIXED64,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__HistogramDataPoint, start_time_unix_nano),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "time_unix_nano",
+ 3,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_FIXED64,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__HistogramDataPoint, time_unix_nano),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "count",
+ 4,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_FIXED64,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__HistogramDataPoint, count),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "sum",
+ 5,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_DOUBLE,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__HistogramDataPoint, sum),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "bucket_counts",
+ 6,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_FIXED64,
+ offsetof(Opentelemetry__Proto__Metrics__V1__HistogramDataPoint, n_bucket_counts),
+ offsetof(Opentelemetry__Proto__Metrics__V1__HistogramDataPoint, bucket_counts),
+ NULL,
+ NULL,
+ 0 | PROTOBUF_C_FIELD_FLAG_PACKED, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "explicit_bounds",
+ 7,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_DOUBLE,
+ offsetof(Opentelemetry__Proto__Metrics__V1__HistogramDataPoint, n_explicit_bounds),
+ offsetof(Opentelemetry__Proto__Metrics__V1__HistogramDataPoint, explicit_bounds),
+ NULL,
+ NULL,
+ 0 | PROTOBUF_C_FIELD_FLAG_PACKED, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "exemplars",
+ 8,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Opentelemetry__Proto__Metrics__V1__HistogramDataPoint, n_exemplars),
+ offsetof(Opentelemetry__Proto__Metrics__V1__HistogramDataPoint, exemplars),
+ &opentelemetry__proto__metrics__v1__exemplar__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "attributes",
+ 9,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Opentelemetry__Proto__Metrics__V1__HistogramDataPoint, n_attributes),
+ offsetof(Opentelemetry__Proto__Metrics__V1__HistogramDataPoint, attributes),
+ &opentelemetry__proto__common__v1__key_value__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "flags",
+ 10,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_UINT32,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__HistogramDataPoint, flags),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned opentelemetry__proto__metrics__v1__histogram_data_point__field_indices_by_name[] = {
+ 7, /* field[7] = attributes */
+ 4, /* field[4] = bucket_counts */
+ 2, /* field[2] = count */
+ 6, /* field[6] = exemplars */
+ 5, /* field[5] = explicit_bounds */
+ 8, /* field[8] = flags */
+ 0, /* field[0] = start_time_unix_nano */
+ 3, /* field[3] = sum */
+ 1, /* field[1] = time_unix_nano */
+};
+static const ProtobufCIntRange opentelemetry__proto__metrics__v1__histogram_data_point__number_ranges[1 + 1] =
+{
+ { 2, 0 },
+ { 0, 9 }
+};
+const ProtobufCMessageDescriptor opentelemetry__proto__metrics__v1__histogram_data_point__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "opentelemetry.proto.metrics.v1.HistogramDataPoint",
+ "HistogramDataPoint",
+ "Opentelemetry__Proto__Metrics__V1__HistogramDataPoint",
+ "opentelemetry.proto.metrics.v1",
+ sizeof(Opentelemetry__Proto__Metrics__V1__HistogramDataPoint),
+ 9,
+ opentelemetry__proto__metrics__v1__histogram_data_point__field_descriptors,
+ opentelemetry__proto__metrics__v1__histogram_data_point__field_indices_by_name,
+ 1, opentelemetry__proto__metrics__v1__histogram_data_point__number_ranges,
+ (ProtobufCMessageInit) opentelemetry__proto__metrics__v1__histogram_data_point__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor opentelemetry__proto__metrics__v1__exponential_histogram_data_point__buckets__field_descriptors[2] =
+{
+ {
+ "offset",
+ 1,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_SINT32,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint__Buckets, offset),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "bucket_counts",
+ 2,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_UINT64,
+ offsetof(Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint__Buckets, n_bucket_counts),
+ offsetof(Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint__Buckets, bucket_counts),
+ NULL,
+ NULL,
+ 0 | PROTOBUF_C_FIELD_FLAG_PACKED, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned opentelemetry__proto__metrics__v1__exponential_histogram_data_point__buckets__field_indices_by_name[] = {
+ 1, /* field[1] = bucket_counts */
+ 0, /* field[0] = offset */
+};
+static const ProtobufCIntRange opentelemetry__proto__metrics__v1__exponential_histogram_data_point__buckets__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor opentelemetry__proto__metrics__v1__exponential_histogram_data_point__buckets__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "opentelemetry.proto.metrics.v1.ExponentialHistogramDataPoint.Buckets",
+ "Buckets",
+ "Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint__Buckets",
+ "opentelemetry.proto.metrics.v1",
+ sizeof(Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint__Buckets),
+ 2,
+ opentelemetry__proto__metrics__v1__exponential_histogram_data_point__buckets__field_descriptors,
+ opentelemetry__proto__metrics__v1__exponential_histogram_data_point__buckets__field_indices_by_name,
+ 1, opentelemetry__proto__metrics__v1__exponential_histogram_data_point__buckets__number_ranges,
+ (ProtobufCMessageInit) opentelemetry__proto__metrics__v1__exponential_histogram_data_point__buckets__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor opentelemetry__proto__metrics__v1__exponential_histogram_data_point__field_descriptors[11] =
+{
+ {
+ "attributes",
+ 1,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint, n_attributes),
+ offsetof(Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint, attributes),
+ &opentelemetry__proto__common__v1__key_value__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "start_time_unix_nano",
+ 2,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_FIXED64,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint, start_time_unix_nano),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "time_unix_nano",
+ 3,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_FIXED64,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint, time_unix_nano),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "count",
+ 4,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_FIXED64,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint, count),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "sum",
+ 5,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_DOUBLE,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint, sum),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "scale",
+ 6,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_SINT32,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint, scale),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "zero_count",
+ 7,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_FIXED64,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint, zero_count),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "positive",
+ 8,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint, positive),
+ &opentelemetry__proto__metrics__v1__exponential_histogram_data_point__buckets__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "negative",
+ 9,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint, negative),
+ &opentelemetry__proto__metrics__v1__exponential_histogram_data_point__buckets__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "flags",
+ 10,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_UINT32,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint, flags),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "exemplars",
+ 11,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint, n_exemplars),
+ offsetof(Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint, exemplars),
+ &opentelemetry__proto__metrics__v1__exemplar__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned opentelemetry__proto__metrics__v1__exponential_histogram_data_point__field_indices_by_name[] = {
+ 0, /* field[0] = attributes */
+ 3, /* field[3] = count */
+ 10, /* field[10] = exemplars */
+ 9, /* field[9] = flags */
+ 8, /* field[8] = negative */
+ 7, /* field[7] = positive */
+ 5, /* field[5] = scale */
+ 1, /* field[1] = start_time_unix_nano */
+ 4, /* field[4] = sum */
+ 2, /* field[2] = time_unix_nano */
+ 6, /* field[6] = zero_count */
+};
+static const ProtobufCIntRange opentelemetry__proto__metrics__v1__exponential_histogram_data_point__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 11 }
+};
+const ProtobufCMessageDescriptor opentelemetry__proto__metrics__v1__exponential_histogram_data_point__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "opentelemetry.proto.metrics.v1.ExponentialHistogramDataPoint",
+ "ExponentialHistogramDataPoint",
+ "Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint",
+ "opentelemetry.proto.metrics.v1",
+ sizeof(Opentelemetry__Proto__Metrics__V1__ExponentialHistogramDataPoint),
+ 11,
+ opentelemetry__proto__metrics__v1__exponential_histogram_data_point__field_descriptors,
+ opentelemetry__proto__metrics__v1__exponential_histogram_data_point__field_indices_by_name,
+ 1, opentelemetry__proto__metrics__v1__exponential_histogram_data_point__number_ranges,
+ (ProtobufCMessageInit) opentelemetry__proto__metrics__v1__exponential_histogram_data_point__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor opentelemetry__proto__metrics__v1__summary_data_point__value_at_quantile__field_descriptors[2] =
+{
+ {
+ "quantile",
+ 1,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_DOUBLE,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__SummaryDataPoint__ValueAtQuantile, quantile),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "value",
+ 2,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_DOUBLE,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__SummaryDataPoint__ValueAtQuantile, value),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned opentelemetry__proto__metrics__v1__summary_data_point__value_at_quantile__field_indices_by_name[] = {
+ 0, /* field[0] = quantile */
+ 1, /* field[1] = value */
+};
+static const ProtobufCIntRange opentelemetry__proto__metrics__v1__summary_data_point__value_at_quantile__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor opentelemetry__proto__metrics__v1__summary_data_point__value_at_quantile__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "opentelemetry.proto.metrics.v1.SummaryDataPoint.ValueAtQuantile",
+ "ValueAtQuantile",
+ "Opentelemetry__Proto__Metrics__V1__SummaryDataPoint__ValueAtQuantile",
+ "opentelemetry.proto.metrics.v1",
+ sizeof(Opentelemetry__Proto__Metrics__V1__SummaryDataPoint__ValueAtQuantile),
+ 2,
+ opentelemetry__proto__metrics__v1__summary_data_point__value_at_quantile__field_descriptors,
+ opentelemetry__proto__metrics__v1__summary_data_point__value_at_quantile__field_indices_by_name,
+ 1, opentelemetry__proto__metrics__v1__summary_data_point__value_at_quantile__number_ranges,
+ (ProtobufCMessageInit) opentelemetry__proto__metrics__v1__summary_data_point__value_at_quantile__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor opentelemetry__proto__metrics__v1__summary_data_point__field_descriptors[7] =
+{
+ {
+ "start_time_unix_nano",
+ 2,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_FIXED64,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__SummaryDataPoint, start_time_unix_nano),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "time_unix_nano",
+ 3,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_FIXED64,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__SummaryDataPoint, time_unix_nano),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "count",
+ 4,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_FIXED64,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__SummaryDataPoint, count),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "sum",
+ 5,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_DOUBLE,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__SummaryDataPoint, sum),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "quantile_values",
+ 6,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Opentelemetry__Proto__Metrics__V1__SummaryDataPoint, n_quantile_values),
+ offsetof(Opentelemetry__Proto__Metrics__V1__SummaryDataPoint, quantile_values),
+ &opentelemetry__proto__metrics__v1__summary_data_point__value_at_quantile__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "attributes",
+ 7,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Opentelemetry__Proto__Metrics__V1__SummaryDataPoint, n_attributes),
+ offsetof(Opentelemetry__Proto__Metrics__V1__SummaryDataPoint, attributes),
+ &opentelemetry__proto__common__v1__key_value__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "flags",
+ 8,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_UINT32,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__SummaryDataPoint, flags),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned opentelemetry__proto__metrics__v1__summary_data_point__field_indices_by_name[] = {
+ 5, /* field[5] = attributes */
+ 2, /* field[2] = count */
+ 6, /* field[6] = flags */
+ 4, /* field[4] = quantile_values */
+ 0, /* field[0] = start_time_unix_nano */
+ 3, /* field[3] = sum */
+ 1, /* field[1] = time_unix_nano */
+};
+static const ProtobufCIntRange opentelemetry__proto__metrics__v1__summary_data_point__number_ranges[1 + 1] =
+{
+ { 2, 0 },
+ { 0, 7 }
+};
+const ProtobufCMessageDescriptor opentelemetry__proto__metrics__v1__summary_data_point__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "opentelemetry.proto.metrics.v1.SummaryDataPoint",
+ "SummaryDataPoint",
+ "Opentelemetry__Proto__Metrics__V1__SummaryDataPoint",
+ "opentelemetry.proto.metrics.v1",
+ sizeof(Opentelemetry__Proto__Metrics__V1__SummaryDataPoint),
+ 7,
+ opentelemetry__proto__metrics__v1__summary_data_point__field_descriptors,
+ opentelemetry__proto__metrics__v1__summary_data_point__field_indices_by_name,
+ 1, opentelemetry__proto__metrics__v1__summary_data_point__number_ranges,
+ (ProtobufCMessageInit) opentelemetry__proto__metrics__v1__summary_data_point__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor opentelemetry__proto__metrics__v1__exemplar__field_descriptors[6] =
+{
+ {
+ "time_unix_nano",
+ 2,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_FIXED64,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__Exemplar, time_unix_nano),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "as_double",
+ 3,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_DOUBLE,
+ offsetof(Opentelemetry__Proto__Metrics__V1__Exemplar, value_case),
+ offsetof(Opentelemetry__Proto__Metrics__V1__Exemplar, as_double),
+ NULL,
+ NULL,
+ 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "span_id",
+ 4,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_BYTES,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__Exemplar, span_id),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "trace_id",
+ 5,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_BYTES,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Metrics__V1__Exemplar, trace_id),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "as_int",
+ 6,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_SFIXED64,
+ offsetof(Opentelemetry__Proto__Metrics__V1__Exemplar, value_case),
+ offsetof(Opentelemetry__Proto__Metrics__V1__Exemplar, as_int),
+ NULL,
+ NULL,
+ 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "filtered_attributes",
+ 7,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Opentelemetry__Proto__Metrics__V1__Exemplar, n_filtered_attributes),
+ offsetof(Opentelemetry__Proto__Metrics__V1__Exemplar, filtered_attributes),
+ &opentelemetry__proto__common__v1__key_value__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned opentelemetry__proto__metrics__v1__exemplar__field_indices_by_name[] = {
+ 1, /* field[1] = as_double */
+ 4, /* field[4] = as_int */
+ 5, /* field[5] = filtered_attributes */
+ 2, /* field[2] = span_id */
+ 0, /* field[0] = time_unix_nano */
+ 3, /* field[3] = trace_id */
+};
+static const ProtobufCIntRange opentelemetry__proto__metrics__v1__exemplar__number_ranges[1 + 1] =
+{
+ { 2, 0 },
+ { 0, 6 }
+};
+const ProtobufCMessageDescriptor opentelemetry__proto__metrics__v1__exemplar__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "opentelemetry.proto.metrics.v1.Exemplar",
+ "Exemplar",
+ "Opentelemetry__Proto__Metrics__V1__Exemplar",
+ "opentelemetry.proto.metrics.v1",
+ sizeof(Opentelemetry__Proto__Metrics__V1__Exemplar),
+ 6,
+ opentelemetry__proto__metrics__v1__exemplar__field_descriptors,
+ opentelemetry__proto__metrics__v1__exemplar__field_indices_by_name,
+ 1, opentelemetry__proto__metrics__v1__exemplar__number_ranges,
+ (ProtobufCMessageInit) opentelemetry__proto__metrics__v1__exemplar__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCEnumValue opentelemetry__proto__metrics__v1__aggregation_temporality__enum_values_by_number[3] =
+{
+ { "AGGREGATION_TEMPORALITY_UNSPECIFIED", "OPENTELEMETRY__PROTO__METRICS__V1__AGGREGATION_TEMPORALITY__AGGREGATION_TEMPORALITY_UNSPECIFIED", 0 },
+ { "AGGREGATION_TEMPORALITY_DELTA", "OPENTELEMETRY__PROTO__METRICS__V1__AGGREGATION_TEMPORALITY__AGGREGATION_TEMPORALITY_DELTA", 1 },
+ { "AGGREGATION_TEMPORALITY_CUMULATIVE", "OPENTELEMETRY__PROTO__METRICS__V1__AGGREGATION_TEMPORALITY__AGGREGATION_TEMPORALITY_CUMULATIVE", 2 },
+};
+static const ProtobufCIntRange opentelemetry__proto__metrics__v1__aggregation_temporality__value_ranges[] = {
+{0, 0},{0, 3}
+};
+static const ProtobufCEnumValueIndex opentelemetry__proto__metrics__v1__aggregation_temporality__enum_values_by_name[3] =
+{
+ { "AGGREGATION_TEMPORALITY_CUMULATIVE", 2 },
+ { "AGGREGATION_TEMPORALITY_DELTA", 1 },
+ { "AGGREGATION_TEMPORALITY_UNSPECIFIED", 0 },
+};
+const ProtobufCEnumDescriptor opentelemetry__proto__metrics__v1__aggregation_temporality__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "opentelemetry.proto.metrics.v1.AggregationTemporality",
+ "AggregationTemporality",
+ "Opentelemetry__Proto__Metrics__V1__AggregationTemporality",
+ "opentelemetry.proto.metrics.v1",
+ 3,
+ opentelemetry__proto__metrics__v1__aggregation_temporality__enum_values_by_number,
+ 3,
+ opentelemetry__proto__metrics__v1__aggregation_temporality__enum_values_by_name,
+ 1,
+ opentelemetry__proto__metrics__v1__aggregation_temporality__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
+static const ProtobufCEnumValue opentelemetry__proto__metrics__v1__data_point_flags__enum_values_by_number[2] =
+{
+ { "FLAG_NONE", "OPENTELEMETRY__PROTO__METRICS__V1__DATA_POINT_FLAGS__FLAG_NONE", 0 },
+ { "FLAG_NO_RECORDED_VALUE", "OPENTELEMETRY__PROTO__METRICS__V1__DATA_POINT_FLAGS__FLAG_NO_RECORDED_VALUE", 1 },
+};
+static const ProtobufCIntRange opentelemetry__proto__metrics__v1__data_point_flags__value_ranges[] = {
+{0, 0},{0, 2}
+};
+static const ProtobufCEnumValueIndex opentelemetry__proto__metrics__v1__data_point_flags__enum_values_by_name[2] =
+{
+ { "FLAG_NONE", 0 },
+ { "FLAG_NO_RECORDED_VALUE", 1 },
+};
+const ProtobufCEnumDescriptor opentelemetry__proto__metrics__v1__data_point_flags__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "opentelemetry.proto.metrics.v1.DataPointFlags",
+ "DataPointFlags",
+ "Opentelemetry__Proto__Metrics__V1__DataPointFlags",
+ "opentelemetry.proto.metrics.v1",
+ 2,
+ opentelemetry__proto__metrics__v1__data_point_flags__enum_values_by_number,
+ 2,
+ opentelemetry__proto__metrics__v1__data_point_flags__enum_values_by_name,
+ 1,
+ opentelemetry__proto__metrics__v1__data_point_flags__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
diff --git a/src/fluent-bit/lib/cmetrics/src/external/opentelemetry_metrics_service.pb-c.c b/src/fluent-bit/lib/cmetrics/src/external/opentelemetry_metrics_service.pb-c.c
new file mode 100644
index 000000000..406930159
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/external/opentelemetry_metrics_service.pb-c.c
@@ -0,0 +1,189 @@
+/* Generated by the protocol buffer compiler. DO NOT EDIT! */
+/* Generated from: metrics_service.proto */
+
+/* Do not generate deprecated warnings for self */
+#ifndef PROTOBUF_C__NO_DEPRECATED
+#define PROTOBUF_C__NO_DEPRECATED
+#endif
+
+#include <opentelemetry/metrics_service.pb-c.h>
+
+void opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__init
+ (Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceRequest *message)
+{
+ static const Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceRequest init_value = OPENTELEMETRY__PROTO__COLLECTOR__METRICS__V1__EXPORT_METRICS_SERVICE_REQUEST__INIT;
+ *message = init_value;
+}
+size_t opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__get_packed_size
+ (const Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceRequest *message)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__pack
+ (const Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceRequest *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__pack_to_buffer
+ (const Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceRequest *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceRequest *
+ opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceRequest *)
+ protobuf_c_message_unpack (&opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__descriptor,
+ allocator, len, data);
+}
+void opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__free_unpacked
+ (Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceRequest *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void opentelemetry__proto__collector__metrics__v1__export_metrics_service_response__init
+ (Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceResponse *message)
+{
+ static const Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceResponse init_value = OPENTELEMETRY__PROTO__COLLECTOR__METRICS__V1__EXPORT_METRICS_SERVICE_RESPONSE__INIT;
+ *message = init_value;
+}
+size_t opentelemetry__proto__collector__metrics__v1__export_metrics_service_response__get_packed_size
+ (const Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceResponse *message)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__collector__metrics__v1__export_metrics_service_response__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t opentelemetry__proto__collector__metrics__v1__export_metrics_service_response__pack
+ (const Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceResponse *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__collector__metrics__v1__export_metrics_service_response__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t opentelemetry__proto__collector__metrics__v1__export_metrics_service_response__pack_to_buffer
+ (const Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceResponse *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__collector__metrics__v1__export_metrics_service_response__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceResponse *
+ opentelemetry__proto__collector__metrics__v1__export_metrics_service_response__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceResponse *)
+ protobuf_c_message_unpack (&opentelemetry__proto__collector__metrics__v1__export_metrics_service_response__descriptor,
+ allocator, len, data);
+}
+void opentelemetry__proto__collector__metrics__v1__export_metrics_service_response__free_unpacked
+ (Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceResponse *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &opentelemetry__proto__collector__metrics__v1__export_metrics_service_response__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+static const ProtobufCFieldDescriptor opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__field_descriptors[1] =
+{
+ {
+ "resource_metrics",
+ 1,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceRequest, n_resource_metrics),
+ offsetof(Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceRequest, resource_metrics),
+ &opentelemetry__proto__metrics__v1__resource_metrics__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__field_indices_by_name[] = {
+ 0, /* field[0] = resource_metrics */
+};
+static const ProtobufCIntRange opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest",
+ "ExportMetricsServiceRequest",
+ "Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceRequest",
+ "opentelemetry.proto.collector.metrics.v1",
+ sizeof(Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceRequest),
+ 1,
+ opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__field_descriptors,
+ opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__field_indices_by_name,
+ 1, opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__number_ranges,
+ (ProtobufCMessageInit) opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+#define opentelemetry__proto__collector__metrics__v1__export_metrics_service_response__field_descriptors NULL
+#define opentelemetry__proto__collector__metrics__v1__export_metrics_service_response__field_indices_by_name NULL
+#define opentelemetry__proto__collector__metrics__v1__export_metrics_service_response__number_ranges NULL
+const ProtobufCMessageDescriptor opentelemetry__proto__collector__metrics__v1__export_metrics_service_response__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceResponse",
+ "ExportMetricsServiceResponse",
+ "Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceResponse",
+ "opentelemetry.proto.collector.metrics.v1",
+ sizeof(Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceResponse),
+ 0,
+ opentelemetry__proto__collector__metrics__v1__export_metrics_service_response__field_descriptors,
+ opentelemetry__proto__collector__metrics__v1__export_metrics_service_response__field_indices_by_name,
+ 0, opentelemetry__proto__collector__metrics__v1__export_metrics_service_response__number_ranges,
+ (ProtobufCMessageInit) opentelemetry__proto__collector__metrics__v1__export_metrics_service_response__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCMethodDescriptor opentelemetry__proto__collector__metrics__v1__metrics_service__method_descriptors[1] =
+{
+ { "Export", &opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__descriptor, &opentelemetry__proto__collector__metrics__v1__export_metrics_service_response__descriptor },
+};
+const unsigned opentelemetry__proto__collector__metrics__v1__metrics_service__method_indices_by_name[] = {
+ 0 /* Export */
+};
+const ProtobufCServiceDescriptor opentelemetry__proto__collector__metrics__v1__metrics_service__descriptor =
+{
+ PROTOBUF_C__SERVICE_DESCRIPTOR_MAGIC,
+ "opentelemetry.proto.collector.metrics.v1.MetricsService",
+ "MetricsService",
+ "Opentelemetry__Proto__Collector__Metrics__V1__MetricsService",
+ "opentelemetry.proto.collector.metrics.v1",
+ 1,
+ opentelemetry__proto__collector__metrics__v1__metrics_service__method_descriptors,
+ opentelemetry__proto__collector__metrics__v1__metrics_service__method_indices_by_name
+};
+void opentelemetry__proto__collector__metrics__v1__metrics_service__export(ProtobufCService *service,
+ const Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceRequest *input,
+ Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceResponse_Closure closure,
+ void *closure_data)
+{
+ assert(service->descriptor == &opentelemetry__proto__collector__metrics__v1__metrics_service__descriptor);
+ service->invoke(service, 0, (const ProtobufCMessage *) input, (ProtobufCClosure) closure, closure_data);
+}
+void opentelemetry__proto__collector__metrics__v1__metrics_service__init (Opentelemetry__Proto__Collector__Metrics__V1__MetricsService_Service *service,
+ Opentelemetry__Proto__Collector__Metrics__V1__MetricsService_ServiceDestroy destroy)
+{
+ protobuf_c_service_generated_init (&service->base,
+ &opentelemetry__proto__collector__metrics__v1__metrics_service__descriptor,
+ (ProtobufCServiceDestroy) destroy);
+}
diff --git a/src/fluent-bit/lib/cmetrics/src/external/opentelemetry_resource.pb-c.c b/src/fluent-bit/lib/cmetrics/src/external/opentelemetry_resource.pb-c.c
new file mode 100644
index 000000000..2efd350ab
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/external/opentelemetry_resource.pb-c.c
@@ -0,0 +1,105 @@
+/* Generated by the protocol buffer compiler. DO NOT EDIT! */
+/* Generated from: resource.proto */
+
+/* Do not generate deprecated warnings for self */
+#ifndef PROTOBUF_C__NO_DEPRECATED
+#define PROTOBUF_C__NO_DEPRECATED
+#endif
+
+#include <opentelemetry/resource.pb-c.h>
+void opentelemetry__proto__resource__v1__resource__init
+ (Opentelemetry__Proto__Resource__V1__Resource *message)
+{
+ static const Opentelemetry__Proto__Resource__V1__Resource init_value = OPENTELEMETRY__PROTO__RESOURCE__V1__RESOURCE__INIT;
+ *message = init_value;
+}
+size_t opentelemetry__proto__resource__v1__resource__get_packed_size
+ (const Opentelemetry__Proto__Resource__V1__Resource *message)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__resource__v1__resource__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t opentelemetry__proto__resource__v1__resource__pack
+ (const Opentelemetry__Proto__Resource__V1__Resource *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__resource__v1__resource__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t opentelemetry__proto__resource__v1__resource__pack_to_buffer
+ (const Opentelemetry__Proto__Resource__V1__Resource *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &opentelemetry__proto__resource__v1__resource__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Opentelemetry__Proto__Resource__V1__Resource *
+ opentelemetry__proto__resource__v1__resource__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Opentelemetry__Proto__Resource__V1__Resource *)
+ protobuf_c_message_unpack (&opentelemetry__proto__resource__v1__resource__descriptor,
+ allocator, len, data);
+}
+void opentelemetry__proto__resource__v1__resource__free_unpacked
+ (Opentelemetry__Proto__Resource__V1__Resource *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &opentelemetry__proto__resource__v1__resource__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+static const ProtobufCFieldDescriptor opentelemetry__proto__resource__v1__resource__field_descriptors[2] =
+{
+ {
+ "attributes",
+ 1,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Opentelemetry__Proto__Resource__V1__Resource, n_attributes),
+ offsetof(Opentelemetry__Proto__Resource__V1__Resource, attributes),
+ &opentelemetry__proto__common__v1__key_value__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "dropped_attributes_count",
+ 2,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_UINT32,
+ 0, /* quantifier_offset */
+ offsetof(Opentelemetry__Proto__Resource__V1__Resource, dropped_attributes_count),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned opentelemetry__proto__resource__v1__resource__field_indices_by_name[] = {
+ 0, /* field[0] = attributes */
+ 1, /* field[1] = dropped_attributes_count */
+};
+static const ProtobufCIntRange opentelemetry__proto__resource__v1__resource__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor opentelemetry__proto__resource__v1__resource__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "opentelemetry.proto.resource.v1.Resource",
+ "Resource",
+ "Opentelemetry__Proto__Resource__V1__Resource",
+ "opentelemetry.proto.resource.v1",
+ sizeof(Opentelemetry__Proto__Resource__V1__Resource),
+ 2,
+ opentelemetry__proto__resource__v1__resource__field_descriptors,
+ opentelemetry__proto__resource__v1__resource__field_indices_by_name,
+ 1, opentelemetry__proto__resource__v1__resource__number_ranges,
+ (ProtobufCMessageInit) opentelemetry__proto__resource__v1__resource__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
diff --git a/src/fluent-bit/lib/cmetrics/src/external/protobuf-c.c b/src/fluent-bit/lib/cmetrics/src/external/protobuf-c.c
new file mode 100644
index 000000000..4d1028cd5
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/external/protobuf-c.c
@@ -0,0 +1,3667 @@
+/*
+ * Copyright (c) 2008-2015, Dave Benson and the protobuf-c authors.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+/*! \file
+ * Support library for `protoc-c` generated code.
+ *
+ * This file implements the public API used by the code generated
+ * by `protoc-c`.
+ *
+ * \authors Dave Benson and the protobuf-c authors
+ *
+ * \copyright 2008-2014. Licensed under the terms of the [BSD-2-Clause] license.
+ */
+
+/**
+ * \todo 64-BIT OPTIMIZATION: certain implementations use 32-bit math
+ * even on 64-bit platforms (uint64_size, uint64_pack, parse_uint64).
+ *
+ * \todo Use size_t consistently.
+ */
+
+#include <stdlib.h> /* for malloc, free */
+#include <string.h> /* for strcmp, strlen, memcpy, memmove, memset */
+
+#include <prometheus_remote_write/protobuf-c.h>
+
+#define TRUE 1
+#define FALSE 0
+
+#define PROTOBUF_C__ASSERT_NOT_REACHED() assert(0)
+
+/* Workaround for Microsoft compilers. */
+#ifdef _MSC_VER
+# define inline __inline
+#endif
+
+/**
+ * \defgroup internal Internal functions and macros
+ *
+ * These are not exported by the library but are useful to developers working
+ * on `libprotobuf-c` itself.
+ */
+
+/**
+ * \defgroup macros Utility macros for manipulating structures
+ *
+ * Macros and constants used to manipulate the base "classes" generated by
+ * `protobuf-c`. They also define limits and check correctness.
+ *
+ * \ingroup internal
+ * @{
+ */
+
+/** The maximum length of a 64-bit integer in varint encoding. */
+#define MAX_UINT64_ENCODED_SIZE 10
+
+#ifndef PROTOBUF_C_UNPACK_ERROR
+# define PROTOBUF_C_UNPACK_ERROR(...)
+#endif
+
+#if !defined(_WIN32) || !defined(PROTOBUF_C_USE_SHARED_LIB)
+const char protobuf_c_empty_string[] = "";
+#endif
+
+/**
+ * Internal `ProtobufCMessage` manipulation macro.
+ *
+ * Base macro for manipulating a `ProtobufCMessage`. Used by STRUCT_MEMBER() and
+ * STRUCT_MEMBER_PTR().
+ */
+#define STRUCT_MEMBER_P(struct_p, struct_offset) \
+ ((void *) ((uint8_t *) (struct_p) + (struct_offset)))
+
+/**
+ * Return field in a `ProtobufCMessage` based on offset.
+ *
+ * Take a pointer to a `ProtobufCMessage` and find the field at the offset.
+ * Cast it to the passed type.
+ */
+#define STRUCT_MEMBER(member_type, struct_p, struct_offset) \
+ (*(member_type *) STRUCT_MEMBER_P((struct_p), (struct_offset)))
+
+/**
+ * Return field in a `ProtobufCMessage` based on offset.
+ *
+ * Take a pointer to a `ProtobufCMessage` and find the field at the offset. Cast
+ * it to a pointer to the passed type.
+ */
+#define STRUCT_MEMBER_PTR(member_type, struct_p, struct_offset) \
+ ((member_type *) STRUCT_MEMBER_P((struct_p), (struct_offset)))
+
+/* Assertions for magic numbers. */
+
+#define ASSERT_IS_ENUM_DESCRIPTOR(desc) \
+ assert((desc)->magic == PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC)
+
+#define ASSERT_IS_MESSAGE_DESCRIPTOR(desc) \
+ assert((desc)->magic == PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC)
+
+#define ASSERT_IS_MESSAGE(message) \
+ ASSERT_IS_MESSAGE_DESCRIPTOR((message)->descriptor)
+
+#define ASSERT_IS_SERVICE_DESCRIPTOR(desc) \
+ assert((desc)->magic == PROTOBUF_C__SERVICE_DESCRIPTOR_MAGIC)
+
+/**@}*/
+
+/* --- version --- */
+
+const char *
+protobuf_c_version(void)
+{
+ return PROTOBUF_C_VERSION;
+}
+
+uint32_t
+protobuf_c_version_number(void)
+{
+ return PROTOBUF_C_VERSION_NUMBER;
+}
+
+/* --- allocator --- */
+
+static void *
+system_alloc(void *allocator_data, size_t size)
+{
+ (void)allocator_data;
+ return malloc(size);
+}
+
+static void
+system_free(void *allocator_data, void *data)
+{
+ (void)allocator_data;
+ free(data);
+}
+
+static inline void *
+do_alloc(ProtobufCAllocator *allocator, size_t size)
+{
+ return allocator->alloc(allocator->allocator_data, size);
+}
+
+static inline void
+do_free(ProtobufCAllocator *allocator, void *data)
+{
+ if (data != NULL)
+ allocator->free(allocator->allocator_data, data);
+}
+
+/*
+ * This allocator uses the system's malloc() and free(). It is the default
+ * allocator used if NULL is passed as the ProtobufCAllocator to an exported
+ * function.
+ */
+static ProtobufCAllocator protobuf_c__allocator = {
+ .alloc = &system_alloc,
+ .free = &system_free,
+ .allocator_data = NULL,
+};
+
+/* === buffer-simple === */
+
+void
+protobuf_c_buffer_simple_append(ProtobufCBuffer *buffer,
+ size_t len, const uint8_t *data)
+{
+ ProtobufCBufferSimple *simp = (ProtobufCBufferSimple *) buffer;
+ size_t new_len = simp->len + len;
+
+ if (new_len > simp->alloced) {
+ ProtobufCAllocator *allocator = simp->allocator;
+ size_t new_alloced = simp->alloced * 2;
+ uint8_t *new_data;
+
+ if (allocator == NULL)
+ allocator = &protobuf_c__allocator;
+ while (new_alloced < new_len)
+ new_alloced += new_alloced;
+ new_data = do_alloc(allocator, new_alloced);
+ if (!new_data)
+ return;
+ memcpy(new_data, simp->data, simp->len);
+ if (simp->must_free_data)
+ do_free(allocator, simp->data);
+ else
+ simp->must_free_data = TRUE;
+ simp->data = new_data;
+ simp->alloced = new_alloced;
+ }
+ memcpy(simp->data + simp->len, data, len);
+ simp->len = new_len;
+}
+
+/**
+ * \defgroup packedsz protobuf_c_message_get_packed_size() implementation
+ *
+ * Routines mainly used by protobuf_c_message_get_packed_size().
+ *
+ * \ingroup internal
+ * @{
+ */
+
+/**
+ * Return the number of bytes required to store the tag for the field. Includes
+ * 3 bits for the wire-type, and a single bit that denotes the end-of-tag.
+ *
+ * \param number
+ * Field tag to encode.
+ * \return
+ * Number of bytes required.
+ */
+static inline size_t
+get_tag_size(uint32_t number)
+{
+ if (number < (1UL << 4)) {
+ return 1;
+ } else if (number < (1UL << 11)) {
+ return 2;
+ } else if (number < (1UL << 18)) {
+ return 3;
+ } else if (number < (1UL << 25)) {
+ return 4;
+ } else {
+ return 5;
+ }
+}
+
+/**
+ * Return the number of bytes required to store a variable-length unsigned
+ * 32-bit integer in base-128 varint encoding.
+ *
+ * \param v
+ * Value to encode.
+ * \return
+ * Number of bytes required.
+ */
+static inline size_t
+uint32_size(uint32_t v)
+{
+ if (v < (1UL << 7)) {
+ return 1;
+ } else if (v < (1UL << 14)) {
+ return 2;
+ } else if (v < (1UL << 21)) {
+ return 3;
+ } else if (v < (1UL << 28)) {
+ return 4;
+ } else {
+ return 5;
+ }
+}
+
+/**
+ * Return the number of bytes required to store a variable-length signed 32-bit
+ * integer in base-128 varint encoding.
+ *
+ * \param v
+ * Value to encode.
+ * \return
+ * Number of bytes required.
+ */
+static inline size_t
+int32_size(int32_t v)
+{
+ if (v < 0) {
+ return 10;
+ } else if (v < (1L << 7)) {
+ return 1;
+ } else if (v < (1L << 14)) {
+ return 2;
+ } else if (v < (1L << 21)) {
+ return 3;
+ } else if (v < (1L << 28)) {
+ return 4;
+ } else {
+ return 5;
+ }
+}
+
+/**
+ * Return the ZigZag-encoded 32-bit unsigned integer form of a 32-bit signed
+ * integer.
+ *
+ * \param v
+ * Value to encode.
+ * \return
+ * ZigZag encoded integer.
+ */
+static inline uint32_t
+zigzag32(int32_t v)
+{
+ // Note: the right-shift must be arithmetic
+ // Note: left shift must be unsigned because of overflow
+ return ((uint32_t)(v) << 1) ^ (uint32_t)(v >> 31);
+}
+
+/**
+ * Return the number of bytes required to store a signed 32-bit integer,
+ * converted to an unsigned 32-bit integer with ZigZag encoding, using base-128
+ * varint encoding.
+ *
+ * \param v
+ * Value to encode.
+ * \return
+ * Number of bytes required.
+ */
+static inline size_t
+sint32_size(int32_t v)
+{
+ return uint32_size(zigzag32(v));
+}
+
+/**
+ * Return the number of bytes required to store a 64-bit unsigned integer in
+ * base-128 varint encoding.
+ *
+ * \param v
+ * Value to encode.
+ * \return
+ * Number of bytes required.
+ */
+static inline size_t
+uint64_size(uint64_t v)
+{
+ uint32_t upper_v = (uint32_t) (v >> 32);
+
+ if (upper_v == 0) {
+ return uint32_size((uint32_t) v);
+ } else if (upper_v < (1UL << 3)) {
+ return 5;
+ } else if (upper_v < (1UL << 10)) {
+ return 6;
+ } else if (upper_v < (1UL << 17)) {
+ return 7;
+ } else if (upper_v < (1UL << 24)) {
+ return 8;
+ } else if (upper_v < (1UL << 31)) {
+ return 9;
+ } else {
+ return 10;
+ }
+}
+
+/**
+ * Return the ZigZag-encoded 64-bit unsigned integer form of a 64-bit signed
+ * integer.
+ *
+ * \param v
+ * Value to encode.
+ * \return
+ * ZigZag encoded integer.
+ */
+static inline uint64_t
+zigzag64(int64_t v)
+{
+ // Note: the right-shift must be arithmetic
+ // Note: left shift must be unsigned because of overflow
+ return ((uint64_t)(v) << 1) ^ (uint64_t)(v >> 63);
+}
+
+/**
+ * Return the number of bytes required to store a signed 64-bit integer,
+ * converted to an unsigned 64-bit integer with ZigZag encoding, using base-128
+ * varint encoding.
+ *
+ * \param v
+ * Value to encode.
+ * \return
+ * Number of bytes required.
+ */
+static inline size_t
+sint64_size(int64_t v)
+{
+ return uint64_size(zigzag64(v));
+}
+
+/**
+ * Calculate the serialized size of a single required message field, including
+ * the space needed by the preceding tag.
+ *
+ * \param field
+ * Field descriptor for member.
+ * \param member
+ * Field to encode.
+ * \return
+ * Number of bytes required.
+ */
+static size_t
+required_field_get_packed_size(const ProtobufCFieldDescriptor *field,
+ const void *member)
+{
+ size_t rv = get_tag_size(field->id);
+
+ switch (field->type) {
+ case PROTOBUF_C_TYPE_SINT32:
+ return rv + sint32_size(*(const int32_t *) member);
+ case PROTOBUF_C_TYPE_ENUM:
+ case PROTOBUF_C_TYPE_INT32:
+ return rv + int32_size(*(const int32_t *) member);
+ case PROTOBUF_C_TYPE_UINT32:
+ return rv + uint32_size(*(const uint32_t *) member);
+ case PROTOBUF_C_TYPE_SINT64:
+ return rv + sint64_size(*(const int64_t *) member);
+ case PROTOBUF_C_TYPE_INT64:
+ case PROTOBUF_C_TYPE_UINT64:
+ return rv + uint64_size(*(const uint64_t *) member);
+ case PROTOBUF_C_TYPE_SFIXED32:
+ case PROTOBUF_C_TYPE_FIXED32:
+ return rv + 4;
+ case PROTOBUF_C_TYPE_SFIXED64:
+ case PROTOBUF_C_TYPE_FIXED64:
+ return rv + 8;
+ case PROTOBUF_C_TYPE_BOOL:
+ return rv + 1;
+ case PROTOBUF_C_TYPE_FLOAT:
+ return rv + 4;
+ case PROTOBUF_C_TYPE_DOUBLE:
+ return rv + 8;
+ case PROTOBUF_C_TYPE_STRING: {
+ const char *str = *(char * const *) member;
+ size_t len = str ? strlen(str) : 0;
+ return rv + uint32_size(len) + len;
+ }
+ case PROTOBUF_C_TYPE_BYTES: {
+ size_t len = ((const ProtobufCBinaryData *) member)->len;
+ return rv + uint32_size(len) + len;
+ }
+ case PROTOBUF_C_TYPE_MESSAGE: {
+ const ProtobufCMessage *msg = *(ProtobufCMessage * const *) member;
+ size_t subrv = msg ? protobuf_c_message_get_packed_size(msg) : 0;
+ return rv + uint32_size(subrv) + subrv;
+ }
+ }
+ PROTOBUF_C__ASSERT_NOT_REACHED();
+ return 0;
+}
+
+/**
+ * Calculate the serialized size of a single oneof message field, including
+ * the space needed by the preceding tag. Returns 0 if the oneof field isn't
+ * selected or is not set.
+ *
+ * \param field
+ * Field descriptor for member.
+ * \param oneof_case
+ * Enum value that selects the field in the oneof.
+ * \param member
+ * Field to encode.
+ * \return
+ * Number of bytes required.
+ */
+static size_t
+oneof_field_get_packed_size(const ProtobufCFieldDescriptor *field,
+ uint32_t oneof_case,
+ const void *member)
+{
+ if (oneof_case != field->id) {
+ return 0;
+ }
+ if (field->type == PROTOBUF_C_TYPE_MESSAGE ||
+ field->type == PROTOBUF_C_TYPE_STRING)
+ {
+ const void *ptr = *(const void * const *) member;
+ if (ptr == NULL || ptr == field->default_value)
+ return 0;
+ }
+ return required_field_get_packed_size(field, member);
+}
+
+/**
+ * Calculate the serialized size of a single optional message field, including
+ * the space needed by the preceding tag. Returns 0 if the optional field isn't
+ * set.
+ *
+ * \param field
+ * Field descriptor for member.
+ * \param has
+ * True if the field exists, false if not.
+ * \param member
+ * Field to encode.
+ * \return
+ * Number of bytes required.
+ */
+static size_t
+optional_field_get_packed_size(const ProtobufCFieldDescriptor *field,
+ const protobuf_c_boolean has,
+ const void *member)
+{
+ if (field->type == PROTOBUF_C_TYPE_MESSAGE ||
+ field->type == PROTOBUF_C_TYPE_STRING)
+ {
+ const void *ptr = *(const void * const *) member;
+ if (ptr == NULL || ptr == field->default_value)
+ return 0;
+ } else {
+ if (!has)
+ return 0;
+ }
+ return required_field_get_packed_size(field, member);
+}
+
+static protobuf_c_boolean
+field_is_zeroish(const ProtobufCFieldDescriptor *field,
+ const void *member)
+{
+ protobuf_c_boolean ret = FALSE;
+
+ switch (field->type) {
+ case PROTOBUF_C_TYPE_BOOL:
+ ret = (0 == *(const protobuf_c_boolean *) member);
+ break;
+ case PROTOBUF_C_TYPE_ENUM:
+ case PROTOBUF_C_TYPE_SINT32:
+ case PROTOBUF_C_TYPE_INT32:
+ case PROTOBUF_C_TYPE_UINT32:
+ case PROTOBUF_C_TYPE_SFIXED32:
+ case PROTOBUF_C_TYPE_FIXED32:
+ ret = (0 == *(const uint32_t *) member);
+ break;
+ case PROTOBUF_C_TYPE_SINT64:
+ case PROTOBUF_C_TYPE_INT64:
+ case PROTOBUF_C_TYPE_UINT64:
+ case PROTOBUF_C_TYPE_SFIXED64:
+ case PROTOBUF_C_TYPE_FIXED64:
+ ret = (0 == *(const uint64_t *) member);
+ break;
+ case PROTOBUF_C_TYPE_FLOAT:
+ ret = (0 == *(const float *) member);
+ break;
+ case PROTOBUF_C_TYPE_DOUBLE:
+ ret = (0 == *(const double *) member);
+ break;
+ case PROTOBUF_C_TYPE_STRING:
+ ret = (NULL == *(const char * const *) member) ||
+ ('\0' == **(const char * const *) member);
+ break;
+ case PROTOBUF_C_TYPE_BYTES:
+ case PROTOBUF_C_TYPE_MESSAGE:
+ ret = (NULL == *(const void * const *) member);
+ break;
+ default:
+ ret = TRUE;
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * Calculate the serialized size of a single unlabeled message field, including
+ * the space needed by the preceding tag. Returns 0 if the field isn't set or
+ * if it is set to a "zeroish" value (null pointer or 0 for numerical values).
+ * Unlabeled fields are supported only in proto3.
+ *
+ * \param field
+ * Field descriptor for member.
+ * \param member
+ * Field to encode.
+ * \return
+ * Number of bytes required.
+ */
+static size_t
+unlabeled_field_get_packed_size(const ProtobufCFieldDescriptor *field,
+ const void *member)
+{
+ if (field_is_zeroish(field, member))
+ return 0;
+ return required_field_get_packed_size(field, member);
+}
+
+/**
+ * Calculate the serialized size of repeated message fields, which may consist
+ * of any number of values (including 0). Includes the space needed by the
+ * preceding tags (as needed).
+ *
+ * \param field
+ * Field descriptor for member.
+ * \param count
+ * Number of repeated field members.
+ * \param member
+ * Field to encode.
+ * \return
+ * Number of bytes required.
+ */
+static size_t
+repeated_field_get_packed_size(const ProtobufCFieldDescriptor *field,
+ size_t count, const void *member)
+{
+ size_t header_size;
+ size_t rv = 0;
+ unsigned i;
+ void *array = *(void * const *) member;
+
+ if (count == 0)
+ return 0;
+ header_size = get_tag_size(field->id);
+ if (0 == (field->flags & PROTOBUF_C_FIELD_FLAG_PACKED))
+ header_size *= count;
+
+ switch (field->type) {
+ case PROTOBUF_C_TYPE_SINT32:
+ for (i = 0; i < count; i++)
+ rv += sint32_size(((int32_t *) array)[i]);
+ break;
+ case PROTOBUF_C_TYPE_ENUM:
+ case PROTOBUF_C_TYPE_INT32:
+ for (i = 0; i < count; i++)
+ rv += int32_size(((int32_t *) array)[i]);
+ break;
+ case PROTOBUF_C_TYPE_UINT32:
+ for (i = 0; i < count; i++)
+ rv += uint32_size(((uint32_t *) array)[i]);
+ break;
+ case PROTOBUF_C_TYPE_SINT64:
+ for (i = 0; i < count; i++)
+ rv += sint64_size(((int64_t *) array)[i]);
+ break;
+ case PROTOBUF_C_TYPE_INT64:
+ case PROTOBUF_C_TYPE_UINT64:
+ for (i = 0; i < count; i++)
+ rv += uint64_size(((uint64_t *) array)[i]);
+ break;
+ case PROTOBUF_C_TYPE_SFIXED32:
+ case PROTOBUF_C_TYPE_FIXED32:
+ case PROTOBUF_C_TYPE_FLOAT:
+ rv += 4 * count;
+ break;
+ case PROTOBUF_C_TYPE_SFIXED64:
+ case PROTOBUF_C_TYPE_FIXED64:
+ case PROTOBUF_C_TYPE_DOUBLE:
+ rv += 8 * count;
+ break;
+ case PROTOBUF_C_TYPE_BOOL:
+ rv += count;
+ break;
+ case PROTOBUF_C_TYPE_STRING:
+ for (i = 0; i < count; i++) {
+ size_t len = strlen(((char **) array)[i]);
+ rv += uint32_size(len) + len;
+ }
+ break;
+ case PROTOBUF_C_TYPE_BYTES:
+ for (i = 0; i < count; i++) {
+ size_t len = ((ProtobufCBinaryData *) array)[i].len;
+ rv += uint32_size(len) + len;
+ }
+ break;
+ case PROTOBUF_C_TYPE_MESSAGE:
+ for (i = 0; i < count; i++) {
+ size_t len = protobuf_c_message_get_packed_size(
+ ((ProtobufCMessage **) array)[i]);
+ rv += uint32_size(len) + len;
+ }
+ break;
+ }
+
+ if (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_PACKED))
+ header_size += uint32_size(rv);
+ return header_size + rv;
+}
+
+/**
+ * Calculate the serialized size of an unknown field, i.e. one that is passed
+ * through mostly uninterpreted. This is required for forward compatibility if
+ * new fields are added to the message descriptor.
+ *
+ * \param field
+ * Unknown field type.
+ * \return
+ * Number of bytes required.
+ */
+static inline size_t
+unknown_field_get_packed_size(const ProtobufCMessageUnknownField *field)
+{
+ return get_tag_size(field->tag) + field->len;
+}
+
+/**@}*/
+
+/*
+ * Calculate the serialized size of the message.
+ */
+size_t protobuf_c_message_get_packed_size(const ProtobufCMessage *message)
+{
+ unsigned i;
+ size_t rv = 0;
+
+ ASSERT_IS_MESSAGE(message);
+ for (i = 0; i < message->descriptor->n_fields; i++) {
+ const ProtobufCFieldDescriptor *field =
+ message->descriptor->fields + i;
+ const void *member =
+ ((const char *) message) + field->offset;
+ const void *qmember =
+ ((const char *) message) + field->quantifier_offset;
+
+ if (field->label == PROTOBUF_C_LABEL_REQUIRED) {
+ rv += required_field_get_packed_size(field, member);
+ } else if ((field->label == PROTOBUF_C_LABEL_OPTIONAL ||
+ field->label == PROTOBUF_C_LABEL_NONE) &&
+ (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_ONEOF))) {
+ rv += oneof_field_get_packed_size(
+ field,
+ *(const uint32_t *) qmember,
+ member
+ );
+ } else if (field->label == PROTOBUF_C_LABEL_OPTIONAL) {
+ rv += optional_field_get_packed_size(
+ field,
+ *(protobuf_c_boolean *) qmember,
+ member
+ );
+ } else if (field->label == PROTOBUF_C_LABEL_NONE) {
+ rv += unlabeled_field_get_packed_size(
+ field,
+ member
+ );
+ } else {
+ rv += repeated_field_get_packed_size(
+ field,
+ *(const size_t *) qmember,
+ member
+ );
+ }
+ }
+ for (i = 0; i < message->n_unknown_fields; i++)
+ rv += unknown_field_get_packed_size(&message->unknown_fields[i]);
+ return rv;
+}
+
+/**
+ * \defgroup pack protobuf_c_message_pack() implementation
+ *
+ * Routines mainly used by protobuf_c_message_pack().
+ *
+ * \ingroup internal
+ * @{
+ */
+
+/**
+ * Pack an unsigned 32-bit integer in base-128 varint encoding and return the
+ * number of bytes written, which must be 5 or less.
+ *
+ * \param value
+ * Value to encode.
+ * \param[out] out
+ * Packed value.
+ * \return
+ * Number of bytes written to `out`.
+ */
+static inline size_t
+uint32_pack(uint32_t value, uint8_t *out)
+{
+ unsigned rv = 0;
+
+ if (value >= 0x80) {
+ out[rv++] = value | 0x80;
+ value >>= 7;
+ if (value >= 0x80) {
+ out[rv++] = value | 0x80;
+ value >>= 7;
+ if (value >= 0x80) {
+ out[rv++] = value | 0x80;
+ value >>= 7;
+ if (value >= 0x80) {
+ out[rv++] = value | 0x80;
+ value >>= 7;
+ }
+ }
+ }
+ }
+ /* assert: value<128 */
+ out[rv++] = value;
+ return rv;
+}
+
+/**
+ * Pack a signed 32-bit integer and return the number of bytes written.
+ * Negative numbers are encoded as two's complement 64-bit integers.
+ *
+ * \param value
+ * Value to encode.
+ * \param[out] out
+ * Packed value.
+ * \return
+ * Number of bytes written to `out`.
+ */
+static inline size_t
+int32_pack(int32_t value, uint8_t *out)
+{
+ if (value < 0) {
+ out[0] = value | 0x80;
+ out[1] = (value >> 7) | 0x80;
+ out[2] = (value >> 14) | 0x80;
+ out[3] = (value >> 21) | 0x80;
+ out[4] = (value >> 28) | 0x80;
+ out[5] = out[6] = out[7] = out[8] = 0xff;
+ out[9] = 0x01;
+ return 10;
+ } else {
+ return uint32_pack(value, out);
+ }
+}
+
+/**
+ * Pack a signed 32-bit integer using ZigZag encoding and return the number of
+ * bytes written.
+ *
+ * \param value
+ * Value to encode.
+ * \param[out] out
+ * Packed value.
+ * \return
+ * Number of bytes written to `out`.
+ */
+static inline size_t
+sint32_pack(int32_t value, uint8_t *out)
+{
+ return uint32_pack(zigzag32(value), out);
+}
+
+/**
+ * Pack a 64-bit unsigned integer using base-128 varint encoding and return the
+ * number of bytes written.
+ *
+ * \param value
+ * Value to encode.
+ * \param[out] out
+ * Packed value.
+ * \return
+ * Number of bytes written to `out`.
+ */
+static size_t
+uint64_pack(uint64_t value, uint8_t *out)
+{
+ uint32_t hi = (uint32_t) (value >> 32);
+ uint32_t lo = (uint32_t) value;
+ unsigned rv;
+
+ if (hi == 0)
+ return uint32_pack((uint32_t) lo, out);
+ out[0] = (lo) | 0x80;
+ out[1] = (lo >> 7) | 0x80;
+ out[2] = (lo >> 14) | 0x80;
+ out[3] = (lo >> 21) | 0x80;
+ if (hi < 8) {
+ out[4] = (hi << 4) | (lo >> 28);
+ return 5;
+ } else {
+ out[4] = ((hi & 7) << 4) | (lo >> 28) | 0x80;
+ hi >>= 3;
+ }
+ rv = 5;
+ while (hi >= 128) {
+ out[rv++] = hi | 0x80;
+ hi >>= 7;
+ }
+ out[rv++] = hi;
+ return rv;
+}
+
+/**
+ * Pack a 64-bit signed integer in ZigZag encoding and return the number of
+ * bytes written.
+ *
+ * \param value
+ * Value to encode.
+ * \param[out] out
+ * Packed value.
+ * \return
+ * Number of bytes written to `out`.
+ */
+static inline size_t
+sint64_pack(int64_t value, uint8_t *out)
+{
+ return uint64_pack(zigzag64(value), out);
+}
+
+/**
+ * Pack a 32-bit quantity in little-endian byte order. Used for protobuf wire
+ * types fixed32, sfixed32, float. Similar to "htole32".
+ *
+ * \param value
+ * Value to encode.
+ * \param[out] out
+ * Packed value.
+ * \return
+ * Number of bytes written to `out`.
+ */
+static inline size_t
+fixed32_pack(uint32_t value, void *out)
+{
+#if !defined(WORDS_BIGENDIAN)
+ memcpy(out, &value, 4);
+#else
+ uint8_t *buf = out;
+
+ buf[0] = value;
+ buf[1] = value >> 8;
+ buf[2] = value >> 16;
+ buf[3] = value >> 24;
+#endif
+ return 4;
+}
+
+/**
+ * Pack a 64-bit quantity in little-endian byte order. Used for protobuf wire
+ * types fixed64, sfixed64, double. Similar to "htole64".
+ *
+ * \todo The big-endian impl is really only good for 32-bit machines, a 64-bit
+ * version would be appreciated, plus a way to decide to use 64-bit math where
+ * convenient.
+ *
+ * \param value
+ * Value to encode.
+ * \param[out] out
+ * Packed value.
+ * \return
+ * Number of bytes written to `out`.
+ */
+static inline size_t
+fixed64_pack(uint64_t value, void *out)
+{
+#if !defined(WORDS_BIGENDIAN)
+ memcpy(out, &value, 8);
+#else
+ fixed32_pack(value, out);
+ fixed32_pack(value >> 32, ((char *) out) + 4);
+#endif
+ return 8;
+}
+
+/**
+ * Pack a boolean value as an integer and return the number of bytes written.
+ *
+ * \todo Perhaps on some platforms *out = !!value would be a better impl, b/c
+ * that is idiomatic C++ in some STL implementations.
+ *
+ * \param value
+ * Value to encode.
+ * \param[out] out
+ * Packed value.
+ * \return
+ * Number of bytes written to `out`.
+ */
+static inline size_t
+boolean_pack(protobuf_c_boolean value, uint8_t *out)
+{
+ *out = value ? TRUE : FALSE;
+ return 1;
+}
+
+/**
+ * Pack a NUL-terminated C string and return the number of bytes written. The
+ * output includes a length delimiter.
+ *
+ * The NULL pointer is treated as an empty string. This isn't really necessary,
+ * but it allows people to leave required strings blank. (See Issue #13 in the
+ * bug tracker for a little more explanation).
+ *
+ * \param str
+ * String to encode.
+ * \param[out] out
+ * Packed value.
+ * \return
+ * Number of bytes written to `out`.
+ */
+static inline size_t
+string_pack(const char *str, uint8_t *out)
+{
+ if (str == NULL) {
+ out[0] = 0;
+ return 1;
+ } else {
+ size_t len = strlen(str);
+ size_t rv = uint32_pack(len, out);
+ memcpy(out + rv, str, len);
+ return rv + len;
+ }
+}
+
+/**
+ * Pack a ProtobufCBinaryData and return the number of bytes written. The output
+ * includes a length delimiter.
+ *
+ * \param bd
+ * ProtobufCBinaryData to encode.
+ * \param[out] out
+ * Packed value.
+ * \return
+ * Number of bytes written to `out`.
+ */
+static inline size_t
+binary_data_pack(const ProtobufCBinaryData *bd, uint8_t *out)
+{
+ size_t len = bd->len;
+ size_t rv = uint32_pack(len, out);
+ memcpy(out + rv, bd->data, len);
+ return rv + len;
+}
+
+/**
+ * Pack a ProtobufCMessage and return the number of bytes written. The output
+ * includes a length delimiter.
+ *
+ * \param message
+ * ProtobufCMessage object to pack.
+ * \param[out] out
+ * Packed message.
+ * \return
+ * Number of bytes written to `out`.
+ */
+static inline size_t
+prefixed_message_pack(const ProtobufCMessage *message, uint8_t *out)
+{
+ if (message == NULL) {
+ out[0] = 0;
+ return 1;
+ } else {
+ size_t rv = protobuf_c_message_pack(message, out + 1);
+ uint32_t rv_packed_size = uint32_size(rv);
+ if (rv_packed_size != 1)
+ memmove(out + rv_packed_size, out + 1, rv);
+ return uint32_pack(rv, out) + rv;
+ }
+}
+
+/**
+ * Pack a field tag.
+ *
+ * Wire-type will be added in required_field_pack().
+ *
+ * \todo Just call uint64_pack on 64-bit platforms.
+ *
+ * \param id
+ * Tag value to encode.
+ * \param[out] out
+ * Packed value.
+ * \return
+ * Number of bytes written to `out`.
+ */
+static size_t
+tag_pack(uint32_t id, uint8_t *out)
+{
+ if (id < (1UL << (32 - 3)))
+ return uint32_pack(id << 3, out);
+ else
+ return uint64_pack(((uint64_t) id) << 3, out);
+}
+
+/**
+ * Pack a required field and return the number of bytes written.
+ *
+ * \param field
+ * Field descriptor.
+ * \param member
+ * The field member.
+ * \param[out] out
+ * Packed value.
+ * \return
+ * Number of bytes written to `out`.
+ */
+static size_t
+required_field_pack(const ProtobufCFieldDescriptor *field,
+ const void *member, uint8_t *out)
+{
+ size_t rv = tag_pack(field->id, out);
+
+ switch (field->type) {
+ case PROTOBUF_C_TYPE_SINT32:
+ out[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
+ return rv + sint32_pack(*(const int32_t *) member, out + rv);
+ case PROTOBUF_C_TYPE_ENUM:
+ case PROTOBUF_C_TYPE_INT32:
+ out[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
+ return rv + int32_pack(*(const int32_t *) member, out + rv);
+ case PROTOBUF_C_TYPE_UINT32:
+ out[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
+ return rv + uint32_pack(*(const uint32_t *) member, out + rv);
+ case PROTOBUF_C_TYPE_SINT64:
+ out[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
+ return rv + sint64_pack(*(const int64_t *) member, out + rv);
+ case PROTOBUF_C_TYPE_INT64:
+ case PROTOBUF_C_TYPE_UINT64:
+ out[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
+ return rv + uint64_pack(*(const uint64_t *) member, out + rv);
+ case PROTOBUF_C_TYPE_SFIXED32:
+ case PROTOBUF_C_TYPE_FIXED32:
+ case PROTOBUF_C_TYPE_FLOAT:
+ out[0] |= PROTOBUF_C_WIRE_TYPE_32BIT;
+ return rv + fixed32_pack(*(const uint32_t *) member, out + rv);
+ case PROTOBUF_C_TYPE_SFIXED64:
+ case PROTOBUF_C_TYPE_FIXED64:
+ case PROTOBUF_C_TYPE_DOUBLE:
+ out[0] |= PROTOBUF_C_WIRE_TYPE_64BIT;
+ return rv + fixed64_pack(*(const uint64_t *) member, out + rv);
+ case PROTOBUF_C_TYPE_BOOL:
+ out[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
+ return rv + boolean_pack(*(const protobuf_c_boolean *) member, out + rv);
+ case PROTOBUF_C_TYPE_STRING:
+ out[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED;
+ return rv + string_pack(*(char *const *) member, out + rv);
+ case PROTOBUF_C_TYPE_BYTES:
+ out[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED;
+ return rv + binary_data_pack((const ProtobufCBinaryData *) member, out + rv);
+ case PROTOBUF_C_TYPE_MESSAGE:
+ out[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED;
+ return rv + prefixed_message_pack(*(ProtobufCMessage * const *) member, out + rv);
+ }
+ PROTOBUF_C__ASSERT_NOT_REACHED();
+ return 0;
+}
+
+/**
+ * Pack a oneof field and return the number of bytes written. Only packs the
+ * field that is selected by the case enum.
+ *
+ * \param field
+ * Field descriptor.
+ * \param oneof_case
+ * Enum value that selects the field in the oneof.
+ * \param member
+ * The field member.
+ * \param[out] out
+ * Packed value.
+ * \return
+ * Number of bytes written to `out`.
+ */
+static size_t
+oneof_field_pack(const ProtobufCFieldDescriptor *field,
+ uint32_t oneof_case,
+ const void *member, uint8_t *out)
+{
+ if (oneof_case != field->id) {
+ return 0;
+ }
+ if (field->type == PROTOBUF_C_TYPE_MESSAGE ||
+ field->type == PROTOBUF_C_TYPE_STRING)
+ {
+ const void *ptr = *(const void * const *) member;
+ if (ptr == NULL || ptr == field->default_value)
+ return 0;
+ }
+ return required_field_pack(field, member, out);
+}
+
+/**
+ * Pack an optional field and return the number of bytes written.
+ *
+ * \param field
+ * Field descriptor.
+ * \param has
+ * Whether the field is set.
+ * \param member
+ * The field member.
+ * \param[out] out
+ * Packed value.
+ * \return
+ * Number of bytes written to `out`.
+ */
+static size_t
+optional_field_pack(const ProtobufCFieldDescriptor *field,
+ const protobuf_c_boolean has,
+ const void *member, uint8_t *out)
+{
+ if (field->type == PROTOBUF_C_TYPE_MESSAGE ||
+ field->type == PROTOBUF_C_TYPE_STRING)
+ {
+ const void *ptr = *(const void * const *) member;
+ if (ptr == NULL || ptr == field->default_value)
+ return 0;
+ } else {
+ if (!has)
+ return 0;
+ }
+ return required_field_pack(field, member, out);
+}
+
+/**
+ * Pack an unlabeled field and return the number of bytes written.
+ *
+ * \param field
+ * Field descriptor.
+ * \param member
+ * The field member.
+ * \param[out] out
+ * Packed value.
+ * \return
+ * Number of bytes written to `out`.
+ */
+static size_t
+unlabeled_field_pack(const ProtobufCFieldDescriptor *field,
+ const void *member, uint8_t *out)
+{
+ if (field_is_zeroish(field, member))
+ return 0;
+ return required_field_pack(field, member, out);
+}
+
+/**
+ * Given a field type, return the in-memory size.
+ *
+ * \todo Implement as a table lookup.
+ *
+ * \param type
+ * Field type.
+ * \return
+ * Size of the field.
+ */
+static inline size_t
+sizeof_elt_in_repeated_array(ProtobufCType type)
+{
+ switch (type) {
+ case PROTOBUF_C_TYPE_SINT32:
+ case PROTOBUF_C_TYPE_INT32:
+ case PROTOBUF_C_TYPE_UINT32:
+ case PROTOBUF_C_TYPE_SFIXED32:
+ case PROTOBUF_C_TYPE_FIXED32:
+ case PROTOBUF_C_TYPE_FLOAT:
+ case PROTOBUF_C_TYPE_ENUM:
+ return 4;
+ case PROTOBUF_C_TYPE_SINT64:
+ case PROTOBUF_C_TYPE_INT64:
+ case PROTOBUF_C_TYPE_UINT64:
+ case PROTOBUF_C_TYPE_SFIXED64:
+ case PROTOBUF_C_TYPE_FIXED64:
+ case PROTOBUF_C_TYPE_DOUBLE:
+ return 8;
+ case PROTOBUF_C_TYPE_BOOL:
+ return sizeof(protobuf_c_boolean);
+ case PROTOBUF_C_TYPE_STRING:
+ case PROTOBUF_C_TYPE_MESSAGE:
+ return sizeof(void *);
+ case PROTOBUF_C_TYPE_BYTES:
+ return sizeof(ProtobufCBinaryData);
+ }
+ PROTOBUF_C__ASSERT_NOT_REACHED();
+ return 0;
+}
+
+/**
+ * Pack an array of 32-bit quantities.
+ *
+ * \param[out] out
+ * Destination.
+ * \param[in] in
+ * Source.
+ * \param[in] n
+ * Number of elements in the source array.
+ */
+static void
+copy_to_little_endian_32(void *out, const void *in, const unsigned n)
+{
+#if !defined(WORDS_BIGENDIAN)
+ memcpy(out, in, n * 4);
+#else
+ unsigned i;
+ const uint32_t *ini = in;
+ for (i = 0; i < n; i++)
+ fixed32_pack(ini[i], (uint32_t *) out + i);
+#endif
+}
+
+/**
+ * Pack an array of 64-bit quantities.
+ *
+ * \param[out] out
+ * Destination.
+ * \param[in] in
+ * Source.
+ * \param[in] n
+ * Number of elements in the source array.
+ */
+static void
+copy_to_little_endian_64(void *out, const void *in, const unsigned n)
+{
+#if !defined(WORDS_BIGENDIAN)
+ memcpy(out, in, n * 8);
+#else
+ unsigned i;
+ const uint64_t *ini = in;
+ for (i = 0; i < n; i++)
+ fixed64_pack(ini[i], (uint64_t *) out + i);
+#endif
+}
+
+/**
+ * Get the minimum number of bytes required to pack a field value of a
+ * particular type.
+ *
+ * \param type
+ * Field type.
+ * \return
+ * Number of bytes.
+ */
+static unsigned
+get_type_min_size(ProtobufCType type)
+{
+ if (type == PROTOBUF_C_TYPE_SFIXED32 ||
+ type == PROTOBUF_C_TYPE_FIXED32 ||
+ type == PROTOBUF_C_TYPE_FLOAT)
+ {
+ return 4;
+ }
+ if (type == PROTOBUF_C_TYPE_SFIXED64 ||
+ type == PROTOBUF_C_TYPE_FIXED64 ||
+ type == PROTOBUF_C_TYPE_DOUBLE)
+ {
+ return 8;
+ }
+ return 1;
+}
+
+/**
+ * Packs the elements of a repeated field and returns the serialised field and
+ * its length.
+ *
+ * \param field
+ * Field descriptor.
+ * \param count
+ * Number of elements in the repeated field array.
+ * \param member
+ * Pointer to the elements for this repeated field.
+ * \param[out] out
+ * Serialised representation of the repeated field.
+ * \return
+ * Number of bytes serialised to `out`.
+ */
+static size_t
+repeated_field_pack(const ProtobufCFieldDescriptor *field,
+ size_t count, const void *member, uint8_t *out)
+{
+ void *array = *(void * const *) member;
+ unsigned i;
+
+ if (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_PACKED)) {
+ unsigned header_len;
+ unsigned len_start;
+ unsigned min_length;
+ unsigned payload_len;
+ unsigned length_size_min;
+ unsigned actual_length_size;
+ uint8_t *payload_at;
+
+ if (count == 0)
+ return 0;
+ header_len = tag_pack(field->id, out);
+ out[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED;
+ len_start = header_len;
+ min_length = get_type_min_size(field->type) * count;
+ length_size_min = uint32_size(min_length);
+ header_len += length_size_min;
+ payload_at = out + header_len;
+
+ switch (field->type) {
+ case PROTOBUF_C_TYPE_SFIXED32:
+ case PROTOBUF_C_TYPE_FIXED32:
+ case PROTOBUF_C_TYPE_FLOAT:
+ copy_to_little_endian_32(payload_at, array, count);
+ payload_at += count * 4;
+ break;
+ case PROTOBUF_C_TYPE_SFIXED64:
+ case PROTOBUF_C_TYPE_FIXED64:
+ case PROTOBUF_C_TYPE_DOUBLE:
+ copy_to_little_endian_64(payload_at, array, count);
+ payload_at += count * 8;
+ break;
+ case PROTOBUF_C_TYPE_ENUM:
+ case PROTOBUF_C_TYPE_INT32: {
+ const int32_t *arr = (const int32_t *) array;
+ for (i = 0; i < count; i++)
+ payload_at += int32_pack(arr[i], payload_at);
+ break;
+ }
+ case PROTOBUF_C_TYPE_SINT32: {
+ const int32_t *arr = (const int32_t *) array;
+ for (i = 0; i < count; i++)
+ payload_at += sint32_pack(arr[i], payload_at);
+ break;
+ }
+ case PROTOBUF_C_TYPE_SINT64: {
+ const int64_t *arr = (const int64_t *) array;
+ for (i = 0; i < count; i++)
+ payload_at += sint64_pack(arr[i], payload_at);
+ break;
+ }
+ case PROTOBUF_C_TYPE_UINT32: {
+ const uint32_t *arr = (const uint32_t *) array;
+ for (i = 0; i < count; i++)
+ payload_at += uint32_pack(arr[i], payload_at);
+ break;
+ }
+ case PROTOBUF_C_TYPE_INT64:
+ case PROTOBUF_C_TYPE_UINT64: {
+ const uint64_t *arr = (const uint64_t *) array;
+ for (i = 0; i < count; i++)
+ payload_at += uint64_pack(arr[i], payload_at);
+ break;
+ }
+ case PROTOBUF_C_TYPE_BOOL: {
+ const protobuf_c_boolean *arr = (const protobuf_c_boolean *) array;
+ for (i = 0; i < count; i++)
+ payload_at += boolean_pack(arr[i], payload_at);
+ break;
+ }
+ default:
+ PROTOBUF_C__ASSERT_NOT_REACHED();
+ }
+
+ payload_len = payload_at - (out + header_len);
+ actual_length_size = uint32_size(payload_len);
+ if (length_size_min != actual_length_size) {
+ assert(actual_length_size == length_size_min + 1);
+ memmove(out + header_len + 1, out + header_len,
+ payload_len);
+ header_len++;
+ }
+ uint32_pack(payload_len, out + len_start);
+ return header_len + payload_len;
+ } else {
+ /* not "packed" cased */
+ /* CONSIDER: optimize this case a bit (by putting the loop inside the switch) */
+ size_t rv = 0;
+ unsigned siz = sizeof_elt_in_repeated_array(field->type);
+
+ for (i = 0; i < count; i++) {
+ rv += required_field_pack(field, array, out + rv);
+ array = (char *)array + siz;
+ }
+ return rv;
+ }
+}
+
+static size_t
+unknown_field_pack(const ProtobufCMessageUnknownField *field, uint8_t *out)
+{
+ size_t rv = tag_pack(field->tag, out);
+ out[0] |= field->wire_type;
+ memcpy(out + rv, field->data, field->len);
+ return rv + field->len;
+}
+
+/**@}*/
+
+size_t
+protobuf_c_message_pack(const ProtobufCMessage *message, uint8_t *out)
+{
+ unsigned i;
+ size_t rv = 0;
+
+ ASSERT_IS_MESSAGE(message);
+ for (i = 0; i < message->descriptor->n_fields; i++) {
+ const ProtobufCFieldDescriptor *field =
+ message->descriptor->fields + i;
+ const void *member = ((const char *) message) + field->offset;
+
+ /*
+ * It doesn't hurt to compute qmember (a pointer to the
+ * quantifier field of the structure), but the pointer is only
+ * valid if the field is:
+ * - a repeated field, or
+ * - a field that is part of a oneof
+ * - an optional field that isn't a pointer type
+ * (Meaning: not a message or a string).
+ */
+ const void *qmember =
+ ((const char *) message) + field->quantifier_offset;
+
+ if (field->label == PROTOBUF_C_LABEL_REQUIRED) {
+ rv += required_field_pack(field, member, out + rv);
+ } else if ((field->label == PROTOBUF_C_LABEL_OPTIONAL ||
+ field->label == PROTOBUF_C_LABEL_NONE) &&
+ (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_ONEOF))) {
+ rv += oneof_field_pack(
+ field,
+ *(const uint32_t *) qmember,
+ member,
+ out + rv
+ );
+ } else if (field->label == PROTOBUF_C_LABEL_OPTIONAL) {
+ rv += optional_field_pack(
+ field,
+ *(const protobuf_c_boolean *) qmember,
+ member,
+ out + rv
+ );
+ } else if (field->label == PROTOBUF_C_LABEL_NONE) {
+ rv += unlabeled_field_pack(field, member, out + rv);
+ } else {
+ rv += repeated_field_pack(field, *(const size_t *) qmember,
+ member, out + rv);
+ }
+ }
+ for (i = 0; i < message->n_unknown_fields; i++)
+ rv += unknown_field_pack(&message->unknown_fields[i], out + rv);
+ return rv;
+}
+
+/**
+ * \defgroup packbuf protobuf_c_message_pack_to_buffer() implementation
+ *
+ * Routines mainly used by protobuf_c_message_pack_to_buffer().
+ *
+ * \ingroup internal
+ * @{
+ */
+
+/**
+ * Pack a required field to a virtual buffer.
+ *
+ * \param field
+ * Field descriptor.
+ * \param member
+ * The element to be packed.
+ * \param[out] buffer
+ * Virtual buffer to append data to.
+ * \return
+ * Number of bytes packed.
+ */
+static size_t
+required_field_pack_to_buffer(const ProtobufCFieldDescriptor *field,
+ const void *member, ProtobufCBuffer *buffer)
+{
+ size_t rv;
+ uint8_t scratch[MAX_UINT64_ENCODED_SIZE * 2];
+
+ rv = tag_pack(field->id, scratch);
+ switch (field->type) {
+ case PROTOBUF_C_TYPE_SINT32:
+ scratch[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
+ rv += sint32_pack(*(const int32_t *) member, scratch + rv);
+ buffer->append(buffer, rv, scratch);
+ break;
+ case PROTOBUF_C_TYPE_ENUM:
+ case PROTOBUF_C_TYPE_INT32:
+ scratch[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
+ rv += int32_pack(*(const int32_t *) member, scratch + rv);
+ buffer->append(buffer, rv, scratch);
+ break;
+ case PROTOBUF_C_TYPE_UINT32:
+ scratch[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
+ rv += uint32_pack(*(const uint32_t *) member, scratch + rv);
+ buffer->append(buffer, rv, scratch);
+ break;
+ case PROTOBUF_C_TYPE_SINT64:
+ scratch[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
+ rv += sint64_pack(*(const int64_t *) member, scratch + rv);
+ buffer->append(buffer, rv, scratch);
+ break;
+ case PROTOBUF_C_TYPE_INT64:
+ case PROTOBUF_C_TYPE_UINT64:
+ scratch[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
+ rv += uint64_pack(*(const uint64_t *) member, scratch + rv);
+ buffer->append(buffer, rv, scratch);
+ break;
+ case PROTOBUF_C_TYPE_SFIXED32:
+ case PROTOBUF_C_TYPE_FIXED32:
+ case PROTOBUF_C_TYPE_FLOAT:
+ scratch[0] |= PROTOBUF_C_WIRE_TYPE_32BIT;
+ rv += fixed32_pack(*(const uint32_t *) member, scratch + rv);
+ buffer->append(buffer, rv, scratch);
+ break;
+ case PROTOBUF_C_TYPE_SFIXED64:
+ case PROTOBUF_C_TYPE_FIXED64:
+ case PROTOBUF_C_TYPE_DOUBLE:
+ scratch[0] |= PROTOBUF_C_WIRE_TYPE_64BIT;
+ rv += fixed64_pack(*(const uint64_t *) member, scratch + rv);
+ buffer->append(buffer, rv, scratch);
+ break;
+ case PROTOBUF_C_TYPE_BOOL:
+ scratch[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
+ rv += boolean_pack(*(const protobuf_c_boolean *) member, scratch + rv);
+ buffer->append(buffer, rv, scratch);
+ break;
+ case PROTOBUF_C_TYPE_STRING: {
+ const char *str = *(char *const *) member;
+ size_t sublen = str ? strlen(str) : 0;
+
+ scratch[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED;
+ rv += uint32_pack(sublen, scratch + rv);
+ buffer->append(buffer, rv, scratch);
+ buffer->append(buffer, sublen, (const uint8_t *) str);
+ rv += sublen;
+ break;
+ }
+ case PROTOBUF_C_TYPE_BYTES: {
+ const ProtobufCBinaryData *bd = ((const ProtobufCBinaryData *) member);
+ size_t sublen = bd->len;
+
+ scratch[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED;
+ rv += uint32_pack(sublen, scratch + rv);
+ buffer->append(buffer, rv, scratch);
+ buffer->append(buffer, sublen, bd->data);
+ rv += sublen;
+ break;
+ }
+ case PROTOBUF_C_TYPE_MESSAGE: {
+ const ProtobufCMessage *msg = *(ProtobufCMessage * const *) member;
+
+ scratch[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED;
+ if (msg == NULL) {
+ rv += uint32_pack(0, scratch + rv);
+ buffer->append(buffer, rv, scratch);
+ } else {
+ size_t sublen = protobuf_c_message_get_packed_size(msg);
+ rv += uint32_pack(sublen, scratch + rv);
+ buffer->append(buffer, rv, scratch);
+ protobuf_c_message_pack_to_buffer(msg, buffer);
+ rv += sublen;
+ }
+ break;
+ }
+ default:
+ PROTOBUF_C__ASSERT_NOT_REACHED();
+ }
+ return rv;
+}
+
+/**
+ * Pack a oneof field to a buffer. Only packs the field that is selected by the case enum.
+ *
+ * \param field
+ * Field descriptor.
+ * \param oneof_case
+ * Enum value that selects the field in the oneof.
+ * \param member
+ * The element to be packed.
+ * \param[out] buffer
+ * Virtual buffer to append data to.
+ * \return
+ * Number of bytes serialised to `buffer`.
+ */
+static size_t
+oneof_field_pack_to_buffer(const ProtobufCFieldDescriptor *field,
+ uint32_t oneof_case,
+ const void *member, ProtobufCBuffer *buffer)
+{
+ if (oneof_case != field->id) {
+ return 0;
+ }
+ if (field->type == PROTOBUF_C_TYPE_MESSAGE ||
+ field->type == PROTOBUF_C_TYPE_STRING)
+ {
+ const void *ptr = *(const void *const *) member;
+ if (ptr == NULL || ptr == field->default_value)
+ return 0;
+ }
+ return required_field_pack_to_buffer(field, member, buffer);
+}
+
+/**
+ * Pack an optional field to a buffer.
+ *
+ * \param field
+ * Field descriptor.
+ * \param has
+ * Whether the field is set.
+ * \param member
+ * The element to be packed.
+ * \param[out] buffer
+ * Virtual buffer to append data to.
+ * \return
+ * Number of bytes serialised to `buffer`.
+ */
+static size_t
+optional_field_pack_to_buffer(const ProtobufCFieldDescriptor *field,
+ const protobuf_c_boolean has,
+ const void *member, ProtobufCBuffer *buffer)
+{
+ if (field->type == PROTOBUF_C_TYPE_MESSAGE ||
+ field->type == PROTOBUF_C_TYPE_STRING)
+ {
+ const void *ptr = *(const void *const *) member;
+ if (ptr == NULL || ptr == field->default_value)
+ return 0;
+ } else {
+ if (!has)
+ return 0;
+ }
+ return required_field_pack_to_buffer(field, member, buffer);
+}
+
+/**
+ * Pack an unlabeled field to a buffer.
+ *
+ * \param field
+ * Field descriptor.
+ * \param member
+ * The element to be packed.
+ * \param[out] buffer
+ * Virtual buffer to append data to.
+ * \return
+ * Number of bytes serialised to `buffer`.
+ */
+static size_t
+unlabeled_field_pack_to_buffer(const ProtobufCFieldDescriptor *field,
+ const void *member, ProtobufCBuffer *buffer)
+{
+ if (field_is_zeroish(field, member))
+ return 0;
+ return required_field_pack_to_buffer(field, member, buffer);
+}
+
+/**
+ * Get the packed size of an array of same field type.
+ *
+ * \param field
+ * Field descriptor.
+ * \param count
+ * Number of elements of this type.
+ * \param array
+ * The elements to get the size of.
+ * \return
+ * Number of bytes required.
+ */
+static size_t
+get_packed_payload_length(const ProtobufCFieldDescriptor *field,
+ unsigned count, const void *array)
+{
+ unsigned rv = 0;
+ unsigned i;
+
+ switch (field->type) {
+ case PROTOBUF_C_TYPE_SFIXED32:
+ case PROTOBUF_C_TYPE_FIXED32:
+ case PROTOBUF_C_TYPE_FLOAT:
+ return count * 4;
+ case PROTOBUF_C_TYPE_SFIXED64:
+ case PROTOBUF_C_TYPE_FIXED64:
+ case PROTOBUF_C_TYPE_DOUBLE:
+ return count * 8;
+ case PROTOBUF_C_TYPE_ENUM:
+ case PROTOBUF_C_TYPE_INT32: {
+ const int32_t *arr = (const int32_t *) array;
+ for (i = 0; i < count; i++)
+ rv += int32_size(arr[i]);
+ break;
+ }
+ case PROTOBUF_C_TYPE_SINT32: {
+ const int32_t *arr = (const int32_t *) array;
+ for (i = 0; i < count; i++)
+ rv += sint32_size(arr[i]);
+ break;
+ }
+ case PROTOBUF_C_TYPE_UINT32: {
+ const uint32_t *arr = (const uint32_t *) array;
+ for (i = 0; i < count; i++)
+ rv += uint32_size(arr[i]);
+ break;
+ }
+ case PROTOBUF_C_TYPE_SINT64: {
+ const int64_t *arr = (const int64_t *) array;
+ for (i = 0; i < count; i++)
+ rv += sint64_size(arr[i]);
+ break;
+ }
+ case PROTOBUF_C_TYPE_INT64:
+ case PROTOBUF_C_TYPE_UINT64: {
+ const uint64_t *arr = (const uint64_t *) array;
+ for (i = 0; i < count; i++)
+ rv += uint64_size(arr[i]);
+ break;
+ }
+ case PROTOBUF_C_TYPE_BOOL:
+ return count;
+ default:
+ PROTOBUF_C__ASSERT_NOT_REACHED();
+ }
+ return rv;
+}
+
+/**
+ * Pack an array of same field type to a virtual buffer.
+ *
+ * \param field
+ * Field descriptor.
+ * \param count
+ * Number of elements of this type.
+ * \param array
+ * The elements to get the size of.
+ * \param[out] buffer
+ * Virtual buffer to append data to.
+ * \return
+ * Number of bytes packed.
+ */
+static size_t
+pack_buffer_packed_payload(const ProtobufCFieldDescriptor *field,
+ unsigned count, const void *array,
+ ProtobufCBuffer *buffer)
+{
+ uint8_t scratch[16];
+ size_t rv = 0;
+ unsigned i;
+
+ switch (field->type) {
+ case PROTOBUF_C_TYPE_SFIXED32:
+ case PROTOBUF_C_TYPE_FIXED32:
+ case PROTOBUF_C_TYPE_FLOAT:
+#if !defined(WORDS_BIGENDIAN)
+ rv = count * 4;
+ goto no_packing_needed;
+#else
+ for (i = 0; i < count; i++) {
+ unsigned len = fixed32_pack(((uint32_t *) array)[i], scratch);
+ buffer->append(buffer, len, scratch);
+ rv += len;
+ }
+ break;
+#endif
+ case PROTOBUF_C_TYPE_SFIXED64:
+ case PROTOBUF_C_TYPE_FIXED64:
+ case PROTOBUF_C_TYPE_DOUBLE:
+#if !defined(WORDS_BIGENDIAN)
+ rv = count * 8;
+ goto no_packing_needed;
+#else
+ for (i = 0; i < count; i++) {
+ unsigned len = fixed64_pack(((uint64_t *) array)[i], scratch);
+ buffer->append(buffer, len, scratch);
+ rv += len;
+ }
+ break;
+#endif
+ case PROTOBUF_C_TYPE_ENUM:
+ case PROTOBUF_C_TYPE_INT32:
+ for (i = 0; i < count; i++) {
+ unsigned len = int32_pack(((int32_t *) array)[i], scratch);
+ buffer->append(buffer, len, scratch);
+ rv += len;
+ }
+ break;
+ case PROTOBUF_C_TYPE_SINT32:
+ for (i = 0; i < count; i++) {
+ unsigned len = sint32_pack(((int32_t *) array)[i], scratch);
+ buffer->append(buffer, len, scratch);
+ rv += len;
+ }
+ break;
+ case PROTOBUF_C_TYPE_UINT32:
+ for (i = 0; i < count; i++) {
+ unsigned len = uint32_pack(((uint32_t *) array)[i], scratch);
+ buffer->append(buffer, len, scratch);
+ rv += len;
+ }
+ break;
+ case PROTOBUF_C_TYPE_SINT64:
+ for (i = 0; i < count; i++) {
+ unsigned len = sint64_pack(((int64_t *) array)[i], scratch);
+ buffer->append(buffer, len, scratch);
+ rv += len;
+ }
+ break;
+ case PROTOBUF_C_TYPE_INT64:
+ case PROTOBUF_C_TYPE_UINT64:
+ for (i = 0; i < count; i++) {
+ unsigned len = uint64_pack(((uint64_t *) array)[i], scratch);
+ buffer->append(buffer, len, scratch);
+ rv += len;
+ }
+ break;
+ case PROTOBUF_C_TYPE_BOOL:
+ for (i = 0; i < count; i++) {
+ unsigned len = boolean_pack(((protobuf_c_boolean *) array)[i], scratch);
+ buffer->append(buffer, len, scratch);
+ rv += len;
+ }
+ return count;
+ default:
+ PROTOBUF_C__ASSERT_NOT_REACHED();
+ }
+ return rv;
+
+#if !defined(WORDS_BIGENDIAN)
+no_packing_needed:
+ buffer->append(buffer, rv, array);
+ return rv;
+#endif
+}
+
+static size_t
+repeated_field_pack_to_buffer(const ProtobufCFieldDescriptor *field,
+ unsigned count, const void *member,
+ ProtobufCBuffer *buffer)
+{
+ char *array = *(char * const *) member;
+
+ if (count == 0)
+ return 0;
+ if (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_PACKED)) {
+ uint8_t scratch[MAX_UINT64_ENCODED_SIZE * 2];
+ size_t rv = tag_pack(field->id, scratch);
+ size_t payload_len = get_packed_payload_length(field, count, array);
+ size_t tmp;
+
+ scratch[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED;
+ rv += uint32_pack(payload_len, scratch + rv);
+ buffer->append(buffer, rv, scratch);
+ tmp = pack_buffer_packed_payload(field, count, array, buffer);
+ assert(tmp == payload_len);
+ return rv + payload_len;
+ } else {
+ size_t siz;
+ unsigned i;
+ /* CONSIDER: optimize this case a bit (by putting the loop inside the switch) */
+ unsigned rv = 0;
+
+ siz = sizeof_elt_in_repeated_array(field->type);
+ for (i = 0; i < count; i++) {
+ rv += required_field_pack_to_buffer(field, array, buffer);
+ array += siz;
+ }
+ return rv;
+ }
+}
+
+static size_t
+unknown_field_pack_to_buffer(const ProtobufCMessageUnknownField *field,
+ ProtobufCBuffer *buffer)
+{
+ uint8_t header[MAX_UINT64_ENCODED_SIZE];
+ size_t rv = tag_pack(field->tag, header);
+
+ header[0] |= field->wire_type;
+ buffer->append(buffer, rv, header);
+ buffer->append(buffer, field->len, field->data);
+ return rv + field->len;
+}
+
+/**@}*/
+
+size_t
+protobuf_c_message_pack_to_buffer(const ProtobufCMessage *message,
+ ProtobufCBuffer *buffer)
+{
+ unsigned i;
+ size_t rv = 0;
+
+ ASSERT_IS_MESSAGE(message);
+ for (i = 0; i < message->descriptor->n_fields; i++) {
+ const ProtobufCFieldDescriptor *field =
+ message->descriptor->fields + i;
+ const void *member =
+ ((const char *) message) + field->offset;
+ const void *qmember =
+ ((const char *) message) + field->quantifier_offset;
+
+ if (field->label == PROTOBUF_C_LABEL_REQUIRED) {
+ rv += required_field_pack_to_buffer(field, member, buffer);
+ } else if ((field->label == PROTOBUF_C_LABEL_OPTIONAL ||
+ field->label == PROTOBUF_C_LABEL_NONE) &&
+ (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_ONEOF))) {
+ rv += oneof_field_pack_to_buffer(
+ field,
+ *(const uint32_t *) qmember,
+ member,
+ buffer
+ );
+ } else if (field->label == PROTOBUF_C_LABEL_OPTIONAL) {
+ rv += optional_field_pack_to_buffer(
+ field,
+ *(const protobuf_c_boolean *) qmember,
+ member,
+ buffer
+ );
+ } else if (field->label == PROTOBUF_C_LABEL_NONE) {
+ rv += unlabeled_field_pack_to_buffer(
+ field,
+ member,
+ buffer
+ );
+ } else {
+ rv += repeated_field_pack_to_buffer(
+ field,
+ *(const size_t *) qmember,
+ member,
+ buffer
+ );
+ }
+ }
+ for (i = 0; i < message->n_unknown_fields; i++)
+ rv += unknown_field_pack_to_buffer(&message->unknown_fields[i], buffer);
+
+ return rv;
+}
+
+/**
+ * \defgroup unpack unpacking implementation
+ *
+ * Routines mainly used by the unpacking functions.
+ *
+ * \ingroup internal
+ * @{
+ */
+
+static inline int
+int_range_lookup(unsigned n_ranges, const ProtobufCIntRange *ranges, int value)
+{
+ unsigned n;
+ unsigned start;
+
+ if (n_ranges == 0)
+ return -1;
+ start = 0;
+ n = n_ranges;
+ while (n > 1) {
+ unsigned mid = start + n / 2;
+
+ if (value < ranges[mid].start_value) {
+ n = mid - start;
+ } else if (value >= ranges[mid].start_value +
+ (int) (ranges[mid + 1].orig_index -
+ ranges[mid].orig_index))
+ {
+ unsigned new_start = mid + 1;
+ n = start + n - new_start;
+ start = new_start;
+ } else
+ return (value - ranges[mid].start_value) +
+ ranges[mid].orig_index;
+ }
+ if (n > 0) {
+ unsigned start_orig_index = ranges[start].orig_index;
+ unsigned range_size =
+ ranges[start + 1].orig_index - start_orig_index;
+
+ if (ranges[start].start_value <= value &&
+ value < (int) (ranges[start].start_value + range_size))
+ {
+ return (value - ranges[start].start_value) +
+ start_orig_index;
+ }
+ }
+ return -1;
+}
+
+static size_t
+parse_tag_and_wiretype(size_t len,
+ const uint8_t *data,
+ uint32_t *tag_out,
+ uint8_t *wiretype_out)
+{
+ unsigned max_rv = len > 5 ? 5 : len;
+ uint32_t tag = (data[0] & 0x7f) >> 3;
+ unsigned shift = 4;
+ unsigned rv;
+
+ /* 0 is not a valid tag value */
+ if ((data[0] & 0xf8) == 0) {
+ return 0;
+ }
+
+ *wiretype_out = data[0] & 7;
+ if ((data[0] & 0x80) == 0) {
+ *tag_out = tag;
+ return 1;
+ }
+ for (rv = 1; rv < max_rv; rv++) {
+ if (data[rv] & 0x80) {
+ tag |= (data[rv] & 0x7f) << shift;
+ shift += 7;
+ } else {
+ tag |= data[rv] << shift;
+ *tag_out = tag;
+ return rv + 1;
+ }
+ }
+ return 0; /* error: bad header */
+}
+
+/* sizeof(ScannedMember) must be <= (1UL<<BOUND_SIZEOF_SCANNED_MEMBER_LOG2) */
+#define BOUND_SIZEOF_SCANNED_MEMBER_LOG2 5
+typedef struct ScannedMember ScannedMember;
+/** Field as it's being read. */
+struct ScannedMember {
+ uint32_t tag; /**< Field tag. */
+ uint8_t wire_type; /**< Field type. */
+ uint8_t length_prefix_len; /**< Prefix length. */
+ const ProtobufCFieldDescriptor *field; /**< Field descriptor. */
+ size_t len; /**< Field length. */
+ const uint8_t *data; /**< Pointer to field data. */
+};
+
+static inline size_t
+scan_length_prefixed_data(size_t len, const uint8_t *data,
+ size_t *prefix_len_out)
+{
+ unsigned hdr_max = len < 5 ? len : 5;
+ unsigned hdr_len;
+ size_t val = 0;
+ unsigned i;
+ unsigned shift = 0;
+
+ for (i = 0; i < hdr_max; i++) {
+ val |= ((size_t)data[i] & 0x7f) << shift;
+ shift += 7;
+ if ((data[i] & 0x80) == 0)
+ break;
+ }
+ if (i == hdr_max) {
+ PROTOBUF_C_UNPACK_ERROR("error parsing length for length-prefixed data");
+ return 0;
+ }
+ hdr_len = i + 1;
+ *prefix_len_out = hdr_len;
+ if (val > INT_MAX) {
+ // Protobuf messages should always be less than 2 GiB in size.
+ // We also want to return early here so that hdr_len + val does
+ // not overflow on 32-bit systems.
+ PROTOBUF_C_UNPACK_ERROR("length prefix of %lu is too large",
+ (unsigned long int)val);
+ return 0;
+ }
+ if (hdr_len + val > len) {
+ PROTOBUF_C_UNPACK_ERROR("data too short after length-prefix of %lu",
+ (unsigned long int)val);
+ return 0;
+ }
+ return hdr_len + val;
+}
+
+static size_t
+max_b128_numbers(size_t len, const uint8_t *data)
+{
+ size_t rv = 0;
+ while (len--)
+ if ((*data++ & 0x80) == 0)
+ ++rv;
+ return rv;
+}
+
+/**@}*/
+
+/**
+ * Merge earlier message into a latter message.
+ *
+ * For numeric types and strings, if the same value appears multiple
+ * times, the parser accepts the last value it sees. For embedded
+ * message fields, the parser merges multiple instances of the same
+ * field. That is, all singular scalar fields in the latter instance
+ * replace those in the former, singular embedded messages are merged,
+ * and repeated fields are concatenated.
+ *
+ * The earlier message should be freed after calling this function, as
+ * some of its fields may have been reused and changed to their default
+ * values during the merge.
+ */
+static protobuf_c_boolean
+merge_messages(ProtobufCMessage *earlier_msg,
+ ProtobufCMessage *latter_msg,
+ ProtobufCAllocator *allocator)
+{
+ unsigned i;
+ const ProtobufCFieldDescriptor *fields =
+ latter_msg->descriptor->fields;
+ for (i = 0; i < latter_msg->descriptor->n_fields; i++) {
+ if (fields[i].label == PROTOBUF_C_LABEL_REPEATED) {
+ size_t *n_earlier =
+ STRUCT_MEMBER_PTR(size_t, earlier_msg,
+ fields[i].quantifier_offset);
+ uint8_t **p_earlier =
+ STRUCT_MEMBER_PTR(uint8_t *, earlier_msg,
+ fields[i].offset);
+ size_t *n_latter =
+ STRUCT_MEMBER_PTR(size_t, latter_msg,
+ fields[i].quantifier_offset);
+ uint8_t **p_latter =
+ STRUCT_MEMBER_PTR(uint8_t *, latter_msg,
+ fields[i].offset);
+
+ if (*n_earlier > 0) {
+ if (*n_latter > 0) {
+ /* Concatenate the repeated field */
+ size_t el_size =
+ sizeof_elt_in_repeated_array(fields[i].type);
+ uint8_t *new_field;
+
+ new_field = do_alloc(allocator,
+ (*n_earlier + *n_latter) * el_size);
+ if (!new_field)
+ return FALSE;
+
+ memcpy(new_field, *p_earlier,
+ *n_earlier * el_size);
+ memcpy(new_field +
+ *n_earlier * el_size,
+ *p_latter,
+ *n_latter * el_size);
+
+ do_free(allocator, *p_latter);
+ do_free(allocator, *p_earlier);
+ *p_latter = new_field;
+ *n_latter = *n_earlier + *n_latter;
+ } else {
+ /* Zero copy the repeated field from the earlier message */
+ *n_latter = *n_earlier;
+ *p_latter = *p_earlier;
+ }
+ /* Make sure the field does not get double freed */
+ *n_earlier = 0;
+ *p_earlier = 0;
+ }
+ } else if (fields[i].label == PROTOBUF_C_LABEL_OPTIONAL ||
+ fields[i].label == PROTOBUF_C_LABEL_NONE) {
+ const ProtobufCFieldDescriptor *field;
+ uint32_t *earlier_case_p = STRUCT_MEMBER_PTR(uint32_t,
+ earlier_msg,
+ fields[i].
+ quantifier_offset);
+ uint32_t *latter_case_p = STRUCT_MEMBER_PTR(uint32_t,
+ latter_msg,
+ fields[i].
+ quantifier_offset);
+ protobuf_c_boolean need_to_merge = FALSE;
+ void *earlier_elem;
+ void *latter_elem;
+ const void *def_val;
+
+ if (fields[i].flags & PROTOBUF_C_FIELD_FLAG_ONEOF) {
+ if (*latter_case_p == 0) {
+ /* lookup correct oneof field */
+ int field_index =
+ int_range_lookup(
+ latter_msg->descriptor
+ ->n_field_ranges,
+ latter_msg->descriptor
+ ->field_ranges,
+ *earlier_case_p);
+ if (field_index < 0)
+ return FALSE;
+ field = latter_msg->descriptor->fields +
+ field_index;
+ } else {
+ /* Oneof is present in the latter message, move on */
+ continue;
+ }
+ } else {
+ field = &fields[i];
+ }
+
+ earlier_elem = STRUCT_MEMBER_P(earlier_msg, field->offset);
+ latter_elem = STRUCT_MEMBER_P(latter_msg, field->offset);
+ def_val = field->default_value;
+
+ switch (field->type) {
+ case PROTOBUF_C_TYPE_MESSAGE: {
+ ProtobufCMessage *em = *(ProtobufCMessage **) earlier_elem;
+ ProtobufCMessage *lm = *(ProtobufCMessage **) latter_elem;
+ if (em != NULL) {
+ if (lm != NULL) {
+ if (!merge_messages(em, lm, allocator))
+ return FALSE;
+ /* Already merged */
+ need_to_merge = FALSE;
+ } else {
+ /* Zero copy the message */
+ need_to_merge = TRUE;
+ }
+ }
+ break;
+ }
+ case PROTOBUF_C_TYPE_BYTES: {
+ uint8_t *e_data =
+ ((ProtobufCBinaryData *) earlier_elem)->data;
+ uint8_t *l_data =
+ ((ProtobufCBinaryData *) latter_elem)->data;
+ const ProtobufCBinaryData *d_bd =
+ (ProtobufCBinaryData *) def_val;
+
+ need_to_merge =
+ (e_data != NULL &&
+ (d_bd == NULL ||
+ e_data != d_bd->data)) &&
+ (l_data == NULL ||
+ (d_bd != NULL &&
+ l_data == d_bd->data));
+ break;
+ }
+ case PROTOBUF_C_TYPE_STRING: {
+ char *e_str = *(char **) earlier_elem;
+ char *l_str = *(char **) latter_elem;
+ const char *d_str = def_val;
+
+ need_to_merge = e_str != d_str && l_str == d_str;
+ break;
+ }
+ default: {
+ /* Could be has field or case enum, the logic is
+ * equivalent, since 0 (FALSE) means not set for
+ * oneof */
+ need_to_merge = (*earlier_case_p != 0) &&
+ (*latter_case_p == 0);
+ break;
+ }
+ }
+
+ if (need_to_merge) {
+ size_t el_size =
+ sizeof_elt_in_repeated_array(field->type);
+ memcpy(latter_elem, earlier_elem, el_size);
+ /*
+ * Reset the element from the old message to 0
+ * to make sure earlier message deallocation
+ * doesn't corrupt zero-copied data in the new
+ * message, earlier message will be freed after
+ * this function is called anyway
+ */
+ memset(earlier_elem, 0, el_size);
+
+ if (field->quantifier_offset != 0) {
+ /* Set the has field or the case enum,
+ * if applicable */
+ *latter_case_p = *earlier_case_p;
+ *earlier_case_p = 0;
+ }
+ }
+ }
+ }
+ return TRUE;
+}
+
+/**
+ * Count packed elements.
+ *
+ * Given a raw slab of packed-repeated values, determine the number of
+ * elements. This function detects certain kinds of errors but not
+ * others; the remaining error checking is done by
+ * parse_packed_repeated_member().
+ */
+static protobuf_c_boolean
+count_packed_elements(ProtobufCType type,
+ size_t len, const uint8_t *data, size_t *count_out)
+{
+ switch (type) {
+ case PROTOBUF_C_TYPE_SFIXED32:
+ case PROTOBUF_C_TYPE_FIXED32:
+ case PROTOBUF_C_TYPE_FLOAT:
+ if (len % 4 != 0) {
+ PROTOBUF_C_UNPACK_ERROR("length must be a multiple of 4 for fixed-length 32-bit types");
+ return FALSE;
+ }
+ *count_out = len / 4;
+ return TRUE;
+ case PROTOBUF_C_TYPE_SFIXED64:
+ case PROTOBUF_C_TYPE_FIXED64:
+ case PROTOBUF_C_TYPE_DOUBLE:
+ if (len % 8 != 0) {
+ PROTOBUF_C_UNPACK_ERROR("length must be a multiple of 8 for fixed-length 64-bit types");
+ return FALSE;
+ }
+ *count_out = len / 8;
+ return TRUE;
+ case PROTOBUF_C_TYPE_ENUM:
+ case PROTOBUF_C_TYPE_INT32:
+ case PROTOBUF_C_TYPE_SINT32:
+ case PROTOBUF_C_TYPE_UINT32:
+ case PROTOBUF_C_TYPE_INT64:
+ case PROTOBUF_C_TYPE_SINT64:
+ case PROTOBUF_C_TYPE_UINT64:
+ *count_out = max_b128_numbers(len, data);
+ return TRUE;
+ case PROTOBUF_C_TYPE_BOOL:
+ *count_out = len;
+ return TRUE;
+ case PROTOBUF_C_TYPE_STRING:
+ case PROTOBUF_C_TYPE_BYTES:
+ case PROTOBUF_C_TYPE_MESSAGE:
+ default:
+ PROTOBUF_C_UNPACK_ERROR("bad protobuf-c type %u for packed-repeated", type);
+ return FALSE;
+ }
+}
+
+static inline uint32_t
+parse_uint32(unsigned len, const uint8_t *data)
+{
+ uint32_t rv = data[0] & 0x7f;
+ if (len > 1) {
+ rv |= ((uint32_t) (data[1] & 0x7f) << 7);
+ if (len > 2) {
+ rv |= ((uint32_t) (data[2] & 0x7f) << 14);
+ if (len > 3) {
+ rv |= ((uint32_t) (data[3] & 0x7f) << 21);
+ if (len > 4)
+ rv |= ((uint32_t) (data[4]) << 28);
+ }
+ }
+ }
+ return rv;
+}
+
+static inline uint32_t
+parse_int32(unsigned len, const uint8_t *data)
+{
+ return parse_uint32(len, data);
+}
+
+static inline int32_t
+unzigzag32(uint32_t v)
+{
+ // Note: Using unsigned types prevents undefined behavior
+ return (int32_t)((v >> 1) ^ (~(v & 1) + 1));
+}
+
+static inline uint32_t
+parse_fixed_uint32(const uint8_t *data)
+{
+#if !defined(WORDS_BIGENDIAN)
+ uint32_t t;
+ memcpy(&t, data, 4);
+ return t;
+#else
+ return data[0] |
+ ((uint32_t) (data[1]) << 8) |
+ ((uint32_t) (data[2]) << 16) |
+ ((uint32_t) (data[3]) << 24);
+#endif
+}
+
+static uint64_t
+parse_uint64(unsigned len, const uint8_t *data)
+{
+ unsigned shift, i;
+ uint64_t rv;
+
+ if (len < 5)
+ return parse_uint32(len, data);
+ rv = ((uint64_t) (data[0] & 0x7f)) |
+ ((uint64_t) (data[1] & 0x7f) << 7) |
+ ((uint64_t) (data[2] & 0x7f) << 14) |
+ ((uint64_t) (data[3] & 0x7f) << 21);
+ shift = 28;
+ for (i = 4; i < len; i++) {
+ rv |= (((uint64_t) (data[i] & 0x7f)) << shift);
+ shift += 7;
+ }
+ return rv;
+}
+
+static inline int64_t
+unzigzag64(uint64_t v)
+{
+ // Note: Using unsigned types prevents undefined behavior
+ return (int64_t)((v >> 1) ^ (~(v & 1) + 1));
+}
+
+static inline uint64_t
+parse_fixed_uint64(const uint8_t *data)
+{
+#if !defined(WORDS_BIGENDIAN)
+ uint64_t t;
+ memcpy(&t, data, 8);
+ return t;
+#else
+ return (uint64_t) parse_fixed_uint32(data) |
+ (((uint64_t) parse_fixed_uint32(data + 4)) << 32);
+#endif
+}
+
+static protobuf_c_boolean
+parse_boolean(unsigned len, const uint8_t *data)
+{
+ unsigned i;
+ for (i = 0; i < len; i++)
+ if (data[i] & 0x7f)
+ return TRUE;
+ return FALSE;
+}
+
+static protobuf_c_boolean
+parse_required_member(ScannedMember *scanned_member,
+ void *member,
+ ProtobufCAllocator *allocator,
+ protobuf_c_boolean maybe_clear)
+{
+ unsigned len = scanned_member->len;
+ const uint8_t *data = scanned_member->data;
+ uint8_t wire_type = scanned_member->wire_type;
+
+ switch (scanned_member->field->type) {
+ case PROTOBUF_C_TYPE_ENUM:
+ case PROTOBUF_C_TYPE_INT32:
+ if (wire_type != PROTOBUF_C_WIRE_TYPE_VARINT)
+ return FALSE;
+ *(int32_t *) member = parse_int32(len, data);
+ return TRUE;
+ case PROTOBUF_C_TYPE_UINT32:
+ if (wire_type != PROTOBUF_C_WIRE_TYPE_VARINT)
+ return FALSE;
+ *(uint32_t *) member = parse_uint32(len, data);
+ return TRUE;
+ case PROTOBUF_C_TYPE_SINT32:
+ if (wire_type != PROTOBUF_C_WIRE_TYPE_VARINT)
+ return FALSE;
+ *(int32_t *) member = unzigzag32(parse_uint32(len, data));
+ return TRUE;
+ case PROTOBUF_C_TYPE_SFIXED32:
+ case PROTOBUF_C_TYPE_FIXED32:
+ case PROTOBUF_C_TYPE_FLOAT:
+ if (wire_type != PROTOBUF_C_WIRE_TYPE_32BIT)
+ return FALSE;
+ *(uint32_t *) member = parse_fixed_uint32(data);
+ return TRUE;
+ case PROTOBUF_C_TYPE_INT64:
+ case PROTOBUF_C_TYPE_UINT64:
+ if (wire_type != PROTOBUF_C_WIRE_TYPE_VARINT)
+ return FALSE;
+ *(uint64_t *) member = parse_uint64(len, data);
+ return TRUE;
+ case PROTOBUF_C_TYPE_SINT64:
+ if (wire_type != PROTOBUF_C_WIRE_TYPE_VARINT)
+ return FALSE;
+ *(int64_t *) member = unzigzag64(parse_uint64(len, data));
+ return TRUE;
+ case PROTOBUF_C_TYPE_SFIXED64:
+ case PROTOBUF_C_TYPE_FIXED64:
+ case PROTOBUF_C_TYPE_DOUBLE:
+ if (wire_type != PROTOBUF_C_WIRE_TYPE_64BIT)
+ return FALSE;
+ *(uint64_t *) member = parse_fixed_uint64(data);
+ return TRUE;
+ case PROTOBUF_C_TYPE_BOOL:
+ *(protobuf_c_boolean *) member = parse_boolean(len, data);
+ return TRUE;
+ case PROTOBUF_C_TYPE_STRING: {
+ char **pstr = member;
+ unsigned pref_len = scanned_member->length_prefix_len;
+
+ if (wire_type != PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED)
+ return FALSE;
+
+ if (maybe_clear && *pstr != NULL) {
+ const char *def = scanned_member->field->default_value;
+ if (*pstr != NULL && *pstr != def)
+ do_free(allocator, *pstr);
+ }
+ *pstr = do_alloc(allocator, len - pref_len + 1);
+ if (*pstr == NULL)
+ return FALSE;
+ memcpy(*pstr, data + pref_len, len - pref_len);
+ (*pstr)[len - pref_len] = 0;
+ return TRUE;
+ }
+ case PROTOBUF_C_TYPE_BYTES: {
+ ProtobufCBinaryData *bd = member;
+ const ProtobufCBinaryData *def_bd;
+ unsigned pref_len = scanned_member->length_prefix_len;
+
+ if (wire_type != PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED)
+ return FALSE;
+
+ def_bd = scanned_member->field->default_value;
+ if (maybe_clear &&
+ bd->data != NULL &&
+ (def_bd == NULL || bd->data != def_bd->data))
+ {
+ do_free(allocator, bd->data);
+ }
+ if (len > pref_len) {
+ bd->data = do_alloc(allocator, len - pref_len);
+ if (bd->data == NULL)
+ return FALSE;
+ memcpy(bd->data, data + pref_len, len - pref_len);
+ } else {
+ bd->data = NULL;
+ }
+ bd->len = len - pref_len;
+ return TRUE;
+ }
+ case PROTOBUF_C_TYPE_MESSAGE: {
+ ProtobufCMessage **pmessage = member;
+ ProtobufCMessage *subm;
+ const ProtobufCMessage *def_mess;
+ protobuf_c_boolean merge_successful = TRUE;
+ unsigned pref_len = scanned_member->length_prefix_len;
+
+ if (wire_type != PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED)
+ return FALSE;
+
+ def_mess = scanned_member->field->default_value;
+ subm = protobuf_c_message_unpack(scanned_member->field->descriptor,
+ allocator,
+ len - pref_len,
+ data + pref_len);
+
+ if (maybe_clear &&
+ *pmessage != NULL &&
+ *pmessage != def_mess)
+ {
+ if (subm != NULL)
+ merge_successful = merge_messages(*pmessage, subm, allocator);
+ /* Delete the previous message */
+ protobuf_c_message_free_unpacked(*pmessage, allocator);
+ }
+ *pmessage = subm;
+ if (subm == NULL || !merge_successful)
+ return FALSE;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static protobuf_c_boolean
+parse_oneof_member (ScannedMember *scanned_member,
+ void *member,
+ ProtobufCMessage *message,
+ ProtobufCAllocator *allocator)
+{
+ uint32_t *oneof_case = STRUCT_MEMBER_PTR(uint32_t, message,
+ scanned_member->field->quantifier_offset);
+
+ /* If we have already parsed a member of this oneof, free it. */
+ if (*oneof_case != 0) {
+ const ProtobufCFieldDescriptor *old_field;
+ size_t el_size;
+ /* lookup field */
+ int field_index =
+ int_range_lookup(message->descriptor->n_field_ranges,
+ message->descriptor->field_ranges,
+ *oneof_case);
+ if (field_index < 0)
+ return FALSE;
+ old_field = message->descriptor->fields + field_index;
+ el_size = sizeof_elt_in_repeated_array(old_field->type);
+
+ switch (old_field->type) {
+ case PROTOBUF_C_TYPE_STRING: {
+ char **pstr = member;
+ const char *def = old_field->default_value;
+ if (*pstr != NULL && *pstr != def)
+ do_free(allocator, *pstr);
+ break;
+ }
+ case PROTOBUF_C_TYPE_BYTES: {
+ ProtobufCBinaryData *bd = member;
+ const ProtobufCBinaryData *def_bd = old_field->default_value;
+ if (bd->data != NULL &&
+ (def_bd == NULL || bd->data != def_bd->data))
+ {
+ do_free(allocator, bd->data);
+ }
+ break;
+ }
+ case PROTOBUF_C_TYPE_MESSAGE: {
+ ProtobufCMessage **pmessage = member;
+ const ProtobufCMessage *def_mess = old_field->default_value;
+ if (*pmessage != NULL && *pmessage != def_mess)
+ protobuf_c_message_free_unpacked(*pmessage, allocator);
+ break;
+ }
+ default:
+ break;
+ }
+
+ memset (member, 0, el_size);
+ }
+ if (!parse_required_member (scanned_member, member, allocator, TRUE))
+ return FALSE;
+
+ *oneof_case = scanned_member->tag;
+ return TRUE;
+}
+
+
+static protobuf_c_boolean
+parse_optional_member(ScannedMember *scanned_member,
+ void *member,
+ ProtobufCMessage *message,
+ ProtobufCAllocator *allocator)
+{
+ if (!parse_required_member(scanned_member, member, allocator, TRUE))
+ return FALSE;
+ if (scanned_member->field->quantifier_offset != 0)
+ STRUCT_MEMBER(protobuf_c_boolean,
+ message,
+ scanned_member->field->quantifier_offset) = TRUE;
+ return TRUE;
+}
+
+static protobuf_c_boolean
+parse_repeated_member(ScannedMember *scanned_member,
+ void *member,
+ ProtobufCMessage *message,
+ ProtobufCAllocator *allocator)
+{
+ const ProtobufCFieldDescriptor *field = scanned_member->field;
+ size_t *p_n = STRUCT_MEMBER_PTR(size_t, message, field->quantifier_offset);
+ size_t siz = sizeof_elt_in_repeated_array(field->type);
+ char *array = *(char **) member;
+
+ if (!parse_required_member(scanned_member, array + siz * (*p_n),
+ allocator, FALSE))
+ {
+ return FALSE;
+ }
+ *p_n += 1;
+ return TRUE;
+}
+
+static unsigned
+scan_varint(unsigned len, const uint8_t *data)
+{
+ unsigned i;
+ if (len > 10)
+ len = 10;
+ for (i = 0; i < len; i++)
+ if ((data[i] & 0x80) == 0)
+ break;
+ if (i == len)
+ return 0;
+ return i + 1;
+}
+
+static protobuf_c_boolean
+parse_packed_repeated_member(ScannedMember *scanned_member,
+ void *member,
+ ProtobufCMessage *message)
+{
+ const ProtobufCFieldDescriptor *field = scanned_member->field;
+ size_t *p_n = STRUCT_MEMBER_PTR(size_t, message, field->quantifier_offset);
+ size_t siz = sizeof_elt_in_repeated_array(field->type);
+ void *array = *(char **) member + siz * (*p_n);
+ const uint8_t *at = scanned_member->data + scanned_member->length_prefix_len;
+ size_t rem = scanned_member->len - scanned_member->length_prefix_len;
+ size_t count = 0;
+#if defined(WORDS_BIGENDIAN)
+ unsigned i;
+#endif
+
+ switch (field->type) {
+ case PROTOBUF_C_TYPE_SFIXED32:
+ case PROTOBUF_C_TYPE_FIXED32:
+ case PROTOBUF_C_TYPE_FLOAT:
+ count = (scanned_member->len - scanned_member->length_prefix_len) / 4;
+#if !defined(WORDS_BIGENDIAN)
+ goto no_unpacking_needed;
+#else
+ for (i = 0; i < count; i++) {
+ ((uint32_t *) array)[i] = parse_fixed_uint32(at);
+ at += 4;
+ }
+ break;
+#endif
+ case PROTOBUF_C_TYPE_SFIXED64:
+ case PROTOBUF_C_TYPE_FIXED64:
+ case PROTOBUF_C_TYPE_DOUBLE:
+ count = (scanned_member->len - scanned_member->length_prefix_len) / 8;
+#if !defined(WORDS_BIGENDIAN)
+ goto no_unpacking_needed;
+#else
+ for (i = 0; i < count; i++) {
+ ((uint64_t *) array)[i] = parse_fixed_uint64(at);
+ at += 8;
+ }
+ break;
+#endif
+ case PROTOBUF_C_TYPE_ENUM:
+ case PROTOBUF_C_TYPE_INT32:
+ while (rem > 0) {
+ unsigned s = scan_varint(rem, at);
+ if (s == 0) {
+ PROTOBUF_C_UNPACK_ERROR("bad packed-repeated int32 value");
+ return FALSE;
+ }
+ ((int32_t *) array)[count++] = parse_int32(s, at);
+ at += s;
+ rem -= s;
+ }
+ break;
+ case PROTOBUF_C_TYPE_SINT32:
+ while (rem > 0) {
+ unsigned s = scan_varint(rem, at);
+ if (s == 0) {
+ PROTOBUF_C_UNPACK_ERROR("bad packed-repeated sint32 value");
+ return FALSE;
+ }
+ ((int32_t *) array)[count++] = unzigzag32(parse_uint32(s, at));
+ at += s;
+ rem -= s;
+ }
+ break;
+ case PROTOBUF_C_TYPE_UINT32:
+ while (rem > 0) {
+ unsigned s = scan_varint(rem, at);
+ if (s == 0) {
+ PROTOBUF_C_UNPACK_ERROR("bad packed-repeated enum or uint32 value");
+ return FALSE;
+ }
+ ((uint32_t *) array)[count++] = parse_uint32(s, at);
+ at += s;
+ rem -= s;
+ }
+ break;
+
+ case PROTOBUF_C_TYPE_SINT64:
+ while (rem > 0) {
+ unsigned s = scan_varint(rem, at);
+ if (s == 0) {
+ PROTOBUF_C_UNPACK_ERROR("bad packed-repeated sint64 value");
+ return FALSE;
+ }
+ ((int64_t *) array)[count++] = unzigzag64(parse_uint64(s, at));
+ at += s;
+ rem -= s;
+ }
+ break;
+ case PROTOBUF_C_TYPE_INT64:
+ case PROTOBUF_C_TYPE_UINT64:
+ while (rem > 0) {
+ unsigned s = scan_varint(rem, at);
+ if (s == 0) {
+ PROTOBUF_C_UNPACK_ERROR("bad packed-repeated int64/uint64 value");
+ return FALSE;
+ }
+ ((int64_t *) array)[count++] = parse_uint64(s, at);
+ at += s;
+ rem -= s;
+ }
+ break;
+ case PROTOBUF_C_TYPE_BOOL:
+ while (rem > 0) {
+ unsigned s = scan_varint(rem, at);
+ if (s == 0) {
+ PROTOBUF_C_UNPACK_ERROR("bad packed-repeated boolean value");
+ return FALSE;
+ }
+ ((protobuf_c_boolean *) array)[count++] = parse_boolean(s, at);
+ at += s;
+ rem -= s;
+ }
+ break;
+ default:
+ PROTOBUF_C__ASSERT_NOT_REACHED();
+ }
+ *p_n += count;
+ return TRUE;
+
+#if !defined(WORDS_BIGENDIAN)
+no_unpacking_needed:
+ memcpy(array, at, count * siz);
+ *p_n += count;
+ return TRUE;
+#endif
+}
+
+static protobuf_c_boolean
+is_packable_type(ProtobufCType type)
+{
+ return
+ type != PROTOBUF_C_TYPE_STRING &&
+ type != PROTOBUF_C_TYPE_BYTES &&
+ type != PROTOBUF_C_TYPE_MESSAGE;
+}
+
+static protobuf_c_boolean
+parse_member(ScannedMember *scanned_member,
+ ProtobufCMessage *message,
+ ProtobufCAllocator *allocator)
+{
+ const ProtobufCFieldDescriptor *field = scanned_member->field;
+ void *member;
+
+ if (field == NULL) {
+ ProtobufCMessageUnknownField *ufield =
+ message->unknown_fields +
+ (message->n_unknown_fields++);
+ ufield->tag = scanned_member->tag;
+ ufield->wire_type = scanned_member->wire_type;
+ ufield->len = scanned_member->len;
+ ufield->data = do_alloc(allocator, scanned_member->len);
+ if (ufield->data == NULL)
+ return FALSE;
+ memcpy(ufield->data, scanned_member->data, ufield->len);
+ return TRUE;
+ }
+ member = (char *) message + field->offset;
+ switch (field->label) {
+ case PROTOBUF_C_LABEL_REQUIRED:
+ return parse_required_member(scanned_member, member,
+ allocator, TRUE);
+ case PROTOBUF_C_LABEL_OPTIONAL:
+ case PROTOBUF_C_LABEL_NONE:
+ if (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_ONEOF)) {
+ return parse_oneof_member(scanned_member, member,
+ message, allocator);
+ } else {
+ return parse_optional_member(scanned_member, member,
+ message, allocator);
+ }
+ case PROTOBUF_C_LABEL_REPEATED:
+ if (scanned_member->wire_type ==
+ PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED &&
+ (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_PACKED) ||
+ is_packable_type(field->type)))
+ {
+ return parse_packed_repeated_member(scanned_member,
+ member, message);
+ } else {
+ return parse_repeated_member(scanned_member,
+ member, message,
+ allocator);
+ }
+ }
+ PROTOBUF_C__ASSERT_NOT_REACHED();
+ return 0;
+}
+
+/**
+ * Initialise messages generated by old code.
+ *
+ * This function is used if desc->message_init == NULL (which occurs
+ * for old code, and which would be useful to support allocating
+ * descriptors dynamically).
+ */
+static void
+message_init_generic(const ProtobufCMessageDescriptor *desc,
+ ProtobufCMessage *message)
+{
+ unsigned i;
+
+ memset(message, 0, desc->sizeof_message);
+ message->descriptor = desc;
+ for (i = 0; i < desc->n_fields; i++) {
+ if (desc->fields[i].default_value != NULL &&
+ desc->fields[i].label != PROTOBUF_C_LABEL_REPEATED)
+ {
+ void *field =
+ STRUCT_MEMBER_P(message, desc->fields[i].offset);
+ const void *dv = desc->fields[i].default_value;
+
+ switch (desc->fields[i].type) {
+ case PROTOBUF_C_TYPE_INT32:
+ case PROTOBUF_C_TYPE_SINT32:
+ case PROTOBUF_C_TYPE_SFIXED32:
+ case PROTOBUF_C_TYPE_UINT32:
+ case PROTOBUF_C_TYPE_FIXED32:
+ case PROTOBUF_C_TYPE_FLOAT:
+ case PROTOBUF_C_TYPE_ENUM:
+ memcpy(field, dv, 4);
+ break;
+ case PROTOBUF_C_TYPE_INT64:
+ case PROTOBUF_C_TYPE_SINT64:
+ case PROTOBUF_C_TYPE_SFIXED64:
+ case PROTOBUF_C_TYPE_UINT64:
+ case PROTOBUF_C_TYPE_FIXED64:
+ case PROTOBUF_C_TYPE_DOUBLE:
+ memcpy(field, dv, 8);
+ break;
+ case PROTOBUF_C_TYPE_BOOL:
+ memcpy(field, dv, sizeof(protobuf_c_boolean));
+ break;
+ case PROTOBUF_C_TYPE_BYTES:
+ memcpy(field, dv, sizeof(ProtobufCBinaryData));
+ break;
+
+ case PROTOBUF_C_TYPE_STRING:
+ case PROTOBUF_C_TYPE_MESSAGE:
+ /*
+ * The next line essentially implements a cast
+ * from const, which is totally unavoidable.
+ */
+ *(const void **) field = dv;
+ break;
+ }
+ }
+ }
+}
+
+/**@}*/
+
+/*
+ * ScannedMember slabs (an unpacking implementation detail). Before doing real
+ * unpacking, we first scan through the elements to see how many there are (for
+ * repeated fields), and which field to use (for non-repeated fields given
+ * twice).
+ *
+ * In order to avoid allocations for small messages, we keep a stack-allocated
+ * slab of ScannedMembers of size FIRST_SCANNED_MEMBER_SLAB_SIZE (16). After we
+ * fill that up, we allocate each slab twice as large as the previous one.
+ */
+#define FIRST_SCANNED_MEMBER_SLAB_SIZE_LOG2 4
+
+/*
+ * The number of slabs, including the stack-allocated ones; choose the number so
+ * that we would overflow if we needed a slab larger than provided.
+ */
+#define MAX_SCANNED_MEMBER_SLAB \
+ (sizeof(unsigned int)*8 - 1 \
+ - BOUND_SIZEOF_SCANNED_MEMBER_LOG2 \
+ - FIRST_SCANNED_MEMBER_SLAB_SIZE_LOG2)
+
+#define REQUIRED_FIELD_BITMAP_SET(index) \
+ (required_fields_bitmap[(index)/8] |= (1UL<<((index)%8)))
+
+#define REQUIRED_FIELD_BITMAP_IS_SET(index) \
+ (required_fields_bitmap[(index)/8] & (1UL<<((index)%8)))
+
+ProtobufCMessage *
+protobuf_c_message_unpack(const ProtobufCMessageDescriptor *desc,
+ ProtobufCAllocator *allocator,
+ size_t len, const uint8_t *data)
+{
+ ProtobufCMessage *rv;
+ size_t rem = len;
+ const uint8_t *at = data;
+ const ProtobufCFieldDescriptor *last_field = desc->fields + 0;
+ ScannedMember first_member_slab[1UL <<
+ FIRST_SCANNED_MEMBER_SLAB_SIZE_LOG2];
+
+ /*
+ * scanned_member_slabs[i] is an array of arrays of ScannedMember.
+ * The first slab (scanned_member_slabs[0] is just a pointer to
+ * first_member_slab), above. All subsequent slabs will be allocated
+ * using the allocator.
+ */
+ ScannedMember *scanned_member_slabs[MAX_SCANNED_MEMBER_SLAB + 1];
+ unsigned which_slab = 0; /* the slab we are currently populating */
+ unsigned in_slab_index = 0; /* number of members in the slab */
+ size_t n_unknown = 0;
+ unsigned f;
+ unsigned j;
+ unsigned i_slab;
+ unsigned last_field_index = 0;
+ unsigned required_fields_bitmap_len;
+ unsigned char required_fields_bitmap_stack[16];
+ unsigned char *required_fields_bitmap = required_fields_bitmap_stack;
+ protobuf_c_boolean required_fields_bitmap_alloced = FALSE;
+
+ ASSERT_IS_MESSAGE_DESCRIPTOR(desc);
+
+ if (allocator == NULL)
+ allocator = &protobuf_c__allocator;
+
+ rv = do_alloc(allocator, desc->sizeof_message);
+ if (!rv)
+ return (NULL);
+ scanned_member_slabs[0] = first_member_slab;
+
+ required_fields_bitmap_len = (desc->n_fields + 7) / 8;
+ if (required_fields_bitmap_len > sizeof(required_fields_bitmap_stack)) {
+ required_fields_bitmap = do_alloc(allocator, required_fields_bitmap_len);
+ if (!required_fields_bitmap) {
+ do_free(allocator, rv);
+ return (NULL);
+ }
+ required_fields_bitmap_alloced = TRUE;
+ }
+ memset(required_fields_bitmap, 0, required_fields_bitmap_len);
+
+ /*
+ * Generated code always defines "message_init". However, we provide a
+ * fallback for (1) users of old protobuf-c generated-code that do not
+ * provide the function, and (2) descriptors constructed from some other
+ * source (most likely, direct construction from the .proto file).
+ */
+ if (desc->message_init != NULL)
+ protobuf_c_message_init(desc, rv);
+ else
+ message_init_generic(desc, rv);
+
+ while (rem > 0) {
+ uint32_t tag;
+ uint8_t wire_type;
+ size_t used = parse_tag_and_wiretype(rem, at, &tag, &wire_type);
+ const ProtobufCFieldDescriptor *field;
+ ScannedMember tmp;
+
+ if (used == 0) {
+ PROTOBUF_C_UNPACK_ERROR("error parsing tag/wiretype at offset %u",
+ (unsigned) (at - data));
+ goto error_cleanup_during_scan;
+ }
+ /*
+ * \todo Consider optimizing for field[1].id == tag, if field[1]
+ * exists!
+ */
+ if (last_field == NULL || last_field->id != tag) {
+ /* lookup field */
+ int field_index =
+ int_range_lookup(desc->n_field_ranges,
+ desc->field_ranges,
+ tag);
+ if (field_index < 0) {
+ field = NULL;
+ n_unknown++;
+ } else {
+ field = desc->fields + field_index;
+ last_field = field;
+ last_field_index = field_index;
+ }
+ } else {
+ field = last_field;
+ }
+
+ if (field != NULL && field->label == PROTOBUF_C_LABEL_REQUIRED)
+ REQUIRED_FIELD_BITMAP_SET(last_field_index);
+
+ at += used;
+ rem -= used;
+ tmp.tag = tag;
+ tmp.wire_type = wire_type;
+ tmp.field = field;
+ tmp.data = at;
+ tmp.length_prefix_len = 0;
+
+ switch (wire_type) {
+ case PROTOBUF_C_WIRE_TYPE_VARINT: {
+ unsigned max_len = rem < 10 ? rem : 10;
+ unsigned i;
+
+ for (i = 0; i < max_len; i++)
+ if ((at[i] & 0x80) == 0)
+ break;
+ if (i == max_len) {
+ PROTOBUF_C_UNPACK_ERROR("unterminated varint at offset %u",
+ (unsigned) (at - data));
+ goto error_cleanup_during_scan;
+ }
+ tmp.len = i + 1;
+ break;
+ }
+ case PROTOBUF_C_WIRE_TYPE_64BIT:
+ if (rem < 8) {
+ PROTOBUF_C_UNPACK_ERROR("too short after 64bit wiretype at offset %u",
+ (unsigned) (at - data));
+ goto error_cleanup_during_scan;
+ }
+ tmp.len = 8;
+ break;
+ case PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED: {
+ size_t pref_len;
+
+ tmp.len = scan_length_prefixed_data(rem, at, &pref_len);
+ if (tmp.len == 0) {
+ /* NOTE: scan_length_prefixed_data calls UNPACK_ERROR */
+ goto error_cleanup_during_scan;
+ }
+ tmp.length_prefix_len = pref_len;
+ break;
+ }
+ case PROTOBUF_C_WIRE_TYPE_32BIT:
+ if (rem < 4) {
+ PROTOBUF_C_UNPACK_ERROR("too short after 32bit wiretype at offset %u",
+ (unsigned) (at - data));
+ goto error_cleanup_during_scan;
+ }
+ tmp.len = 4;
+ break;
+ default:
+ PROTOBUF_C_UNPACK_ERROR("unsupported tag %u at offset %u",
+ wire_type, (unsigned) (at - data));
+ goto error_cleanup_during_scan;
+ }
+
+ if (in_slab_index == (1UL <<
+ (which_slab + FIRST_SCANNED_MEMBER_SLAB_SIZE_LOG2)))
+ {
+ size_t size;
+
+ in_slab_index = 0;
+ if (which_slab == MAX_SCANNED_MEMBER_SLAB) {
+ PROTOBUF_C_UNPACK_ERROR("too many fields");
+ goto error_cleanup_during_scan;
+ }
+ which_slab++;
+ size = sizeof(ScannedMember)
+ << (which_slab + FIRST_SCANNED_MEMBER_SLAB_SIZE_LOG2);
+ scanned_member_slabs[which_slab] = do_alloc(allocator, size);
+ if (scanned_member_slabs[which_slab] == NULL)
+ goto error_cleanup_during_scan;
+ }
+ scanned_member_slabs[which_slab][in_slab_index++] = tmp;
+
+ if (field != NULL && field->label == PROTOBUF_C_LABEL_REPEATED) {
+ size_t *n = STRUCT_MEMBER_PTR(size_t, rv,
+ field->quantifier_offset);
+ if (wire_type == PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED &&
+ (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_PACKED) ||
+ is_packable_type(field->type)))
+ {
+ size_t count;
+ if (!count_packed_elements(field->type,
+ tmp.len -
+ tmp.length_prefix_len,
+ tmp.data +
+ tmp.length_prefix_len,
+ &count))
+ {
+ PROTOBUF_C_UNPACK_ERROR("counting packed elements");
+ goto error_cleanup_during_scan;
+ }
+ *n += count;
+ } else {
+ *n += 1;
+ }
+ }
+
+ at += tmp.len;
+ rem -= tmp.len;
+ }
+
+ /* allocate space for repeated fields, also check that all required fields have been set */
+ for (f = 0; f < desc->n_fields; f++) {
+ const ProtobufCFieldDescriptor *field = desc->fields + f;
+ if (field->label == PROTOBUF_C_LABEL_REPEATED) {
+ size_t siz =
+ sizeof_elt_in_repeated_array(field->type);
+ size_t *n_ptr =
+ STRUCT_MEMBER_PTR(size_t, rv,
+ field->quantifier_offset);
+ if (*n_ptr != 0) {
+ unsigned n = *n_ptr;
+ void *a;
+ *n_ptr = 0;
+ assert(rv->descriptor != NULL);
+#define CLEAR_REMAINING_N_PTRS() \
+ for(f++;f < desc->n_fields; f++) \
+ { \
+ field = desc->fields + f; \
+ if (field->label == PROTOBUF_C_LABEL_REPEATED) \
+ STRUCT_MEMBER (size_t, rv, field->quantifier_offset) = 0; \
+ }
+ a = do_alloc(allocator, siz * n);
+ if (!a) {
+ CLEAR_REMAINING_N_PTRS();
+ goto error_cleanup;
+ }
+ STRUCT_MEMBER(void *, rv, field->offset) = a;
+ }
+ } else if (field->label == PROTOBUF_C_LABEL_REQUIRED) {
+ if (field->default_value == NULL &&
+ !REQUIRED_FIELD_BITMAP_IS_SET(f))
+ {
+ CLEAR_REMAINING_N_PTRS();
+ PROTOBUF_C_UNPACK_ERROR("message '%s': missing required field '%s'",
+ desc->name, field->name);
+ goto error_cleanup;
+ }
+ }
+ }
+#undef CLEAR_REMAINING_N_PTRS
+
+ /* allocate space for unknown fields */
+ if (n_unknown) {
+ rv->unknown_fields = do_alloc(allocator,
+ n_unknown * sizeof(ProtobufCMessageUnknownField));
+ if (rv->unknown_fields == NULL)
+ goto error_cleanup;
+ }
+
+ /* do real parsing */
+ for (i_slab = 0; i_slab <= which_slab; i_slab++) {
+ unsigned max = (i_slab == which_slab) ?
+ in_slab_index : (1UL << (i_slab + 4));
+ ScannedMember *slab = scanned_member_slabs[i_slab];
+
+ for (j = 0; j < max; j++) {
+ if (!parse_member(slab + j, rv, allocator)) {
+ PROTOBUF_C_UNPACK_ERROR("error parsing member %s of %s",
+ slab->field ? slab->field->name : "*unknown-field*",
+ desc->name);
+ goto error_cleanup;
+ }
+ }
+ }
+
+ /* cleanup */
+ for (j = 1; j <= which_slab; j++)
+ do_free(allocator, scanned_member_slabs[j]);
+ if (required_fields_bitmap_alloced)
+ do_free(allocator, required_fields_bitmap);
+ return rv;
+
+error_cleanup:
+ protobuf_c_message_free_unpacked(rv, allocator);
+ for (j = 1; j <= which_slab; j++)
+ do_free(allocator, scanned_member_slabs[j]);
+ if (required_fields_bitmap_alloced)
+ do_free(allocator, required_fields_bitmap);
+ return NULL;
+
+error_cleanup_during_scan:
+ do_free(allocator, rv);
+ for (j = 1; j <= which_slab; j++)
+ do_free(allocator, scanned_member_slabs[j]);
+ if (required_fields_bitmap_alloced)
+ do_free(allocator, required_fields_bitmap);
+ return NULL;
+}
+
+void
+protobuf_c_message_free_unpacked(ProtobufCMessage *message,
+ ProtobufCAllocator *allocator)
+{
+ const ProtobufCMessageDescriptor *desc;
+ unsigned f;
+
+ if (message == NULL)
+ return;
+
+ desc = message->descriptor;
+
+ ASSERT_IS_MESSAGE(message);
+
+ if (allocator == NULL)
+ allocator = &protobuf_c__allocator;
+ message->descriptor = NULL;
+ for (f = 0; f < desc->n_fields; f++) {
+ if (0 != (desc->fields[f].flags & PROTOBUF_C_FIELD_FLAG_ONEOF) &&
+ desc->fields[f].id !=
+ STRUCT_MEMBER(uint32_t, message, desc->fields[f].quantifier_offset))
+ {
+ /* This is not the selected oneof, skip it */
+ continue;
+ }
+
+ if (desc->fields[f].label == PROTOBUF_C_LABEL_REPEATED) {
+ size_t n = STRUCT_MEMBER(size_t,
+ message,
+ desc->fields[f].quantifier_offset);
+ void *arr = STRUCT_MEMBER(void *,
+ message,
+ desc->fields[f].offset);
+
+ if (arr != NULL) {
+ if (desc->fields[f].type == PROTOBUF_C_TYPE_STRING) {
+ unsigned i;
+ for (i = 0; i < n; i++)
+ do_free(allocator, ((char **) arr)[i]);
+ } else if (desc->fields[f].type == PROTOBUF_C_TYPE_BYTES) {
+ unsigned i;
+ for (i = 0; i < n; i++)
+ do_free(allocator, ((ProtobufCBinaryData *) arr)[i].data);
+ } else if (desc->fields[f].type == PROTOBUF_C_TYPE_MESSAGE) {
+ unsigned i;
+ for (i = 0; i < n; i++)
+ protobuf_c_message_free_unpacked(
+ ((ProtobufCMessage **) arr)[i],
+ allocator
+ );
+ }
+ do_free(allocator, arr);
+ }
+ } else if (desc->fields[f].type == PROTOBUF_C_TYPE_STRING) {
+ char *str = STRUCT_MEMBER(char *, message,
+ desc->fields[f].offset);
+
+ if (str && str != desc->fields[f].default_value)
+ do_free(allocator, str);
+ } else if (desc->fields[f].type == PROTOBUF_C_TYPE_BYTES) {
+ void *data = STRUCT_MEMBER(ProtobufCBinaryData, message,
+ desc->fields[f].offset).data;
+ const ProtobufCBinaryData *default_bd;
+
+ default_bd = desc->fields[f].default_value;
+ if (data != NULL &&
+ (default_bd == NULL ||
+ default_bd->data != data))
+ {
+ do_free(allocator, data);
+ }
+ } else if (desc->fields[f].type == PROTOBUF_C_TYPE_MESSAGE) {
+ ProtobufCMessage *sm;
+
+ sm = STRUCT_MEMBER(ProtobufCMessage *, message,
+ desc->fields[f].offset);
+ if (sm && sm != desc->fields[f].default_value)
+ protobuf_c_message_free_unpacked(sm, allocator);
+ }
+ }
+
+ for (f = 0; f < message->n_unknown_fields; f++)
+ do_free(allocator, message->unknown_fields[f].data);
+ if (message->unknown_fields != NULL)
+ do_free(allocator, message->unknown_fields);
+
+ do_free(allocator, message);
+}
+
+void
+protobuf_c_message_init(const ProtobufCMessageDescriptor * descriptor,
+ void *message)
+{
+ descriptor->message_init((ProtobufCMessage *) (message));
+}
+
+protobuf_c_boolean
+protobuf_c_message_check(const ProtobufCMessage *message)
+{
+ unsigned i;
+
+ if (!message ||
+ !message->descriptor ||
+ message->descriptor->magic != PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC)
+ {
+ return FALSE;
+ }
+
+ for (i = 0; i < message->descriptor->n_fields; i++) {
+ const ProtobufCFieldDescriptor *f = message->descriptor->fields + i;
+ ProtobufCType type = f->type;
+ ProtobufCLabel label = f->label;
+ void *field = STRUCT_MEMBER_P (message, f->offset);
+
+ if (f->flags & PROTOBUF_C_FIELD_FLAG_ONEOF) {
+ const uint32_t *oneof_case = STRUCT_MEMBER_P (message, f->quantifier_offset);
+ if (f->id != *oneof_case) {
+ continue; //Do not check if it is an unpopulated oneof member.
+ }
+ }
+
+ if (label == PROTOBUF_C_LABEL_REPEATED) {
+ size_t *quantity = STRUCT_MEMBER_P (message, f->quantifier_offset);
+
+ if (*quantity > 0 && *(void **) field == NULL) {
+ return FALSE;
+ }
+
+ if (type == PROTOBUF_C_TYPE_MESSAGE) {
+ ProtobufCMessage **submessage = *(ProtobufCMessage ***) field;
+ unsigned j;
+ for (j = 0; j < *quantity; j++) {
+ if (!protobuf_c_message_check(submessage[j]))
+ return FALSE;
+ }
+ } else if (type == PROTOBUF_C_TYPE_STRING) {
+ char **string = *(char ***) field;
+ unsigned j;
+ for (j = 0; j < *quantity; j++) {
+ if (!string[j])
+ return FALSE;
+ }
+ } else if (type == PROTOBUF_C_TYPE_BYTES) {
+ ProtobufCBinaryData *bd = *(ProtobufCBinaryData **) field;
+ unsigned j;
+ for (j = 0; j < *quantity; j++) {
+ if (bd[j].len > 0 && bd[j].data == NULL)
+ return FALSE;
+ }
+ }
+
+ } else { /* PROTOBUF_C_LABEL_REQUIRED or PROTOBUF_C_LABEL_OPTIONAL */
+
+ if (type == PROTOBUF_C_TYPE_MESSAGE) {
+ ProtobufCMessage *submessage = *(ProtobufCMessage **) field;
+ if (label == PROTOBUF_C_LABEL_REQUIRED || submessage != NULL) {
+ if (!protobuf_c_message_check(submessage))
+ return FALSE;
+ }
+ } else if (type == PROTOBUF_C_TYPE_STRING) {
+ char *string = *(char **) field;
+ if (label == PROTOBUF_C_LABEL_REQUIRED && string == NULL)
+ return FALSE;
+ } else if (type == PROTOBUF_C_TYPE_BYTES) {
+ protobuf_c_boolean *has = STRUCT_MEMBER_P (message, f->quantifier_offset);
+ ProtobufCBinaryData *bd = field;
+ if (label == PROTOBUF_C_LABEL_REQUIRED || *has == TRUE) {
+ if (bd->len > 0 && bd->data == NULL)
+ return FALSE;
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+/* === services === */
+
+typedef void (*GenericHandler) (void *service,
+ const ProtobufCMessage *input,
+ ProtobufCClosure closure,
+ void *closure_data);
+void
+protobuf_c_service_invoke_internal(ProtobufCService *service,
+ unsigned method_index,
+ const ProtobufCMessage *input,
+ ProtobufCClosure closure,
+ void *closure_data)
+{
+ GenericHandler *handlers;
+ GenericHandler handler;
+
+ /*
+ * Verify that method_index is within range. If this fails, you are
+ * likely invoking a newly added method on an old service. (Although
+ * other memory corruption bugs can cause this assertion too.)
+ */
+ assert(method_index < service->descriptor->n_methods);
+
+ /*
+ * Get the array of virtual methods (which are enumerated by the
+ * generated code).
+ */
+ handlers = (GenericHandler *) (service + 1);
+
+ /*
+ * Get our method and invoke it.
+ * \todo Seems like handler == NULL is a situation that needs handling.
+ */
+ handler = handlers[method_index];
+ (*handler)(service, input, closure, closure_data);
+}
+
+void
+protobuf_c_service_generated_init(ProtobufCService *service,
+ const ProtobufCServiceDescriptor *descriptor,
+ ProtobufCServiceDestroy destroy)
+{
+ ASSERT_IS_SERVICE_DESCRIPTOR(descriptor);
+ service->descriptor = descriptor;
+ service->destroy = destroy;
+ service->invoke = protobuf_c_service_invoke_internal;
+ memset(service + 1, 0, descriptor->n_methods * sizeof(GenericHandler));
+}
+
+void protobuf_c_service_destroy(ProtobufCService *service)
+{
+ service->destroy(service);
+}
+
+/* --- querying the descriptors --- */
+
+const ProtobufCEnumValue *
+protobuf_c_enum_descriptor_get_value_by_name(const ProtobufCEnumDescriptor *desc,
+ const char *name)
+{
+ unsigned start = 0;
+ unsigned count;
+
+ if (desc == NULL || desc->values_by_name == NULL)
+ return NULL;
+
+ count = desc->n_value_names;
+
+ while (count > 1) {
+ unsigned mid = start + count / 2;
+ int rv = strcmp(desc->values_by_name[mid].name, name);
+ if (rv == 0)
+ return desc->values + desc->values_by_name[mid].index;
+ else if (rv < 0) {
+ count = start + count - (mid + 1);
+ start = mid + 1;
+ } else
+ count = mid - start;
+ }
+ if (count == 0)
+ return NULL;
+ if (strcmp(desc->values_by_name[start].name, name) == 0)
+ return desc->values + desc->values_by_name[start].index;
+ return NULL;
+}
+
+const ProtobufCEnumValue *
+protobuf_c_enum_descriptor_get_value(const ProtobufCEnumDescriptor *desc,
+ int value)
+{
+ int rv = int_range_lookup(desc->n_value_ranges, desc->value_ranges, value);
+ if (rv < 0)
+ return NULL;
+ return desc->values + rv;
+}
+
+const ProtobufCFieldDescriptor *
+protobuf_c_message_descriptor_get_field_by_name(const ProtobufCMessageDescriptor *desc,
+ const char *name)
+{
+ unsigned start = 0;
+ unsigned count;
+ const ProtobufCFieldDescriptor *field;
+
+ if (desc == NULL || desc->fields_sorted_by_name == NULL)
+ return NULL;
+
+ count = desc->n_fields;
+
+ while (count > 1) {
+ unsigned mid = start + count / 2;
+ int rv;
+ field = desc->fields + desc->fields_sorted_by_name[mid];
+ rv = strcmp(field->name, name);
+ if (rv == 0)
+ return field;
+ else if (rv < 0) {
+ count = start + count - (mid + 1);
+ start = mid + 1;
+ } else
+ count = mid - start;
+ }
+ if (count == 0)
+ return NULL;
+ field = desc->fields + desc->fields_sorted_by_name[start];
+ if (strcmp(field->name, name) == 0)
+ return field;
+ return NULL;
+}
+
+const ProtobufCFieldDescriptor *
+protobuf_c_message_descriptor_get_field(const ProtobufCMessageDescriptor *desc,
+ unsigned value)
+{
+ int rv = int_range_lookup(desc->n_field_ranges,desc->field_ranges, value);
+ if (rv < 0)
+ return NULL;
+ return desc->fields + rv;
+}
+
+const ProtobufCMethodDescriptor *
+protobuf_c_service_descriptor_get_method_by_name(const ProtobufCServiceDescriptor *desc,
+ const char *name)
+{
+ unsigned start = 0;
+ unsigned count;
+
+ if (desc == NULL || desc->method_indices_by_name == NULL)
+ return NULL;
+
+ count = desc->n_methods;
+
+ while (count > 1) {
+ unsigned mid = start + count / 2;
+ unsigned mid_index = desc->method_indices_by_name[mid];
+ const char *mid_name = desc->methods[mid_index].name;
+ int rv = strcmp(mid_name, name);
+
+ if (rv == 0)
+ return desc->methods + desc->method_indices_by_name[mid];
+ if (rv < 0) {
+ count = start + count - (mid + 1);
+ start = mid + 1;
+ } else {
+ count = mid - start;
+ }
+ }
+ if (count == 0)
+ return NULL;
+ if (strcmp(desc->methods[desc->method_indices_by_name[start]].name, name) == 0)
+ return desc->methods + desc->method_indices_by_name[start];
+ return NULL;
+}
diff --git a/src/fluent-bit/lib/cmetrics/src/external/remote.pb-c.c b/src/fluent-bit/lib/cmetrics/src/external/remote.pb-c.c
new file mode 100644
index 000000000..c26695eb9
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/external/remote.pb-c.c
@@ -0,0 +1,615 @@
+/* Generated by the protocol buffer compiler. DO NOT EDIT! */
+/* Generated from: remote.proto */
+
+/* Do not generate deprecated warnings for self */
+#ifndef PROTOBUF_C__NO_DEPRECATED
+#define PROTOBUF_C__NO_DEPRECATED
+#endif
+
+#include <prometheus_remote_write/remote.pb-c.h>
+
+void prometheus__write_request__init
+ (Prometheus__WriteRequest *message)
+{
+ static const Prometheus__WriteRequest init_value = PROMETHEUS__WRITE_REQUEST__INIT;
+ *message = init_value;
+}
+size_t prometheus__write_request__get_packed_size
+ (const Prometheus__WriteRequest *message)
+{
+ assert(message->base.descriptor == &prometheus__write_request__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t prometheus__write_request__pack
+ (const Prometheus__WriteRequest *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &prometheus__write_request__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t prometheus__write_request__pack_to_buffer
+ (const Prometheus__WriteRequest *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &prometheus__write_request__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Prometheus__WriteRequest *
+ prometheus__write_request__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Prometheus__WriteRequest *)
+ protobuf_c_message_unpack (&prometheus__write_request__descriptor,
+ allocator, len, data);
+}
+void prometheus__write_request__free_unpacked
+ (Prometheus__WriteRequest *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &prometheus__write_request__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void prometheus__read_request__init
+ (Prometheus__ReadRequest *message)
+{
+ static const Prometheus__ReadRequest init_value = PROMETHEUS__READ_REQUEST__INIT;
+ *message = init_value;
+}
+size_t prometheus__read_request__get_packed_size
+ (const Prometheus__ReadRequest *message)
+{
+ assert(message->base.descriptor == &prometheus__read_request__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t prometheus__read_request__pack
+ (const Prometheus__ReadRequest *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &prometheus__read_request__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t prometheus__read_request__pack_to_buffer
+ (const Prometheus__ReadRequest *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &prometheus__read_request__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Prometheus__ReadRequest *
+ prometheus__read_request__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Prometheus__ReadRequest *)
+ protobuf_c_message_unpack (&prometheus__read_request__descriptor,
+ allocator, len, data);
+}
+void prometheus__read_request__free_unpacked
+ (Prometheus__ReadRequest *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &prometheus__read_request__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void prometheus__read_response__init
+ (Prometheus__ReadResponse *message)
+{
+ static const Prometheus__ReadResponse init_value = PROMETHEUS__READ_RESPONSE__INIT;
+ *message = init_value;
+}
+size_t prometheus__read_response__get_packed_size
+ (const Prometheus__ReadResponse *message)
+{
+ assert(message->base.descriptor == &prometheus__read_response__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t prometheus__read_response__pack
+ (const Prometheus__ReadResponse *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &prometheus__read_response__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t prometheus__read_response__pack_to_buffer
+ (const Prometheus__ReadResponse *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &prometheus__read_response__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Prometheus__ReadResponse *
+ prometheus__read_response__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Prometheus__ReadResponse *)
+ protobuf_c_message_unpack (&prometheus__read_response__descriptor,
+ allocator, len, data);
+}
+void prometheus__read_response__free_unpacked
+ (Prometheus__ReadResponse *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &prometheus__read_response__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void prometheus__query__init
+ (Prometheus__Query *message)
+{
+ static const Prometheus__Query init_value = PROMETHEUS__QUERY__INIT;
+ *message = init_value;
+}
+size_t prometheus__query__get_packed_size
+ (const Prometheus__Query *message)
+{
+ assert(message->base.descriptor == &prometheus__query__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t prometheus__query__pack
+ (const Prometheus__Query *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &prometheus__query__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t prometheus__query__pack_to_buffer
+ (const Prometheus__Query *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &prometheus__query__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Prometheus__Query *
+ prometheus__query__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Prometheus__Query *)
+ protobuf_c_message_unpack (&prometheus__query__descriptor,
+ allocator, len, data);
+}
+void prometheus__query__free_unpacked
+ (Prometheus__Query *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &prometheus__query__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void prometheus__query_result__init
+ (Prometheus__QueryResult *message)
+{
+ static const Prometheus__QueryResult init_value = PROMETHEUS__QUERY_RESULT__INIT;
+ *message = init_value;
+}
+size_t prometheus__query_result__get_packed_size
+ (const Prometheus__QueryResult *message)
+{
+ assert(message->base.descriptor == &prometheus__query_result__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t prometheus__query_result__pack
+ (const Prometheus__QueryResult *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &prometheus__query_result__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t prometheus__query_result__pack_to_buffer
+ (const Prometheus__QueryResult *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &prometheus__query_result__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Prometheus__QueryResult *
+ prometheus__query_result__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Prometheus__QueryResult *)
+ protobuf_c_message_unpack (&prometheus__query_result__descriptor,
+ allocator, len, data);
+}
+void prometheus__query_result__free_unpacked
+ (Prometheus__QueryResult *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &prometheus__query_result__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void prometheus__chunked_read_response__init
+ (Prometheus__ChunkedReadResponse *message)
+{
+ static const Prometheus__ChunkedReadResponse init_value = PROMETHEUS__CHUNKED_READ_RESPONSE__INIT;
+ *message = init_value;
+}
+size_t prometheus__chunked_read_response__get_packed_size
+ (const Prometheus__ChunkedReadResponse *message)
+{
+ assert(message->base.descriptor == &prometheus__chunked_read_response__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t prometheus__chunked_read_response__pack
+ (const Prometheus__ChunkedReadResponse *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &prometheus__chunked_read_response__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t prometheus__chunked_read_response__pack_to_buffer
+ (const Prometheus__ChunkedReadResponse *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &prometheus__chunked_read_response__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Prometheus__ChunkedReadResponse *
+ prometheus__chunked_read_response__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Prometheus__ChunkedReadResponse *)
+ protobuf_c_message_unpack (&prometheus__chunked_read_response__descriptor,
+ allocator, len, data);
+}
+void prometheus__chunked_read_response__free_unpacked
+ (Prometheus__ChunkedReadResponse *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &prometheus__chunked_read_response__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+static const ProtobufCFieldDescriptor prometheus__write_request__field_descriptors[2] =
+{
+ {
+ "timeseries",
+ 1,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Prometheus__WriteRequest, n_timeseries),
+ offsetof(Prometheus__WriteRequest, timeseries),
+ &prometheus__time_series__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "metadata",
+ 3,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Prometheus__WriteRequest, n_metadata),
+ offsetof(Prometheus__WriteRequest, metadata),
+ &prometheus__metric_metadata__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned prometheus__write_request__field_indices_by_name[] = {
+ 1, /* field[1] = metadata */
+ 0, /* field[0] = timeseries */
+};
+static const ProtobufCIntRange prometheus__write_request__number_ranges[2 + 1] =
+{
+ { 1, 0 },
+ { 3, 1 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor prometheus__write_request__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "prometheus.WriteRequest",
+ "WriteRequest",
+ "Prometheus__WriteRequest",
+ "prometheus",
+ sizeof(Prometheus__WriteRequest),
+ 2,
+ prometheus__write_request__field_descriptors,
+ prometheus__write_request__field_indices_by_name,
+ 2, prometheus__write_request__number_ranges,
+ (ProtobufCMessageInit) prometheus__write_request__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCEnumValue prometheus__read_request__response_type__enum_values_by_number[2] =
+{
+ { "SAMPLES", "PROMETHEUS__READ_REQUEST__RESPONSE_TYPE__SAMPLES", 0 },
+ { "STREAMED_XOR_CHUNKS", "PROMETHEUS__READ_REQUEST__RESPONSE_TYPE__STREAMED_XOR_CHUNKS", 1 },
+};
+static const ProtobufCIntRange prometheus__read_request__response_type__value_ranges[] = {
+{0, 0},{0, 2}
+};
+static const ProtobufCEnumValueIndex prometheus__read_request__response_type__enum_values_by_name[2] =
+{
+ { "SAMPLES", 0 },
+ { "STREAMED_XOR_CHUNKS", 1 },
+};
+const ProtobufCEnumDescriptor prometheus__read_request__response_type__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "prometheus.ReadRequest.ResponseType",
+ "ResponseType",
+ "Prometheus__ReadRequest__ResponseType",
+ "prometheus",
+ 2,
+ prometheus__read_request__response_type__enum_values_by_number,
+ 2,
+ prometheus__read_request__response_type__enum_values_by_name,
+ 1,
+ prometheus__read_request__response_type__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
+static const ProtobufCFieldDescriptor prometheus__read_request__field_descriptors[2] =
+{
+ {
+ "queries",
+ 1,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Prometheus__ReadRequest, n_queries),
+ offsetof(Prometheus__ReadRequest, queries),
+ &prometheus__query__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "accepted_response_types",
+ 2,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_ENUM,
+ offsetof(Prometheus__ReadRequest, n_accepted_response_types),
+ offsetof(Prometheus__ReadRequest, accepted_response_types),
+ &prometheus__read_request__response_type__descriptor,
+ NULL,
+ 0 | PROTOBUF_C_FIELD_FLAG_PACKED, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned prometheus__read_request__field_indices_by_name[] = {
+ 1, /* field[1] = accepted_response_types */
+ 0, /* field[0] = queries */
+};
+static const ProtobufCIntRange prometheus__read_request__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor prometheus__read_request__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "prometheus.ReadRequest",
+ "ReadRequest",
+ "Prometheus__ReadRequest",
+ "prometheus",
+ sizeof(Prometheus__ReadRequest),
+ 2,
+ prometheus__read_request__field_descriptors,
+ prometheus__read_request__field_indices_by_name,
+ 1, prometheus__read_request__number_ranges,
+ (ProtobufCMessageInit) prometheus__read_request__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor prometheus__read_response__field_descriptors[1] =
+{
+ {
+ "results",
+ 1,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Prometheus__ReadResponse, n_results),
+ offsetof(Prometheus__ReadResponse, results),
+ &prometheus__query_result__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned prometheus__read_response__field_indices_by_name[] = {
+ 0, /* field[0] = results */
+};
+static const ProtobufCIntRange prometheus__read_response__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor prometheus__read_response__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "prometheus.ReadResponse",
+ "ReadResponse",
+ "Prometheus__ReadResponse",
+ "prometheus",
+ sizeof(Prometheus__ReadResponse),
+ 1,
+ prometheus__read_response__field_descriptors,
+ prometheus__read_response__field_indices_by_name,
+ 1, prometheus__read_response__number_ranges,
+ (ProtobufCMessageInit) prometheus__read_response__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor prometheus__query__field_descriptors[4] =
+{
+ {
+ "start_timestamp_ms",
+ 1,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_INT64,
+ 0, /* quantifier_offset */
+ offsetof(Prometheus__Query, start_timestamp_ms),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "end_timestamp_ms",
+ 2,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_INT64,
+ 0, /* quantifier_offset */
+ offsetof(Prometheus__Query, end_timestamp_ms),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "matchers",
+ 3,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Prometheus__Query, n_matchers),
+ offsetof(Prometheus__Query, matchers),
+ &prometheus__label_matcher__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "hints",
+ 4,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(Prometheus__Query, hints),
+ &prometheus__read_hints__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned prometheus__query__field_indices_by_name[] = {
+ 1, /* field[1] = end_timestamp_ms */
+ 3, /* field[3] = hints */
+ 2, /* field[2] = matchers */
+ 0, /* field[0] = start_timestamp_ms */
+};
+static const ProtobufCIntRange prometheus__query__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 4 }
+};
+const ProtobufCMessageDescriptor prometheus__query__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "prometheus.Query",
+ "Query",
+ "Prometheus__Query",
+ "prometheus",
+ sizeof(Prometheus__Query),
+ 4,
+ prometheus__query__field_descriptors,
+ prometheus__query__field_indices_by_name,
+ 1, prometheus__query__number_ranges,
+ (ProtobufCMessageInit) prometheus__query__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor prometheus__query_result__field_descriptors[1] =
+{
+ {
+ "timeseries",
+ 1,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Prometheus__QueryResult, n_timeseries),
+ offsetof(Prometheus__QueryResult, timeseries),
+ &prometheus__time_series__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned prometheus__query_result__field_indices_by_name[] = {
+ 0, /* field[0] = timeseries */
+};
+static const ProtobufCIntRange prometheus__query_result__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor prometheus__query_result__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "prometheus.QueryResult",
+ "QueryResult",
+ "Prometheus__QueryResult",
+ "prometheus",
+ sizeof(Prometheus__QueryResult),
+ 1,
+ prometheus__query_result__field_descriptors,
+ prometheus__query_result__field_indices_by_name,
+ 1, prometheus__query_result__number_ranges,
+ (ProtobufCMessageInit) prometheus__query_result__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor prometheus__chunked_read_response__field_descriptors[2] =
+{
+ {
+ "chunked_series",
+ 1,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Prometheus__ChunkedReadResponse, n_chunked_series),
+ offsetof(Prometheus__ChunkedReadResponse, chunked_series),
+ &prometheus__chunked_series__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "query_index",
+ 2,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_INT64,
+ 0, /* quantifier_offset */
+ offsetof(Prometheus__ChunkedReadResponse, query_index),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned prometheus__chunked_read_response__field_indices_by_name[] = {
+ 0, /* field[0] = chunked_series */
+ 1, /* field[1] = query_index */
+};
+static const ProtobufCIntRange prometheus__chunked_read_response__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor prometheus__chunked_read_response__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "prometheus.ChunkedReadResponse",
+ "ChunkedReadResponse",
+ "Prometheus__ChunkedReadResponse",
+ "prometheus",
+ sizeof(Prometheus__ChunkedReadResponse),
+ 2,
+ prometheus__chunked_read_response__field_descriptors,
+ prometheus__chunked_read_response__field_indices_by_name,
+ 1, prometheus__chunked_read_response__number_ranges,
+ (ProtobufCMessageInit) prometheus__chunked_read_response__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
diff --git a/src/fluent-bit/lib/cmetrics/src/external/types.pb-c.c b/src/fluent-bit/lib/cmetrics/src/external/types.pb-c.c
new file mode 100644
index 000000000..782815930
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/src/external/types.pb-c.c
@@ -0,0 +1,1092 @@
+/* Generated by the protocol buffer compiler. DO NOT EDIT! */
+/* Generated from: types.proto */
+
+/* Do not generate deprecated warnings for self */
+#ifndef PROTOBUF_C__NO_DEPRECATED
+#define PROTOBUF_C__NO_DEPRECATED
+#endif
+
+#include <prometheus_remote_write/types.pb-c.h>
+
+void prometheus__metric_metadata__init
+ (Prometheus__MetricMetadata *message)
+{
+ static const Prometheus__MetricMetadata init_value = PROMETHEUS__METRIC_METADATA__INIT;
+ *message = init_value;
+}
+size_t prometheus__metric_metadata__get_packed_size
+ (const Prometheus__MetricMetadata *message)
+{
+ assert(message->base.descriptor == &prometheus__metric_metadata__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t prometheus__metric_metadata__pack
+ (const Prometheus__MetricMetadata *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &prometheus__metric_metadata__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t prometheus__metric_metadata__pack_to_buffer
+ (const Prometheus__MetricMetadata *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &prometheus__metric_metadata__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Prometheus__MetricMetadata *
+ prometheus__metric_metadata__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Prometheus__MetricMetadata *)
+ protobuf_c_message_unpack (&prometheus__metric_metadata__descriptor,
+ allocator, len, data);
+}
+void prometheus__metric_metadata__free_unpacked
+ (Prometheus__MetricMetadata *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &prometheus__metric_metadata__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void prometheus__sample__init
+ (Prometheus__Sample *message)
+{
+ static const Prometheus__Sample init_value = PROMETHEUS__SAMPLE__INIT;
+ *message = init_value;
+}
+size_t prometheus__sample__get_packed_size
+ (const Prometheus__Sample *message)
+{
+ assert(message->base.descriptor == &prometheus__sample__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t prometheus__sample__pack
+ (const Prometheus__Sample *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &prometheus__sample__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t prometheus__sample__pack_to_buffer
+ (const Prometheus__Sample *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &prometheus__sample__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Prometheus__Sample *
+ prometheus__sample__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Prometheus__Sample *)
+ protobuf_c_message_unpack (&prometheus__sample__descriptor,
+ allocator, len, data);
+}
+void prometheus__sample__free_unpacked
+ (Prometheus__Sample *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &prometheus__sample__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void prometheus__time_series__init
+ (Prometheus__TimeSeries *message)
+{
+ static const Prometheus__TimeSeries init_value = PROMETHEUS__TIME_SERIES__INIT;
+ *message = init_value;
+}
+size_t prometheus__time_series__get_packed_size
+ (const Prometheus__TimeSeries *message)
+{
+ assert(message->base.descriptor == &prometheus__time_series__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t prometheus__time_series__pack
+ (const Prometheus__TimeSeries *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &prometheus__time_series__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t prometheus__time_series__pack_to_buffer
+ (const Prometheus__TimeSeries *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &prometheus__time_series__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Prometheus__TimeSeries *
+ prometheus__time_series__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Prometheus__TimeSeries *)
+ protobuf_c_message_unpack (&prometheus__time_series__descriptor,
+ allocator, len, data);
+}
+void prometheus__time_series__free_unpacked
+ (Prometheus__TimeSeries *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &prometheus__time_series__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void prometheus__label__init
+ (Prometheus__Label *message)
+{
+ static const Prometheus__Label init_value = PROMETHEUS__LABEL__INIT;
+ *message = init_value;
+}
+size_t prometheus__label__get_packed_size
+ (const Prometheus__Label *message)
+{
+ assert(message->base.descriptor == &prometheus__label__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t prometheus__label__pack
+ (const Prometheus__Label *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &prometheus__label__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t prometheus__label__pack_to_buffer
+ (const Prometheus__Label *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &prometheus__label__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Prometheus__Label *
+ prometheus__label__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Prometheus__Label *)
+ protobuf_c_message_unpack (&prometheus__label__descriptor,
+ allocator, len, data);
+}
+void prometheus__label__free_unpacked
+ (Prometheus__Label *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &prometheus__label__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void prometheus__labels__init
+ (Prometheus__Labels *message)
+{
+ static const Prometheus__Labels init_value = PROMETHEUS__LABELS__INIT;
+ *message = init_value;
+}
+size_t prometheus__labels__get_packed_size
+ (const Prometheus__Labels *message)
+{
+ assert(message->base.descriptor == &prometheus__labels__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t prometheus__labels__pack
+ (const Prometheus__Labels *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &prometheus__labels__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t prometheus__labels__pack_to_buffer
+ (const Prometheus__Labels *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &prometheus__labels__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Prometheus__Labels *
+ prometheus__labels__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Prometheus__Labels *)
+ protobuf_c_message_unpack (&prometheus__labels__descriptor,
+ allocator, len, data);
+}
+void prometheus__labels__free_unpacked
+ (Prometheus__Labels *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &prometheus__labels__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void prometheus__label_matcher__init
+ (Prometheus__LabelMatcher *message)
+{
+ static const Prometheus__LabelMatcher init_value = PROMETHEUS__LABEL_MATCHER__INIT;
+ *message = init_value;
+}
+size_t prometheus__label_matcher__get_packed_size
+ (const Prometheus__LabelMatcher *message)
+{
+ assert(message->base.descriptor == &prometheus__label_matcher__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t prometheus__label_matcher__pack
+ (const Prometheus__LabelMatcher *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &prometheus__label_matcher__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t prometheus__label_matcher__pack_to_buffer
+ (const Prometheus__LabelMatcher *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &prometheus__label_matcher__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Prometheus__LabelMatcher *
+ prometheus__label_matcher__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Prometheus__LabelMatcher *)
+ protobuf_c_message_unpack (&prometheus__label_matcher__descriptor,
+ allocator, len, data);
+}
+void prometheus__label_matcher__free_unpacked
+ (Prometheus__LabelMatcher *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &prometheus__label_matcher__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void prometheus__read_hints__init
+ (Prometheus__ReadHints *message)
+{
+ static const Prometheus__ReadHints init_value = PROMETHEUS__READ_HINTS__INIT;
+ *message = init_value;
+}
+size_t prometheus__read_hints__get_packed_size
+ (const Prometheus__ReadHints *message)
+{
+ assert(message->base.descriptor == &prometheus__read_hints__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t prometheus__read_hints__pack
+ (const Prometheus__ReadHints *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &prometheus__read_hints__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t prometheus__read_hints__pack_to_buffer
+ (const Prometheus__ReadHints *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &prometheus__read_hints__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Prometheus__ReadHints *
+ prometheus__read_hints__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Prometheus__ReadHints *)
+ protobuf_c_message_unpack (&prometheus__read_hints__descriptor,
+ allocator, len, data);
+}
+void prometheus__read_hints__free_unpacked
+ (Prometheus__ReadHints *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &prometheus__read_hints__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void prometheus__chunk__init
+ (Prometheus__Chunk *message)
+{
+ static const Prometheus__Chunk init_value = PROMETHEUS__CHUNK__INIT;
+ *message = init_value;
+}
+size_t prometheus__chunk__get_packed_size
+ (const Prometheus__Chunk *message)
+{
+ assert(message->base.descriptor == &prometheus__chunk__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t prometheus__chunk__pack
+ (const Prometheus__Chunk *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &prometheus__chunk__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t prometheus__chunk__pack_to_buffer
+ (const Prometheus__Chunk *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &prometheus__chunk__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Prometheus__Chunk *
+ prometheus__chunk__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Prometheus__Chunk *)
+ protobuf_c_message_unpack (&prometheus__chunk__descriptor,
+ allocator, len, data);
+}
+void prometheus__chunk__free_unpacked
+ (Prometheus__Chunk *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &prometheus__chunk__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void prometheus__chunked_series__init
+ (Prometheus__ChunkedSeries *message)
+{
+ static const Prometheus__ChunkedSeries init_value = PROMETHEUS__CHUNKED_SERIES__INIT;
+ *message = init_value;
+}
+size_t prometheus__chunked_series__get_packed_size
+ (const Prometheus__ChunkedSeries *message)
+{
+ assert(message->base.descriptor == &prometheus__chunked_series__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t prometheus__chunked_series__pack
+ (const Prometheus__ChunkedSeries *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &prometheus__chunked_series__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t prometheus__chunked_series__pack_to_buffer
+ (const Prometheus__ChunkedSeries *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &prometheus__chunked_series__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Prometheus__ChunkedSeries *
+ prometheus__chunked_series__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Prometheus__ChunkedSeries *)
+ protobuf_c_message_unpack (&prometheus__chunked_series__descriptor,
+ allocator, len, data);
+}
+void prometheus__chunked_series__free_unpacked
+ (Prometheus__ChunkedSeries *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &prometheus__chunked_series__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+static const ProtobufCEnumValue prometheus__metric_metadata__metric_type__enum_values_by_number[8] =
+{
+ { "UNKNOWN", "PROMETHEUS__METRIC_METADATA__METRIC_TYPE__UNKNOWN", 0 },
+ { "COUNTER", "PROMETHEUS__METRIC_METADATA__METRIC_TYPE__COUNTER", 1 },
+ { "GAUGE", "PROMETHEUS__METRIC_METADATA__METRIC_TYPE__GAUGE", 2 },
+ { "HISTOGRAM", "PROMETHEUS__METRIC_METADATA__METRIC_TYPE__HISTOGRAM", 3 },
+ { "GAUGEHISTOGRAM", "PROMETHEUS__METRIC_METADATA__METRIC_TYPE__GAUGEHISTOGRAM", 4 },
+ { "SUMMARY", "PROMETHEUS__METRIC_METADATA__METRIC_TYPE__SUMMARY", 5 },
+ { "INFO", "PROMETHEUS__METRIC_METADATA__METRIC_TYPE__INFO", 6 },
+ { "STATESET", "PROMETHEUS__METRIC_METADATA__METRIC_TYPE__STATESET", 7 },
+};
+static const ProtobufCIntRange prometheus__metric_metadata__metric_type__value_ranges[] = {
+{0, 0},{0, 8}
+};
+static const ProtobufCEnumValueIndex prometheus__metric_metadata__metric_type__enum_values_by_name[8] =
+{
+ { "COUNTER", 1 },
+ { "GAUGE", 2 },
+ { "GAUGEHISTOGRAM", 4 },
+ { "HISTOGRAM", 3 },
+ { "INFO", 6 },
+ { "STATESET", 7 },
+ { "SUMMARY", 5 },
+ { "UNKNOWN", 0 },
+};
+const ProtobufCEnumDescriptor prometheus__metric_metadata__metric_type__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "prometheus.MetricMetadata.MetricType",
+ "MetricType",
+ "Prometheus__MetricMetadata__MetricType",
+ "prometheus",
+ 8,
+ prometheus__metric_metadata__metric_type__enum_values_by_number,
+ 8,
+ prometheus__metric_metadata__metric_type__enum_values_by_name,
+ 1,
+ prometheus__metric_metadata__metric_type__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
+static const ProtobufCFieldDescriptor prometheus__metric_metadata__field_descriptors[4] =
+{
+ {
+ "type",
+ 1,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_ENUM,
+ 0, /* quantifier_offset */
+ offsetof(Prometheus__MetricMetadata, type),
+ &prometheus__metric_metadata__metric_type__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "metric_family_name",
+ 2,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Prometheus__MetricMetadata, metric_family_name),
+ NULL,
+ &protobuf_c_empty_string,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "help",
+ 4,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Prometheus__MetricMetadata, help),
+ NULL,
+ &protobuf_c_empty_string,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "unit",
+ 5,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Prometheus__MetricMetadata, unit),
+ NULL,
+ &protobuf_c_empty_string,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned prometheus__metric_metadata__field_indices_by_name[] = {
+ 2, /* field[2] = help */
+ 1, /* field[1] = metric_family_name */
+ 0, /* field[0] = type */
+ 3, /* field[3] = unit */
+};
+static const ProtobufCIntRange prometheus__metric_metadata__number_ranges[2 + 1] =
+{
+ { 1, 0 },
+ { 4, 2 },
+ { 0, 4 }
+};
+const ProtobufCMessageDescriptor prometheus__metric_metadata__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "prometheus.MetricMetadata",
+ "MetricMetadata",
+ "Prometheus__MetricMetadata",
+ "prometheus",
+ sizeof(Prometheus__MetricMetadata),
+ 4,
+ prometheus__metric_metadata__field_descriptors,
+ prometheus__metric_metadata__field_indices_by_name,
+ 2, prometheus__metric_metadata__number_ranges,
+ (ProtobufCMessageInit) prometheus__metric_metadata__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor prometheus__sample__field_descriptors[2] =
+{
+ {
+ "value",
+ 1,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_DOUBLE,
+ 0, /* quantifier_offset */
+ offsetof(Prometheus__Sample, value),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "timestamp",
+ 2,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_INT64,
+ 0, /* quantifier_offset */
+ offsetof(Prometheus__Sample, timestamp),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned prometheus__sample__field_indices_by_name[] = {
+ 1, /* field[1] = timestamp */
+ 0, /* field[0] = value */
+};
+static const ProtobufCIntRange prometheus__sample__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor prometheus__sample__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "prometheus.Sample",
+ "Sample",
+ "Prometheus__Sample",
+ "prometheus",
+ sizeof(Prometheus__Sample),
+ 2,
+ prometheus__sample__field_descriptors,
+ prometheus__sample__field_indices_by_name,
+ 1, prometheus__sample__number_ranges,
+ (ProtobufCMessageInit) prometheus__sample__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor prometheus__time_series__field_descriptors[2] =
+{
+ {
+ "labels",
+ 1,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Prometheus__TimeSeries, n_labels),
+ offsetof(Prometheus__TimeSeries, labels),
+ &prometheus__label__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "samples",
+ 2,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Prometheus__TimeSeries, n_samples),
+ offsetof(Prometheus__TimeSeries, samples),
+ &prometheus__sample__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned prometheus__time_series__field_indices_by_name[] = {
+ 0, /* field[0] = labels */
+ 1, /* field[1] = samples */
+};
+static const ProtobufCIntRange prometheus__time_series__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor prometheus__time_series__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "prometheus.TimeSeries",
+ "TimeSeries",
+ "Prometheus__TimeSeries",
+ "prometheus",
+ sizeof(Prometheus__TimeSeries),
+ 2,
+ prometheus__time_series__field_descriptors,
+ prometheus__time_series__field_indices_by_name,
+ 1, prometheus__time_series__number_ranges,
+ (ProtobufCMessageInit) prometheus__time_series__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor prometheus__label__field_descriptors[2] =
+{
+ {
+ "name",
+ 1,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Prometheus__Label, name),
+ NULL,
+ &protobuf_c_empty_string,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "value",
+ 2,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Prometheus__Label, value),
+ NULL,
+ &protobuf_c_empty_string,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned prometheus__label__field_indices_by_name[] = {
+ 0, /* field[0] = name */
+ 1, /* field[1] = value */
+};
+static const ProtobufCIntRange prometheus__label__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor prometheus__label__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "prometheus.Label",
+ "Label",
+ "Prometheus__Label",
+ "prometheus",
+ sizeof(Prometheus__Label),
+ 2,
+ prometheus__label__field_descriptors,
+ prometheus__label__field_indices_by_name,
+ 1, prometheus__label__number_ranges,
+ (ProtobufCMessageInit) prometheus__label__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor prometheus__labels__field_descriptors[1] =
+{
+ {
+ "labels",
+ 1,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Prometheus__Labels, n_labels),
+ offsetof(Prometheus__Labels, labels),
+ &prometheus__label__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned prometheus__labels__field_indices_by_name[] = {
+ 0, /* field[0] = labels */
+};
+static const ProtobufCIntRange prometheus__labels__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor prometheus__labels__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "prometheus.Labels",
+ "Labels",
+ "Prometheus__Labels",
+ "prometheus",
+ sizeof(Prometheus__Labels),
+ 1,
+ prometheus__labels__field_descriptors,
+ prometheus__labels__field_indices_by_name,
+ 1, prometheus__labels__number_ranges,
+ (ProtobufCMessageInit) prometheus__labels__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCEnumValue prometheus__label_matcher__type__enum_values_by_number[4] =
+{
+ { "EQ", "PROMETHEUS__LABEL_MATCHER__TYPE__EQ", 0 },
+ { "NEQ", "PROMETHEUS__LABEL_MATCHER__TYPE__NEQ", 1 },
+ { "RE", "PROMETHEUS__LABEL_MATCHER__TYPE__RE", 2 },
+ { "NRE", "PROMETHEUS__LABEL_MATCHER__TYPE__NRE", 3 },
+};
+static const ProtobufCIntRange prometheus__label_matcher__type__value_ranges[] = {
+{0, 0},{0, 4}
+};
+static const ProtobufCEnumValueIndex prometheus__label_matcher__type__enum_values_by_name[4] =
+{
+ { "EQ", 0 },
+ { "NEQ", 1 },
+ { "NRE", 3 },
+ { "RE", 2 },
+};
+const ProtobufCEnumDescriptor prometheus__label_matcher__type__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "prometheus.LabelMatcher.Type",
+ "Type",
+ "Prometheus__LabelMatcher__Type",
+ "prometheus",
+ 4,
+ prometheus__label_matcher__type__enum_values_by_number,
+ 4,
+ prometheus__label_matcher__type__enum_values_by_name,
+ 1,
+ prometheus__label_matcher__type__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
+static const ProtobufCFieldDescriptor prometheus__label_matcher__field_descriptors[3] =
+{
+ {
+ "type",
+ 1,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_ENUM,
+ 0, /* quantifier_offset */
+ offsetof(Prometheus__LabelMatcher, type),
+ &prometheus__label_matcher__type__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "name",
+ 2,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Prometheus__LabelMatcher, name),
+ NULL,
+ &protobuf_c_empty_string,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "value",
+ 3,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Prometheus__LabelMatcher, value),
+ NULL,
+ &protobuf_c_empty_string,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned prometheus__label_matcher__field_indices_by_name[] = {
+ 1, /* field[1] = name */
+ 0, /* field[0] = type */
+ 2, /* field[2] = value */
+};
+static const ProtobufCIntRange prometheus__label_matcher__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 3 }
+};
+const ProtobufCMessageDescriptor prometheus__label_matcher__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "prometheus.LabelMatcher",
+ "LabelMatcher",
+ "Prometheus__LabelMatcher",
+ "prometheus",
+ sizeof(Prometheus__LabelMatcher),
+ 3,
+ prometheus__label_matcher__field_descriptors,
+ prometheus__label_matcher__field_indices_by_name,
+ 1, prometheus__label_matcher__number_ranges,
+ (ProtobufCMessageInit) prometheus__label_matcher__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor prometheus__read_hints__field_descriptors[7] =
+{
+ {
+ "step_ms",
+ 1,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_INT64,
+ 0, /* quantifier_offset */
+ offsetof(Prometheus__ReadHints, step_ms),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "func",
+ 2,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Prometheus__ReadHints, func),
+ NULL,
+ &protobuf_c_empty_string,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "start_ms",
+ 3,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_INT64,
+ 0, /* quantifier_offset */
+ offsetof(Prometheus__ReadHints, start_ms),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "end_ms",
+ 4,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_INT64,
+ 0, /* quantifier_offset */
+ offsetof(Prometheus__ReadHints, end_ms),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "grouping",
+ 5,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_STRING,
+ offsetof(Prometheus__ReadHints, n_grouping),
+ offsetof(Prometheus__ReadHints, grouping),
+ NULL,
+ &protobuf_c_empty_string,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "by",
+ 6,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_BOOL,
+ 0, /* quantifier_offset */
+ offsetof(Prometheus__ReadHints, by),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "range_ms",
+ 7,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_INT64,
+ 0, /* quantifier_offset */
+ offsetof(Prometheus__ReadHints, range_ms),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned prometheus__read_hints__field_indices_by_name[] = {
+ 5, /* field[5] = by */
+ 3, /* field[3] = end_ms */
+ 1, /* field[1] = func */
+ 4, /* field[4] = grouping */
+ 6, /* field[6] = range_ms */
+ 2, /* field[2] = start_ms */
+ 0, /* field[0] = step_ms */
+};
+static const ProtobufCIntRange prometheus__read_hints__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 7 }
+};
+const ProtobufCMessageDescriptor prometheus__read_hints__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "prometheus.ReadHints",
+ "ReadHints",
+ "Prometheus__ReadHints",
+ "prometheus",
+ sizeof(Prometheus__ReadHints),
+ 7,
+ prometheus__read_hints__field_descriptors,
+ prometheus__read_hints__field_indices_by_name,
+ 1, prometheus__read_hints__number_ranges,
+ (ProtobufCMessageInit) prometheus__read_hints__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCEnumValue prometheus__chunk__encoding__enum_values_by_number[2] =
+{
+ { "UNKNOWN", "PROMETHEUS__CHUNK__ENCODING__UNKNOWN", 0 },
+ { "XOR", "PROMETHEUS__CHUNK__ENCODING__XOR", 1 },
+};
+static const ProtobufCIntRange prometheus__chunk__encoding__value_ranges[] = {
+{0, 0},{0, 2}
+};
+static const ProtobufCEnumValueIndex prometheus__chunk__encoding__enum_values_by_name[2] =
+{
+ { "UNKNOWN", 0 },
+ { "XOR", 1 },
+};
+const ProtobufCEnumDescriptor prometheus__chunk__encoding__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "prometheus.Chunk.Encoding",
+ "Encoding",
+ "Prometheus__Chunk__Encoding",
+ "prometheus",
+ 2,
+ prometheus__chunk__encoding__enum_values_by_number,
+ 2,
+ prometheus__chunk__encoding__enum_values_by_name,
+ 1,
+ prometheus__chunk__encoding__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
+static const ProtobufCFieldDescriptor prometheus__chunk__field_descriptors[4] =
+{
+ {
+ "min_time_ms",
+ 1,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_INT64,
+ 0, /* quantifier_offset */
+ offsetof(Prometheus__Chunk, min_time_ms),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "max_time_ms",
+ 2,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_INT64,
+ 0, /* quantifier_offset */
+ offsetof(Prometheus__Chunk, max_time_ms),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "type",
+ 3,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_ENUM,
+ 0, /* quantifier_offset */
+ offsetof(Prometheus__Chunk, type),
+ &prometheus__chunk__encoding__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "data",
+ 4,
+ PROTOBUF_C_LABEL_NONE,
+ PROTOBUF_C_TYPE_BYTES,
+ 0, /* quantifier_offset */
+ offsetof(Prometheus__Chunk, data),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned prometheus__chunk__field_indices_by_name[] = {
+ 3, /* field[3] = data */
+ 1, /* field[1] = max_time_ms */
+ 0, /* field[0] = min_time_ms */
+ 2, /* field[2] = type */
+};
+static const ProtobufCIntRange prometheus__chunk__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 4 }
+};
+const ProtobufCMessageDescriptor prometheus__chunk__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "prometheus.Chunk",
+ "Chunk",
+ "Prometheus__Chunk",
+ "prometheus",
+ sizeof(Prometheus__Chunk),
+ 4,
+ prometheus__chunk__field_descriptors,
+ prometheus__chunk__field_indices_by_name,
+ 1, prometheus__chunk__number_ranges,
+ (ProtobufCMessageInit) prometheus__chunk__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor prometheus__chunked_series__field_descriptors[2] =
+{
+ {
+ "labels",
+ 1,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Prometheus__ChunkedSeries, n_labels),
+ offsetof(Prometheus__ChunkedSeries, labels),
+ &prometheus__label__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "chunks",
+ 2,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Prometheus__ChunkedSeries, n_chunks),
+ offsetof(Prometheus__ChunkedSeries, chunks),
+ &prometheus__chunk__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned prometheus__chunked_series__field_indices_by_name[] = {
+ 1, /* field[1] = chunks */
+ 0, /* field[0] = labels */
+};
+static const ProtobufCIntRange prometheus__chunked_series__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor prometheus__chunked_series__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "prometheus.ChunkedSeries",
+ "ChunkedSeries",
+ "Prometheus__ChunkedSeries",
+ "prometheus",
+ sizeof(Prometheus__ChunkedSeries),
+ 2,
+ prometheus__chunked_series__field_descriptors,
+ prometheus__chunked_series__field_indices_by_name,
+ 1, prometheus__chunked_series__number_ranges,
+ (ProtobufCMessageInit) prometheus__chunked_series__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
diff --git a/src/fluent-bit/lib/cmetrics/tests/CMakeLists.txt b/src/fluent-bit/lib/cmetrics/tests/CMakeLists.txt
new file mode 100644
index 000000000..d7ff19b35
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/tests/CMakeLists.txt
@@ -0,0 +1,51 @@
+set(UNIT_TESTS_FILES
+ basic.c
+ gauge.c
+ counter.c
+ summary.c
+ histogram.c
+ untyped.c
+ atomic_operations.c
+ encoding.c
+ decoding.c
+ cat.c
+ issues.c
+ null_label.c
+ )
+
+if (CMT_BUILD_PROMETHEUS_DECODER)
+ set(UNIT_TESTS_FILES
+ ${UNIT_TESTS_FILES}
+ prometheus_lexer.c
+ prometheus_parser.c)
+endif()
+
+set(CMT_TESTS_DATA_PATH "${CMAKE_CURRENT_SOURCE_DIR}/data")
+configure_file(
+ "${CMAKE_CURRENT_SOURCE_DIR}/cmt_tests_config.h.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/cmt_tests_config.h"
+ )
+
+# Prepare list of unit tests
+foreach(source_file ${UNIT_TESTS_FILES})
+ get_filename_component(source_file_we ${source_file} NAME_WE)
+ set(source_file_we cmt-test-${source_file_we})
+
+ add_executable(
+ ${source_file_we}
+ ${source_file}
+ util.c
+ encode_output.c
+ )
+
+ target_link_libraries(${source_file_we} cmetrics-static cfl-static fluent-otel-proto)
+
+if(NOT CMT_SYSTEM_WINDOWS)
+ target_link_libraries(${source_file_we} pthread)
+endif()
+
+ add_test(NAME ${source_file_we}
+ COMMAND ${CMAKE_BINARY_DIR}/tests/${source_file_we}
+ WORKING_DIRECTORY ${CMAKE_HOME_DIRECTORY}/tests)
+ set_tests_properties(${source_file_we} PROPERTIES LABELS "internal")
+endforeach()
diff --git a/src/fluent-bit/lib/cmetrics/tests/atomic_operations.c b/src/fluent-bit/lib/cmetrics/tests/atomic_operations.c
new file mode 100644
index 000000000..6734a59cf
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/tests/atomic_operations.c
@@ -0,0 +1,120 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_atomic.h>
+
+#if defined (_WIN32) || defined (_WIN64)
+#include <windows.h>
+#else
+#include <pthread.h>
+#endif
+
+#include "cmt_tests.h"
+
+#define THREAD_COUNT 100
+#define CYCLE_COUNT 10000
+#define EXPECTED_VALUE (THREAD_COUNT * CYCLE_COUNT)
+
+uint64_t global_counter;
+
+static inline void add_through_compare_exchange(uint64_t val)
+{
+ uint64_t old;
+ uint64_t new;
+ int result;
+
+ do {
+ old = global_counter;
+ new = old + val;
+
+ result = cmt_atomic_compare_exchange(&global_counter, old, new);
+ }
+ while(0 == result);
+}
+
+void *worker_thread_add_through_compare_exchange(void *ptr)
+{
+ int local_counter;
+
+ for (local_counter = 0 ; local_counter < CYCLE_COUNT ; local_counter++) {
+ add_through_compare_exchange(1);
+ }
+
+ return NULL;
+}
+
+#if defined (_WIN32) || defined (_WIN64)
+
+void test_atomic_operations()
+{
+ HANDLE threads[THREAD_COUNT];
+ DWORD thread_ids[THREAD_COUNT];
+ int thread_index;
+ DWORD result;
+
+ cmt_initialize();
+
+ global_counter = 0;
+
+ for(thread_index = 0 ; thread_index < THREAD_COUNT ; thread_index++)
+ {
+ threads[thread_index] = CreateThread(NULL, 0,
+ (LPTHREAD_START_ROUTINE) worker_thread_add_through_compare_exchange,
+ NULL, 0, &thread_ids[thread_index]);
+ }
+
+ for(thread_index = 0 ; thread_index < THREAD_COUNT ; thread_index++)
+ {
+ result = WaitForSingleObject(threads[thread_index], INFINITE);
+ }
+
+ TEST_CHECK(global_counter == EXPECTED_VALUE);
+}
+
+#else
+
+void test_atomic_operations()
+{
+ pthread_t threads[THREAD_COUNT];
+ int thread_index;
+
+ cmt_initialize();
+
+ global_counter = 0;
+
+ for(thread_index = 0 ; thread_index < THREAD_COUNT ; thread_index++)
+ {
+ pthread_create(&threads[thread_index], NULL,
+ worker_thread_add_through_compare_exchange, NULL);
+ }
+
+ for(thread_index = 0 ; thread_index < THREAD_COUNT ; thread_index++)
+ {
+ pthread_join(threads[thread_index], NULL);
+ }
+
+ TEST_CHECK(global_counter == EXPECTED_VALUE);
+}
+#endif
+
+TEST_LIST = {
+ {"atomic_operations", test_atomic_operations},
+ { 0 }
+};
diff --git a/src/fluent-bit/lib/cmetrics/tests/basic.c b/src/fluent-bit/lib/cmetrics/tests/basic.c
new file mode 100644
index 000000000..459dc65fe
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/tests/basic.c
@@ -0,0 +1,130 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_counter.h>
+#include <cmetrics/cmt_encode_msgpack.h>
+#include <cmetrics/cmt_decode_msgpack.h>
+#include <cmetrics/cmt_encode_text.h>
+
+#include "cmt_tests.h"
+
+static struct cmt *sample_data()
+{
+ double val;
+ uint64_t ts;
+ struct cmt *cmt;
+ struct cmt_counter *c1;
+ struct cmt_counter *c2;
+
+ cmt = cmt_create();
+
+ c1 = cmt_counter_create(cmt, "kubernetes", "network", "load", "Network load",
+ 2, (char *[]) {"hostname", "app"});
+
+ ts = 0;
+
+ cmt_counter_get_val(c1, 0, NULL, &val);
+ cmt_counter_inc(c1, ts, 0, NULL);
+ cmt_counter_add(c1, ts, 2, 0, NULL);
+ cmt_counter_get_val(c1, 0, NULL, &val);
+
+ cmt_counter_inc(c1, ts, 2, (char *[]) {"localhost", "cmetrics"});
+ cmt_counter_get_val(c1, 2, (char *[]) {"localhost", "cmetrics"}, &val);
+ cmt_counter_add(c1, ts, 10.55, 2, (char *[]) {"localhost", "test"});
+ cmt_counter_get_val(c1, 2, (char *[]) {"localhost", "test"}, &val);
+ cmt_counter_set(c1, ts, 12.15, 2, (char *[]) {"localhost", "test"});
+ cmt_counter_set(c1, ts, 1, 2, (char *[]) {"localhost", "test"});
+
+
+ c2 = cmt_counter_create(cmt, "kubernetes", "network", "cpu", "CPU load",
+ 2, (char *[]) {"hostname", "app"});
+
+ ts = 0;
+
+ cmt_counter_get_val(c2, 0, NULL, &val);
+ cmt_counter_inc(c2, ts, 0, NULL);
+ cmt_counter_add(c2, ts, 2, 0, NULL);
+ cmt_counter_get_val(c2, 0, NULL, &val);
+
+ cmt_counter_inc(c2, ts, 2, (char *[]) {"localhost", "cmetrics"});
+ cmt_counter_get_val(c2, 2, (char *[]) {"localhost", "cmetrics"}, &val);
+ cmt_counter_add(c2, ts, 10.55, 2, (char *[]) {"localhost", "test"});
+ cmt_counter_get_val(c2, 2, (char *[]) {"localhost", "test"}, &val);
+ cmt_counter_set(c2, ts, 12.15, 2, (char *[]) {"localhost", "test"});
+ cmt_counter_set(c2, ts, 1, 2, (char *[]) {"localhost", "test"});
+
+ return cmt;
+}
+
+void test_basic()
+{
+ int ret;
+ int error;
+ size_t off = 0;
+ cfl_sds_t text1;
+ cfl_sds_t text2;
+ char *mp_buf;
+ size_t mp_size;
+ struct cmt *cmt1;
+ struct cmt *cmt2;
+
+ cmt1 = sample_data();
+ TEST_CHECK(cmt1 != NULL);
+
+ /* encode to text */
+ text1 = cmt_encode_text_create(cmt1);
+ TEST_CHECK(text1 != NULL);
+
+ /* encode to msgpack */
+ ret = cmt_encode_msgpack_create(cmt1, &mp_buf, &mp_size);
+ TEST_CHECK(ret == 0);
+
+ /* decode msgpack into cmt2 */
+ ret = cmt_decode_msgpack_create(&cmt2, mp_buf, mp_size, &off);
+ TEST_CHECK(ret == 0);
+
+ /* encode cmt2 to text */
+ text2 = cmt_encode_text_create(cmt2);
+ TEST_CHECK(text2 != NULL);
+
+ /* compate both texts */
+ error = 0;
+ if ((cfl_sds_len(text1) != cfl_sds_len(text2)) ||
+ strcmp(text1, text2) != 0) {
+
+ printf("\n");
+ printf("====== EXPECTED OUTPUT =====\n%s", text1);
+ printf("\n\n");
+ printf("====== RECEIVED OUTPUT =====\n%s\n", text2);
+ error = 1;
+ }
+ TEST_CHECK(error == 0);
+
+ cmt_encode_msgpack_destroy(mp_buf);
+ cmt_encode_text_destroy(text1);
+ cmt_encode_text_destroy(text2);
+ cmt_destroy(cmt1);
+ cmt_destroy(cmt2);
+}
+
+TEST_LIST = {
+ {"basic", test_basic},
+ { 0 }
+};
diff --git a/src/fluent-bit/lib/cmetrics/tests/cat.c b/src/fluent-bit/lib/cmetrics/tests/cat.c
new file mode 100644
index 000000000..c1bf982e9
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/tests/cat.c
@@ -0,0 +1,238 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_counter.h>
+#include <cmetrics/cmt_gauge.h>
+#include <cmetrics/cmt_untyped.h>
+#include <cmetrics/cmt_histogram.h>
+#include <cmetrics/cmt_summary.h>
+#include <cmetrics/cmt_encode_text.h>
+#include <cmetrics/cmt_cat.h>
+
+#include "cmt_tests.h"
+
+/* values to observe in a histogram */
+double hist_observe_values[10] = {
+ 0.0 , 1.02, 2.04, 3.06,
+ 4.08, 5.10, 6.12, 7.14,
+ 8.16, 9.18
+ };
+
+/*
+ * histogram bucket values: the values computed in the buckets,
+ * all of them are uint64_t.
+ *
+ * Note that on all examples we use the default buckets values, created manually
+ * and through the API:
+ *
+ * - 11 bucket values
+ * - 1 +Inf bucket value
+ */
+uint64_t hist_buckets_values[12] = {1, 1, 1, 1, 1, 1, 1, 1,
+ 3, 5, 10, 10};
+/* histogram _count value */
+uint64_t hist_count = 10;
+
+/* histogram _sum value */
+double hist_sum = 45.9;
+
+void test_cat()
+{
+ int i;
+ int ret;
+ uint64_t val;
+ uint64_t ts;
+ cfl_sds_t text;
+ double sum;
+ uint64_t count;
+ double q[6];
+ double r[6];
+ struct cmt *cmt1;
+ struct cmt *cmt2;
+ struct cmt *cmt3;
+ struct cmt *cmt4;
+ struct cmt *cmt5;
+ struct cmt_counter *c;
+ struct cmt_gauge *g;
+ struct cmt_untyped *u;
+ struct cmt_histogram *h;
+ struct cmt_histogram_buckets *buckets;
+ struct cmt_summary *s;
+
+ /* cmetrics 1 */
+ cmt1 = cmt_create();
+ TEST_CHECK(cmt1 != NULL);
+
+ c = cmt_counter_create(cmt1, "cmetrics", "test", "cat_counter", "first counter",
+ 2, (char *[]) {"label1", "label2"});
+ TEST_CHECK(c != NULL);
+
+ g = cmt_gauge_create(cmt1, "cmetrics", "test", "cat_gauge", "first gauge",
+ 2, (char *[]) {"label3", "label4"});
+ TEST_CHECK(g != NULL);
+
+ u = cmt_untyped_create(cmt1, "cmetrics", "test", "cat_untyped", "first untyped",
+ 2, (char *[]) {"label5", "label6"});
+ TEST_CHECK(u != NULL);
+
+
+ ts = cfl_time_now();
+ cmt_counter_set(c, ts, 1.1, 2, (char *[]) {"aaa", "bbb"});
+
+ ts = cfl_time_now();
+ cmt_gauge_set(g, ts, 1.2, 2, (char *[]) {"yyy", "xxx"});
+
+ ts = cfl_time_now();
+ cmt_untyped_set(u, ts, 1.3, 2, (char *[]) {"qwe", "asd"});
+
+ /* cmetrics 2 */
+ cmt2 = cmt_create();
+ TEST_CHECK(cmt2 != NULL);
+
+ c = cmt_counter_create(cmt2, "cmetrics", "test", "cat_counter", "second counter",
+ 2, (char *[]) {"label1", "label2"});
+ TEST_CHECK(c != NULL);
+
+ g = cmt_gauge_create(cmt1, "cmetrics", "test", "cat_gauge", "first gauge",
+ 2, (char *[]) {"label3", "label4"});
+ TEST_CHECK(g != NULL);
+
+ ts = cfl_time_now();
+ cmt_counter_set(c, ts, 2.1, 2, (char *[]) {"ccc", "ddd"});
+
+ /* no labels */
+ cmt_counter_set(c, ts, 5, 0, NULL);
+
+ ts = cfl_time_now();
+ cmt_gauge_add(g, ts, 10, 2, (char *[]) {"tyu", "iop"});
+
+ /*
+ * CAT
+ * ---
+ */
+
+ cmt3 = cmt_create();
+ TEST_CHECK(cmt3 != NULL);
+
+ ret = cmt_cat(cmt3, cmt1);
+ TEST_CHECK(ret == 0);
+
+ ret = cmt_cat(cmt3, cmt2);
+ TEST_CHECK(ret == 0);
+
+ /* Create buckets */
+ buckets = cmt_histogram_buckets_create(11,
+ 0.005, 0.01, 0.025, 0.05,
+ 0.1, 0.25, 0.5, 1.0, 2.5,
+ 5.0, 10.0);
+ TEST_CHECK(buckets != NULL);
+
+ cmt4 = cmt_create();
+ TEST_CHECK(cmt4 != NULL);
+
+ /* Create a histogram metric type */
+ h = cmt_histogram_create(cmt4,
+ "k8s", "network", "load", "Network load",
+ buckets,
+ 1, (char *[]) {"my_label"});
+ TEST_CHECK(h != NULL);
+
+ ts = cfl_time_now();
+ for (i = 0; i < sizeof(hist_observe_values)/(sizeof(double)); i++) {
+ val = hist_observe_values[i];
+ cmt_histogram_observe(h, ts, val, 1, (char *[]) {"my_label"});
+ }
+
+ ret = cmt_cat(cmt4, cmt3);
+ TEST_CHECK(ret == 0);
+
+ cmt5 = cmt_create();
+ TEST_CHECK(cmt5 != NULL);
+
+ ts = cfl_time_now();
+
+ /* set quantiles */
+ q[0] = 0.1;
+ q[1] = 0.2;
+ q[2] = 0.3;
+ q[3] = 0.4;
+ q[4] = 0.5;
+ q[5] = 1.0;
+
+ r[0] = 1;
+ r[1] = 2;
+ r[2] = 3;
+ r[3] = 4;
+ r[4] = 5;
+ r[5] = 6;
+
+ /* Create a gauge metric type */
+ s = cmt_summary_create(cmt5,
+ "spring", "kafka_listener", "seconds", "Kafka Listener Timer",
+ 6, q,
+ 3, (char *[]) {"exception", "name", "result"});
+ TEST_CHECK(s != NULL);
+
+ /* no quantiles, labels */
+ sum = 0.0;
+ count = 1;
+
+ cmt_summary_set_default(s, ts, NULL, sum, count,
+ 3, (char *[]) {"ListenerExecutionFailedException",
+ "org.springframework.kafka.KafkaListenerEndpointContainer#0-0",
+ "failure"});
+
+ /* no quantiles, labels */
+ sum = 0.1;
+ count = 2;
+ cmt_summary_set_default(s, ts, NULL, sum, count,
+ 3, (char *[]) {"none",
+ "org.springframework.kafka.KafkaListenerEndpointContainer#0-0",
+ "success"});
+
+ /* quantiles, labels */
+ sum = 0.2;
+ count = 3;
+ cmt_summary_set_default(s, ts, r, sum, count,
+ 3, (char *[]) {"extra test",
+ "org.springframework.kafka.KafkaListenerEndpointContainer#0-0",
+ "success"});
+
+ ret = cmt_cat(cmt5, cmt4);
+ TEST_CHECK(ret == 0);
+
+ /* check output */
+ text = cmt_encode_text_create(cmt5);
+ printf("====>\n%s\n", text);
+
+ cmt_encode_text_destroy(text);
+
+ /* destroy contexts */
+ cmt_destroy(cmt1);
+ cmt_destroy(cmt2);
+ cmt_destroy(cmt3);
+ cmt_destroy(cmt4);
+ cmt_destroy(cmt5);
+}
+
+TEST_LIST = {
+ {"cat", test_cat},
+ { 0 }
+};
diff --git a/src/fluent-bit/lib/cmetrics/tests/cmt_tests.h b/src/fluent-bit/lib/cmetrics/tests/cmt_tests.h
new file mode 100644
index 000000000..dd8dad801
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/tests/cmt_tests.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#ifndef CMT_TESTS_H
+#define CMT_TESTS_H
+
+#include "lib/acutest/acutest.h"
+
+#define MSGPACK_STABILITY_TEST_ITERATION_COUNT 1000
+#define MSGPACK_PARTIAL_PROCESSING_ELEMENT_COUNT 20
+
+#include "tests/cmt_tests_config.h"
+#include "encode_output.h"
+
+#include <cmetrics/cmetrics.h>
+
+cfl_sds_t read_file(const char *path);
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/tests/cmt_tests_config.h.in b/src/fluent-bit/lib/cmetrics/tests/cmt_tests_config.h.in
new file mode 100644
index 000000000..fcc5a75b6
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/tests/cmt_tests_config.h.in
@@ -0,0 +1,25 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#ifndef CMT_TESTS_CONFIG_H
+#define CMT_TESTS_CONFIG_H
+
+#define CMT_TESTS_DATA_PATH "@CMT_TESTS_DATA_PATH@"
+
+#endif
diff --git a/src/fluent-bit/lib/cmetrics/tests/counter.c b/src/fluent-bit/lib/cmetrics/tests/counter.c
new file mode 100644
index 000000000..cfe83c6cf
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/tests/counter.c
@@ -0,0 +1,264 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_counter.h>
+#include <cmetrics/cmt_encode_msgpack.h>
+#include <cmetrics/cmt_decode_msgpack.h>
+#include <cmetrics/cmt_encode_prometheus.h>
+#include <cmetrics/cmt_encode_text.h>
+
+#include "cmt_tests.h"
+
+static struct cmt *generate_encoder_test_data()
+{
+ double val;
+ uint64_t ts;
+ struct cmt *cmt;
+ struct cmt_counter *c;
+
+ printf("version: %s", cmt_version());
+ cmt = cmt_create();
+
+ c = cmt_counter_create(cmt, "kubernetes", "network", "load", "Network load",
+ 2, (char *[]) {"hostname", "app"});
+
+ ts = cfl_time_now();
+
+ cmt_counter_get_val(c, 0, NULL, &val);
+ cmt_counter_inc(c, ts, 0, NULL);
+ cmt_counter_add(c, ts, 2, 0, NULL);
+ cmt_counter_get_val(c, 0, NULL, &val);
+
+ cmt_counter_inc(c, ts, 2, (char *[]) {"localhost", "cmetrics"});
+ cmt_counter_get_val(c, 2, (char *[]) {"localhost", "cmetrics"}, &val);
+ cmt_counter_add(c, ts, 10.55, 2, (char *[]) {"localhost", "test"});
+ cmt_counter_get_val(c, 2, (char *[]) {"localhost", "test"}, &val);
+ cmt_counter_set(c, ts, 12.15, 2, (char *[]) {"localhost", "test"});
+ cmt_counter_set(c, ts, 1, 2, (char *[]) {"localhost", "test"});
+
+ return cmt;
+}
+
+void test_msgpack()
+{
+ struct cmt *cmt = NULL;
+ struct cmt *cmt2 = NULL;
+ int result = 0;
+ size_t offset = 0;
+ char *msgpack_buffer_a = NULL;
+ char *msgpack_buffer_b = NULL;
+ size_t msgpack_buffer_size_a = 0;
+ size_t msgpack_buffer_size_b = 0;
+
+ cmt_initialize();
+
+ cmt = generate_encoder_test_data();
+ TEST_CHECK(NULL != cmt);
+
+ result = cmt_encode_msgpack_create(cmt, &msgpack_buffer_a, &msgpack_buffer_size_a);
+ TEST_CHECK(0 == result);
+
+ result = cmt_decode_msgpack_create(&cmt2, msgpack_buffer_a, msgpack_buffer_size_a,
+ &offset);
+ TEST_CHECK(0 == result);
+
+ result = cmt_encode_msgpack_create(cmt, &msgpack_buffer_b, &msgpack_buffer_size_b);
+ TEST_CHECK(0 == result);
+
+ TEST_CHECK(msgpack_buffer_size_a == msgpack_buffer_size_b);
+
+ result = memcmp(msgpack_buffer_a, msgpack_buffer_b, msgpack_buffer_size_a);
+
+ cmt_destroy(cmt);
+ cmt_decode_msgpack_destroy(cmt2);
+ cmt_encode_msgpack_destroy(msgpack_buffer_a);
+ cmt_encode_msgpack_destroy(msgpack_buffer_b);
+}
+
+void test_prometheus()
+{
+ struct cmt *cmt = NULL;
+ cfl_sds_t prom = NULL;
+
+ cmt_initialize();
+
+ cmt = generate_encoder_test_data();
+ TEST_CHECK(NULL != cmt);
+
+ prom = cmt_encode_prometheus_create(cmt, CMT_TRUE);
+ TEST_CHECK(NULL != prom);
+ printf("%s\n", prom);
+
+ cmt_destroy(cmt);
+ cmt_encode_prometheus_destroy(prom);
+}
+
+void test_text()
+{
+ struct cmt *cmt = NULL;
+ cfl_sds_t text = NULL;
+
+ cmt_initialize();
+
+ cmt = generate_encoder_test_data();
+ TEST_CHECK(cmt != NULL);
+
+ text = cmt_encode_text_create(cmt);
+ TEST_CHECK(text != NULL);
+
+ cmt_destroy(cmt);
+ cmt_encode_text_destroy(text);
+}
+
+
+void test_counter()
+{
+ int ret;
+ double val = 1;
+ uint64_t ts;
+ struct cmt *cmt;
+ struct cmt_counter *c;
+
+ cmt_initialize();
+
+ cmt = cmt_create();
+ TEST_CHECK(cmt != NULL);
+
+ /* Create a counter metric type */
+ c = cmt_counter_create(cmt, "kubernetes", "network", "load", "Network load",
+ 0, NULL);
+ TEST_CHECK(c != NULL);
+
+ /* Timestamp */
+ ts = cfl_time_now();
+
+ /* Default value */
+ ret = cmt_counter_get_val(c, 0, NULL, &val);
+ TEST_CHECK(ret == 0);
+ TEST_CHECK(val == 0.0);
+
+ /* Increment by one */
+ cmt_counter_inc(c, ts, 0, NULL);
+ ret = cmt_counter_get_val(c, 0, NULL, &val);
+ TEST_CHECK(val == 1.0);
+
+ /* Add two */
+ cmt_counter_add(c, ts, 2, 0, NULL);
+ ret = cmt_counter_get_val(c, 0, NULL, &val);
+
+ TEST_CHECK(ret == 0);
+ TEST_CHECK(val == 3.0);
+
+ cmt_destroy(cmt);
+}
+
+void test_labels()
+{
+ int ret;
+ double val;
+ uint64_t ts;
+ struct cmt *cmt;
+ struct cmt_counter *c;
+
+ cmt_initialize();
+
+ cmt = cmt_create();
+ TEST_CHECK(cmt != NULL);
+
+ /* Create a counter metric type */
+ c = cmt_counter_create(cmt, "kubernetes", "network", "load", "Network load",
+ 2, (char *[]) {"hostname", "app"});
+ TEST_CHECK(c != NULL);
+
+ /* Timestamp */
+ ts = cfl_time_now();
+
+ /*
+ * Test 1: hash zero (no labels)
+ * -----------------------------
+ */
+
+ /*
+ * Default value: this call should fail since the metric has not been
+ * initialized.
+ */
+ ret = cmt_counter_get_val(c, 0, NULL, &val);
+ TEST_CHECK(ret == -1);
+
+ /* Increment hash zero by 1 */
+ ret = cmt_counter_inc(c, ts, 0, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* validate value */
+ ret = cmt_counter_get_val(c, 0, NULL, &val);
+ TEST_CHECK(ret == 0);
+ TEST_CHECK(val == 1.0);
+
+ /* Add two */
+ ret = cmt_counter_add(c, ts, 2, 0, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Check that hash zero val is 3.0 */
+ ret = cmt_counter_get_val(c, 0, NULL, &val);
+ TEST_CHECK(ret == 0);
+ TEST_CHECK(val == 3.0);
+
+ /*
+ * Test 2: custom labels
+ * ---------------------
+ */
+
+ /* Increment custom metric */
+ ret = cmt_counter_inc(c, ts, 2, (char *[]) {"localhost", "cmetrics"});
+ TEST_CHECK(ret == 0);
+
+ /* Check val = 1 */
+ ret = cmt_counter_get_val(c, 2, (char *[]) {"localhost", "cmetrics"}, &val);
+ TEST_CHECK(ret == 0);
+ TEST_CHECK(val == 1.000);
+
+ /* Add 10 to another metric using a different second label */
+ ret = cmt_counter_add(c, ts, 10.55, 2, (char *[]) {"localhost", "test"});
+ TEST_CHECK(ret == 0);
+
+ /* Validate the value */
+ ret = cmt_counter_get_val(c, 2, (char *[]) {"localhost", "test"}, &val);
+ TEST_CHECK(ret == 0);
+ TEST_CHECK(val == 10.55);
+
+ /* Valid counter set */
+ ret = cmt_counter_set(c, ts, 12.15, 2, (char *[]) {"localhost", "test"});
+ TEST_CHECK(ret == 0);
+
+ /* Invalid counter set */
+ ret = cmt_counter_set(c, ts, 1, 2, (char *[]) {"localhost", "test"});
+ TEST_CHECK(ret == -1);
+
+ cmt_destroy(cmt);
+}
+
+TEST_LIST = {
+ {"basic", test_counter},
+ {"labels", test_labels},
+ {"msgpack", test_msgpack},
+ {"prometheus", test_prometheus},
+ {"text", test_text},
+ { 0 }
+};
diff --git a/src/fluent-bit/lib/cmetrics/tests/data/histogram_different_label_count.txt b/src/fluent-bit/lib/cmetrics/tests/data/histogram_different_label_count.txt
new file mode 100644
index 000000000..3aab53682
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/tests/data/histogram_different_label_count.txt
@@ -0,0 +1,14 @@
+# HELP k8s_network_load Network load
+# TYPE k8s_network_load histogram
+k8s_network_load_bucket{le="0.05"} 0 0
+k8s_network_load_bucket{le="5.0"} 1 0
+k8s_network_load_bucket{le="10.0"} 2 0
+k8s_network_load_bucket{le="+Inf"} 3 0
+k8s_network_load_sum 1013 0
+k8s_network_load_count 3 0
+k8s_network_load_bucket{le="0.05",my_label="my_val"} 0 0
+k8s_network_load_bucket{le="5.0",my_label="my_val"} 1 0
+k8s_network_load_bucket{le="10.0",my_label="my_val"} 2 0
+k8s_network_load_bucket{le="+Inf",my_label="my_val"} 3 0
+k8s_network_load_sum{my_label="my_val"} 1013 0
+k8s_network_load_count{my_label="my_val"} 3 0
diff --git a/src/fluent-bit/lib/cmetrics/tests/data/issue_6534.txt b/src/fluent-bit/lib/cmetrics/tests/data/issue_6534.txt
new file mode 100644
index 000000000..d19ebe99a
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/tests/data/issue_6534.txt
@@ -0,0 +1,165 @@
+# HELP dotnet_threadpool_num_threads The number of active threads in the thread pool
+# TYPE dotnet_threadpool_num_threads gauge
+dotnet_threadpool_num_threads 6
+# HELP dotnet_jit_method_total Total number of methods compiled by the JIT compiler
+# TYPE dotnet_jit_method_total counter
+dotnet_jit_method_total 6476
+# HELP dotnet_gc_heap_size_bytes The current size of all heaps (only updated after a garbage collection)
+# TYPE dotnet_gc_heap_size_bytes gauge
+dotnet_gc_heap_size_bytes{gc_generation="0"} 24
+dotnet_gc_heap_size_bytes{gc_generation="loh"} 550280
+dotnet_gc_heap_size_bytes{gc_generation="2"} 2625704
+dotnet_gc_heap_size_bytes{gc_generation="1"} 226944
+# HELP dotnet_gc_collection_count_total Counts the number of garbage collections that have occurred, broken down by generation number and the reason for the collection.
+# TYPE dotnet_gc_collection_count_total counter
+dotnet_gc_collection_count_total{gc_generation="1",gc_reason="alloc_small"} 133
+dotnet_gc_collection_count_total{gc_generation="0",gc_reason="alloc_small"} 618
+dotnet_gc_collection_count_total{gc_generation="2",gc_reason="alloc_small"} 8
+# HELP dotnet_threadpool_timer_count The number of timers active
+# TYPE dotnet_threadpool_timer_count gauge
+dotnet_threadpool_timer_count 5
+# HELP dotnet_threadpool_queue_length Measures the queue length of the thread pool. Values greater than 0 indicate a backlog of work for the threadpool to process.
+# TYPE dotnet_threadpool_queue_length histogram
+dotnet_threadpool_queue_length_sum 5
+dotnet_threadpool_queue_length_count 321733
+dotnet_threadpool_queue_length_bucket{le="0"} 321728
+dotnet_threadpool_queue_length_bucket{le="1"} 321733
+dotnet_threadpool_queue_length_bucket{le="10"} 321733
+dotnet_threadpool_queue_length_bucket{le="100"} 321733
+dotnet_threadpool_queue_length_bucket{le="1000"} 321733
+dotnet_threadpool_queue_length_bucket{le="+Inf"} 321733
+# HELP dotnet_gc_cpu_ratio The percentage of process CPU time spent running garbage collections
+# TYPE dotnet_gc_cpu_ratio gauge
+dotnet_gc_cpu_ratio 0
+# HELP dotnet_collection_count_total GC collection count
+# TYPE dotnet_collection_count_total counter
+dotnet_collection_count_total{generation="0"} 759
+dotnet_collection_count_total{generation="2"} 8
+dotnet_collection_count_total{generation="1"} 141
+# HELP dotnet_threadpool_adjustments_total The total number of changes made to the size of the thread pool, labeled by the reason for change
+# TYPE dotnet_threadpool_adjustments_total counter
+dotnet_threadpool_adjustments_total{adjustment_reason="starvation"} 957
+dotnet_threadpool_adjustments_total{adjustment_reason="warmup"} 4
+dotnet_threadpool_adjustments_total{adjustment_reason="thread_timed_out"} 2409
+dotnet_threadpool_adjustments_total{adjustment_reason="climbing_move"} 93458
+# HELP process_open_handles Number of open handles
+# TYPE process_open_handles gauge
+process_open_handles 264
+# HELP dotnet_gc_pause_ratio The percentage of time the process spent paused for garbage collection
+# TYPE dotnet_gc_pause_ratio gauge
+dotnet_gc_pause_ratio 0
+# HELP process_cpu_seconds_total Total user and system CPU time spent in seconds.
+# TYPE process_cpu_seconds_total counter
+process_cpu_seconds_total 1450.85
+# HELP dotnet_gc_pause_seconds The amount of time execution was paused for garbage collection
+# TYPE dotnet_gc_pause_seconds histogram
+dotnet_gc_pause_seconds_sum 1.3192573999999997
+dotnet_gc_pause_seconds_count 759
+dotnet_gc_pause_seconds_bucket{le="0.001"} 7
+dotnet_gc_pause_seconds_bucket{le="0.01"} 747
+dotnet_gc_pause_seconds_bucket{le="0.05"} 759
+dotnet_gc_pause_seconds_bucket{le="0.1"} 759
+dotnet_gc_pause_seconds_bucket{le="0.5"} 759
+dotnet_gc_pause_seconds_bucket{le="1"} 759
+dotnet_gc_pause_seconds_bucket{le="10"} 759
+dotnet_gc_pause_seconds_bucket{le="+Inf"} 759
+# HELP dotnet_jit_il_bytes Total bytes of IL compiled by the JIT compiler
+# TYPE dotnet_jit_il_bytes gauge
+dotnet_jit_il_bytes 487850
+# HELP dotnet_gc_collection_seconds The amount of time spent running garbage collections
+# TYPE dotnet_gc_collection_seconds histogram
+dotnet_gc_collection_seconds_sum{gc_generation="1",gc_type="non_concurrent_gc"} 0.20421500000000006
+dotnet_gc_collection_seconds_count{gc_generation="1",gc_type="non_concurrent_gc"} 133
+dotnet_gc_collection_seconds_bucket{gc_generation="1",gc_type="non_concurrent_gc",le="0.001"} 3
+dotnet_gc_collection_seconds_bucket{gc_generation="1",gc_type="non_concurrent_gc",le="0.01"} 133
+dotnet_gc_collection_seconds_bucket{gc_generation="1",gc_type="non_concurrent_gc",le="0.05"} 133
+dotnet_gc_collection_seconds_bucket{gc_generation="1",gc_type="non_concurrent_gc",le="0.1"} 133
+dotnet_gc_collection_seconds_bucket{gc_generation="1",gc_type="non_concurrent_gc",le="0.5"} 133
+dotnet_gc_collection_seconds_bucket{gc_generation="1",gc_type="non_concurrent_gc",le="1"} 133
+dotnet_gc_collection_seconds_bucket{gc_generation="1",gc_type="non_concurrent_gc",le="10"} 133
+dotnet_gc_collection_seconds_bucket{gc_generation="1",gc_type="non_concurrent_gc",le="+Inf"} 133
+dotnet_gc_collection_seconds_sum 0
+dotnet_gc_collection_seconds_count 0
+dotnet_gc_collection_seconds_bucket{le="0.001"} 0
+dotnet_gc_collection_seconds_bucket{le="0.01"} 0
+dotnet_gc_collection_seconds_bucket{le="0.05"} 0
+dotnet_gc_collection_seconds_bucket{le="0.1"} 0
+dotnet_gc_collection_seconds_bucket{le="0.5"} 0
+dotnet_gc_collection_seconds_bucket{le="1"} 0
+dotnet_gc_collection_seconds_bucket{le="10"} 0
+dotnet_gc_collection_seconds_bucket{le="+Inf"} 0
+dotnet_gc_collection_seconds_sum{gc_generation="2",gc_type="non_concurrent_gc"} 0.09344780000000001
+dotnet_gc_collection_seconds_count{gc_generation="2",gc_type="non_concurrent_gc"} 8
+dotnet_gc_collection_seconds_bucket{gc_generation="2",gc_type="non_concurrent_gc",le="0.001"} 0
+dotnet_gc_collection_seconds_bucket{gc_generation="2",gc_type="non_concurrent_gc",le="0.01"} 0
+dotnet_gc_collection_seconds_bucket{gc_generation="2",gc_type="non_concurrent_gc",le="0.05"} 8
+dotnet_gc_collection_seconds_bucket{gc_generation="2",gc_type="non_concurrent_gc",le="0.1"} 8
+dotnet_gc_collection_seconds_bucket{gc_generation="2",gc_type="non_concurrent_gc",le="0.5"} 8
+dotnet_gc_collection_seconds_bucket{gc_generation="2",gc_type="non_concurrent_gc",le="1"} 8
+dotnet_gc_collection_seconds_bucket{gc_generation="2",gc_type="non_concurrent_gc",le="10"} 8
+dotnet_gc_collection_seconds_bucket{gc_generation="2",gc_type="non_concurrent_gc",le="+Inf"} 8
+dotnet_gc_collection_seconds_sum{gc_generation="0",gc_type="non_concurrent_gc"} 0.855451900000001
+dotnet_gc_collection_seconds_count{gc_generation="0",gc_type="non_concurrent_gc"} 618
+dotnet_gc_collection_seconds_bucket{gc_generation="0",gc_type="non_concurrent_gc",le="0.001"} 127
+dotnet_gc_collection_seconds_bucket{gc_generation="0",gc_type="non_concurrent_gc",le="0.01"} 617
+dotnet_gc_collection_seconds_bucket{gc_generation="0",gc_type="non_concurrent_gc",le="0.05"} 618
+dotnet_gc_collection_seconds_bucket{gc_generation="0",gc_type="non_concurrent_gc",le="0.1"} 618
+dotnet_gc_collection_seconds_bucket{gc_generation="0",gc_type="non_concurrent_gc",le="0.5"} 618
+dotnet_gc_collection_seconds_bucket{gc_generation="0",gc_type="non_concurrent_gc",le="1"} 618
+dotnet_gc_collection_seconds_bucket{gc_generation="0",gc_type="non_concurrent_gc",le="10"} 618
+dotnet_gc_collection_seconds_bucket{gc_generation="0",gc_type="non_concurrent_gc",le="+Inf"} 618
+# HELP dotnet_contention_total The number of locks contended
+# TYPE dotnet_contention_total counter
+dotnet_contention_total 6758
+# HELP dotnet_contention_seconds_total The total amount of time spent contending locks
+# TYPE dotnet_contention_seconds_total counter
+dotnet_contention_seconds_total 1.0246322000000074
+# HELP dotnet_gc_memory_total_available_bytes The upper limit on the amount of physical memory .NET can allocate to
+# TYPE dotnet_gc_memory_total_available_bytes gauge
+dotnet_gc_memory_total_available_bytes 805306368
+# HELP dotnet_build_info Build information about prometheus-net.DotNetRuntime and the environment
+# TYPE dotnet_build_info gauge
+dotnet_build_info{version="4.2.4.0",target_framework=".NETCoreApp,Version=v6.0",runtime_version=".NET 6.0.11",os_version="Linux 5.4.0-1094-azure #100~18.04.1-Ubuntu SMP Mon Oct 17 11:44:30 UTC 2022",process_architecture="X64",gc_mode="Workstation"} 1
+# HELP dotnet_internal_recycle_count prometheus-net.DotNetRuntime internal metric. Counts the number of times the underlying event listeners have been recycled
+# TYPE dotnet_internal_recycle_count counter
+dotnet_internal_recycle_count 3
+# HELP dotnet_gc_allocated_bytes_total The total number of bytes allocated on the managed heap
+# TYPE dotnet_gc_allocated_bytes_total counter
+dotnet_gc_allocated_bytes_total{gc_heap="soh"} 19853322336
+# HELP process_start_time_seconds Start time of the process since unix epoch in seconds.
+# TYPE process_start_time_seconds gauge
+process_start_time_seconds 1670526623.05
+# HELP process_cpu_count The number of processor cores available to this process.
+# TYPE process_cpu_count gauge
+process_cpu_count 1
+# HELP dotnet_gc_pinned_objects The number of pinned objects
+# TYPE dotnet_gc_pinned_objects gauge
+dotnet_gc_pinned_objects 0
+# HELP dotnet_total_memory_bytes Total known allocated memory
+# TYPE dotnet_total_memory_bytes gauge
+dotnet_total_memory_bytes 20979896
+# HELP process_virtual_memory_bytes Virtual memory size in bytes.
+# TYPE process_virtual_memory_bytes gauge
+process_virtual_memory_bytes 8562679808
+# HELP dotnet_threadpool_throughput_total The total number of work items that have finished execution in the thread pool
+# TYPE dotnet_threadpool_throughput_total counter
+dotnet_threadpool_throughput_total 3381388
+# HELP process_working_set_bytes Process working set
+# TYPE process_working_set_bytes gauge
+process_working_set_bytes 135118848
+# HELP process_num_threads Total number of threads
+# TYPE process_num_threads gauge
+process_num_threads 21
+# HELP dotnet_gc_finalization_queue_length The number of objects waiting to be finalized
+# TYPE dotnet_gc_finalization_queue_length gauge
+dotnet_gc_finalization_queue_length 15
+# HELP process_private_memory_bytes Process private memory size
+# TYPE process_private_memory_bytes gauge
+process_private_memory_bytes 247390208
+# HELP dotnet_exceptions_total Count of exceptions thrown, broken down by type
+# TYPE dotnet_exceptions_total counter
+dotnet_exceptions_total{type="System.Net.Http.HttpRequestException"} 792
+dotnet_exceptions_total{type="System.ObjectDisposedException"} 11977
+dotnet_exceptions_total{type="System.IO.DirectoryNotFoundException"} 14
+dotnet_exceptions_total{type="System.Net.Sockets.SocketException"} 258287
+dotnet_exceptions_total{type="Grpc.Core.RpcException"} 72
diff --git a/src/fluent-bit/lib/cmetrics/tests/data/issue_71.txt b/src/fluent-bit/lib/cmetrics/tests/data/issue_71.txt
new file mode 100644
index 000000000..2f16af55d
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/tests/data/issue_71.txt
@@ -0,0 +1,11 @@
+# HELP node_power_supply_info info of /sys/class/power_supply/<power_supply>.
+# TYPE node_power_supply_info gauge
+node_power_supply_info{power_supply="AC",type="Mains"} 1
+node_power_supply_info{power_supply="ucsi-source-psy-USBC000:001",type="USB",usb_type="[C] PD PD_PPS"} 1
+node_power_supply_info{power_supply="ucsi-source-psy-USBC000:002",type="USB",usb_type="C [PD] PD_PPS"} 1
+node_power_supply_info{capacity_level="Normal",manufacturer="SMP",model_name="02DL005",power_supply="BAT0",serial_number="4195",status="Discharging",technology="Li-poly",type="Battery"} 1
+# HELP node_power_supply_online online value of /sys/class/power_supply/<power_supply>.
+# TYPE node_power_supply_online gauge
+node_power_supply_online{power_supply="AC"} 0
+node_power_supply_online{power_supply="ucsi-source-psy-USBC000:001"} 0
+node_power_supply_online{power_supply="ucsi-source-psy-USBC000:002"} 1
diff --git a/src/fluent-bit/lib/cmetrics/tests/data/issue_fluent_bit_5541.txt b/src/fluent-bit/lib/cmetrics/tests/data/issue_fluent_bit_5541.txt
new file mode 100644
index 000000000..b51a46c5d
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/tests/data/issue_fluent_bit_5541.txt
@@ -0,0 +1,19 @@
+# HELP http_request_duration_seconds HTTP request latency (seconds)
+# TYPE http_request_duration_seconds histogram
+http_request_duration_seconds_bucket{le="0.005"} 2.0
+http_request_duration_seconds_bucket{le="0.01"} 2.0
+http_request_duration_seconds_bucket{le="0.025"} 2.0
+http_request_duration_seconds_bucket{le="0.05"} 2.0
+http_request_duration_seconds_bucket{le="0.075"} 2.0
+http_request_duration_seconds_bucket{le="0.1"} 2.0
+http_request_duration_seconds_bucket{le="0.25"} 2.0
+http_request_duration_seconds_bucket{le="0.5"} 2.0
+http_request_duration_seconds_bucket{le="0.75"} 2.0
+http_request_duration_seconds_bucket{le="1.0"} 2.0
+http_request_duration_seconds_bucket{le="2.5"} 2.0
+http_request_duration_seconds_bucket{le="5.0"} 2.0
+http_request_duration_seconds_bucket{le="7.5"} 2.0
+http_request_duration_seconds_bucket{le="10.0"} 2.0
+http_request_duration_seconds_bucket{le="+Inf"} 2.0
+http_request_duration_seconds_count 2.0
+http_request_duration_seconds_sum 0.0006913102697581053
diff --git a/src/fluent-bit/lib/cmetrics/tests/data/issue_fluent_bit_5894.txt b/src/fluent-bit/lib/cmetrics/tests/data/issue_fluent_bit_5894.txt
new file mode 100644
index 000000000..482494053
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/tests/data/issue_fluent_bit_5894.txt
@@ -0,0 +1,141 @@
+# HELP process_start_time_seconds Start time of the process since unix epoch.
+# TYPE process_start_time_seconds gauge
+process_start_time_seconds 1.660594096832E9
+# HELP hikaricp_connections_timeout_total Connection timeout total count
+# TYPE hikaricp_connections_timeout_total counter
+hikaricp_connections_timeout_total{pool="mcadb",} 0.0
+# HELP spring_kafka_listener_seconds_max Kafka Listener Timer
+# TYPE spring_kafka_listener_seconds_max gauge
+spring_kafka_listener_seconds_max{exception="ListenerExecutionFailedException",name="org.springframework.kafka.KafkaListenerEndpointContainer#0-0",result="failure",} 0.0
+spring_kafka_listener_seconds_max{exception="none",name="org.springframework.kafka.KafkaListenerEndpointContainer#0-0",result="success",} 0.0
+# HELP spring_kafka_listener_seconds Kafka Listener Timer
+# TYPE spring_kafka_listener_seconds summary
+spring_kafka_listener_seconds_count{exception="ListenerExecutionFailedException",name="org.springframework.kafka.KafkaListenerEndpointContainer#0-0",result="failure",} 0.0
+spring_kafka_listener_seconds_sum{exception="ListenerExecutionFailedException",name="org.springframework.kafka.KafkaListenerEndpointContainer#0-0",result="failure",} 0.0
+spring_kafka_listener_seconds_count{exception="none",name="org.springframework.kafka.KafkaListenerEndpointContainer#0-0",result="success",} 0.0
+spring_kafka_listener_seconds_sum{exception="none",name="org.springframework.kafka.KafkaListenerEndpointContainer#0-0",result="success",} 0.0
+# HELP process_files_max_files The maximum file descriptor count
+# TYPE process_files_max_files gauge
+process_files_max_files 1048576.0
+# HELP hikaricp_connections_pending Pending threads
+# TYPE hikaricp_connections_pending gauge
+hikaricp_connections_pending{pool="mcadb",} 0.0
+# HELP jvm_memory_committed_bytes The amount of memory in bytes that is committed for the Java virtual machine to use
+# TYPE jvm_memory_committed_bytes gauge
+jvm_memory_committed_bytes{area="nonheap",id="CodeHeap 'profiled nmethods'",} 1.605632E7
+jvm_memory_committed_bytes{area="heap",id="G1 Survivor Space",} 2.097152E7
+jvm_memory_committed_bytes{area="heap",id="G1 Old Gen",} 2.32783872E8
+jvm_memory_committed_bytes{area="nonheap",id="Metaspace",} 1.03374848E8
+jvm_memory_committed_bytes{area="nonheap",id="CodeHeap 'non-nmethods'",} 4390912.0
+jvm_memory_committed_bytes{area="heap",id="G1 Eden Space",} 3.73293056E8
+jvm_memory_committed_bytes{area="nonheap",id="Compressed Class Space",} 1.3500416E7
+jvm_memory_committed_bytes{area="nonheap",id="CodeHeap 'non-profiled nmethods'",} 4521984.0
+# HELP process_files_open_files The open file descriptor count
+# TYPE process_files_open_files gauge
+process_files_open_files 290.0
+# HELP rabbitmq_consumed_total
+# TYPE rabbitmq_consumed_total counter
+rabbitmq_consumed_total{name="rabbit",} 0.0
+# HELP hikaricp_connections_usage_seconds Connection usage time
+# TYPE hikaricp_connections_usage_seconds summary
+hikaricp_connections_usage_seconds_count{pool="mcadb",} 0.0
+hikaricp_connections_usage_seconds_sum{pool="mcadb",} 0.0
+# HELP kafka_consumer_sync_time_max_seconds The max time taken for a group sync.
+# TYPE kafka_consumer_sync_time_max_seconds gauge
+kafka_consumer_sync_time_max_seconds{client_id="consumer-1",} NaN
+# HELP kafka_consumer_fetch_latency_avg_seconds The average time taken for a fetch request.
+# TYPE kafka_consumer_fetch_latency_avg_seconds gauge
+kafka_consumer_fetch_latency_avg_seconds{client_id="consumer-1",} NaN
+# HELP rabbitmq_channels
+# TYPE rabbitmq_channels gauge
+rabbitmq_channels{name="rabbit",} 0.0
+# HELP kafka_consumer_sync_rate_syncs The number of group syncs per second. Group synchronization is the second and last phase of the rebalance protocol. A large value indicates group instability.
+# TYPE kafka_consumer_sync_rate_syncs gauge
+kafka_consumer_sync_rate_syncs{client_id="consumer-1",} 0.0
+# HELP jvm_classes_loaded_classes The number of classes that are currently loaded in the Java virtual machine
+# TYPE jvm_classes_loaded_classes gauge
+jvm_classes_loaded_classes 17220.0
+# HELP jdbc_connections_min
+# TYPE jdbc_connections_min gauge
+jdbc_connections_min{name="dataSource",} 10.0
+# HELP kafka_consumer_fetch_throttle_time_avg_seconds The average throttle time. When quotas are enabled, the broker may delay fetch requests in order to throttle a consumer which has exceeded its limit. This metric indicates how throttling time has been added to fetch requests on average.
+# TYPE kafka_consumer_fetch_throttle_time_avg_seconds gauge
+kafka_consumer_fetch_throttle_time_avg_seconds{client_id="consumer-1",} NaN
+# HELP rabbitmq_failed_to_publish_total
+# TYPE rabbitmq_failed_to_publish_total counter
+rabbitmq_failed_to_publish_total{name="rabbit",} 0.0
+
+# HELP tomcat_sessions_active_max_sessions
+# TYPE tomcat_sessions_active_max_sessions gauge
+tomcat_sessions_active_max_sessions 0.0
+# HELP process_cpu_usage The "recent cpu usage" for the Java Virtual Machine process
+# TYPE process_cpu_usage gauge
+process_cpu_usage 7.079390305569602E-4
+# HELP jvm_buffer_total_capacity_bytes An estimate of the total capacity of the buffers in this pool
+# TYPE jvm_buffer_total_capacity_bytes gauge
+jvm_buffer_total_capacity_bytes{id="mapped",} 0.0
+jvm_buffer_total_capacity_bytes{id="direct",} 81920.0
+# HELP kafka_consumer_fetch_throttle_time_max_seconds The maximum throttle time.
+# TYPE kafka_consumer_fetch_throttle_time_max_seconds gauge
+kafka_consumer_fetch_throttle_time_max_seconds{client_id="consumer-1",} NaN
+# HELP system_load_average_1m The sum of the number of runnable entities queued to available processors and the number of runnable entities running on the available processors averaged over a period of time
+# TYPE system_load_average_1m gauge
+system_load_average_1m 0.52
+# HELP rabbitmq_acknowledged_published_total
+# TYPE rabbitmq_acknowledged_published_total counter
+rabbitmq_acknowledged_published_total{name="rabbit",} 0.0
+# HELP kafka_consumer_join_time_avg_seconds The average time taken for a group rejoin. This value can get as high as the configured session timeout for the consumer, but should usually be lower.
+# TYPE kafka_consumer_join_time_avg_seconds gauge
+kafka_consumer_join_time_avg_seconds{client_id="consumer-1",} NaN
+# HELP jdbc_connections_max
+# TYPE jdbc_connections_max gauge
+jdbc_connections_max{name="dataSource",} 10.0
+# HELP kafka_consumer_assigned_partitions The number of partitions currently assigned to this consumer.
+# TYPE kafka_consumer_assigned_partitions gauge
+kafka_consumer_assigned_partitions{client_id="consumer-1",} 0.0
+# HELP tomcat_sessions_rejected_sessions_total
+# TYPE tomcat_sessions_rejected_sessions_total counter
+tomcat_sessions_rejected_sessions_total 0.0
+
+# HELP kafka_consumer_heartbeat_response_time_max_seconds The max time taken to receive a response to a heartbeat request.
+# TYPE kafka_consumer_heartbeat_response_time_max_seconds gauge
+kafka_consumer_heartbeat_response_time_max_seconds{client_id="consumer-1",} NaN
+# HELP jvm_threads_daemon_threads The current number of live daemon threads
+# TYPE jvm_threads_daemon_threads gauge
+jvm_threads_daemon_threads 20.0
+# HELP system_cpu_count The number of processors available to the Java virtual machine
+# TYPE system_cpu_count gauge
+system_cpu_count 16.0
+# HELP jvm_buffer_count_buffers An estimate of the number of buffers in the pool
+# TYPE jvm_buffer_count_buffers gauge
+jvm_buffer_count_buffers{id="mapped",} 0.0
+jvm_buffer_count_buffers{id="direct",} 10.0
+# HELP kafka_consumer_io_wait_time_avg_seconds The average length of time the I/O thread spent waiting for a socket to be ready for reads or writes.
+# TYPE kafka_consumer_io_wait_time_avg_seconds gauge
+kafka_consumer_io_wait_time_avg_seconds{client_id="consumer-1",} 0.047184790159065626
+# HELP jvm_memory_max_bytes The maximum amount of memory in bytes that can be used for memory management
+# TYPE jvm_memory_max_bytes gauge
+jvm_memory_max_bytes{area="nonheap",id="CodeHeap 'profiled nmethods'",} 1.22028032E8
+jvm_memory_max_bytes{area="heap",id="G1 Survivor Space",} -1.0
+jvm_memory_max_bytes{area="heap",id="G1 Old Gen",} 8.331984896E9
+jvm_memory_max_bytes{area="nonheap",id="Metaspace",} -1.0
+jvm_memory_max_bytes{area="nonheap",id="CodeHeap 'non-nmethods'",} 7598080.0
+jvm_memory_max_bytes{area="heap",id="G1 Eden Space",} -1.0
+jvm_memory_max_bytes{area="nonheap",id="Compressed Class Space",} 1.073741824E9
+jvm_memory_max_bytes{area="nonheap",id="CodeHeap 'non-profiled nmethods'",} 1.22032128E8
+# HELP jvm_gc_pause_seconds Time spent in GC pause
+# TYPE jvm_gc_pause_seconds summary
+jvm_gc_pause_seconds_count{action="end of minor GC",cause="Metadata GC Threshold",} 2.0
+jvm_gc_pause_seconds_sum{action="end of minor GC",cause="Metadata GC Threshold",} 0.031
+jvm_gc_pause_seconds_count{action="end of minor GC",cause="G1 Evacuation Pause",} 1.0
+jvm_gc_pause_seconds_sum{action="end of minor GC",cause="G1 Evacuation Pause",} 0.016
+# HELP jvm_gc_pause_seconds_max Time spent in GC pause
+# TYPE jvm_gc_pause_seconds_max gauge
+jvm_gc_pause_seconds_max{action="end of minor GC",cause="Metadata GC Threshold",} 0.02
+jvm_gc_pause_seconds_max{action="end of minor GC",cause="G1 Evacuation Pause",} 0.0
+# HELP kafka_consumer_connection_count_connections The current number of active connections.
+# TYPE kafka_consumer_connection_count_connections gauge
+kafka_consumer_connection_count_connections{client_id="consumer-1",} 0.0
+# HELP jdbc_connections_active
+# TYPE jdbc_connections_active gauge
+jdbc_connections_active{name="dataSource",} 0.0
diff --git a/src/fluent-bit/lib/cmetrics/tests/data/issue_fluent_bit_6021.txt b/src/fluent-bit/lib/cmetrics/tests/data/issue_fluent_bit_6021.txt
new file mode 100644
index 000000000..4f8cb76ba
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/tests/data/issue_fluent_bit_6021.txt
@@ -0,0 +1,221 @@
+# TYPE envoy_cluster_manager_cds_init_fetch_timeout counter
+envoy_cluster_manager_cds_init_fetch_timeout{} 0
+
+# TYPE envoy_cluster_manager_cds_update_attempt counter
+envoy_cluster_manager_cds_update_attempt{} 1
+
+# TYPE envoy_cluster_manager_cds_update_failure counter
+envoy_cluster_manager_cds_update_failure{} 0
+
+# TYPE envoy_http_downstream_cx_length_ms histogram
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="admin",le="0.5"} 0
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="admin",le="1"} 0
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="admin",le="5"} 0
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="admin",le="10"} 0
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="admin",le="25"} 1
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="admin",le="50"} 1
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="admin",le="100"} 1
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="admin",le="250"} 1
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="admin",le="500"} 1
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="admin",le="1000"} 1
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="admin",le="2500"} 1
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="admin",le="5000"} 1
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="admin",le="10000"} 1
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="admin",le="30000"} 1
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="admin",le="60000"} 1
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="admin",le="300000"} 1
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="admin",le="600000"} 1
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="admin",le="1800000"} 1
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="admin",le="3600000"} 1
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="admin",le="+Inf"} 1
+envoy_http_downstream_cx_length_ms_sum{envoy_http_conn_manager_prefix="admin"} 15.5
+envoy_http_downstream_cx_length_ms_count{envoy_http_conn_manager_prefix="admin"} 1
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="ingress_http",le="0.5"} 0
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="ingress_http",le="1"} 0
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="ingress_http",le="5"} 0
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="ingress_http",le="10"} 0
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="ingress_http",le="25"} 0
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="ingress_http",le="50"} 0
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="ingress_http",le="100"} 0
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="ingress_http",le="250"} 0
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="ingress_http",le="500"} 0
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="ingress_http",le="1000"} 0
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="ingress_http",le="2500"} 0
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="ingress_http",le="5000"} 0
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="ingress_http",le="10000"} 0
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="ingress_http",le="30000"} 0
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="ingress_http",le="60000"} 0
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="ingress_http",le="300000"} 0
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="ingress_http",le="600000"} 0
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="ingress_http",le="1800000"} 0
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="ingress_http",le="3600000"} 0
+envoy_http_downstream_cx_length_ms_bucket{envoy_http_conn_manager_prefix="ingress_http",le="+Inf"} 0
+envoy_http_downstream_cx_length_ms_sum{envoy_http_conn_manager_prefix="ingress_http"} 0
+envoy_http_downstream_cx_length_ms_count{envoy_http_conn_manager_prefix="ingress_http"} 0
+
+# TYPE envoy_http_downstream_rq_time histogram
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="admin",le="0.5"} 0
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="admin",le="1"} 0
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="admin",le="5"} 10
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="admin",le="10"} 10
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="admin",le="25"} 10
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="admin",le="50"} 10
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="admin",le="100"} 10
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="admin",le="250"} 10
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="admin",le="500"} 10
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="admin",le="1000"} 10
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="admin",le="2500"} 10
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="admin",le="5000"} 10
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="admin",le="10000"} 10
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="admin",le="30000"} 10
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="admin",le="60000"} 10
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="admin",le="300000"} 10
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="admin",le="600000"} 10
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="admin",le="1800000"} 10
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="admin",le="3600000"} 10
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="admin",le="+Inf"} 10
+envoy_http_downstream_rq_time_sum{envoy_http_conn_manager_prefix="admin"} 25.5
+envoy_http_downstream_rq_time_count{envoy_http_conn_manager_prefix="admin"} 10
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="ingress_http",le="0.5"} 0
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="ingress_http",le="1"} 0
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="ingress_http",le="5"} 0
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="ingress_http",le="10"} 0
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="ingress_http",le="25"} 0
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="ingress_http",le="50"} 0
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="ingress_http",le="100"} 0
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="ingress_http",le="250"} 0
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="ingress_http",le="500"} 0
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="ingress_http",le="1000"} 0
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="ingress_http",le="2500"} 0
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="ingress_http",le="5000"} 0
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="ingress_http",le="10000"} 0
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="ingress_http",le="30000"} 0
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="ingress_http",le="60000"} 0
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="ingress_http",le="300000"} 0
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="ingress_http",le="600000"} 0
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="ingress_http",le="1800000"} 0
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="ingress_http",le="3600000"} 0
+envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix="ingress_http",le="+Inf"} 0
+envoy_http_downstream_rq_time_sum{envoy_http_conn_manager_prefix="ingress_http"} 0
+envoy_http_downstream_rq_time_count{envoy_http_conn_manager_prefix="ingress_http"} 0
+
+# TYPE envoy_listener_admin_downstream_cx_length_ms histogram
+envoy_listener_admin_downstream_cx_length_ms_bucket{le="0.5"} 0
+envoy_listener_admin_downstream_cx_length_ms_bucket{le="1"} 0
+envoy_listener_admin_downstream_cx_length_ms_bucket{le="5"} 0
+envoy_listener_admin_downstream_cx_length_ms_bucket{le="10"} 0
+envoy_listener_admin_downstream_cx_length_ms_bucket{le="25"} 1
+envoy_listener_admin_downstream_cx_length_ms_bucket{le="50"} 1
+envoy_listener_admin_downstream_cx_length_ms_bucket{le="100"} 1
+envoy_listener_admin_downstream_cx_length_ms_bucket{le="250"} 1
+envoy_listener_admin_downstream_cx_length_ms_bucket{le="500"} 1
+envoy_listener_admin_downstream_cx_length_ms_bucket{le="1000"} 1
+envoy_listener_admin_downstream_cx_length_ms_bucket{le="2500"} 1
+envoy_listener_admin_downstream_cx_length_ms_bucket{le="5000"} 1
+envoy_listener_admin_downstream_cx_length_ms_bucket{le="10000"} 1
+envoy_listener_admin_downstream_cx_length_ms_bucket{le="30000"} 1
+envoy_listener_admin_downstream_cx_length_ms_bucket{le="60000"} 1
+envoy_listener_admin_downstream_cx_length_ms_bucket{le="300000"} 1
+envoy_listener_admin_downstream_cx_length_ms_bucket{le="600000"} 1
+envoy_listener_admin_downstream_cx_length_ms_bucket{le="1800000"} 1
+envoy_listener_admin_downstream_cx_length_ms_bucket{le="3600000"} 1
+envoy_listener_admin_downstream_cx_length_ms_bucket{le="+Inf"} 1
+envoy_listener_admin_downstream_cx_length_ms_sum{} 15.5
+envoy_listener_admin_downstream_cx_length_ms_count{} 1
+
+# TYPE envoy_listener_downstream_cx_length_ms histogram
+envoy_listener_downstream_cx_length_ms_bucket{envoy_listener_address="0.0.0.0_10000",le="0.5"} 0
+envoy_listener_downstream_cx_length_ms_bucket{envoy_listener_address="0.0.0.0_10000",le="1"} 0
+envoy_listener_downstream_cx_length_ms_bucket{envoy_listener_address="0.0.0.0_10000",le="5"} 0
+envoy_listener_downstream_cx_length_ms_bucket{envoy_listener_address="0.0.0.0_10000",le="10"} 0
+envoy_listener_downstream_cx_length_ms_bucket{envoy_listener_address="0.0.0.0_10000",le="25"} 0
+envoy_listener_downstream_cx_length_ms_bucket{envoy_listener_address="0.0.0.0_10000",le="50"} 0
+envoy_listener_downstream_cx_length_ms_bucket{envoy_listener_address="0.0.0.0_10000",le="100"} 0
+envoy_listener_downstream_cx_length_ms_bucket{envoy_listener_address="0.0.0.0_10000",le="250"} 0
+envoy_listener_downstream_cx_length_ms_bucket{envoy_listener_address="0.0.0.0_10000",le="500"} 0
+envoy_listener_downstream_cx_length_ms_bucket{envoy_listener_address="0.0.0.0_10000",le="1000"} 0
+envoy_listener_downstream_cx_length_ms_bucket{envoy_listener_address="0.0.0.0_10000",le="2500"} 0
+envoy_listener_downstream_cx_length_ms_bucket{envoy_listener_address="0.0.0.0_10000",le="5000"} 0
+envoy_listener_downstream_cx_length_ms_bucket{envoy_listener_address="0.0.0.0_10000",le="10000"} 0
+envoy_listener_downstream_cx_length_ms_bucket{envoy_listener_address="0.0.0.0_10000",le="30000"} 0
+envoy_listener_downstream_cx_length_ms_bucket{envoy_listener_address="0.0.0.0_10000",le="60000"} 0
+envoy_listener_downstream_cx_length_ms_bucket{envoy_listener_address="0.0.0.0_10000",le="300000"} 0
+envoy_listener_downstream_cx_length_ms_bucket{envoy_listener_address="0.0.0.0_10000",le="600000"} 0
+envoy_listener_downstream_cx_length_ms_bucket{envoy_listener_address="0.0.0.0_10000",le="1800000"} 0
+envoy_listener_downstream_cx_length_ms_bucket{envoy_listener_address="0.0.0.0_10000",le="3600000"} 0
+envoy_listener_downstream_cx_length_ms_bucket{envoy_listener_address="0.0.0.0_10000",le="+Inf"} 0
+envoy_listener_downstream_cx_length_ms_sum{envoy_listener_address="0.0.0.0_10000"} 0
+envoy_listener_downstream_cx_length_ms_count{envoy_listener_address="0.0.0.0_10000"} 0
+
+# TYPE envoy_listener_manager_lds_update_duration histogram
+envoy_listener_manager_lds_update_duration_bucket{le="0.5"} 0
+envoy_listener_manager_lds_update_duration_bucket{le="1"} 0
+envoy_listener_manager_lds_update_duration_bucket{le="5"} 0
+envoy_listener_manager_lds_update_duration_bucket{le="10"} 0
+envoy_listener_manager_lds_update_duration_bucket{le="25"} 0
+envoy_listener_manager_lds_update_duration_bucket{le="50"} 0
+envoy_listener_manager_lds_update_duration_bucket{le="100"} 0
+envoy_listener_manager_lds_update_duration_bucket{le="250"} 0
+envoy_listener_manager_lds_update_duration_bucket{le="500"} 0
+envoy_listener_manager_lds_update_duration_bucket{le="1000"} 0
+envoy_listener_manager_lds_update_duration_bucket{le="2500"} 0
+envoy_listener_manager_lds_update_duration_bucket{le="5000"} 0
+envoy_listener_manager_lds_update_duration_bucket{le="10000"} 0
+envoy_listener_manager_lds_update_duration_bucket{le="30000"} 0
+envoy_listener_manager_lds_update_duration_bucket{le="60000"} 0
+envoy_listener_manager_lds_update_duration_bucket{le="300000"} 0
+envoy_listener_manager_lds_update_duration_bucket{le="600000"} 0
+envoy_listener_manager_lds_update_duration_bucket{le="1800000"} 0
+envoy_listener_manager_lds_update_duration_bucket{le="3600000"} 0
+envoy_listener_manager_lds_update_duration_bucket{le="+Inf"} 0
+envoy_listener_manager_lds_update_duration_sum{} 0
+envoy_listener_manager_lds_update_duration_count{} 0
+
+# TYPE envoy_sds_tls_sds_update_duration histogram
+envoy_sds_tls_sds_update_duration_bucket{le="0.5"} 0
+envoy_sds_tls_sds_update_duration_bucket{le="1"} 0
+envoy_sds_tls_sds_update_duration_bucket{le="5"} 0
+envoy_sds_tls_sds_update_duration_bucket{le="10"} 0
+envoy_sds_tls_sds_update_duration_bucket{le="25"} 0
+envoy_sds_tls_sds_update_duration_bucket{le="50"} 0
+envoy_sds_tls_sds_update_duration_bucket{le="100"} 0
+envoy_sds_tls_sds_update_duration_bucket{le="250"} 0
+envoy_sds_tls_sds_update_duration_bucket{le="500"} 0
+envoy_sds_tls_sds_update_duration_bucket{le="1000"} 0
+envoy_sds_tls_sds_update_duration_bucket{le="2500"} 0
+envoy_sds_tls_sds_update_duration_bucket{le="5000"} 0
+envoy_sds_tls_sds_update_duration_bucket{le="10000"} 0
+envoy_sds_tls_sds_update_duration_bucket{le="30000"} 0
+envoy_sds_tls_sds_update_duration_bucket{le="60000"} 0
+envoy_sds_tls_sds_update_duration_bucket{le="300000"} 0
+envoy_sds_tls_sds_update_duration_bucket{le="600000"} 0
+envoy_sds_tls_sds_update_duration_bucket{le="1800000"} 0
+envoy_sds_tls_sds_update_duration_bucket{le="3600000"} 0
+envoy_sds_tls_sds_update_duration_bucket{le="+Inf"} 0
+envoy_sds_tls_sds_update_duration_sum{} 0
+envoy_sds_tls_sds_update_duration_count{} 0
+
+# TYPE envoy_server_initialization_time_ms histogram
+envoy_server_initialization_time_ms_bucket{le="0.5"} 0
+envoy_server_initialization_time_ms_bucket{le="1"} 0
+envoy_server_initialization_time_ms_bucket{le="5"} 0
+envoy_server_initialization_time_ms_bucket{le="10"} 0
+envoy_server_initialization_time_ms_bucket{le="25"} 0
+envoy_server_initialization_time_ms_bucket{le="50"} 1
+envoy_server_initialization_time_ms_bucket{le="100"} 1
+envoy_server_initialization_time_ms_bucket{le="250"} 1
+envoy_server_initialization_time_ms_bucket{le="500"} 1
+envoy_server_initialization_time_ms_bucket{le="1000"} 1
+envoy_server_initialization_time_ms_bucket{le="2500"} 1
+envoy_server_initialization_time_ms_bucket{le="5000"} 1
+envoy_server_initialization_time_ms_bucket{le="10000"} 1
+envoy_server_initialization_time_ms_bucket{le="30000"} 1
+envoy_server_initialization_time_ms_bucket{le="60000"} 1
+envoy_server_initialization_time_ms_bucket{le="300000"} 1
+envoy_server_initialization_time_ms_bucket{le="600000"} 1
+envoy_server_initialization_time_ms_bucket{le="1800000"} 1
+envoy_server_initialization_time_ms_bucket{le="3600000"} 1
+envoy_server_initialization_time_ms_bucket{le="+Inf"} 1
+envoy_server_initialization_time_ms_sum{} 30.5
+envoy_server_initialization_time_ms_count{} 1
+
diff --git a/src/fluent-bit/lib/cmetrics/tests/data/pr_168.txt b/src/fluent-bit/lib/cmetrics/tests/data/pr_168.txt
new file mode 100644
index 000000000..f6fa0ad0d
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/tests/data/pr_168.txt
@@ -0,0 +1,22 @@
+# HELP prometheus_engine_query_duration_seconds Query timings
+# TYPE prometheus_engine_query_duration_seconds summary
+prometheus_engine_query_duration_seconds{slice="inner_eval",quantile="0.5"} NaN
+prometheus_engine_query_duration_seconds{slice="inner_eval",quantile="0.9"} NaN
+prometheus_engine_query_duration_seconds{slice="inner_eval",quantile="0.99"} NaN
+prometheus_engine_query_duration_seconds_sum{slice="inner_eval"} 0
+prometheus_engine_query_duration_seconds_count{slice="inner_eval"} 0
+prometheus_engine_query_duration_seconds{slice="prepare_time",quantile="0.5"} NaN
+prometheus_engine_query_duration_seconds{slice="prepare_time",quantile="0.9"} NaN
+prometheus_engine_query_duration_seconds{slice="prepare_time",quantile="0.99"} NaN
+prometheus_engine_query_duration_seconds_sum{slice="prepare_time"} 0
+prometheus_engine_query_duration_seconds_count{slice="prepare_time"} 0
+prometheus_engine_query_duration_seconds{slice="queue_time",quantile="0.5"} NaN
+prometheus_engine_query_duration_seconds{slice="queue_time",quantile="0.9"} NaN
+prometheus_engine_query_duration_seconds{slice="queue_time",quantile="0.99"} NaN
+prometheus_engine_query_duration_seconds_sum{slice="queue_time"} 0
+prometheus_engine_query_duration_seconds_count{slice="queue_time"} 0
+prometheus_engine_query_duration_seconds{slice="result_sort",quantile="0.5"} NaN
+prometheus_engine_query_duration_seconds{slice="result_sort",quantile="0.9"} NaN
+prometheus_engine_query_duration_seconds{slice="result_sort",quantile="0.99"} NaN
+prometheus_engine_query_duration_seconds_sum{slice="result_sort"} 0
+prometheus_engine_query_duration_seconds_count{slice="result_sort"} 0
diff --git a/src/fluent-bit/lib/cmetrics/tests/decoding.c b/src/fluent-bit/lib/cmetrics/tests/decoding.c
new file mode 100644
index 000000000..823861bdb
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/tests/decoding.c
@@ -0,0 +1,184 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_gauge.h>
+#include <cmetrics/cmt_counter.h>
+#include <cmetrics/cmt_summary.h>
+#include <cmetrics/cmt_histogram.h>
+#include <cmetrics/cmt_encode_prometheus.h>
+#include <cmetrics/cmt_decode_opentelemetry.h>
+#include <cmetrics/cmt_encode_opentelemetry.h>
+
+#include "cmt_tests.h"
+
+static struct cmt *generate_encoder_test_data()
+{
+ double quantiles[5];
+ struct cmt_histogram_buckets *buckets;
+ double val;
+ struct cmt *cmt;
+ uint64_t ts;
+ struct cmt_gauge *g1;
+ struct cmt_counter *c1;
+ struct cmt_summary *s1;
+ struct cmt_histogram *h1;
+
+ ts = 0;
+ cmt = cmt_create();
+
+ c1 = cmt_counter_create(cmt, "kubernetes", "network", "load_counter", "Network load counter",
+ 2, (char *[]) {"hostname", "app"});
+
+ cmt_counter_get_val(c1, 0, NULL, &val);
+ cmt_counter_inc(c1, ts, 0, NULL);
+ cmt_counter_add(c1, ts, 2, 0, NULL);
+ cmt_counter_get_val(c1, 0, NULL, &val);
+
+ cmt_counter_inc(c1, ts, 2, (char *[]) {"localhost", "cmetrics"});
+ cmt_counter_get_val(c1, 2, (char *[]) {"localhost", "cmetrics"}, &val);
+ cmt_counter_add(c1, ts, 10.55, 2, (char *[]) {"localhost", "test"});
+ cmt_counter_get_val(c1, 2, (char *[]) {"localhost", "test"}, &val);
+ cmt_counter_set(c1, ts, 12.15, 2, (char *[]) {"localhost", "test"});
+ cmt_counter_set(c1, ts, 1, 2, (char *[]) {"localhost", "test"});
+
+ g1 = cmt_gauge_create(cmt, "kubernetes", "network", "load_gauge", "Network load gauge", 0, NULL);
+
+ cmt_gauge_get_val(g1, 0, NULL, &val);
+ cmt_gauge_set(g1, ts, 2.0, 0, NULL);
+ cmt_gauge_get_val(g1, 0, NULL, &val);
+ cmt_gauge_inc(g1, ts, 0, NULL);
+ cmt_gauge_get_val(g1, 0, NULL, &val);
+ cmt_gauge_sub(g1, ts, 2, 0, NULL);
+ cmt_gauge_get_val(g1, 0, NULL, &val);
+ cmt_gauge_dec(g1, ts, 0, NULL);
+ cmt_gauge_get_val(g1, 0, NULL, &val);
+ cmt_gauge_inc(g1, ts, 0, NULL);
+
+ buckets = cmt_histogram_buckets_create(3, 0.05, 5.0, 10.0);
+
+ h1 = cmt_histogram_create(cmt,
+ "k8s", "network", "load_histogram", "Network load histogram",
+ buckets,
+ 1, (char *[]) {"my_label"});
+
+ cmt_histogram_observe(h1, ts, 0.001, 0, NULL);
+ cmt_histogram_observe(h1, ts, 0.020, 0, NULL);
+ cmt_histogram_observe(h1, ts, 5.0, 0, NULL);
+ cmt_histogram_observe(h1, ts, 8.0, 0, NULL);
+ cmt_histogram_observe(h1, ts, 1000, 0, NULL);
+
+ cmt_histogram_observe(h1, ts, 0.001, 1, (char *[]) {"my_val"});
+ cmt_histogram_observe(h1, ts, 0.020, 1, (char *[]) {"my_val"});
+ cmt_histogram_observe(h1, ts, 5.0, 1, (char *[]) {"my_val"});
+ cmt_histogram_observe(h1, ts, 8.0, 1, (char *[]) {"my_val"});
+ cmt_histogram_observe(h1, ts, 1000, 1, (char *[]) {"my_val"});;
+
+ quantiles[0] = 0.1;
+ quantiles[1] = 0.2;
+ quantiles[2] = 0.3;
+ quantiles[3] = 0.4;
+ quantiles[4] = 0.5;
+
+ s1 = cmt_summary_create(cmt,
+ "k8s", "disk", "load_summary", "Disk load summary",
+ 5, quantiles,
+ 1, (char *[]) {"my_label"});
+
+ quantiles[0] = 1.1;
+ quantiles[1] = 2.2;
+ quantiles[2] = 3.3;
+ quantiles[3] = 4.4;
+ quantiles[4] = 5.5;
+
+ cmt_summary_set_default(s1, ts, quantiles, 51.612894511314444, 10, 0, NULL);
+
+ quantiles[0] = 11.11;
+ quantiles[1] = 0;
+ quantiles[2] = 33.33;
+ quantiles[3] = 44.44;
+ quantiles[4] = 55.55;
+
+ cmt_summary_set_default(s1, ts, quantiles, 51.612894511314444, 10, 1, (char *[]) {"my_val"});
+
+ return cmt;
+}
+
+void test_opentelemetry()
+{
+ cfl_sds_t reference_prometheus_context;
+ cfl_sds_t opentelemetry_context;
+ struct cfl_list decoded_context_list;
+ cfl_sds_t prometheus_context;
+ struct cmt *decoded_context;
+ size_t offset;
+ int result;
+ struct cmt *cmt;
+
+ offset = 0;
+
+ cmt_initialize();
+
+ cmt = generate_encoder_test_data();
+ TEST_CHECK(cmt != NULL);
+
+ reference_prometheus_context = cmt_encode_prometheus_create(cmt, CMT_TRUE);
+ TEST_CHECK(reference_prometheus_context != NULL);
+
+ if (reference_prometheus_context != NULL) {
+ opentelemetry_context = cmt_encode_opentelemetry_create(cmt);
+ TEST_CHECK(opentelemetry_context != NULL);
+
+ if (opentelemetry_context != NULL) {
+ result = cmt_decode_opentelemetry_create(&decoded_context_list,
+ opentelemetry_context,
+ cfl_sds_len(opentelemetry_context),
+ &offset);
+
+ if (TEST_CHECK(result == 0)) {
+ decoded_context = cfl_list_entry_first(&decoded_context_list, struct cmt, _head);
+
+ if (TEST_CHECK(result == 0)) {
+ prometheus_context = cmt_encode_prometheus_create(decoded_context,
+ CMT_TRUE);
+ TEST_CHECK(prometheus_context != NULL);
+
+ if (prometheus_context != NULL) {
+ TEST_CHECK(strcmp(prometheus_context,
+ reference_prometheus_context) == 0);
+
+ cmt_encode_prometheus_destroy(prometheus_context);
+ }
+ }
+
+ cmt_decode_opentelemetry_destroy(&decoded_context_list);
+ }
+ }
+
+ cmt_encode_opentelemetry_destroy(opentelemetry_context);
+ cmt_encode_prometheus_destroy(reference_prometheus_context);
+ }
+
+ cmt_destroy(cmt);
+}
+
+TEST_LIST = {
+ {"opentelemetry", test_opentelemetry},
+ { 0 }
+};
diff --git a/src/fluent-bit/lib/cmetrics/tests/encode_output.c b/src/fluent-bit/lib/cmetrics/tests/encode_output.c
new file mode 100644
index 000000000..7d54ebfaf
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/tests/encode_output.c
@@ -0,0 +1,67 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+
+/*
+ * This file is a helper utility just to try out all the encoders: given a specific
+ * CMetrics context, encode to all possible formats and destroy them. This is
+ * useful to trap potential memory leaks or errors when doing changes to the
+ * metric types.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_encode_msgpack.h>
+#include <cmetrics/cmt_encode_prometheus_remote_write.h>
+#include <cmetrics/cmt_encode_prometheus.h>
+#include <cmetrics/cmt_encode_opentelemetry.h>
+#include <cmetrics/cmt_encode_text.h>
+#include <cmetrics/cmt_encode_influx.h>
+
+int cmt_test_encode_all(struct cmt *cmt)
+{
+ char *out_buf;
+ size_t out_size;
+ cfl_sds_t sds_buf;
+
+ /* text */
+ sds_buf = cmt_encode_text_create(cmt);
+ cmt_encode_text_destroy(sds_buf);
+
+ /* prometheus */
+ sds_buf = cmt_encode_prometheus_create(cmt, CMT_TRUE);
+ cmt_encode_prometheus_destroy(sds_buf);
+
+ /* prometheus remote write */
+ sds_buf = cmt_encode_prometheus_remote_write_create(cmt);
+ cmt_encode_prometheus_remote_write_destroy(sds_buf);
+
+ /* msgpack */
+ cmt_encode_msgpack_create(cmt, &out_buf, &out_size);
+ cmt_encode_msgpack_destroy(out_buf);
+
+ /* influx */
+ sds_buf = cmt_encode_influx_create(cmt);
+ cmt_encode_influx_destroy(sds_buf);
+
+ /* opentelemetry */
+ sds_buf = cmt_encode_opentelemetry_create(cmt);
+ cmt_encode_opentelemetry_destroy(sds_buf);
+
+ return 0;
+}
diff --git a/src/fluent-bit/lib/cmetrics/tests/encode_output.h b/src/fluent-bit/lib/cmetrics/tests/encode_output.h
new file mode 100644
index 000000000..929ea1a33
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/tests/encode_output.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#ifndef CMT_TESTS_ENCODE_OUTPUT_H
+#define CMT_TESTS_ENCODE_OUTPUT_H
+
+#include <cmetrics/cmetrics.h>
+
+int cmt_test_encode_all(struct cmt *cmt);
+
+#endif
+
diff --git a/src/fluent-bit/lib/cmetrics/tests/encoding.c b/src/fluent-bit/lib/cmetrics/tests/encoding.c
new file mode 100644
index 000000000..77a580273
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/tests/encoding.c
@@ -0,0 +1,1056 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+#ifdef __GNUC__
+#define _GNU_SOURCE
+#endif
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_gauge.h>
+#include <cmetrics/cmt_counter.h>
+#include <cmetrics/cmt_summary.h>
+#include <cmetrics/cmt_histogram.h>
+#include <cmetrics/cmt_encode_msgpack.h>
+#include <cmetrics/cmt_decode_msgpack.h>
+#include <cmetrics/cmt_encode_prometheus_remote_write.h>
+#include <cmetrics/cmt_encode_prometheus.h>
+#include <cmetrics/cmt_encode_opentelemetry.h>
+#include <cmetrics/cmt_encode_text.h>
+#include <cmetrics/cmt_encode_influx.h>
+#include <cmetrics/cmt_encode_splunk_hec.h>
+
+#include "cmt_tests.h"
+
+static struct cmt *generate_simple_encoder_test_data()
+{
+ double val;
+ uint64_t ts;
+ struct cmt *cmt;
+ struct cmt_counter *c;
+
+ cmt = cmt_create();
+
+ c = cmt_counter_create(cmt, "kubernetes", "network", "load", "Network load",
+ 2, (char *[]) {"hostname", "app"});
+
+ ts = 0;
+
+ cmt_counter_get_val(c, 0, NULL, &val);
+ cmt_counter_inc(c, ts, 0, NULL);
+ cmt_counter_add(c, ts, 2, 0, NULL);
+ cmt_counter_get_val(c, 0, NULL, &val);
+
+ cmt_counter_inc(c, ts, 2, (char *[]) {"localhost", "cmetrics"});
+ cmt_counter_get_val(c, 2, (char *[]) {"localhost", "cmetrics"}, &val);
+ cmt_counter_add(c, ts, 10.55, 2, (char *[]) {"localhost", "test"});
+ cmt_counter_get_val(c, 2, (char *[]) {"localhost", "test"}, &val);
+ cmt_counter_set(c, ts, 12.15, 2, (char *[]) {"localhost", "test"});
+ cmt_counter_set(c, ts, 1, 2, (char *[]) {"localhost", "test"});
+
+ return cmt;
+}
+
+static struct cmt *generate_encoder_test_data()
+{
+ double quantiles[5];
+ struct cmt_histogram_buckets *buckets;
+ double val;
+ struct cmt *cmt;
+ uint64_t ts;
+ struct cmt_gauge *g1;
+ struct cmt_counter *c1;
+ struct cmt_summary *s1;
+ struct cmt_histogram *h1;
+
+ ts = 0;
+ cmt = cmt_create();
+
+ c1 = cmt_counter_create(cmt, "kubernetes", "network", "load_counter", "Network load counter",
+ 2, (char *[]) {"hostname", "app"});
+
+ cmt_counter_get_val(c1, 0, NULL, &val);
+ cmt_counter_inc(c1, ts, 0, NULL);
+ cmt_counter_add(c1, ts, 2, 0, NULL);
+ cmt_counter_get_val(c1, 0, NULL, &val);
+
+ cmt_counter_inc(c1, ts, 2, (char *[]) {"localhost", "cmetrics"});
+ cmt_counter_get_val(c1, 2, (char *[]) {"localhost", "cmetrics"}, &val);
+ cmt_counter_add(c1, ts, 10.55, 2, (char *[]) {"localhost", "test"});
+ cmt_counter_get_val(c1, 2, (char *[]) {"localhost", "test"}, &val);
+ cmt_counter_set(c1, ts, 12.15, 2, (char *[]) {"localhost", "test"});
+ cmt_counter_set(c1, ts, 1, 2, (char *[]) {"localhost", "test"});
+
+ g1 = cmt_gauge_create(cmt, "kubernetes", "network", "load_gauge", "Network load gauge", 0, NULL);
+
+ cmt_gauge_get_val(g1, 0, NULL, &val);
+ cmt_gauge_set(g1, ts, 2.0, 0, NULL);
+ cmt_gauge_get_val(g1, 0, NULL, &val);
+ cmt_gauge_inc(g1, ts, 0, NULL);
+ cmt_gauge_get_val(g1, 0, NULL, &val);
+ cmt_gauge_sub(g1, ts, 2, 0, NULL);
+ cmt_gauge_get_val(g1, 0, NULL, &val);
+ cmt_gauge_dec(g1, ts, 0, NULL);
+ cmt_gauge_get_val(g1, 0, NULL, &val);
+ cmt_gauge_inc(g1, ts, 0, NULL);
+
+ buckets = cmt_histogram_buckets_create(3, 0.05, 5.0, 10.0);
+
+ h1 = cmt_histogram_create(cmt,
+ "k8s", "network", "load_histogram", "Network load histogram",
+ buckets,
+ 1, (char *[]) {"my_label"});
+
+ cmt_histogram_observe(h1, ts, 0.001, 0, NULL);
+ cmt_histogram_observe(h1, ts, 0.020, 0, NULL);
+ cmt_histogram_observe(h1, ts, 5.0, 0, NULL);
+ cmt_histogram_observe(h1, ts, 8.0, 0, NULL);
+ cmt_histogram_observe(h1, ts, 1000, 0, NULL);
+
+ cmt_histogram_observe(h1, ts, 0.001, 1, (char *[]) {"my_val"});
+ cmt_histogram_observe(h1, ts, 0.020, 1, (char *[]) {"my_val"});
+ cmt_histogram_observe(h1, ts, 5.0, 1, (char *[]) {"my_val"});
+ cmt_histogram_observe(h1, ts, 8.0, 1, (char *[]) {"my_val"});
+ cmt_histogram_observe(h1, ts, 1000, 1, (char *[]) {"my_val"});;
+
+ quantiles[0] = 0.1;
+ quantiles[1] = 0.2;
+ quantiles[2] = 0.3;
+ quantiles[3] = 0.4;
+ quantiles[4] = 0.5;
+
+ s1 = cmt_summary_create(cmt,
+ "k8s", "disk", "load_summary", "Disk load summary",
+ 5, quantiles,
+ 1, (char *[]) {"my_label"});
+
+ quantiles[0] = 1.1;
+ quantiles[1] = 2.2;
+ quantiles[2] = 3.3;
+ quantiles[3] = 4.4;
+ quantiles[4] = 5.5;
+
+ cmt_summary_set_default(s1, ts, quantiles, 51.612894511314444, 10, 0, NULL);
+
+ quantiles[0] = 11.11;
+ quantiles[1] = 0;
+ quantiles[2] = 33.33;
+ quantiles[3] = 44.44;
+ quantiles[4] = 55.55;
+
+ cmt_summary_set_default(s1, ts, quantiles, 51.612894511314444, 10, 1, (char *[]) {"my_val"});
+
+ return cmt;
+}
+
+/*
+ * perform the following data encoding and compare msgpack buffsers
+ *
+ * CMT -> MSGPACK -> CMT -> MSGPACK
+ * | |
+ * |---> compare <----|
+ */
+
+void test_cmt_to_msgpack()
+{
+ int ret;
+ size_t offset = 0;
+ char *mp1_buf = NULL;
+ size_t mp1_size = 0;
+ char *mp2_buf = NULL;
+ size_t mp2_size = 0;
+ struct cmt *cmt1 = NULL;
+ struct cmt *cmt2 = NULL;
+
+ cmt_initialize();
+
+ /* Generate context with data */
+ cmt1 = generate_encoder_test_data();
+ TEST_CHECK(cmt1 != NULL);
+
+ /* CMT1 -> Msgpack */
+ ret = cmt_encode_msgpack_create(cmt1, &mp1_buf, &mp1_size);
+ TEST_CHECK(ret == 0);
+
+ /* Msgpack -> CMT2 */
+ ret = cmt_decode_msgpack_create(&cmt2, mp1_buf, mp1_size, &offset);
+ TEST_CHECK(ret == 0);
+
+ /* CMT2 -> Msgpack */
+ ret = cmt_encode_msgpack_create(cmt2, &mp2_buf, &mp2_size);
+ TEST_CHECK(ret == 0);
+
+ /* Compare msgpacks */
+ TEST_CHECK(mp1_size == mp2_size);
+ if (mp1_size == mp2_size) {
+ TEST_CHECK(memcmp(mp1_buf, mp2_buf, mp1_size) == 0);
+ }
+
+ cmt_destroy(cmt1);
+ cmt_decode_msgpack_destroy(cmt2);
+ cmt_encode_msgpack_destroy(mp1_buf);
+ cmt_encode_msgpack_destroy(mp2_buf);
+}
+
+/*
+ * Encode a context, corrupt the last metric in the msgpack packet
+ * and invoke the decoder to verify if there are any leaks.
+ *
+ * CMT -> MSGPACK -> CMT
+ *
+ * Note: this function is meant to be executed in linux while using
+ * valgrind
+ */
+
+void test_cmt_to_msgpack_cleanup_on_error()
+{
+#ifdef __linux__
+ int ret;
+ size_t offset = 0;
+ char *mp1_buf = NULL;
+ size_t mp1_size = 0;
+ struct cmt *cmt1 = NULL;
+ struct cmt *cmt2 = NULL;
+ char *key_buffer = NULL;
+ char *key_haystack = NULL;
+
+ cmt_initialize();
+
+ /* Generate context with data */
+ cmt1 = generate_encoder_test_data();
+ TEST_CHECK(cmt1 != NULL);
+
+ /* CMT1 -> Msgpack */
+ ret = cmt_encode_msgpack_create(cmt1, &mp1_buf, &mp1_size);
+ TEST_CHECK(ret == 0);
+
+ key_haystack = &mp1_buf[mp1_size - 32];
+ key_buffer = memmem(key_haystack, 32, "hash", 4);
+
+ TEST_CHECK(key_buffer != NULL);
+
+ /* This turns the last 'hash' entry into 'hasq' which causes
+ * the map consumer in the decoder to detect an unprocessed entry
+ * and abort in `unpack_metric` which means a lot of allocations
+ * have been made including but not limited to temporary
+ * histogram bucket arrays and completely decoded histograms
+ */
+ key_buffer[3] = 'q';
+
+ /* Msgpack -> CMT2 */
+ ret = cmt_decode_msgpack_create(&cmt2, mp1_buf, mp1_size, &offset);
+
+ cmt_destroy(cmt1);
+ cmt_encode_msgpack_destroy(mp1_buf);
+
+ TEST_CHECK(ret != 0);
+ TEST_CHECK(cmt2 == NULL);
+
+#endif
+}
+
+/*
+ * perform the following data encoding and compare msgpack buffsers
+ *
+ * CMT -> MSGPACK -> CMT -> TEXT
+ * CMT -> TEXT
+ * | |
+ * |---> compare <----|
+ */
+void test_cmt_to_msgpack_integrity()
+{
+ int ret;
+ size_t offset = 0;
+ char *mp1_buf = NULL;
+ size_t mp1_size = 0;
+ char *text1_buf = NULL;
+ size_t text1_size = 0;
+ char *text2_buf = NULL;
+ size_t text2_size = 0;
+ struct cmt *cmt1 = NULL;
+ struct cmt *cmt2 = NULL;
+
+ /* Generate context with data */
+ cmt1 = generate_encoder_test_data();
+ TEST_CHECK(cmt1 != NULL);
+
+ /* CMT1 -> Msgpack */
+ ret = cmt_encode_msgpack_create(cmt1, &mp1_buf, &mp1_size);
+ TEST_CHECK(ret == 0);
+
+ /* Msgpack -> CMT2 */
+ ret = cmt_decode_msgpack_create(&cmt2, mp1_buf, mp1_size, &offset);
+ TEST_CHECK(ret == 0);
+
+ /* CMT1 -> Text */
+ text1_buf = cmt_encode_text_create(cmt1);
+ TEST_CHECK(text1_buf != NULL);
+ text1_size = cfl_sds_len(text1_buf);
+
+ /* CMT2 -> Text */
+ text2_buf = cmt_encode_text_create(cmt2);
+ TEST_CHECK(text2_buf != NULL);
+ text2_size = cfl_sds_len(text2_buf);
+
+ /* Compare msgpacks */
+ TEST_CHECK(text1_size == text2_size);
+ TEST_CHECK(memcmp(text1_buf, text2_buf, text1_size) == 0);
+
+ cmt_destroy(cmt1);
+
+ cmt_decode_msgpack_destroy(cmt2);
+ cmt_encode_msgpack_destroy(mp1_buf);
+
+ cmt_encode_text_destroy(text1_buf);
+ cmt_encode_text_destroy(text2_buf);
+}
+
+void test_cmt_msgpack_partial_processing()
+{
+ int ret = 0;
+ int iteration = 0;
+ size_t offset = 0;
+ char *mp1_buf = NULL;
+ size_t mp1_size = 0;
+ struct cmt *cmt1 = NULL;
+ struct cmt *cmt2 = NULL;
+ double base_counter_value = 0;
+ size_t expected_gauge_count = 0;
+ double current_counter_value = 0;
+ size_t expected_counter_count = 0;
+ struct cmt_counter *first_counter = NULL;
+ cfl_sds_t serialized_data_buffer = NULL;
+ size_t serialized_data_buffer_length = 0;
+
+ /* Generate an encoder context with more than one counter */
+ cmt1 = generate_encoder_test_data();
+ TEST_CHECK(NULL != cmt1);
+
+ /* Find the first counter so we can get its value before re-encoding it N times
+ * for the test, that way we can ensure that the decoded contexts we get in the
+ * next phase are individual ones and not just a glitch
+ */
+
+ first_counter = cfl_list_entry_first(&cmt1->counters, struct cmt_counter, _head);
+ TEST_CHECK(NULL != first_counter);
+
+ ret = cmt_counter_get_val(first_counter, 0, NULL, &base_counter_value);
+ TEST_CHECK(0 == ret);
+
+ expected_counter_count = cfl_list_size(&cmt1->counters);
+ expected_gauge_count = cfl_list_size(&cmt1->gauges);
+
+ /* Since we are modifying the counter on each iteration we have to re-encode it */
+ for (iteration = 0 ;
+ iteration < MSGPACK_PARTIAL_PROCESSING_ELEMENT_COUNT ;
+ iteration++) {
+
+ ret = cmt_counter_inc(first_counter, 0, 0, NULL);
+ TEST_CHECK(0 == ret);
+
+ ret = cmt_encode_msgpack_create(cmt1, &mp1_buf, &mp1_size);
+ TEST_CHECK(0 == ret);
+
+ if (NULL == serialized_data_buffer) {
+ serialized_data_buffer = cfl_sds_create_len(mp1_buf, mp1_size);
+ TEST_CHECK(NULL != serialized_data_buffer);
+ }
+ else {
+ cfl_sds_cat_safe(&serialized_data_buffer, mp1_buf, mp1_size);
+ /* TEST_CHECK(0 == ret); */
+ }
+
+ cmt_encode_msgpack_destroy(mp1_buf);
+ }
+
+ cmt_destroy(cmt1);
+
+ /* In this phase we invoke the decoder with until it retunrs an error indicating that
+ * there is not enough data in the input buffer, for each cycle we compare the value
+ * for the first counter which should be be incremental.
+ *
+ * We also check that the iteration count matches the pre established count.
+ */
+
+ ret = 0;
+ offset = 0;
+ iteration = 0;
+ serialized_data_buffer_length = cfl_sds_len(serialized_data_buffer);
+
+ while (CMT_DECODE_MSGPACK_SUCCESS == ret) {
+ ret = cmt_decode_msgpack_create(&cmt2, serialized_data_buffer,
+ serialized_data_buffer_length, &offset);
+
+ if (CMT_DECODE_MSGPACK_INSUFFICIENT_DATA == ret) {
+ break;
+ }
+ else if (CMT_DECODE_MSGPACK_SUCCESS != ret) {
+ break;
+ }
+
+ TEST_CHECK(0 == ret);
+
+ first_counter = cfl_list_entry_first(&cmt2->counters, struct cmt_counter, _head);
+ TEST_CHECK(NULL != first_counter);
+
+ ret = cmt_counter_get_val(first_counter, 0, NULL, &current_counter_value);
+ TEST_CHECK(0 == ret);
+
+ TEST_CHECK(base_counter_value == (current_counter_value - iteration - 1));
+
+ TEST_CHECK(expected_counter_count == cfl_list_size(&cmt2->counters));
+ TEST_CHECK(expected_gauge_count == cfl_list_size(&cmt2->gauges));
+
+ cmt_decode_msgpack_destroy(cmt2);
+
+ iteration++;
+ }
+
+ TEST_CHECK(MSGPACK_PARTIAL_PROCESSING_ELEMENT_COUNT == iteration);
+
+ cfl_sds_destroy(serialized_data_buffer);
+}
+
+void test_cmt_to_msgpack_stability()
+{
+ int ret = 0;
+ int iteration = 0;
+ size_t offset = 0;
+ char *mp1_buf = NULL;
+ size_t mp1_size = 0;
+ struct cmt *cmt1 = NULL;
+ struct cmt *cmt2 = NULL;
+
+ for (iteration = 0 ; iteration < MSGPACK_STABILITY_TEST_ITERATION_COUNT ; iteration++) {
+ cmt1 = generate_encoder_test_data();
+ TEST_CHECK(cmt1 != NULL);
+
+ ret = cmt_encode_msgpack_create(cmt1, &mp1_buf, &mp1_size);
+ TEST_CHECK(ret == 0);
+
+ offset = 0;
+ ret = cmt_decode_msgpack_create(&cmt2, mp1_buf, mp1_size, &offset);
+ TEST_CHECK(ret == 0);
+
+ cmt_destroy(cmt1);
+ cmt_decode_msgpack_destroy(cmt2);
+ cmt_encode_msgpack_destroy(mp1_buf);
+ }
+
+}
+
+void test_cmt_to_msgpack_labels()
+{
+ int ret;
+ size_t offset = 0;
+ char *mp1_buf = NULL;
+ size_t mp1_size = 1;
+ char *mp2_buf = NULL;
+ size_t mp2_size = 2;
+ struct cmt *cmt1 = NULL;
+ struct cmt *cmt2 = NULL;
+ cfl_sds_t text_result;
+ const char expected_text[] = "1970-01-01T00:00:00.000000000Z kubernetes_network_load{dev=\"Calyptia\",lang=\"C\"} = 3\n" \
+ "1970-01-01T00:00:00.000000000Z kubernetes_network_load{dev=\"Calyptia\",lang=\"C\",hostname=\"localhost\",app=\"cmetrics\"} = 1\n" \
+ "1970-01-01T00:00:00.000000000Z kubernetes_network_load{dev=\"Calyptia\",lang=\"C\",hostname=\"localhost\",app=\"test\"} = 12.15\n";
+
+ cmt_initialize();
+
+ /* Generate context with data */
+ cmt1 = generate_simple_encoder_test_data();
+ TEST_CHECK(NULL != cmt1);
+
+ /* CMT1 -> Msgpack */
+ ret = cmt_encode_msgpack_create(cmt1, &mp1_buf, &mp1_size);
+ TEST_CHECK(0 == ret);
+
+ /* Msgpack -> CMT2 */
+ ret = cmt_decode_msgpack_create(&cmt2, mp1_buf, mp1_size, &offset);
+ TEST_CHECK(0 == ret);
+
+ /* CMT2 -> Msgpack */
+ ret = cmt_encode_msgpack_create(cmt2, &mp2_buf, &mp2_size);
+ TEST_CHECK(0 == ret);
+
+ /* Compare msgpacks */
+ TEST_CHECK(mp1_size == mp2_size);
+ TEST_CHECK(0 == memcmp(mp1_buf, mp2_buf, mp1_size));
+
+ /* append static labels */
+ cmt_label_add(cmt2, "dev", "Calyptia");
+ cmt_label_add(cmt2, "lang", "C");
+
+ text_result = cmt_encode_text_create(cmt2);
+ TEST_CHECK(NULL != text_result);
+ TEST_CHECK(0 == strcmp(text_result, expected_text));
+
+ cmt_destroy(cmt1);
+ cmt_encode_text_destroy(text_result);
+ cmt_decode_msgpack_destroy(cmt2);
+ cmt_encode_msgpack_destroy(mp1_buf);
+ cmt_encode_msgpack_destroy(mp2_buf);
+}
+
+void test_prometheus_remote_write()
+{
+ struct cmt *cmt;
+ cfl_sds_t payload;
+ FILE *sample_file;
+
+ cmt_initialize();
+
+ cmt = generate_encoder_test_data();
+
+ payload = cmt_encode_prometheus_remote_write_create(cmt);
+ TEST_CHECK(NULL != payload);
+
+ if (payload == NULL) {
+ cmt_destroy(cmt);
+
+ return;
+ }
+
+ printf("\n\nDumping remote write payload to prometheus_remote_write_payload.bin, in order to test it \
+we need to compress it using snappys scmd :\n\
+scmd -c prometheus_remote_write_payload.bin prometheus_remote_write_payload.snp\n\n\
+and then send it using curl :\n\
+curl -v 'http://localhost:9090/receive' -H 'Content-Type: application/x-protobuf' \
+-H 'X-Prometheus-Remote-Write-Version: 0.1.0' -H 'User-Agent: metrics-worker' \
+--data-binary '@prometheus_remote_write_payload.snp'\n\n");
+
+ sample_file = fopen("prometheus_remote_write_payload.bin", "wb+");
+
+ fwrite(payload, 1, cfl_sds_len(payload), sample_file);
+
+ fclose(sample_file);
+
+ cmt_encode_prometheus_remote_write_destroy(payload);
+
+ cmt_destroy(cmt);
+}
+
+void test_opentelemetry()
+{
+ cfl_sds_t payload;
+ struct cmt *cmt;
+ FILE *sample_file;
+
+ cmt_initialize();
+
+ cmt = generate_encoder_test_data();
+
+ payload = cmt_encode_opentelemetry_create(cmt);
+ TEST_CHECK(NULL != payload);
+
+ if (payload == NULL) {
+ cmt_destroy(cmt);
+
+ return;
+ }
+
+ printf("\n\nDumping remote write payload to opentelemetry_payload.bin, in order to test it \
+we need to send it to our opentelemetry http endpoint using curl :\n\
+curl -v 'http://localhost:9090/v1/metrics' -H 'Content-Type: application/x-protobuf' \
+-H 'User-Agent: metrics-worker' \
+--data-binary '@opentelemetry_payload.bin'\n\n");
+
+ sample_file = fopen("opentelemetry_payload.bin", "wb+");
+
+ fwrite(payload, 1, cfl_sds_len(payload), sample_file);
+
+ fclose(sample_file);
+
+ cmt_encode_prometheus_remote_write_destroy(payload);
+
+ cmt_destroy(cmt);
+}
+
+void test_prometheus()
+{
+ uint64_t ts;
+ cfl_sds_t text;
+ struct cmt *cmt;
+ struct cmt_counter *c;
+
+ char *out1 = "# HELP cmt_labels_test \"Static\\\\ labels \\ntest\n"
+ "# TYPE cmt_labels_test counter\n"
+ "cmt_labels_test 1 0\n"
+ "cmt_labels_test{host=\"calyptia.com\",app=\"cmetrics\"} 2 0\n"
+ "cmt_labels_test{host=\"\\\"calyptia.com\\\"\",app=\"cme\\\\tr\\nics\"} 1 0\n";
+
+ char *out2 = "# HELP cmt_labels_test \"Static\\\\ labels \\ntest\n"
+ "# TYPE cmt_labels_test counter\n"
+ "cmt_labels_test{dev=\"Calyptia\",lang=\"C\\\"\\\\\\n\"} 1 0\n"
+ "cmt_labels_test{dev=\"Calyptia\",lang=\"C\\\"\\\\\\n\",host=\"calyptia.com\",app=\"cmetrics\"} 2 0\n"
+ "cmt_labels_test{dev=\"Calyptia\",lang=\"C\\\"\\\\\\n\",host=\"\\\"calyptia.com\\\"\",app=\"cme\\\\tr\\nics\"} 1 0\n";
+
+ cmt_initialize();
+
+ cmt = cmt_create();
+ TEST_CHECK(cmt != NULL);
+
+ c = cmt_counter_create(cmt, "cmt", "labels", "test", "\"Static\\ labels \ntest",
+ 2, (char *[]) {"host", "app"});
+
+ ts = 0;
+ cmt_counter_inc(c, ts, 0, NULL);
+ cmt_counter_inc(c, ts, 2, (char *[]) {"calyptia.com", "cmetrics"});
+ cmt_counter_inc(c, ts, 2, (char *[]) {"calyptia.com", "cmetrics"});
+ cmt_counter_inc(c, ts, 2, (char *[]) {"\"calyptia.com\"", "cme\\tr\nics"});
+
+ /* Encode to prometheus (no static labels) */
+ text = cmt_encode_prometheus_create(cmt, CMT_TRUE);
+ printf("\n%s\n", text);
+ TEST_CHECK(strcmp(text, out1) == 0);
+ cmt_encode_prometheus_destroy(text);
+
+ /* append static labels */
+ cmt_label_add(cmt, "dev", "Calyptia");
+ cmt_label_add(cmt, "lang", "C\"\\\n");
+
+ text = cmt_encode_prometheus_create(cmt, CMT_TRUE);
+ printf("%s\n", text);
+ TEST_CHECK(strcmp(text, out2) == 0);
+ cmt_encode_prometheus_destroy(text);
+
+ cmt_destroy(cmt);
+}
+
+void test_text()
+{
+ uint64_t ts;
+ cfl_sds_t text;
+ struct cmt *cmt;
+ struct cmt_counter *c;
+
+ char *out1 = \
+ "1970-01-01T00:00:00.000000000Z cmt_labels_test = 1\n"
+ "1970-01-01T00:00:00.000000000Z cmt_labels_test{host=\"calyptia.com\",app=\"cmetrics\"} = 2\n";
+
+ char *out2 = \
+ "1970-01-01T00:00:00.000000000Z cmt_labels_test{dev=\"Calyptia\",lang=\"C\"} = 1\n"
+ "1970-01-01T00:00:00.000000000Z cmt_labels_test{dev=\"Calyptia\",lang=\"C\",host=\"calyptia.com\",app=\"cmetrics\"} = 2\n";
+
+ cmt_initialize();
+
+ cmt = cmt_create();
+ TEST_CHECK(cmt != NULL);
+
+ c = cmt_counter_create(cmt, "cmt", "labels", "test", "Static labels test",
+ 2, (char *[]) {"host", "app"});
+
+ ts = 0;
+ cmt_counter_inc(c, ts, 0, NULL);
+ cmt_counter_inc(c, ts, 2, (char *[]) {"calyptia.com", "cmetrics"});
+ cmt_counter_inc(c, ts, 2, (char *[]) {"calyptia.com", "cmetrics"});
+
+ /* Encode to prometheus (no static labels) */
+ text = cmt_encode_text_create(cmt);
+ printf("\n%s\n", text);
+ TEST_CHECK(strcmp(text, out1) == 0);
+ cmt_encode_text_destroy(text);
+
+ /* append static labels */
+ cmt_label_add(cmt, "dev", "Calyptia");
+ cmt_label_add(cmt, "lang", "C");
+
+ text = cmt_encode_text_create(cmt);
+ printf("%s\n", text);
+ TEST_CHECK(strcmp(text, out2) == 0);
+ cmt_encode_text_destroy(text);
+
+ cmt_destroy(cmt);
+}
+
+void test_influx()
+{
+ uint64_t ts;
+ cfl_sds_t text;
+ struct cmt *cmt;
+ struct cmt_counter *c1;
+ struct cmt_counter *c2;
+
+ char *out1 = \
+ "cmt_labels test=1 1435658235000000123\n"
+ "cmt_labels,host=calyptia.com,app=cmetrics test=2 1435658235000000123\n"
+ "cmt,host=aaa,app=bbb nosubsystem=1 1435658235000000123\n";
+
+ char *out2 = \
+ "cmt_labels,dev=Calyptia,lang=C test=1 1435658235000000123\n"
+ "cmt_labels,dev=Calyptia,lang=C,host=calyptia.com,app=cmetrics test=2 1435658235000000123\n"
+ "cmt,dev=Calyptia,lang=C,host=aaa,app=bbb nosubsystem=1 1435658235000000123\n";
+
+ cmt = cmt_create();
+ TEST_CHECK(cmt != NULL);
+
+ c1 = cmt_counter_create(cmt, "cmt", "labels", "test", "Static labels test",
+ 2, (char *[]) {"host", "app"});
+
+ ts = 1435658235000000123;
+ cmt_counter_inc(c1, ts, 0, NULL);
+ cmt_counter_inc(c1, ts, 2, (char *[]) {"calyptia.com", "cmetrics"});
+ cmt_counter_inc(c1, ts, 2, (char *[]) {"calyptia.com", "cmetrics"});
+
+ c2 = cmt_counter_create(cmt, "cmt", "", "nosubsystem", "No subsystem",
+ 2, (char *[]) {"host", "app"});
+
+ cmt_counter_inc(c2, ts, 2, (char *[]) {"aaa", "bbb"});
+
+ /* Encode to prometheus (no static labels) */
+ text = cmt_encode_influx_create(cmt);
+ printf("%s\n", text);
+ TEST_CHECK(strcmp(text, out1) == 0);
+ cmt_encode_influx_destroy(text);
+
+ /* append static labels */
+ cmt_label_add(cmt, "dev", "Calyptia");
+ cmt_label_add(cmt, "lang", "C");
+
+ text = cmt_encode_influx_create(cmt);
+ printf("%s\n", text);
+ TEST_CHECK(strcmp(text, out2) == 0);
+ cmt_encode_influx_destroy(text);
+
+ cmt_destroy(cmt);
+}
+
+void test_splunk_hec()
+{
+ uint64_t ts;
+ cfl_sds_t text;
+ struct cmt *cmt;
+ struct cmt_counter *c1;
+ struct cmt_counter *c2;
+ const char *host = "localhost", *index = "fluent-bit-metrics", *source = "fluent-bit-cmetrics", *source_type = "cmetrics";
+
+ char *out1 = \
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:labels.test\":1.0}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:labels.test\":2.0,\"host\":\"calyptia.com\",\"app\":\"cmetrics\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:nosubsystem\":1.0,\"host\":\"aaa\",\"app\":\"bbb\"}}";
+
+ char *out2 = \
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"fields\":{\"metric_name:labels.test\":1.0,\"dev\":\"Calyptia\",\"lang\":\"C\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"fields\":{\"metric_name:labels.test\":2.0,\"dev\":\"Calyptia\",\"lang\":\"C\",\"host\":\"calyptia.com\",\"app\":\"cmetrics\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"fields\":{\"metric_name:nosubsystem\":1.0,\"dev\":\"Calyptia\",\"lang\":\"C\",\"host\":\"aaa\",\"app\":\"bbb\"}}";
+
+ cmt = cmt_create();
+ TEST_CHECK(cmt != NULL);
+
+ c1 = cmt_counter_create(cmt, "cmt", "labels", "test", "Static labels test",
+ 2, (char *[]) {"host", "app"});
+
+ ts = 1435658235000000123;
+ cmt_counter_inc(c1, ts, 0, NULL);
+ cmt_counter_inc(c1, ts, 2, (char *[]) {"calyptia.com", "cmetrics"});
+ cmt_counter_inc(c1, ts, 2, (char *[]) {"calyptia.com", "cmetrics"});
+
+ c2 = cmt_counter_create(cmt, "cmt", "", "nosubsystem", "No subsystem",
+ 2, (char *[]) {"host", "app"});
+
+ cmt_counter_inc(c2, ts, 2, (char *[]) {"aaa", "bbb"});
+
+ /* Encode to splunk hec (no static labels) */
+ text = cmt_encode_splunk_hec_create(cmt, host, index, source, source_type);
+ printf("%s\n", text);
+ TEST_CHECK(strcmp(text, out1) == 0);
+ cmt_encode_splunk_hec_destroy(text);
+
+ /* append static labels */
+ cmt_label_add(cmt, "dev", "Calyptia");
+ cmt_label_add(cmt, "lang", "C");
+
+ text = cmt_encode_splunk_hec_create(cmt, host, index, NULL, NULL);
+ printf("%s\n", text);
+ TEST_CHECK(strcmp(text, out2) == 0);
+ cmt_encode_splunk_hec_destroy(text);
+
+ cmt_destroy(cmt);
+}
+
+
+void test_splunk_hec_floating_point()
+{
+ uint64_t ts;
+ cfl_sds_t text;
+ struct cmt *cmt;
+ struct cmt_counter *c1;
+ struct cmt_counter *c2;
+ const char *host = "localhost", *index = "fluent-bit-metrics", *source = "fluent-bit-cmetrics", *source_type = "cmetrics";
+
+ char *out1 = \
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:labels.test\":0.0}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:labels.test\":2.340000e+12,\"host\":\"calyptia.com\",\"app\":\"cmetrics\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:nosubsystem\":0.0}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:nosubsystem\":5.000000e+15,\"host\":\"aaa\",\"app\":\"bbb\"}}";
+ char *out2 = \
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"fields\":{\"metric_name:labels.test\":0.0,\"dev\":\"Calyptia\",\"lang\":\"C\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"fields\":{\"metric_name:labels.test\":2.340000e+12,\"dev\":\"Calyptia\",\"lang\":\"C\",\"host\":\"calyptia.com\",\"app\":\"cmetrics\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"fields\":{\"metric_name:nosubsystem\":0.0,\"dev\":\"Calyptia\",\"lang\":\"C\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"fields\":{\"metric_name:nosubsystem\":5.000000e+15,\"dev\":\"Calyptia\",\"lang\":\"C\",\"host\":\"aaa\",\"app\":\"bbb\"}}";
+
+ cmt = cmt_create();
+ TEST_CHECK(cmt != NULL);
+
+ c1 = cmt_counter_create(cmt, "cmt", "labels", "test", "Static labels test",
+ 2, (char *[]) {"host", "app"});
+
+ ts = 1435658235000000123;
+ cmt_counter_set(c1, ts, 0, 0, NULL);
+ cmt_counter_add(c1, ts, 2e+10, 2, (char *[]) {"calyptia.com", "cmetrics"});
+ cmt_counter_add(c1, ts, 2.32e+12, 2, (char *[]) {"calyptia.com", "cmetrics"});
+
+ c2 = cmt_counter_create(cmt, "cmt", "", "nosubsystem", "No subsystem",
+ 2, (char *[]) {"host", "app"});
+
+ cmt_counter_set(c2, ts, 0, 0, NULL);
+ cmt_counter_add(c2, ts, 5e+15, 2, (char *[]) {"aaa", "bbb"});
+
+ /* Encode to splunk hec (no static labels) */
+ text = cmt_encode_splunk_hec_create(cmt, host, index, source, source_type);
+ printf("%s\n", text);
+ TEST_CHECK(strcmp(text, out1) == 0);
+ cmt_encode_splunk_hec_destroy(text);
+
+ /* append static labels */
+ cmt_label_add(cmt, "dev", "Calyptia");
+ cmt_label_add(cmt, "lang", "C");
+
+ text = cmt_encode_splunk_hec_create(cmt, host, index, NULL, NULL);
+ printf("%s\n", text);
+ TEST_CHECK(strcmp(text, out2) == 0);
+ cmt_encode_splunk_hec_destroy(text);
+
+ cmt_destroy(cmt);
+}
+
+/* values to observe in a histogram */
+double hist_observe_values[10] = {
+ 0.0 , 1.02, 2.04, 3.06,
+ 4.08, 5.10, 6.12, 7.14,
+ 8.16, 9.18
+ };
+
+static int histogram_observe_all(struct cmt_histogram *h,
+ uint64_t timestamp,
+ int labels_count, char **labels_vals)
+{
+ int i;
+ double val;
+
+ for (i = 0; i < sizeof(hist_observe_values)/(sizeof(double)); i++) {
+ val = hist_observe_values[i];
+ cmt_histogram_observe(h, timestamp, val, labels_count, labels_vals);
+ }
+
+ return i;
+}
+
+void test_splunk_hec_histogram()
+{
+ uint64_t ts;
+ cfl_sds_t text;
+ struct cmt *cmt;
+ struct cmt_histogram *h;
+ struct cmt_histogram_buckets *buckets;
+ const char *host = "localhost", *index = "fluent-bit-metrics", *source = "fluent-bit-cmetrics", *source_type = "cmetrics";
+
+ char *out1 =
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_bucket\":1.0,\"le\":\"0.005\",\"metric_type\":\"Histogram\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_bucket\":1.0,\"le\":\"0.01\",\"metric_type\":\"Histogram\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_bucket\":1.0,\"le\":\"0.025\",\"metric_type\":\"Histogram\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_bucket\":1.0,\"le\":\"0.05\",\"metric_type\":\"Histogram\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_bucket\":1.0,\"le\":\"0.1\",\"metric_type\":\"Histogram\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_bucket\":1.0,\"le\":\"0.25\",\"metric_type\":\"Histogram\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_bucket\":1.0,\"le\":\"0.5\",\"metric_type\":\"Histogram\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_bucket\":1.0,\"le\":\"1.0\",\"metric_type\":\"Histogram\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_bucket\":3.0,\"le\":\"2.5\",\"metric_type\":\"Histogram\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_bucket\":5.0,\"le\":\"5.0\",\"metric_type\":\"Histogram\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_bucket\":10.0,\"le\":\"10.0\",\"metric_type\":\"Histogram\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_bucket\":10.0,\"le\":\"+Inf\",\"metric_type\":\"Histogram\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_sum\":45.0,\"metric_type\":\"Histogram\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_count\":10.0,\"metric_type\":\"Histogram\"}}";
+ char *out2 =
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_bucket\":1.0,\"le\":\"0.005\",\"static\":\"test\",\"metric_type\":\"Histogram\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_bucket\":1.0,\"le\":\"0.01\",\"static\":\"test\",\"metric_type\":\"Histogram\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_bucket\":1.0,\"le\":\"0.025\",\"static\":\"test\",\"metric_type\":\"Histogram\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_bucket\":1.0,\"le\":\"0.05\",\"static\":\"test\",\"metric_type\":\"Histogram\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_bucket\":1.0,\"le\":\"0.1\",\"static\":\"test\",\"metric_type\":\"Histogram\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_bucket\":1.0,\"le\":\"0.25\",\"static\":\"test\",\"metric_type\":\"Histogram\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_bucket\":1.0,\"le\":\"0.5\",\"static\":\"test\",\"metric_type\":\"Histogram\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_bucket\":1.0,\"le\":\"1.0\",\"static\":\"test\",\"metric_type\":\"Histogram\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_bucket\":3.0,\"le\":\"2.5\",\"static\":\"test\",\"metric_type\":\"Histogram\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_bucket\":5.0,\"le\":\"5.0\",\"static\":\"test\",\"metric_type\":\"Histogram\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_bucket\":10.0,\"le\":\"10.0\",\"static\":\"test\",\"metric_type\":\"Histogram\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_bucket\":10.0,\"le\":\"+Inf\",\"static\":\"test\",\"metric_type\":\"Histogram\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_sum\":45.0,\"static\":\"test\",\"metric_type\":\"Histogram\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_count\":10.0,\"static\":\"test\",\"metric_type\":\"Histogram\"}}";
+
+ cmt_initialize();
+
+ /* CMetrics context */
+ cmt = cmt_create();
+ TEST_CHECK(cmt != NULL);
+
+ /* Timestamp */
+ ts = 1435658235000000123;
+
+ /* Create buckets */
+ buckets = cmt_histogram_buckets_create(11,
+ 0.005, 0.01, 0.025, 0.05,
+ 0.1, 0.25, 0.5, 1.0, 2.5,
+ 5.0, 10.0);
+ TEST_CHECK(buckets != NULL);
+
+ /* Create a gauge metric type */
+ h = cmt_histogram_create(cmt,
+ "k8s", "network", "load", "Network load",
+ buckets,
+ 1, (char *[]) {"my_label"});
+ TEST_CHECK(h != NULL);
+
+ /* no labels */
+ histogram_observe_all(h, ts, 0, NULL);
+ text = cmt_encode_splunk_hec_create(cmt, host, index, source, source_type);
+ printf("%s\n", text);
+ TEST_CHECK(strcmp(text, out1) == 0);
+ cmt_encode_splunk_hec_destroy(text);
+
+ /* static label: register static label for the context */
+ cmt_label_add(cmt, "static", "test");
+ text = cmt_encode_splunk_hec_create(cmt, host, index, source, source_type);
+ printf("%s\n", text);
+ TEST_CHECK(strcmp(text, out2) == 0);
+ cmt_encode_splunk_hec_destroy(text);
+
+ /* defined labels: add a custom label value */
+ histogram_observe_all(h, ts, 1, (char *[]) {"val"});
+ text = cmt_encode_splunk_hec_create(cmt, host, index, source, source_type);
+ printf("%s\n", text);
+ cmt_encode_splunk_hec_destroy(text);
+
+ cmt_destroy(cmt);
+}
+
+void test_splunk_hec_summary()
+{
+ double sum;
+ uint64_t count;
+ uint64_t ts;
+ double q[6];
+ double r[6];
+ cfl_sds_t text;
+ struct cmt *cmt;
+ struct cmt_summary *s;
+ const char *host = "localhost", *index = "fluent-bit-metrics", *source = "fluent-bit-cmetrics", *source_type = "cmetrics";
+
+ char *out1 =
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_sum\":51.0,\"metric_type\":\"Summary\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_count\":10.0,\"metric_type\":\"Summary\"}}";
+ char *out2 =
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load\":1.0,\"qt\":\"0.1\",\"metric_type\":\"Summary\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load\":2.0,\"qt\":\"0.2\",\"metric_type\":\"Summary\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load\":3.0,\"qt\":\"0.3\",\"metric_type\":\"Summary\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load\":4.0,\"qt\":\"0.4\",\"metric_type\":\"Summary\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load\":5.0,\"qt\":\"0.5\",\"metric_type\":\"Summary\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load\":6.0,\"qt\":\"1.0\",\"metric_type\":\"Summary\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_sum\":51.0,\"metric_type\":\"Summary\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_count\":10.0,\"metric_type\":\"Summary\"}}";
+ char *out3 =
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load\":1.0,\"qt\":\"0.1\",\"static\":\"test\",\"metric_type\":\"Summary\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load\":2.0,\"qt\":\"0.2\",\"static\":\"test\",\"metric_type\":\"Summary\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load\":3.0,\"qt\":\"0.3\",\"static\":\"test\",\"metric_type\":\"Summary\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load\":4.0,\"qt\":\"0.4\",\"static\":\"test\",\"metric_type\":\"Summary\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load\":5.0,\"qt\":\"0.5\",\"static\":\"test\",\"metric_type\":\"Summary\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load\":6.0,\"qt\":\"1.0\",\"static\":\"test\",\"metric_type\":\"Summary\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_sum\":51.0,\"static\":\"test\",\"metric_type\":\"Summary\"}}"
+ "{\"host\":\"localhost\",\"time\":1435658235.000000123,\"event\":\"metric\",\"index\":\"fluent-bit-metrics\",\"source\":\"fluent-bit-cmetrics\",\"sourcetype\":\"cmetrics\",\"fields\":{\"metric_name:network.load_count\":10.0,\"static\":\"test\",\"metric_type\":\"Summary\"}}";
+
+ cmt_initialize();
+
+ /* Timestamp */
+ ts = 1435658235000000123;
+
+ /* CMetrics context */
+ cmt = cmt_create();
+ TEST_CHECK(cmt != NULL);
+
+ /* set quantiles, no labels */
+ q[0] = 0.1;
+ q[1] = 0.2;
+ q[2] = 0.3;
+ q[3] = 0.4;
+ q[4] = 0.5;
+ q[5] = 1.0;
+
+ r[0] = 1;
+ r[1] = 2;
+ r[2] = 3;
+ r[3] = 4;
+ r[4] = 5;
+ r[5] = 6;
+
+ /* Create a gauge metric type */
+ s = cmt_summary_create(cmt,
+ "k8s", "network", "load", "Network load",
+ 6, q,
+ 1, (char *[]) {"my_label"});
+ TEST_CHECK(s != NULL);
+
+ count = 10;
+ sum = 51.612894511314444;
+
+ /* no quantiles, no labels */
+ cmt_summary_set_default(s, ts, NULL, sum, count, 0, NULL);
+ text = cmt_encode_splunk_hec_create(cmt, host, index, source, source_type);
+ printf("%s\n", text);
+ TEST_CHECK(strcmp(text, out1) == 0);
+ cmt_encode_splunk_hec_destroy(text);
+
+ cmt_summary_set_default(s, ts, r, sum, count, 0, NULL);
+ text = cmt_encode_splunk_hec_create(cmt, host, index, source, source_type);
+ printf("%s\n", text);
+ TEST_CHECK(strcmp(text, out2) == 0);
+ cmt_encode_splunk_hec_destroy(text);
+
+ /* static label: register static label for the context */
+ cmt_label_add(cmt, "static", "test");
+ text = cmt_encode_splunk_hec_create(cmt, host, index, source, source_type);
+ printf("%s\n", text);
+ TEST_CHECK(strcmp(text, out3) == 0);
+ cmt_encode_splunk_hec_destroy(text);
+
+ cmt_destroy(cmt);
+}
+
+TEST_LIST = {
+ {"cmt_msgpack_cleanup_on_error", test_cmt_to_msgpack_cleanup_on_error},
+ {"cmt_msgpack_partial_processing", test_cmt_msgpack_partial_processing},
+ {"prometheus_remote_write", test_prometheus_remote_write},
+ {"cmt_msgpack_stability", test_cmt_to_msgpack_stability},
+ {"cmt_msgpack_integrity", test_cmt_to_msgpack_integrity},
+ {"cmt_msgpack_labels", test_cmt_to_msgpack_labels},
+ {"cmt_msgpack", test_cmt_to_msgpack},
+ {"opentelemetry", test_opentelemetry},
+ {"prometheus", test_prometheus},
+ {"text", test_text},
+ {"influx", test_influx},
+ {"splunk_hec", test_splunk_hec},
+ {"splunk_hec_floating_point", test_splunk_hec_floating_point},
+ {"splunk_hec_histogram", test_splunk_hec_histogram},
+ {"splunk_hec_summary", test_splunk_hec_summary},
+ { 0 }
+};
diff --git a/src/fluent-bit/lib/cmetrics/tests/gauge.c b/src/fluent-bit/lib/cmetrics/tests/gauge.c
new file mode 100644
index 000000000..3c2196010
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/tests/gauge.c
@@ -0,0 +1,174 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_gauge.h>
+#include <cmetrics/cmt_encode_prometheus.h>
+
+#include "cmt_tests.h"
+
+void test_gauge()
+{
+ int ret;
+ double val;
+ uint64_t ts;
+ struct cmt *cmt;
+ struct cmt_gauge *g;
+
+ cmt_initialize();
+
+ cmt = cmt_create();
+ TEST_CHECK(cmt != NULL);
+
+ /* Create a gauge metric type */
+ g = cmt_gauge_create(cmt, "kubernetes", "network", "load", "Network load", 0, NULL);
+ TEST_CHECK(g != NULL);
+
+ /* Timestamp */
+ ts = cfl_time_now();
+
+ /* Default value */
+ ret = cmt_gauge_get_val(g, 0, NULL, &val);
+ TEST_CHECK(val == 0.0);
+
+ /* Set a value of two */
+ cmt_gauge_set(g, ts, 2.0, 0, NULL);
+ ret = cmt_gauge_get_val(g, 0, NULL, &val);
+ TEST_CHECK(ret == 0);
+ TEST_CHECK(val == 2.0);
+
+ /* Increment one */
+ cmt_gauge_inc(g, ts, 0, NULL);
+ ret = cmt_gauge_get_val(g, 0, NULL, &val);
+ TEST_CHECK(ret == 0);
+ TEST_CHECK(val == 3.0);
+
+ /* Substract 2 */
+ ret = cmt_gauge_sub(g, ts, 2, 0, NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = cmt_gauge_get_val(g, 0, NULL, &val);
+ TEST_CHECK(ret == 0);
+ TEST_CHECK(val == 1.0);
+
+ /* Decrement by one */
+ ret = cmt_gauge_dec(g, ts, 0, NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = cmt_gauge_get_val(g, 0, NULL, &val);
+ TEST_CHECK(ret == 0);
+ TEST_CHECK(val == 0.0);
+
+ cmt_destroy(cmt);
+}
+
+void test_labels()
+{
+ int ret;
+ double val;
+ uint64_t ts;
+ cfl_sds_t prom;
+ struct cmt *cmt;
+ struct cmt_gauge *g;
+
+ cmt_initialize();
+
+ cmt = cmt_create();
+ TEST_CHECK(cmt != NULL);
+
+ /* Create a counter metric type */
+ g = cmt_gauge_create(cmt, "kubernetes", "network", "load", "Network load",
+ 2, (char *[]) {"hostname", "app"});
+ TEST_CHECK(g != NULL);
+
+ /* Timestamp */
+ ts = cfl_time_now();
+
+ /*
+ * Test 1: hash zero (no labels)
+ * -----------------------------
+ */
+
+ /* Default value for hash zero */
+ ret = cmt_gauge_get_val(g, 0, NULL, &val);
+ TEST_CHECK(ret == -1);
+
+ /* Increment hash zero by 1 */
+ ret = cmt_gauge_inc(g, ts, 0, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Check the new value */
+ ret = cmt_gauge_get_val(g, 0, NULL, &val);
+ TEST_CHECK(ret == 0);
+ TEST_CHECK(val == 1.0);
+
+ /* Add two */
+ ret = cmt_gauge_add(g, ts, 2, 0, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Check that hash zero val is 3.0 */
+ ret = cmt_gauge_get_val(g, 0, NULL, &val);
+ TEST_CHECK(ret == 0);
+ TEST_CHECK(val == 3.0);
+
+ /*
+ * Test 2: custom labels
+ * ---------------------
+ */
+
+ /* Increment custom metric */
+ ret = cmt_gauge_inc(g, ts, 2, (char *[]) {"localhost", "cmetrics"});
+ TEST_CHECK(ret == 0);
+
+ /* Check ret = 1 */
+ ret = cmt_gauge_get_val(g, 2, (char *[]) {"localhost", "cmetrics"}, &val);
+ TEST_CHECK(ret == 0);
+ TEST_CHECK(val == 1.000);
+
+ /* Add 10 to another metric using a different second label */
+ ret = cmt_gauge_add(g, ts, 10, 2, (char *[]) {"localhost", "test"});
+ TEST_CHECK(ret == 0);
+
+ /* Validate the value */
+ ret = cmt_gauge_get_val(g, 2, (char *[]) {"localhost", "test"}, &val);
+ TEST_CHECK(ret == 0);
+ TEST_CHECK(val == 10.00);
+
+ /* Substract two */
+ ret = cmt_gauge_sub(g, ts, 2.5, 2, (char *[]) {"localhost", "test"});
+ TEST_CHECK(ret == 0);
+
+ /* Validate the value */
+ ret = cmt_gauge_get_val(g, 2, (char *[]) {"localhost", "test"}, &val);
+ TEST_CHECK(ret == 0);
+ TEST_CHECK(val == 7.50);
+
+ printf("\n");
+ prom = cmt_encode_prometheus_create(cmt, CMT_TRUE);
+ printf("%s\n", prom);
+ cmt_encode_prometheus_destroy(prom);
+
+ cmt_destroy(cmt);
+}
+
+TEST_LIST = {
+ {"basic" , test_gauge},
+ {"labels", test_labels},
+ { 0 }
+};
diff --git a/src/fluent-bit/lib/cmetrics/tests/histogram.c b/src/fluent-bit/lib/cmetrics/tests/histogram.c
new file mode 100644
index 000000000..d5fc80bbb
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/tests/histogram.c
@@ -0,0 +1,212 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_histogram.h>
+#include <cmetrics/cmt_encode_prometheus.h>
+#include <cmetrics/cmt_map.h>
+#include "cmt_tests.h"
+
+#include <math.h>
+#include <float.h>
+#include <stdbool.h>
+
+/* values to observe in a histogram */
+double hist_observe_values[10] = {
+ 0.0 , 1.02, 2.04, 3.06,
+ 4.08, 5.10, 6.12, 7.14,
+ 8.16, 9.18
+ };
+
+/*
+ * histogram bucket values: the values computed in the buckets,
+ * all of them are uint64_t.
+ *
+ * Note that on all examples we use the default buckets values, created manually
+ * and through the API:
+ *
+ * - 11 bucket values
+ * - 1 +Inf bucket value
+ */
+uint64_t hist_buckets_values[12] = {1, 1, 1, 1, 1, 1, 1, 1,
+ 3, 5, 10, 10};
+
+/* histogram _count value */
+uint64_t hist_count = 10;
+
+/* histogram _sum value */
+double hist_sum = 45.9;
+
+bool fequal(double a, double b)
+{
+ return (fabs(a - b) < (DBL_EPSILON * fabs(a + b)));
+}
+
+static void histogram_check(struct cmt_histogram *h,
+ int labels_count, char **labels_vals)
+{
+ int i;
+ int ret;
+ uint64_t val;
+ struct cmt_metric *metric;
+
+ /* retrieve the metric context */
+ metric = cmt_map_metric_get(&h->opts, h->map,
+ labels_count, labels_vals, CMT_TRUE);
+ TEST_CHECK(metric != NULL);
+
+ /* check bucket values */
+ for (i = 0; i < (sizeof(hist_buckets_values)/sizeof(uint64_t)); i++) {
+ val = cmt_metric_hist_get_value(metric, i);
+ TEST_CHECK(val == hist_buckets_values[i]);
+ }
+
+ /* check _count */
+ TEST_CHECK(hist_count == cmt_metric_hist_get_count_value(metric));
+
+ /* check _sum */
+ ret = fequal(hist_sum, cmt_metric_hist_get_sum_value(metric));
+ TEST_CHECK(ret != 0);
+}
+
+static int histogram_observe_all(struct cmt_histogram *h,
+ uint64_t timestamp,
+ int labels_count, char **labels_vals)
+{
+ int i;
+ double val;
+
+ for (i = 0; i < sizeof(hist_observe_values)/(sizeof(double)); i++) {
+ val = hist_observe_values[i];
+ cmt_histogram_observe(h, timestamp, val, labels_count, labels_vals);
+ }
+
+ return i;
+}
+
+static void prometheus_encode_test(struct cmt *cmt)
+{
+ cfl_sds_t buf;
+
+ buf = cmt_encode_prometheus_create(cmt, CMT_FALSE);
+ printf("\n%s\n", buf);
+ cmt_encode_prometheus_destroy(buf);
+}
+
+
+void test_histogram()
+{
+ uint64_t ts;
+ struct cmt *cmt;
+ struct cmt_histogram *h;
+ struct cmt_histogram_buckets *buckets;
+
+ cmt_initialize();
+
+ /* Timestamp */
+ ts = cfl_time_now();
+
+ /* CMetrics context */
+ cmt = cmt_create();
+ TEST_CHECK(cmt != NULL);
+
+ /* Create buckets */
+ buckets = cmt_histogram_buckets_create(11,
+ 0.005, 0.01, 0.025, 0.05,
+ 0.1, 0.25, 0.5, 1.0, 2.5,
+ 5.0, 10.0);
+ TEST_CHECK(buckets != NULL);
+
+ /* Create a gauge metric type */
+ h = cmt_histogram_create(cmt,
+ "k8s", "network", "load", "Network load",
+ buckets,
+ 1, (char *[]) {"my_label"});
+ TEST_CHECK(h != NULL);
+
+ /* no labels */
+ histogram_observe_all(h, ts, 0, NULL);
+ histogram_check(h, 0, NULL);
+ prometheus_encode_test(cmt);
+
+ /* static label: register static label for the context */
+ cmt_label_add(cmt, "static", "test");
+ histogram_check(h, 0, NULL);
+ prometheus_encode_test(cmt);
+
+ /* defined labels: add a custom label value */
+ histogram_observe_all(h, ts, 1, (char *[]) {"val"});
+ histogram_check(h, 1, (char *[]) {"val"});
+ prometheus_encode_test(cmt);
+
+ cmt_destroy(cmt);
+}
+
+void test_set_defaults()
+{
+ uint64_t ts;
+ struct cmt *cmt;
+ struct cmt_histogram *h;
+ struct cmt_histogram_buckets *buckets;
+
+ cmt_initialize();
+
+ /* Timestamp */
+ ts = cfl_time_now();
+
+ /* CMetrics context */
+ cmt = cmt_create();
+ TEST_CHECK(cmt != NULL);
+
+ /* Create buckets */
+ buckets = cmt_histogram_buckets_default_create();
+ TEST_CHECK(buckets != NULL);
+
+ /* Create a gauge metric type */
+ h = cmt_histogram_create(cmt,
+ "k8s", "network", "load", "Network load",
+ buckets,
+ 1, (char *[]) {"my_label"});
+ TEST_CHECK(h != NULL);
+
+ /* set default buckets values / no labels */
+ cmt_histogram_set_default(h, ts,
+ hist_buckets_values,
+ hist_sum, hist_count, 0, NULL);
+ histogram_check(h, 0, NULL);
+ prometheus_encode_test(cmt);
+
+ /* static label: register static label for the context */
+ cmt_label_add(cmt, "static", "test");
+ histogram_check(h, 0, NULL);
+ prometheus_encode_test(cmt);
+
+ /* perform observation with labels */
+ histogram_observe_all(h, ts, 1, (char *[]) {"val"});
+ histogram_check(h, 1, (char *[]) {"val"});
+ prometheus_encode_test(cmt);
+
+ cmt_destroy(cmt);
+}
+
+TEST_LIST = {
+ {"histogram" , test_histogram},
+ {"set_defaults", test_set_defaults},
+ { 0 }
+};
diff --git a/src/fluent-bit/lib/cmetrics/tests/issues.c b/src/fluent-bit/lib/cmetrics/tests/issues.c
new file mode 100644
index 000000000..8e6116e26
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/tests/issues.c
@@ -0,0 +1,95 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_counter.h>
+#include <cmetrics/cmt_encode_msgpack.h>
+#include <cmetrics/cmt_decode_msgpack.h>
+#include <cmetrics/cmt_encode_text.h>
+
+#include "cmt_tests.h"
+
+static struct cmt *generate_encoder_test_data()
+{
+ uint64_t ts;
+ struct cmt *cmt;
+ struct cmt_counter *c1;
+ struct cmt_counter *c2;
+
+ ts = 0;
+ cmt = cmt_create();
+
+ c1 = cmt_counter_create(cmt, "kubernetes", "", "load", "Network load",
+ 2, (char *[]) {"hostname", "app"});
+ cmt_counter_set(c1, ts, 10, 0, NULL);
+
+ c2 = cmt_counter_create(cmt, "kubernetes", "", "cpu", "CPU load",
+ 2, (char *[]) {"hostname", "app"});
+ cmt_counter_set(c2, ts, 10, 0, NULL);
+
+ return cmt;
+}
+
+
+void test_issue_54()
+{
+ const char expected_text[] = "1970-01-01T00:00:00.000000000Z kubernetes_load{tag1=\"tag1\",tag2=\"tag2\"} = 10\n" \
+ "1970-01-01T00:00:00.000000000Z kubernetes_cpu{tag1=\"tag1\",tag2=\"tag2\"} = 10\n";
+ cfl_sds_t text_result;
+ size_t mp1_size;
+ char *mp1_buf;
+ size_t offset;
+ int result;
+ struct cmt *cmt2;
+ struct cmt *cmt1;
+
+ cmt_initialize();
+
+ /* Generate context with data */
+ cmt1 = generate_encoder_test_data();
+ TEST_CHECK(NULL != cmt1);
+
+ /* append static labels */
+ cmt_label_add(cmt1, "tag1", "tag1");
+ cmt_label_add(cmt1, "tag2", "tag2");
+
+ /* CMT1 -> Msgpack */
+ result = cmt_encode_msgpack_create(cmt1, &mp1_buf, &mp1_size);
+ TEST_CHECK(0 == result);
+
+ /* Msgpack -> CMT2 */
+ offset = 0;
+ result = cmt_decode_msgpack_create(&cmt2, mp1_buf, mp1_size, &offset);
+ TEST_CHECK(0 == result);
+
+ text_result = cmt_encode_text_create(cmt2);
+
+ TEST_CHECK(NULL != text_result);
+ TEST_CHECK(0 == strcmp(text_result, expected_text));
+
+ cmt_encode_text_destroy(text_result);
+ cmt_decode_msgpack_destroy(cmt2);
+ cmt_encode_msgpack_destroy(mp1_buf);
+ cmt_destroy(cmt1);
+}
+
+TEST_LIST = {
+ {"issue_54", test_issue_54},
+ { 0 }
+};
diff --git a/src/fluent-bit/lib/cmetrics/tests/lib/acutest/acutest.h b/src/fluent-bit/lib/cmetrics/tests/lib/acutest/acutest.h
new file mode 100644
index 000000000..1d13044f0
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/tests/lib/acutest/acutest.h
@@ -0,0 +1,1794 @@
+/*
+ * Acutest -- Another C/C++ Unit Test facility
+ * <https://github.com/mity/acutest>
+ *
+ * Copyright 2013-2020 Martin Mitas
+ * Copyright 2019 Garrett D'Amore
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef ACUTEST_H
+#define ACUTEST_H
+
+
+/************************
+ *** Public interface ***
+ ************************/
+
+/* By default, "acutest.h" provides the main program entry point (function
+ * main()). However, if the test suite is composed of multiple source files
+ * which include "acutest.h", then this causes a problem of multiple main()
+ * definitions. To avoid this problem, #define macro TEST_NO_MAIN in all
+ * compilation units but one.
+ */
+
+/* Macro to specify list of unit tests in the suite.
+ * The unit test implementation MUST provide list of unit tests it implements
+ * with this macro:
+ *
+ * TEST_LIST = {
+ * { "test1_name", test1_func_ptr },
+ * { "test2_name", test2_func_ptr },
+ * ...
+ * { NULL, NULL } // zeroed record marking the end of the list
+ * };
+ *
+ * The list specifies names of each test (must be unique) and pointer to
+ * a function implementing it. The function does not take any arguments
+ * and has no return values, i.e. every test function has to be compatible
+ * with this prototype:
+ *
+ * void test_func(void);
+ *
+ * Note the list has to be ended with a zeroed record.
+ */
+#define TEST_LIST const struct acutest_test_ acutest_list_[]
+
+
+/* Macros for testing whether an unit test succeeds or fails. These macros
+ * can be used arbitrarily in functions implementing the unit tests.
+ *
+ * If any condition fails throughout execution of a test, the test fails.
+ *
+ * TEST_CHECK takes only one argument (the condition), TEST_CHECK_ allows
+ * also to specify an error message to print out if the condition fails.
+ * (It expects printf-like format string and its parameters). The macros
+ * return non-zero (condition passes) or 0 (condition fails).
+ *
+ * That can be useful when more conditions should be checked only if some
+ * preceding condition passes, as illustrated in this code snippet:
+ *
+ * SomeStruct* ptr = allocate_some_struct();
+ * if(TEST_CHECK(ptr != NULL)) {
+ * TEST_CHECK(ptr->member1 < 100);
+ * TEST_CHECK(ptr->member2 > 200);
+ * }
+ */
+#define TEST_CHECK_(cond,...) acutest_check_((cond), __FILE__, __LINE__, __VA_ARGS__)
+#define TEST_CHECK(cond) acutest_check_((cond), __FILE__, __LINE__, "%s", #cond)
+
+
+/* These macros are the same as TEST_CHECK_ and TEST_CHECK except that if the
+ * condition fails, the currently executed unit test is immediately aborted.
+ *
+ * That is done either by calling abort() if the unit test is executed as a
+ * child process; or via longjmp() if the unit test is executed within the
+ * main Acutest process.
+ *
+ * As a side effect of such abortion, your unit tests may cause memory leaks,
+ * unflushed file descriptors, and other phenomena caused by the abortion.
+ *
+ * Therefore you should not use these as a general replacement for TEST_CHECK.
+ * Use it with some caution, especially if your test causes some other side
+ * effects to the outside world (e.g. communicating with some server, inserting
+ * into a database etc.).
+ */
+#define TEST_ASSERT_(cond,...) \
+ do { \
+ if(!acutest_check_((cond), __FILE__, __LINE__, __VA_ARGS__)) \
+ acutest_abort_(); \
+ } while(0)
+#define TEST_ASSERT(cond) \
+ do { \
+ if(!acutest_check_((cond), __FILE__, __LINE__, "%s", #cond)) \
+ acutest_abort_(); \
+ } while(0)
+
+
+#ifdef __cplusplus
+/* Macros to verify that the code (the 1st argument) throws exception of given
+ * type (the 2nd argument). (Note these macros are only available in C++.)
+ *
+ * TEST_EXCEPTION_ is like TEST_EXCEPTION but accepts custom printf-like
+ * message.
+ *
+ * For example:
+ *
+ * TEST_EXCEPTION(function_that_throw(), ExpectedExceptionType);
+ *
+ * If the function_that_throw() throws ExpectedExceptionType, the check passes.
+ * If the function throws anything incompatible with ExpectedExceptionType
+ * (or if it does not thrown an exception at all), the check fails.
+ */
+#define TEST_EXCEPTION(code, exctype) \
+ do { \
+ bool exc_ok_ = false; \
+ const char *msg_ = NULL; \
+ try { \
+ code; \
+ msg_ = "No exception thrown."; \
+ } catch(exctype const&) { \
+ exc_ok_= true; \
+ } catch(...) { \
+ msg_ = "Unexpected exception thrown."; \
+ } \
+ acutest_check_(exc_ok_, __FILE__, __LINE__, #code " throws " #exctype);\
+ if(msg_ != NULL) \
+ acutest_message_("%s", msg_); \
+ } while(0)
+#define TEST_EXCEPTION_(code, exctype, ...) \
+ do { \
+ bool exc_ok_ = false; \
+ const char *msg_ = NULL; \
+ try { \
+ code; \
+ msg_ = "No exception thrown."; \
+ } catch(exctype const&) { \
+ exc_ok_= true; \
+ } catch(...) { \
+ msg_ = "Unexpected exception thrown."; \
+ } \
+ acutest_check_(exc_ok_, __FILE__, __LINE__, __VA_ARGS__); \
+ if(msg_ != NULL) \
+ acutest_message_("%s", msg_); \
+ } while(0)
+#endif /* #ifdef __cplusplus */
+
+
+/* Sometimes it is useful to split execution of more complex unit tests to some
+ * smaller parts and associate those parts with some names.
+ *
+ * This is especially handy if the given unit test is implemented as a loop
+ * over some vector of multiple testing inputs. Using these macros allow to use
+ * sort of subtitle for each iteration of the loop (e.g. outputting the input
+ * itself or a name associated to it), so that if any TEST_CHECK condition
+ * fails in the loop, it can be easily seen which iteration triggers the
+ * failure, without the need to manually output the iteration-specific data in
+ * every single TEST_CHECK inside the loop body.
+ *
+ * TEST_CASE allows to specify only single string as the name of the case,
+ * TEST_CASE_ provides all the power of printf-like string formatting.
+ *
+ * Note that the test cases cannot be nested. Starting a new test case ends
+ * implicitly the previous one. To end the test case explicitly (e.g. to end
+ * the last test case after exiting the loop), you may use TEST_CASE(NULL).
+ */
+#define TEST_CASE_(...) acutest_case_(__VA_ARGS__)
+#define TEST_CASE(name) acutest_case_("%s", name)
+
+
+/* Maximal output per TEST_CASE call. Longer messages are cut.
+ * You may define another limit prior including "acutest.h"
+ */
+#ifndef TEST_CASE_MAXSIZE
+ #define TEST_CASE_MAXSIZE 64
+#endif
+
+
+/* printf-like macro for outputting an extra information about a failure.
+ *
+ * Intended use is to output some computed output versus the expected value,
+ * e.g. like this:
+ *
+ * if(!TEST_CHECK(produced == expected)) {
+ * TEST_MSG("Expected: %d", expected);
+ * TEST_MSG("Produced: %d", produced);
+ * }
+ *
+ * Note the message is only written down if the most recent use of any checking
+ * macro (like e.g. TEST_CHECK or TEST_EXCEPTION) in the current test failed.
+ * This means the above is equivalent to just this:
+ *
+ * TEST_CHECK(produced == expected);
+ * TEST_MSG("Expected: %d", expected);
+ * TEST_MSG("Produced: %d", produced);
+ *
+ * The macro can deal with multi-line output fairly well. It also automatically
+ * adds a final new-line if there is none present.
+ */
+#define TEST_MSG(...) acutest_message_(__VA_ARGS__)
+
+
+/* Maximal output per TEST_MSG call. Longer messages are cut.
+ * You may define another limit prior including "acutest.h"
+ */
+#ifndef TEST_MSG_MAXSIZE
+ #define TEST_MSG_MAXSIZE 1024
+#endif
+
+
+/* Macro for dumping a block of memory.
+ *
+ * Its intended use is very similar to what TEST_MSG is for, but instead of
+ * generating any printf-like message, this is for dumping raw block of a
+ * memory in a hexadecimal form:
+ *
+ * TEST_CHECK(size_produced == size_expected &&
+ * memcmp(addr_produced, addr_expected, size_produced) == 0);
+ * TEST_DUMP("Expected:", addr_expected, size_expected);
+ * TEST_DUMP("Produced:", addr_produced, size_produced);
+ */
+#define TEST_DUMP(title, addr, size) acutest_dump_(title, addr, size)
+
+/* Maximal output per TEST_DUMP call (in bytes to dump). Longer blocks are cut.
+ * You may define another limit prior including "acutest.h"
+ */
+#ifndef TEST_DUMP_MAXSIZE
+ #define TEST_DUMP_MAXSIZE 1024
+#endif
+
+
+/* Common test initialiation/clean-up
+ *
+ * In some test suites, it may be needed to perform some sort of the same
+ * initialization and/or clean-up in all the tests.
+ *
+ * Such test suites may use macros TEST_INIT and/or TEST_FINI prior including
+ * this header. The expansion of the macro is then used as a body of helper
+ * function called just before executing every single (TEST_INIT) or just after
+ * it ends (TEST_FINI).
+ *
+ * Examples of various ways how to use the macro TEST_INIT:
+ *
+ * #define TEST_INIT my_init_func();
+ * #define TEST_INIT my_init_func() // Works even without the semicolon
+ * #define TEST_INIT setlocale(LC_ALL, NULL);
+ * #define TEST_INIT { setlocale(LC_ALL, NULL); my_init_func(); }
+ *
+ * TEST_FINI is to be used in the same way.
+ */
+
+
+/**********************
+ *** Implementation ***
+ **********************/
+
+/* The unit test files should not rely on anything below. */
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <setjmp.h>
+
+#if defined(unix) || defined(__unix__) || defined(__unix) || defined(__APPLE__)
+ #define ACUTEST_UNIX_ 1
+ #include <errno.h>
+ #include <libgen.h>
+ #include <unistd.h>
+ #include <sys/types.h>
+ #include <sys/wait.h>
+ #include <signal.h>
+ #include <time.h>
+
+ #if defined CLOCK_PROCESS_CPUTIME_ID && defined CLOCK_MONOTONIC
+ #define ACUTEST_HAS_POSIX_TIMER_ 1
+ #endif
+#endif
+
+#if defined(_gnu_linux_) || defined(__linux__)
+ #define ACUTEST_LINUX_ 1
+ #include <fcntl.h>
+ #include <sys/stat.h>
+#endif
+
+#if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__)
+ #define ACUTEST_WIN_ 1
+ #include <windows.h>
+ #include <io.h>
+#endif
+
+#ifdef __cplusplus
+ #include <exception>
+#endif
+
+#ifdef __has_include
+ #if __has_include(<valgrind.h>)
+ #include <valgrind.h>
+ #endif
+#endif
+
+/* Enable the use of the non-standard keyword __attribute__ to silence warnings under some compilers */
+#if defined(__GNUC__) || defined(__clang__)
+ #define ACUTEST_ATTRIBUTE_(attr) __attribute__((attr))
+#else
+ #define ACUTEST_ATTRIBUTE_(attr)
+#endif
+
+/* Note our global private identifiers end with '_' to mitigate risk of clash
+ * with the unit tests implementation. */
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#ifdef _MSC_VER
+ /* In the multi-platform code like ours, we cannot use the non-standard
+ * "safe" functions from Microsoft C lib like e.g. sprintf_s() instead of
+ * standard sprintf(). Hence, lets disable the warning C4996. */
+ #pragma warning(push)
+ #pragma warning(disable: 4996)
+#endif
+
+
+struct acutest_test_ {
+ const char* name;
+ void (*func)(void);
+};
+
+struct acutest_test_data_ {
+ unsigned char flags;
+ double duration;
+};
+
+enum {
+ ACUTEST_FLAG_RUN_ = 1 << 0,
+ ACUTEST_FLAG_SUCCESS_ = 1 << 1,
+ ACUTEST_FLAG_FAILURE_ = 1 << 2,
+};
+
+extern const struct acutest_test_ acutest_list_[];
+
+int acutest_check_(int cond, const char* file, int line, const char* fmt, ...);
+void acutest_case_(const char* fmt, ...);
+void acutest_message_(const char* fmt, ...);
+void acutest_dump_(const char* title, const void* addr, size_t size);
+void acutest_abort_(void) ACUTEST_ATTRIBUTE_(noreturn);
+
+
+#ifndef TEST_NO_MAIN
+
+static char* acutest_argv0_ = NULL;
+static size_t acutest_list_size_ = 0;
+static struct acutest_test_data_* acutest_test_data_ = NULL;
+static size_t acutest_count_ = 0;
+static int acutest_no_exec_ = -1;
+static int acutest_no_summary_ = 0;
+static int acutest_tap_ = 0;
+static int acutest_skip_mode_ = 0;
+static int acutest_worker_ = 0;
+static int acutest_worker_index_ = 0;
+static int acutest_cond_failed_ = 0;
+static int acutest_was_aborted_ = 0;
+static FILE *acutest_xml_output_ = NULL;
+
+static int acutest_stat_failed_units_ = 0;
+static int acutest_stat_run_units_ = 0;
+
+static const struct acutest_test_* acutest_current_test_ = NULL;
+static int acutest_current_index_ = 0;
+static char acutest_case_name_[TEST_CASE_MAXSIZE] = "";
+static int acutest_test_already_logged_ = 0;
+static int acutest_case_already_logged_ = 0;
+static int acutest_verbose_level_ = 2;
+static int acutest_test_failures_ = 0;
+static int acutest_colorize_ = 0;
+static int acutest_timer_ = 0;
+
+static int acutest_abort_has_jmp_buf_ = 0;
+static jmp_buf acutest_abort_jmp_buf_;
+
+
+static void
+acutest_cleanup_(void)
+{
+ free((void*) acutest_test_data_);
+}
+
+static void ACUTEST_ATTRIBUTE_(noreturn)
+acutest_exit_(int exit_code)
+{
+ acutest_cleanup_();
+ exit(exit_code);
+}
+
+#if defined ACUTEST_WIN_
+ typedef LARGE_INTEGER acutest_timer_type_;
+ static LARGE_INTEGER acutest_timer_freq_;
+ static acutest_timer_type_ acutest_timer_start_;
+ static acutest_timer_type_ acutest_timer_end_;
+
+ static void
+ acutest_timer_init_(void)
+ {
+ QueryPerformanceFrequency(&acutest_timer_freq_);
+ }
+
+ static void
+ acutest_timer_get_time_(LARGE_INTEGER* ts)
+ {
+ QueryPerformanceCounter(ts);
+ }
+
+ static double
+ acutest_timer_diff_(LARGE_INTEGER start, LARGE_INTEGER end)
+ {
+ double duration = (double)(end.QuadPart - start.QuadPart);
+ duration /= (double)acutest_timer_freq_.QuadPart;
+ return duration;
+ }
+
+ static void
+ acutest_timer_print_diff_(void)
+ {
+ printf("%.6lf secs", acutest_timer_diff_(acutest_timer_start_, acutest_timer_end_));
+ }
+#elif defined ACUTEST_HAS_POSIX_TIMER_
+ static clockid_t acutest_timer_id_;
+ typedef struct timespec acutest_timer_type_;
+ static acutest_timer_type_ acutest_timer_start_;
+ static acutest_timer_type_ acutest_timer_end_;
+
+ static void
+ acutest_timer_init_(void)
+ {
+ if(acutest_timer_ == 1)
+ acutest_timer_id_ = CLOCK_MONOTONIC;
+ else if(acutest_timer_ == 2)
+ acutest_timer_id_ = CLOCK_PROCESS_CPUTIME_ID;
+ }
+
+ static void
+ acutest_timer_get_time_(struct timespec* ts)
+ {
+ clock_gettime(acutest_timer_id_, ts);
+ }
+
+ static double
+ acutest_timer_diff_(struct timespec start, struct timespec end)
+ {
+ double endns;
+ double startns;
+
+ endns = end.tv_sec;
+ endns *= 1e9;
+ endns += end.tv_nsec;
+
+ startns = start.tv_sec;
+ startns *= 1e9;
+ startns += start.tv_nsec;
+
+ return ((endns - startns)/ 1e9);
+ }
+
+ static void
+ acutest_timer_print_diff_(void)
+ {
+ printf("%.6lf secs",
+ acutest_timer_diff_(acutest_timer_start_, acutest_timer_end_));
+ }
+#else
+ typedef int acutest_timer_type_;
+ static acutest_timer_type_ acutest_timer_start_;
+ static acutest_timer_type_ acutest_timer_end_;
+
+ void
+ acutest_timer_init_(void)
+ {}
+
+ static void
+ acutest_timer_get_time_(int* ts)
+ {
+ (void) ts;
+ }
+
+ static double
+ acutest_timer_diff_(int start, int end)
+ {
+ (void) start;
+ (void) end;
+ return 0.0;
+ }
+
+ static void
+ acutest_timer_print_diff_(void)
+ {}
+#endif
+
+#define ACUTEST_COLOR_DEFAULT_ 0
+#define ACUTEST_COLOR_GREEN_ 1
+#define ACUTEST_COLOR_RED_ 2
+#define ACUTEST_COLOR_DEFAULT_INTENSIVE_ 3
+#define ACUTEST_COLOR_GREEN_INTENSIVE_ 4
+#define ACUTEST_COLOR_RED_INTENSIVE_ 5
+
+static int ACUTEST_ATTRIBUTE_(format (printf, 2, 3))
+acutest_colored_printf_(int color, const char* fmt, ...)
+{
+ va_list args;
+ char buffer[256];
+ int n;
+
+ va_start(args, fmt);
+ vsnprintf(buffer, sizeof(buffer), fmt, args);
+ va_end(args);
+ buffer[sizeof(buffer)-1] = '\0';
+
+ if(!acutest_colorize_) {
+ return printf("%s", buffer);
+ }
+
+#if defined ACUTEST_UNIX_
+ {
+ const char* col_str;
+ switch(color) {
+ case ACUTEST_COLOR_GREEN_: col_str = "\033[0;32m"; break;
+ case ACUTEST_COLOR_RED_: col_str = "\033[0;31m"; break;
+ case ACUTEST_COLOR_GREEN_INTENSIVE_: col_str = "\033[1;32m"; break;
+ case ACUTEST_COLOR_RED_INTENSIVE_: col_str = "\033[1;31m"; break;
+ case ACUTEST_COLOR_DEFAULT_INTENSIVE_: col_str = "\033[1m"; break;
+ default: col_str = "\033[0m"; break;
+ }
+ printf("%s", col_str);
+ n = printf("%s", buffer);
+ printf("\033[0m");
+ return n;
+ }
+#elif defined ACUTEST_WIN_
+ {
+ HANDLE h;
+ CONSOLE_SCREEN_BUFFER_INFO info;
+ WORD attr;
+
+ h = GetStdHandle(STD_OUTPUT_HANDLE);
+ GetConsoleScreenBufferInfo(h, &info);
+
+ switch(color) {
+ case ACUTEST_COLOR_GREEN_: attr = FOREGROUND_GREEN; break;
+ case ACUTEST_COLOR_RED_: attr = FOREGROUND_RED; break;
+ case ACUTEST_COLOR_GREEN_INTENSIVE_: attr = FOREGROUND_GREEN | FOREGROUND_INTENSITY; break;
+ case ACUTEST_COLOR_RED_INTENSIVE_: attr = FOREGROUND_RED | FOREGROUND_INTENSITY; break;
+ case ACUTEST_COLOR_DEFAULT_INTENSIVE_: attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY; break;
+ default: attr = 0; break;
+ }
+ if(attr != 0)
+ SetConsoleTextAttribute(h, attr);
+ n = printf("%s", buffer);
+ SetConsoleTextAttribute(h, info.wAttributes);
+ return n;
+ }
+#else
+ n = printf("%s", buffer);
+ return n;
+#endif
+}
+
+static void
+acutest_begin_test_line_(const struct acutest_test_* test)
+{
+ if(!acutest_tap_) {
+ if(acutest_verbose_level_ >= 3) {
+ acutest_colored_printf_(ACUTEST_COLOR_DEFAULT_INTENSIVE_, "Test %s:\n", test->name);
+ acutest_test_already_logged_++;
+ } else if(acutest_verbose_level_ >= 1) {
+ int n;
+ char spaces[48];
+
+ n = acutest_colored_printf_(ACUTEST_COLOR_DEFAULT_INTENSIVE_, "Test %s... ", test->name);
+ memset(spaces, ' ', sizeof(spaces));
+ if(n < (int) sizeof(spaces))
+ printf("%.*s", (int) sizeof(spaces) - n, spaces);
+ } else {
+ acutest_test_already_logged_ = 1;
+ }
+ }
+}
+
+static void
+acutest_finish_test_line_(int result)
+{
+ if(acutest_tap_) {
+ const char* str = (result == 0) ? "ok" : "not ok";
+
+ printf("%s %d - %s\n", str, acutest_current_index_ + 1, acutest_current_test_->name);
+
+ if(result == 0 && acutest_timer_) {
+ printf("# Duration: ");
+ acutest_timer_print_diff_();
+ printf("\n");
+ }
+ } else {
+ int color = (result == 0) ? ACUTEST_COLOR_GREEN_INTENSIVE_ : ACUTEST_COLOR_RED_INTENSIVE_;
+ const char* str = (result == 0) ? "OK" : "FAILED";
+ printf("[ ");
+ acutest_colored_printf_(color, "%s", str);
+ printf(" ]");
+
+ if(result == 0 && acutest_timer_) {
+ printf(" ");
+ acutest_timer_print_diff_();
+ }
+
+ printf("\n");
+ }
+}
+
+static void
+acutest_line_indent_(int level)
+{
+ static const char spaces[] = " ";
+ int n = level * 2;
+
+ if(acutest_tap_ && n > 0) {
+ n--;
+ printf("#");
+ }
+
+ while(n > 16) {
+ printf("%s", spaces);
+ n -= 16;
+ }
+ printf("%.*s", n, spaces);
+}
+
+int ACUTEST_ATTRIBUTE_(format (printf, 4, 5))
+acutest_check_(int cond, const char* file, int line, const char* fmt, ...)
+{
+ const char *result_str;
+ int result_color;
+ int verbose_level;
+
+ if(cond) {
+ result_str = "ok";
+ result_color = ACUTEST_COLOR_GREEN_;
+ verbose_level = 3;
+ } else {
+ if(!acutest_test_already_logged_ && acutest_current_test_ != NULL)
+ acutest_finish_test_line_(-1);
+
+ result_str = "failed";
+ result_color = ACUTEST_COLOR_RED_;
+ verbose_level = 2;
+ acutest_test_failures_++;
+ acutest_test_already_logged_++;
+ }
+
+ if(acutest_verbose_level_ >= verbose_level) {
+ va_list args;
+
+ if(!acutest_case_already_logged_ && acutest_case_name_[0]) {
+ acutest_line_indent_(1);
+ acutest_colored_printf_(ACUTEST_COLOR_DEFAULT_INTENSIVE_, "Case %s:\n", acutest_case_name_);
+ acutest_test_already_logged_++;
+ acutest_case_already_logged_++;
+ }
+
+ acutest_line_indent_(acutest_case_name_[0] ? 2 : 1);
+ if(file != NULL) {
+#ifdef ACUTEST_WIN_
+ const char* lastsep1 = strrchr(file, '\\');
+ const char* lastsep2 = strrchr(file, '/');
+ if(lastsep1 == NULL)
+ lastsep1 = file-1;
+ if(lastsep2 == NULL)
+ lastsep2 = file-1;
+ file = (lastsep1 > lastsep2 ? lastsep1 : lastsep2) + 1;
+#else
+ const char* lastsep = strrchr(file, '/');
+ if(lastsep != NULL)
+ file = lastsep+1;
+#endif
+ printf("%s:%d: Check ", file, line);
+ }
+
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+
+ printf("... ");
+ acutest_colored_printf_(result_color, "%s", result_str);
+ printf("\n");
+ acutest_test_already_logged_++;
+ }
+
+ acutest_cond_failed_ = (cond == 0);
+ return !acutest_cond_failed_;
+}
+
+void ACUTEST_ATTRIBUTE_(format (printf, 1, 2))
+acutest_case_(const char* fmt, ...)
+{
+ va_list args;
+
+ if(acutest_verbose_level_ < 2)
+ return;
+
+ if(acutest_case_name_[0]) {
+ acutest_case_already_logged_ = 0;
+ acutest_case_name_[0] = '\0';
+ }
+
+ if(fmt == NULL)
+ return;
+
+ va_start(args, fmt);
+ vsnprintf(acutest_case_name_, sizeof(acutest_case_name_) - 1, fmt, args);
+ va_end(args);
+ acutest_case_name_[sizeof(acutest_case_name_) - 1] = '\0';
+
+ if(acutest_verbose_level_ >= 3) {
+ acutest_line_indent_(1);
+ acutest_colored_printf_(ACUTEST_COLOR_DEFAULT_INTENSIVE_, "Case %s:\n", acutest_case_name_);
+ acutest_test_already_logged_++;
+ acutest_case_already_logged_++;
+ }
+}
+
+void ACUTEST_ATTRIBUTE_(format (printf, 1, 2))
+acutest_message_(const char* fmt, ...)
+{
+ char buffer[TEST_MSG_MAXSIZE];
+ char* line_beg;
+ char* line_end;
+ va_list args;
+
+ if(acutest_verbose_level_ < 2)
+ return;
+
+ /* We allow extra message only when something is already wrong in the
+ * current test. */
+ if(acutest_current_test_ == NULL || !acutest_cond_failed_)
+ return;
+
+ va_start(args, fmt);
+ vsnprintf(buffer, TEST_MSG_MAXSIZE, fmt, args);
+ va_end(args);
+ buffer[TEST_MSG_MAXSIZE-1] = '\0';
+
+ line_beg = buffer;
+ while(1) {
+ line_end = strchr(line_beg, '\n');
+ if(line_end == NULL)
+ break;
+ acutest_line_indent_(acutest_case_name_[0] ? 3 : 2);
+ printf("%.*s\n", (int)(line_end - line_beg), line_beg);
+ line_beg = line_end + 1;
+ }
+ if(line_beg[0] != '\0') {
+ acutest_line_indent_(acutest_case_name_[0] ? 3 : 2);
+ printf("%s\n", line_beg);
+ }
+}
+
+void
+acutest_dump_(const char* title, const void* addr, size_t size)
+{
+ static const size_t BYTES_PER_LINE = 16;
+ size_t line_beg;
+ size_t truncate = 0;
+
+ if(acutest_verbose_level_ < 2)
+ return;
+
+ /* We allow extra message only when something is already wrong in the
+ * current test. */
+ if(acutest_current_test_ == NULL || !acutest_cond_failed_)
+ return;
+
+ if(size > TEST_DUMP_MAXSIZE) {
+ truncate = size - TEST_DUMP_MAXSIZE;
+ size = TEST_DUMP_MAXSIZE;
+ }
+
+ acutest_line_indent_(acutest_case_name_[0] ? 3 : 2);
+ printf((title[strlen(title)-1] == ':') ? "%s\n" : "%s:\n", title);
+
+ for(line_beg = 0; line_beg < size; line_beg += BYTES_PER_LINE) {
+ size_t line_end = line_beg + BYTES_PER_LINE;
+ size_t off;
+
+ acutest_line_indent_(acutest_case_name_[0] ? 4 : 3);
+ printf("%08lx: ", (unsigned long)line_beg);
+ for(off = line_beg; off < line_end; off++) {
+ if(off < size)
+ printf(" %02x", ((const unsigned char*)addr)[off]);
+ else
+ printf(" ");
+ }
+
+ printf(" ");
+ for(off = line_beg; off < line_end; off++) {
+ unsigned char byte = ((const unsigned char*)addr)[off];
+ if(off < size)
+ printf("%c", (iscntrl(byte) ? '.' : byte));
+ else
+ break;
+ }
+
+ printf("\n");
+ }
+
+ if(truncate > 0) {
+ acutest_line_indent_(acutest_case_name_[0] ? 4 : 3);
+ printf(" ... (and more %u bytes)\n", (unsigned) truncate);
+ }
+}
+
+/* This is called just before each test */
+static void
+acutest_init_(const char *test_name)
+{
+#ifdef TEST_INIT
+ TEST_INIT
+ ; /* Allow for a single unterminated function call */
+#endif
+
+ /* Suppress any warnings about unused variable. */
+ (void) test_name;
+}
+
+/* This is called after each test */
+static void
+acutest_fini_(const char *test_name)
+{
+#ifdef TEST_FINI
+ TEST_FINI
+ ; /* Allow for a single unterminated function call */
+#endif
+
+ /* Suppress any warnings about unused variable. */
+ (void) test_name;
+}
+
+void
+acutest_abort_(void)
+{
+ if(acutest_abort_has_jmp_buf_) {
+ longjmp(acutest_abort_jmp_buf_, 1);
+ } else {
+ if(acutest_current_test_ != NULL)
+ acutest_fini_(acutest_current_test_->name);
+ abort();
+ }
+}
+
+static void
+acutest_list_names_(void)
+{
+ const struct acutest_test_* test;
+
+ printf("Unit tests:\n");
+ for(test = &acutest_list_[0]; test->func != NULL; test++)
+ printf(" %s\n", test->name);
+}
+
+static void
+acutest_remember_(int i)
+{
+ if(acutest_test_data_[i].flags & ACUTEST_FLAG_RUN_)
+ return;
+
+ acutest_test_data_[i].flags |= ACUTEST_FLAG_RUN_;
+ acutest_count_++;
+}
+
+static void
+acutest_set_success_(int i, int success)
+{
+ acutest_test_data_[i].flags |= success ? ACUTEST_FLAG_SUCCESS_ : ACUTEST_FLAG_FAILURE_;
+}
+
+static void
+acutest_set_duration_(int i, double duration)
+{
+ acutest_test_data_[i].duration = duration;
+}
+
+static int
+acutest_name_contains_word_(const char* name, const char* pattern)
+{
+ static const char word_delim[] = " \t-_/.,:;";
+ const char* substr;
+ size_t pattern_len;
+
+ pattern_len = strlen(pattern);
+
+ substr = strstr(name, pattern);
+ while(substr != NULL) {
+ int starts_on_word_boundary = (substr == name || strchr(word_delim, substr[-1]) != NULL);
+ int ends_on_word_boundary = (substr[pattern_len] == '\0' || strchr(word_delim, substr[pattern_len]) != NULL);
+
+ if(starts_on_word_boundary && ends_on_word_boundary)
+ return 1;
+
+ substr = strstr(substr+1, pattern);
+ }
+
+ return 0;
+}
+
+static int
+acutest_lookup_(const char* pattern)
+{
+ int i;
+ int n = 0;
+
+ /* Try exact match. */
+ for(i = 0; i < (int) acutest_list_size_; i++) {
+ if(strcmp(acutest_list_[i].name, pattern) == 0) {
+ acutest_remember_(i);
+ n++;
+ break;
+ }
+ }
+ if(n > 0)
+ return n;
+
+ /* Try word match. */
+ for(i = 0; i < (int) acutest_list_size_; i++) {
+ if(acutest_name_contains_word_(acutest_list_[i].name, pattern)) {
+ acutest_remember_(i);
+ n++;
+ }
+ }
+ if(n > 0)
+ return n;
+
+ /* Try relaxed match. */
+ for(i = 0; i < (int) acutest_list_size_; i++) {
+ if(strstr(acutest_list_[i].name, pattern) != NULL) {
+ acutest_remember_(i);
+ n++;
+ }
+ }
+
+ return n;
+}
+
+
+/* Called if anything goes bad in Acutest, or if the unit test ends in other
+ * way then by normal returning from its function (e.g. exception or some
+ * abnormal child process termination). */
+static void ACUTEST_ATTRIBUTE_(format (printf, 1, 2))
+acutest_error_(const char* fmt, ...)
+{
+ if(acutest_verbose_level_ == 0)
+ return;
+
+ if(acutest_verbose_level_ >= 2) {
+ va_list args;
+
+ acutest_line_indent_(1);
+ if(acutest_verbose_level_ >= 3)
+ acutest_colored_printf_(ACUTEST_COLOR_RED_INTENSIVE_, "ERROR: ");
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+ printf("\n");
+ }
+
+ if(acutest_verbose_level_ >= 3) {
+ printf("\n");
+ }
+}
+
+/* Call directly the given test unit function. */
+static int
+acutest_do_run_(const struct acutest_test_* test, int index)
+{
+ int status = -1;
+
+ acutest_was_aborted_ = 0;
+ acutest_current_test_ = test;
+ acutest_current_index_ = index;
+ acutest_test_failures_ = 0;
+ acutest_test_already_logged_ = 0;
+ acutest_cond_failed_ = 0;
+
+#ifdef __cplusplus
+ try {
+#endif
+ acutest_init_(test->name);
+ acutest_begin_test_line_(test);
+
+ /* This is good to do in case the test unit crashes. */
+ fflush(stdout);
+ fflush(stderr);
+
+ if(!acutest_worker_) {
+ acutest_abort_has_jmp_buf_ = 1;
+ if(setjmp(acutest_abort_jmp_buf_) != 0) {
+ acutest_was_aborted_ = 1;
+ goto aborted;
+ }
+ }
+
+ acutest_timer_get_time_(&acutest_timer_start_);
+ test->func();
+aborted:
+ acutest_abort_has_jmp_buf_ = 0;
+ acutest_timer_get_time_(&acutest_timer_end_);
+
+ if(acutest_verbose_level_ >= 3) {
+ acutest_line_indent_(1);
+ if(acutest_test_failures_ == 0) {
+ acutest_colored_printf_(ACUTEST_COLOR_GREEN_INTENSIVE_, "SUCCESS: ");
+ printf("All conditions have passed.\n");
+
+ if(acutest_timer_) {
+ acutest_line_indent_(1);
+ printf("Duration: ");
+ acutest_timer_print_diff_();
+ printf("\n");
+ }
+ } else {
+ acutest_colored_printf_(ACUTEST_COLOR_RED_INTENSIVE_, "FAILED: ");
+ if(!acutest_was_aborted_) {
+ printf("%d condition%s %s failed.\n",
+ acutest_test_failures_,
+ (acutest_test_failures_ == 1) ? "" : "s",
+ (acutest_test_failures_ == 1) ? "has" : "have");
+ } else {
+ printf("Aborted.\n");
+ }
+ }
+ printf("\n");
+ } else if(acutest_verbose_level_ >= 1 && acutest_test_failures_ == 0) {
+ acutest_finish_test_line_(0);
+ }
+
+ status = (acutest_test_failures_ == 0) ? 0 : -1;
+
+#ifdef __cplusplus
+ } catch(std::exception& e) {
+ const char* what = e.what();
+ acutest_check_(0, NULL, 0, "Threw std::exception");
+ if(what != NULL)
+ acutest_message_("std::exception::what(): %s", what);
+
+ if(acutest_verbose_level_ >= 3) {
+ acutest_line_indent_(1);
+ acutest_colored_printf_(ACUTEST_COLOR_RED_INTENSIVE_, "FAILED: ");
+ printf("C++ exception.\n\n");
+ }
+ } catch(...) {
+ acutest_check_(0, NULL, 0, "Threw an exception");
+
+ if(acutest_verbose_level_ >= 3) {
+ acutest_line_indent_(1);
+ acutest_colored_printf_(ACUTEST_COLOR_RED_INTENSIVE_, "FAILED: ");
+ printf("C++ exception.\n\n");
+ }
+ }
+#endif
+
+ acutest_fini_(test->name);
+ acutest_case_(NULL);
+ acutest_current_test_ = NULL;
+
+ return status;
+}
+
+/* Trigger the unit test. If possible (and not suppressed) it starts a child
+ * process who calls acutest_do_run_(), otherwise it calls acutest_do_run_()
+ * directly. */
+static void
+acutest_run_(const struct acutest_test_* test, int index, int master_index)
+{
+ int failed = 1;
+ acutest_timer_type_ start, end;
+
+ acutest_current_test_ = test;
+ acutest_test_already_logged_ = 0;
+ acutest_timer_get_time_(&start);
+
+ if(!acutest_no_exec_) {
+
+#if defined(ACUTEST_UNIX_)
+
+ pid_t pid;
+ int exit_code;
+
+ /* Make sure the child starts with empty I/O buffers. */
+ fflush(stdout);
+ fflush(stderr);
+
+ pid = fork();
+ if(pid == (pid_t)-1) {
+ acutest_error_("Cannot fork. %s [%d]", strerror(errno), errno);
+ failed = 1;
+ } else if(pid == 0) {
+ /* Child: Do the test. */
+ acutest_worker_ = 1;
+ failed = (acutest_do_run_(test, index) != 0);
+ acutest_exit_(failed ? 1 : 0);
+ } else {
+ /* Parent: Wait until child terminates and analyze its exit code. */
+ waitpid(pid, &exit_code, 0);
+ if(WIFEXITED(exit_code)) {
+ switch(WEXITSTATUS(exit_code)) {
+ case 0: failed = 0; break; /* test has passed. */
+ case 1: /* noop */ break; /* "normal" failure. */
+ default: acutest_error_("Unexpected exit code [%d]", WEXITSTATUS(exit_code));
+ }
+ } else if(WIFSIGNALED(exit_code)) {
+ char tmp[32];
+ const char* signame;
+ switch(WTERMSIG(exit_code)) {
+ case SIGINT: signame = "SIGINT"; break;
+ case SIGHUP: signame = "SIGHUP"; break;
+ case SIGQUIT: signame = "SIGQUIT"; break;
+ case SIGABRT: signame = "SIGABRT"; break;
+ case SIGKILL: signame = "SIGKILL"; break;
+ case SIGSEGV: signame = "SIGSEGV"; break;
+ case SIGILL: signame = "SIGILL"; break;
+ case SIGTERM: signame = "SIGTERM"; break;
+ default: sprintf(tmp, "signal %d", WTERMSIG(exit_code)); signame = tmp; break;
+ }
+ acutest_error_("Test interrupted by %s.", signame);
+ } else {
+ acutest_error_("Test ended in an unexpected way [%d].", exit_code);
+ }
+ }
+
+#elif defined(ACUTEST_WIN_)
+
+ char buffer[512] = {0};
+ STARTUPINFOA startupInfo;
+ PROCESS_INFORMATION processInfo;
+ DWORD exitCode;
+
+ /* Windows has no fork(). So we propagate all info into the child
+ * through a command line arguments. */
+ _snprintf(buffer, sizeof(buffer)-1,
+ "%s --worker=%d %s --no-exec --no-summary %s --verbose=%d --color=%s -- \"%s\"",
+ acutest_argv0_, index, acutest_timer_ ? "--time" : "",
+ acutest_tap_ ? "--tap" : "", acutest_verbose_level_,
+ acutest_colorize_ ? "always" : "never",
+ test->name);
+ memset(&startupInfo, 0, sizeof(startupInfo));
+ startupInfo.cb = sizeof(STARTUPINFO);
+ if(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo)) {
+ WaitForSingleObject(processInfo.hProcess, INFINITE);
+ GetExitCodeProcess(processInfo.hProcess, &exitCode);
+ CloseHandle(processInfo.hThread);
+ CloseHandle(processInfo.hProcess);
+ failed = (exitCode != 0);
+ if(exitCode > 1) {
+ switch(exitCode) {
+ case 3: acutest_error_("Aborted."); break;
+ case 0xC0000005: acutest_error_("Access violation."); break;
+ default: acutest_error_("Test ended in an unexpected way [%lu].", exitCode); break;
+ }
+ }
+ } else {
+ acutest_error_("Cannot create unit test subprocess [%ld].", GetLastError());
+ failed = 1;
+ }
+
+#else
+
+ /* A platform where we don't know how to run child process. */
+ failed = (acutest_do_run_(test, index) != 0);
+
+#endif
+
+ } else {
+ /* Child processes suppressed through --no-exec. */
+ failed = (acutest_do_run_(test, index) != 0);
+ }
+ acutest_timer_get_time_(&end);
+
+ acutest_current_test_ = NULL;
+
+ acutest_stat_run_units_++;
+ if(failed)
+ acutest_stat_failed_units_++;
+
+ acutest_set_success_(master_index, !failed);
+ acutest_set_duration_(master_index, acutest_timer_diff_(start, end));
+}
+
+#if defined(ACUTEST_WIN_)
+/* Callback for SEH events. */
+static LONG CALLBACK
+acutest_seh_exception_filter_(EXCEPTION_POINTERS *ptrs)
+{
+ acutest_check_(0, NULL, 0, "Unhandled SEH exception");
+ acutest_message_("Exception code: 0x%08lx", ptrs->ExceptionRecord->ExceptionCode);
+ acutest_message_("Exception address: 0x%p", ptrs->ExceptionRecord->ExceptionAddress);
+
+ fflush(stdout);
+ fflush(stderr);
+
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+#endif
+
+
+#define ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ 0x0001
+#define ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_ 0x0002
+
+#define ACUTEST_CMDLINE_OPTID_NONE_ 0
+#define ACUTEST_CMDLINE_OPTID_UNKNOWN_ (-0x7fffffff + 0)
+#define ACUTEST_CMDLINE_OPTID_MISSINGARG_ (-0x7fffffff + 1)
+#define ACUTEST_CMDLINE_OPTID_BOGUSARG_ (-0x7fffffff + 2)
+
+typedef struct acutest_test_CMDLINE_OPTION_ {
+ char shortname;
+ const char* longname;
+ int id;
+ unsigned flags;
+} ACUTEST_CMDLINE_OPTION_;
+
+static int
+acutest_cmdline_handle_short_opt_group_(const ACUTEST_CMDLINE_OPTION_* options,
+ const char* arggroup,
+ int (*callback)(int /*optval*/, const char* /*arg*/))
+{
+ const ACUTEST_CMDLINE_OPTION_* opt;
+ int i;
+ int ret = 0;
+
+ for(i = 0; arggroup[i] != '\0'; i++) {
+ for(opt = options; opt->id != 0; opt++) {
+ if(arggroup[i] == opt->shortname)
+ break;
+ }
+
+ if(opt->id != 0 && !(opt->flags & ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_)) {
+ ret = callback(opt->id, NULL);
+ } else {
+ /* Unknown option. */
+ char badoptname[3];
+ badoptname[0] = '-';
+ badoptname[1] = arggroup[i];
+ badoptname[2] = '\0';
+ ret = callback((opt->id != 0 ? ACUTEST_CMDLINE_OPTID_MISSINGARG_ : ACUTEST_CMDLINE_OPTID_UNKNOWN_),
+ badoptname);
+ }
+
+ if(ret != 0)
+ break;
+ }
+
+ return ret;
+}
+
+#define ACUTEST_CMDLINE_AUXBUF_SIZE_ 32
+
+static int
+acutest_cmdline_read_(const ACUTEST_CMDLINE_OPTION_* options, int argc, char** argv,
+ int (*callback)(int /*optval*/, const char* /*arg*/))
+{
+
+ const ACUTEST_CMDLINE_OPTION_* opt;
+ char auxbuf[ACUTEST_CMDLINE_AUXBUF_SIZE_+1];
+ int after_doubledash = 0;
+ int i = 1;
+ int ret = 0;
+
+ auxbuf[ACUTEST_CMDLINE_AUXBUF_SIZE_] = '\0';
+
+ while(i < argc) {
+ if(after_doubledash || strcmp(argv[i], "-") == 0) {
+ /* Non-option argument. */
+ ret = callback(ACUTEST_CMDLINE_OPTID_NONE_, argv[i]);
+ } else if(strcmp(argv[i], "--") == 0) {
+ /* End of options. All the remaining members are non-option arguments. */
+ after_doubledash = 1;
+ } else if(argv[i][0] != '-') {
+ /* Non-option argument. */
+ ret = callback(ACUTEST_CMDLINE_OPTID_NONE_, argv[i]);
+ } else {
+ for(opt = options; opt->id != 0; opt++) {
+ if(opt->longname != NULL && strncmp(argv[i], "--", 2) == 0) {
+ size_t len = strlen(opt->longname);
+ if(strncmp(argv[i]+2, opt->longname, len) == 0) {
+ /* Regular long option. */
+ if(argv[i][2+len] == '\0') {
+ /* with no argument provided. */
+ if(!(opt->flags & ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_))
+ ret = callback(opt->id, NULL);
+ else
+ ret = callback(ACUTEST_CMDLINE_OPTID_MISSINGARG_, argv[i]);
+ break;
+ } else if(argv[i][2+len] == '=') {
+ /* with an argument provided. */
+ if(opt->flags & (ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ | ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_)) {
+ ret = callback(opt->id, argv[i]+2+len+1);
+ } else {
+ sprintf(auxbuf, "--%s", opt->longname);
+ ret = callback(ACUTEST_CMDLINE_OPTID_BOGUSARG_, auxbuf);
+ }
+ break;
+ } else {
+ continue;
+ }
+ }
+ } else if(opt->shortname != '\0' && argv[i][0] == '-') {
+ if(argv[i][1] == opt->shortname) {
+ /* Regular short option. */
+ if(opt->flags & ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_) {
+ if(argv[i][2] != '\0')
+ ret = callback(opt->id, argv[i]+2);
+ else if(i+1 < argc)
+ ret = callback(opt->id, argv[++i]);
+ else
+ ret = callback(ACUTEST_CMDLINE_OPTID_MISSINGARG_, argv[i]);
+ break;
+ } else {
+ ret = callback(opt->id, NULL);
+
+ /* There might be more (argument-less) short options
+ * grouped together. */
+ if(ret == 0 && argv[i][2] != '\0')
+ ret = acutest_cmdline_handle_short_opt_group_(options, argv[i]+2, callback);
+ break;
+ }
+ }
+ }
+ }
+
+ if(opt->id == 0) { /* still not handled? */
+ if(argv[i][0] != '-') {
+ /* Non-option argument. */
+ ret = callback(ACUTEST_CMDLINE_OPTID_NONE_, argv[i]);
+ } else {
+ /* Unknown option. */
+ char* badoptname = argv[i];
+
+ if(strncmp(badoptname, "--", 2) == 0) {
+ /* Strip any argument from the long option. */
+ char* assignment = strchr(badoptname, '=');
+ if(assignment != NULL) {
+ size_t len = assignment - badoptname;
+ if(len > ACUTEST_CMDLINE_AUXBUF_SIZE_)
+ len = ACUTEST_CMDLINE_AUXBUF_SIZE_;
+ strncpy(auxbuf, badoptname, len);
+ auxbuf[len] = '\0';
+ badoptname = auxbuf;
+ }
+ }
+
+ ret = callback(ACUTEST_CMDLINE_OPTID_UNKNOWN_, badoptname);
+ }
+ }
+ }
+
+ if(ret != 0)
+ return ret;
+ i++;
+ }
+
+ return ret;
+}
+
+static void
+acutest_help_(void)
+{
+ printf("Usage: %s [options] [test...]\n", acutest_argv0_);
+ printf("\n");
+ printf("Run the specified unit tests; or if the option '--skip' is used, run all\n");
+ printf("tests in the suite but those listed. By default, if no tests are specified\n");
+ printf("on the command line, all unit tests in the suite are run.\n");
+ printf("\n");
+ printf("Options:\n");
+ printf(" -s, --skip Execute all unit tests but the listed ones\n");
+ printf(" --exec[=WHEN] If supported, execute unit tests as child processes\n");
+ printf(" (WHEN is one of 'auto', 'always', 'never')\n");
+ printf(" -E, --no-exec Same as --exec=never\n");
+#if defined ACUTEST_WIN_
+ printf(" -t, --time Measure test duration\n");
+#elif defined ACUTEST_HAS_POSIX_TIMER_
+ printf(" -t, --time Measure test duration (real time)\n");
+ printf(" --time=TIMER Measure test duration, using given timer\n");
+ printf(" (TIMER is one of 'real', 'cpu')\n");
+#endif
+ printf(" --no-summary Suppress printing of test results summary\n");
+ printf(" --tap Produce TAP-compliant output\n");
+ printf(" (See https://testanything.org/)\n");
+ printf(" -x, --xml-output=FILE Enable XUnit output to the given file\n");
+ printf(" -l, --list List unit tests in the suite and exit\n");
+ printf(" -v, --verbose Make output more verbose\n");
+ printf(" --verbose=LEVEL Set verbose level to LEVEL:\n");
+ printf(" 0 ... Be silent\n");
+ printf(" 1 ... Output one line per test (and summary)\n");
+ printf(" 2 ... As 1 and failed conditions (this is default)\n");
+ printf(" 3 ... As 1 and all conditions (and extended summary)\n");
+ printf(" -q, --quiet Same as --verbose=0\n");
+ printf(" --color[=WHEN] Enable colorized output\n");
+ printf(" (WHEN is one of 'auto', 'always', 'never')\n");
+ printf(" --no-color Same as --color=never\n");
+ printf(" -h, --help Display this help and exit\n");
+
+ if(acutest_list_size_ < 16) {
+ printf("\n");
+ acutest_list_names_();
+ }
+}
+
+static const ACUTEST_CMDLINE_OPTION_ acutest_cmdline_options_[] = {
+ { 's', "skip", 's', 0 },
+ { 0, "exec", 'e', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ },
+ { 'E', "no-exec", 'E', 0 },
+#if defined ACUTEST_WIN_
+ { 't', "time", 't', 0 },
+ { 0, "timer", 't', 0 }, /* kept for compatibility */
+#elif defined ACUTEST_HAS_POSIX_TIMER_
+ { 't', "time", 't', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ },
+ { 0, "timer", 't', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ }, /* kept for compatibility */
+#endif
+ { 0, "no-summary", 'S', 0 },
+ { 0, "tap", 'T', 0 },
+ { 'l', "list", 'l', 0 },
+ { 'v', "verbose", 'v', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ },
+ { 'q', "quiet", 'q', 0 },
+ { 0, "color", 'c', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ },
+ { 0, "no-color", 'C', 0 },
+ { 'h', "help", 'h', 0 },
+ { 0, "worker", 'w', ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_ }, /* internal */
+ { 'x', "xml-output", 'x', ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_ },
+ { 0, NULL, 0, 0 }
+};
+
+static int
+acutest_cmdline_callback_(int id, const char* arg)
+{
+ switch(id) {
+ case 's':
+ acutest_skip_mode_ = 1;
+ break;
+
+ case 'e':
+ if(arg == NULL || strcmp(arg, "always") == 0) {
+ acutest_no_exec_ = 0;
+ } else if(strcmp(arg, "never") == 0) {
+ acutest_no_exec_ = 1;
+ } else if(strcmp(arg, "auto") == 0) {
+ /*noop*/
+ } else {
+ fprintf(stderr, "%s: Unrecognized argument '%s' for option --exec.\n", acutest_argv0_, arg);
+ fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_);
+ acutest_exit_(2);
+ }
+ break;
+
+ case 'E':
+ acutest_no_exec_ = 1;
+ break;
+
+ case 't':
+#if defined ACUTEST_WIN_ || defined ACUTEST_HAS_POSIX_TIMER_
+ if(arg == NULL || strcmp(arg, "real") == 0) {
+ acutest_timer_ = 1;
+ #ifndef ACUTEST_WIN_
+ } else if(strcmp(arg, "cpu") == 0) {
+ acutest_timer_ = 2;
+ #endif
+ } else {
+ fprintf(stderr, "%s: Unrecognized argument '%s' for option --time.\n", acutest_argv0_, arg);
+ fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_);
+ acutest_exit_(2);
+ }
+#endif
+ break;
+
+ case 'S':
+ acutest_no_summary_ = 1;
+ break;
+
+ case 'T':
+ acutest_tap_ = 1;
+ break;
+
+ case 'l':
+ acutest_list_names_();
+ acutest_exit_(0);
+ break;
+
+ case 'v':
+ acutest_verbose_level_ = (arg != NULL ? atoi(arg) : acutest_verbose_level_+1);
+ break;
+
+ case 'q':
+ acutest_verbose_level_ = 0;
+ break;
+
+ case 'c':
+ if(arg == NULL || strcmp(arg, "always") == 0) {
+ acutest_colorize_ = 1;
+ } else if(strcmp(arg, "never") == 0) {
+ acutest_colorize_ = 0;
+ } else if(strcmp(arg, "auto") == 0) {
+ /*noop*/
+ } else {
+ fprintf(stderr, "%s: Unrecognized argument '%s' for option --color.\n", acutest_argv0_, arg);
+ fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_);
+ acutest_exit_(2);
+ }
+ break;
+
+ case 'C':
+ acutest_colorize_ = 0;
+ break;
+
+ case 'h':
+ acutest_help_();
+ acutest_exit_(0);
+ break;
+
+ case 'w':
+ acutest_worker_ = 1;
+ acutest_worker_index_ = atoi(arg);
+ break;
+ case 'x':
+ acutest_xml_output_ = fopen(arg, "w");
+ if (!acutest_xml_output_) {
+ fprintf(stderr, "Unable to open '%s': %s\n", arg, strerror(errno));
+ acutest_exit_(2);
+ }
+ break;
+
+ case 0:
+ if(acutest_lookup_(arg) == 0) {
+ fprintf(stderr, "%s: Unrecognized unit test '%s'\n", acutest_argv0_, arg);
+ fprintf(stderr, "Try '%s --list' for list of unit tests.\n", acutest_argv0_);
+ acutest_exit_(2);
+ }
+ break;
+
+ case ACUTEST_CMDLINE_OPTID_UNKNOWN_:
+ fprintf(stderr, "Unrecognized command line option '%s'.\n", arg);
+ fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_);
+ acutest_exit_(2);
+ break;
+
+ case ACUTEST_CMDLINE_OPTID_MISSINGARG_:
+ fprintf(stderr, "The command line option '%s' requires an argument.\n", arg);
+ fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_);
+ acutest_exit_(2);
+ break;
+
+ case ACUTEST_CMDLINE_OPTID_BOGUSARG_:
+ fprintf(stderr, "The command line option '%s' does not expect an argument.\n", arg);
+ fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_);
+ acutest_exit_(2);
+ break;
+ }
+
+ return 0;
+}
+
+
+#ifdef ACUTEST_LINUX_
+static int
+acutest_is_tracer_present_(void)
+{
+ /* Must be large enough so the line 'TracerPid: ${PID}' can fit in. */
+ static const int OVERLAP = 32;
+
+ char buf[256+OVERLAP+1];
+ int tracer_present = 0;
+ int fd;
+ size_t n_read = 0;
+
+ fd = open("/proc/self/status", O_RDONLY);
+ if(fd == -1)
+ return 0;
+
+ while(1) {
+ static const char pattern[] = "TracerPid:";
+ const char* field;
+
+ while(n_read < sizeof(buf) - 1) {
+ ssize_t n;
+
+ n = read(fd, buf + n_read, sizeof(buf) - 1 - n_read);
+ if(n <= 0)
+ break;
+ n_read += n;
+ }
+ buf[n_read] = '\0';
+
+ field = strstr(buf, pattern);
+ if(field != NULL && field < buf + sizeof(buf) - OVERLAP) {
+ pid_t tracer_pid = (pid_t) atoi(field + sizeof(pattern) - 1);
+ tracer_present = (tracer_pid != 0);
+ break;
+ }
+
+ if(n_read == sizeof(buf)-1) {
+ memmove(buf, buf + sizeof(buf)-1 - OVERLAP, OVERLAP);
+ n_read = OVERLAP;
+ } else {
+ break;
+ }
+ }
+
+ close(fd);
+ return tracer_present;
+}
+#endif
+
+int
+main(int argc, char** argv)
+{
+ int i;
+
+ acutest_argv0_ = argv[0];
+
+#if defined ACUTEST_UNIX_
+ acutest_colorize_ = isatty(STDOUT_FILENO);
+#elif defined ACUTEST_WIN_
+ #if defined _BORLANDC_
+ acutest_colorize_ = isatty(_fileno(stdout));
+ #else
+ acutest_colorize_ = _isatty(_fileno(stdout));
+ #endif
+#else
+ acutest_colorize_ = 0;
+#endif
+
+ /* Count all test units */
+ acutest_list_size_ = 0;
+ for(i = 0; acutest_list_[i].func != NULL; i++)
+ acutest_list_size_++;
+
+ acutest_test_data_ = (struct acutest_test_data_*)calloc(acutest_list_size_, sizeof(struct acutest_test_data_));
+ if(acutest_test_data_ == NULL) {
+ fprintf(stderr, "Out of memory.\n");
+ acutest_exit_(2);
+ }
+
+ /* Parse options */
+ acutest_cmdline_read_(acutest_cmdline_options_, argc, argv, acutest_cmdline_callback_);
+
+ /* Initialize the proper timer. */
+ acutest_timer_init_();
+
+#if defined(ACUTEST_WIN_)
+ SetUnhandledExceptionFilter(acutest_seh_exception_filter_);
+#ifdef _MSC_VER
+ _set_abort_behavior(0, _WRITE_ABORT_MSG);
+#endif
+#endif
+
+ /* By default, we want to run all tests. */
+ if(acutest_count_ == 0) {
+ for(i = 0; acutest_list_[i].func != NULL; i++)
+ acutest_remember_(i);
+ }
+
+ /* Guess whether we want to run unit tests as child processes. */
+ if(acutest_no_exec_ < 0) {
+ acutest_no_exec_ = 0;
+
+ if(acutest_count_ <= 1) {
+ acutest_no_exec_ = 1;
+ } else {
+#ifdef ACUTEST_WIN_
+ if(IsDebuggerPresent())
+ acutest_no_exec_ = 1;
+#endif
+#ifdef ACUTEST_LINUX_
+ if(acutest_is_tracer_present_())
+ acutest_no_exec_ = 1;
+#endif
+#ifdef RUNNING_ON_VALGRIND
+ /* RUNNING_ON_VALGRIND is provided by optionally included <valgrind.h> */
+ if(RUNNING_ON_VALGRIND)
+ acutest_no_exec_ = 1;
+#endif
+ }
+ }
+
+ if(acutest_tap_) {
+ /* TAP requires we know test result ("ok", "not ok") before we output
+ * anything about the test, and this gets problematic for larger verbose
+ * levels. */
+ if(acutest_verbose_level_ > 2)
+ acutest_verbose_level_ = 2;
+
+ /* TAP harness should provide some summary. */
+ acutest_no_summary_ = 1;
+
+ if(!acutest_worker_)
+ printf("1..%d\n", (int) acutest_count_);
+ }
+
+ int index = acutest_worker_index_;
+ for(i = 0; acutest_list_[i].func != NULL; i++) {
+ int run = (acutest_test_data_[i].flags & ACUTEST_FLAG_RUN_);
+ if (acutest_skip_mode_) /* Run all tests except those listed. */
+ run = !run;
+ if(run)
+ acutest_run_(&acutest_list_[i], index++, i);
+ }
+
+ /* Write a summary */
+ if(!acutest_no_summary_ && acutest_verbose_level_ >= 1) {
+ if(acutest_verbose_level_ >= 3) {
+ acutest_colored_printf_(ACUTEST_COLOR_DEFAULT_INTENSIVE_, "Summary:\n");
+
+ printf(" Count of all unit tests: %4d\n", (int) acutest_list_size_);
+ printf(" Count of run unit tests: %4d\n", acutest_stat_run_units_);
+ printf(" Count of failed unit tests: %4d\n", acutest_stat_failed_units_);
+ printf(" Count of skipped unit tests: %4d\n", (int) acutest_list_size_ - acutest_stat_run_units_);
+ }
+
+ if(acutest_stat_failed_units_ == 0) {
+ acutest_colored_printf_(ACUTEST_COLOR_GREEN_INTENSIVE_, "SUCCESS:");
+ printf(" All unit tests have passed.\n");
+ } else {
+ acutest_colored_printf_(ACUTEST_COLOR_RED_INTENSIVE_, "FAILED:");
+ printf(" %d of %d unit tests %s failed.\n",
+ acutest_stat_failed_units_, acutest_stat_run_units_,
+ (acutest_stat_failed_units_ == 1) ? "has" : "have");
+ }
+
+ if(acutest_verbose_level_ >= 3)
+ printf("\n");
+ }
+
+ if (acutest_xml_output_) {
+#if defined ACUTEST_UNIX_
+ char *suite_name = basename(argv[0]);
+#elif defined ACUTEST_WIN_
+ char suite_name[_MAX_FNAME];
+ _splitpath(argv[0], NULL, NULL, suite_name, NULL);
+#else
+ const char *suite_name = argv[0];
+#endif
+ fprintf(acutest_xml_output_, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+ fprintf(acutest_xml_output_, "<testsuite name=\"%s\" tests=\"%d\" errors=\"%d\" failures=\"%d\" skip=\"%d\">\n",
+ suite_name, (int)acutest_list_size_, acutest_stat_failed_units_, acutest_stat_failed_units_,
+ (int)acutest_list_size_ - acutest_stat_run_units_);
+ for(i = 0; acutest_list_[i].func != NULL; i++) {
+ struct acutest_test_data_ *details = &acutest_test_data_[i];
+ fprintf(acutest_xml_output_, " <testcase name=\"%s\" time=\"%.2f\">\n", acutest_list_[i].name, details->duration);
+ if (details->flags & ACUTEST_FLAG_FAILURE_)
+ fprintf(acutest_xml_output_, " <failure />\n");
+ if (!(details->flags & ACUTEST_FLAG_FAILURE_) && !(details->flags & ACUTEST_FLAG_SUCCESS_))
+ fprintf(acutest_xml_output_, " <skipped />\n");
+ fprintf(acutest_xml_output_, " </testcase>\n");
+ }
+ fprintf(acutest_xml_output_, "</testsuite>\n");
+ fclose(acutest_xml_output_);
+ }
+
+ acutest_cleanup_();
+
+ return (acutest_stat_failed_units_ == 0) ? 0 : 1;
+}
+
+
+#endif /* #ifndef TEST_NO_MAIN */
+
+#ifdef _MSC_VER
+ #pragma warning(pop)
+#endif
+
+#ifdef __cplusplus
+ } /* extern "C" */
+#endif
+
+#endif /* #ifndef ACUTEST_H */
diff --git a/src/fluent-bit/lib/cmetrics/tests/null_label.c b/src/fluent-bit/lib/cmetrics/tests/null_label.c
new file mode 100644
index 000000000..2d23bc131
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/tests/null_label.c
@@ -0,0 +1,167 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_counter.h>
+#include <cmetrics/cmt_encode_prometheus.h>
+
+#include "cmt_tests.h"
+
+void test_labels()
+{
+ int ret;
+ double val = 1;
+ uint64_t ts;
+ struct cmt *cmt;
+ struct cmt_counter *c;
+
+ cmt = cmt_create();
+ c = cmt_counter_create(cmt, "test", "dummy", "labels", "testing labels",
+ 6, (char *[]) {"A", "B", "C", "D", "E", "F"});
+
+ ts = cfl_time_now();
+
+ ret = cmt_counter_get_val(c, 0, NULL, &val);
+ TEST_CHECK(ret == -1);
+ TEST_CHECK((uint64_t) val == 1);
+
+ cmt_counter_inc(c, ts, 0, NULL);
+ cmt_counter_add(c, ts, 2, 0, NULL);
+ cmt_counter_get_val(c, 0, NULL, &val);
+ TEST_CHECK((uint64_t) val == 3);
+
+ /* --- case 1 --- */
+ cmt_counter_inc(c, ts, 6, (char *[]) {"1", NULL, "98", NULL, NULL, NULL});
+
+ /* check retrieval with no labels */
+ cmt_counter_get_val(c, 0, NULL, &val);
+ TEST_CHECK((uint64_t) val == 3);
+
+ /* check real value */
+ cmt_counter_get_val(c, 6, (char *[]) {"1", NULL, "98", NULL, NULL, NULL}, &val);
+ TEST_CHECK((uint64_t) val == 1);
+
+
+ /* --- case 2 --- */
+ cmt_counter_set(c, ts, 5, 6, (char *[]) {"1", "2", "98", "100", "200", "300"});
+
+ /* check retrieval with no labels */
+ cmt_counter_get_val(c, 0, NULL, &val);
+ TEST_CHECK((uint64_t) val == 3);
+
+ /* check real value */
+ cmt_counter_get_val(c, 6, (char *[]) {"1", "2", "98", "100", "200", "300"}, &val);
+ TEST_CHECK((uint64_t) val == 5);
+
+ /* --- check that 'case 1' still matches --- */
+ cmt_counter_get_val(c, 0, NULL, &val);
+ TEST_CHECK((uint64_t) val == 3);
+
+ /* check real value */
+ cmt_counter_get_val(c, 6, (char *[]) {"1", NULL, "98", NULL, NULL, NULL}, &val);
+ TEST_CHECK((uint64_t) val == 1);
+
+ cmt_destroy(cmt);
+}
+
+void test_encoding()
+{
+ cfl_sds_t result;
+ struct cmt *cmt;
+ struct cmt_counter *c;
+
+ cmt = cmt_create();
+ c = cmt_counter_create(cmt, "test", "dummy", "labels", "testing labels",
+ 6, (char *[]) {"A", "B", "C", "D", "E", "F"});
+
+ cmt_counter_inc(c, 0, 6, (char *[]) {NULL,NULL,NULL,NULL,NULL,NULL});
+ cmt_counter_inc(c, 0, 6, (char *[]) {NULL,NULL,NULL,NULL,NULL,NULL});
+ result = cmt_encode_prometheus_create(cmt, CMT_TRUE);
+ TEST_CHECK(strcmp(result,
+ "# HELP test_dummy_labels testing labels\n"
+ "# TYPE test_dummy_labels counter\n"
+ "test_dummy_labels 2 0\n"
+ ) == 0);
+ cfl_sds_destroy(result);
+
+ cmt_counter_inc(c, 0, 6, (char *[]) {NULL,"b",NULL,NULL,NULL,NULL});
+ result = cmt_encode_prometheus_create(cmt, CMT_TRUE);
+ TEST_CHECK(strcmp(result,
+ "# HELP test_dummy_labels testing labels\n"
+ "# TYPE test_dummy_labels counter\n"
+ "test_dummy_labels 2 0\n"
+ "test_dummy_labels{B=\"b\"} 1 0\n"
+ ) == 0);
+ cfl_sds_destroy(result);
+
+ cmt_counter_inc(c, 0, 6, (char *[]) {NULL,"b",NULL,NULL,NULL,NULL});
+ result = cmt_encode_prometheus_create(cmt, CMT_TRUE);
+ TEST_CHECK(strcmp(result,
+ "# HELP test_dummy_labels testing labels\n"
+ "# TYPE test_dummy_labels counter\n"
+ "test_dummy_labels 2 0\n"
+ "test_dummy_labels{B=\"b\"} 2 0\n"
+ ) == 0);
+ cfl_sds_destroy(result);
+
+
+ cmt_counter_set(c, 0, 5, 6, (char *[]) {NULL,NULL,NULL,"d",NULL,NULL});
+ result = cmt_encode_prometheus_create(cmt, CMT_TRUE);
+ TEST_CHECK(strcmp(result,
+ "# HELP test_dummy_labels testing labels\n"
+ "# TYPE test_dummy_labels counter\n"
+ "test_dummy_labels 2 0\n"
+ "test_dummy_labels{B=\"b\"} 2 0\n"
+ "test_dummy_labels{D=\"d\"} 5 0\n"
+ ) == 0);
+ cfl_sds_destroy(result);
+
+ cmt_counter_set(c, 0, 50, 6, (char *[]) {NULL,"b",NULL,"d",NULL,"f"});
+ result = cmt_encode_prometheus_create(cmt, CMT_TRUE);
+ TEST_CHECK(strcmp(result,
+ "# HELP test_dummy_labels testing labels\n"
+ "# TYPE test_dummy_labels counter\n"
+ "test_dummy_labels 2 0\n"
+ "test_dummy_labels{B=\"b\"} 2 0\n"
+ "test_dummy_labels{D=\"d\"} 5 0\n"
+ "test_dummy_labels{B=\"b\",D=\"d\",F=\"f\"} 50 0\n"
+ ) == 0);
+ cfl_sds_destroy(result);
+
+ cmt_counter_inc(c, 0, 6, (char *[]) {"a","b","c","d","e","f"});
+ result = cmt_encode_prometheus_create(cmt, CMT_TRUE);
+ TEST_CHECK(strcmp(result,
+ "# HELP test_dummy_labels testing labels\n"
+ "# TYPE test_dummy_labels counter\n"
+ "test_dummy_labels 2 0\n"
+ "test_dummy_labels{B=\"b\"} 2 0\n"
+ "test_dummy_labels{D=\"d\"} 5 0\n"
+ "test_dummy_labels{B=\"b\",D=\"d\",F=\"f\"} 50 0\n"
+ "test_dummy_labels{A=\"a\",B=\"b\",C=\"c\",D=\"d\",E=\"e\",F=\"f\"} 1 0\n"
+ ) == 0);
+ cfl_sds_destroy(result);
+
+ cmt_destroy(cmt);
+}
+
+TEST_LIST = {
+ {"labels", test_labels},
+ {"encoding", test_encoding},
+ { 0 }
+};
diff --git a/src/fluent-bit/lib/cmetrics/tests/prometheus_lexer.c b/src/fluent-bit/lib/cmetrics/tests/prometheus_lexer.c
new file mode 100644
index 000000000..fe132f817
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/tests/prometheus_lexer.c
@@ -0,0 +1,218 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_counter.h>
+#include <cmetrics/cmt_decode_prometheus.h>
+#include <cmetrics/cmt_encode_prometheus_remote_write.h>
+#include <stdio.h>
+
+#include "cmt_decode_prometheus_parser.h"
+#include "cmt_tests.h"
+
+struct fixture {
+ yyscan_t scanner;
+ YY_BUFFER_STATE buf;
+ YYSTYPE lval;
+ struct cmt_decode_prometheus_context context;
+ const char *text;
+};
+
+struct fixture *init(const char *test)
+{
+ struct fixture *f = malloc(sizeof(*f));
+ memset(f, 0, sizeof(*f));
+ cmt_decode_prometheus_lex_init(&f->scanner);
+ f->buf = cmt_decode_prometheus__scan_string(test, f->scanner);
+ return f;
+}
+
+void destroy(struct fixture *f)
+{
+ cmt_decode_prometheus__delete_buffer(f->buf, f->scanner);
+ cmt_decode_prometheus_lex_destroy(f->scanner);
+ free(f);
+}
+
+int lex(struct fixture *f)
+{
+ return cmt_decode_prometheus_lex(&f->lval, f->scanner, &(f->context));
+}
+
+void test_comment()
+{
+ struct fixture *f = init("# this is just a comment");
+ TEST_CHECK(lex(f) == 0); // 0 means EOF
+ destroy(f);
+}
+
+void test_help()
+{
+ struct fixture *f = init("# HELP cmt_labels_test Static \\\\labels\\n test");
+
+ TEST_CHECK(lex(f) == HELP);
+ TEST_CHECK(strcmp(f->lval.str, "cmt_labels_test") == 0);
+ cfl_sds_destroy(f->lval.str);
+
+ TEST_CHECK(lex(f) == METRIC_DOC);
+ TEST_CHECK(strcmp(f->lval.str, "Static \\labels\n test") == 0);
+ cfl_sds_destroy(f->lval.str);
+
+ destroy(f);
+
+ f = init("# HELP cmt_labels_test Static \\\\labels\\n test\n");
+
+ TEST_CHECK(lex(f) == HELP);
+ TEST_CHECK(strcmp(f->lval.str, "cmt_labels_test") == 0);
+ cfl_sds_destroy(f->lval.str);
+
+ TEST_CHECK(lex(f) == METRIC_DOC);
+ TEST_CHECK(strcmp(f->lval.str, "Static \\labels\n test") == 0);
+ cfl_sds_destroy(f->lval.str);
+
+ destroy(f);
+}
+
+void test_type()
+{
+ struct fixture *f = init("# TYPE metric_name gauge");
+
+ TEST_CHECK(lex(f) == TYPE);
+ TEST_CHECK(strcmp(f->lval.str, "metric_name") == 0);
+ cfl_sds_destroy(f->lval.str);
+
+ TEST_CHECK(lex(f) == GAUGE);
+
+ destroy(f);
+}
+
+void test_simple()
+{
+ struct fixture *f = init(
+ "# HELP cmt_labels_test Static labels test\n"
+ "# TYPE cmt_labels_test counter\n"
+ "cmt_labels_test 1 0\n"
+ "metric2{host=\"calyptia.com\",app=\"cmetrics \\n \\\\ \\\"\"} 2.5 0\n"
+ "# HELP metric1 Second HELP tag\n"
+ "metric1{escapes=\"\\n \\\\ \\\"\"} 4.12 5\n"
+ );
+
+ TEST_CHECK(lex(f) == HELP);
+ TEST_CHECK(strcmp(f->lval.str, "cmt_labels_test") == 0);
+ cfl_sds_destroy(f->lval.str);
+
+ TEST_CHECK(lex(f) == METRIC_DOC);
+ TEST_CHECK(strcmp(f->lval.str, "Static labels test") == 0);
+ cfl_sds_destroy(f->lval.str);
+
+ TEST_CHECK(lex(f) == TYPE);
+ TEST_CHECK(strcmp(f->lval.str, "cmt_labels_test") == 0);
+ cfl_sds_destroy(f->lval.str);
+
+ TEST_CHECK(lex(f) == COUNTER);
+
+ TEST_CHECK(lex(f) == IDENTIFIER);
+ TEST_CHECK(strcmp(f->lval.str, "cmt_labels_test") == 0);
+ cfl_sds_destroy(f->lval.str);
+
+ TEST_CHECK(lex(f) == NUMSTR);
+ TEST_CHECK(strcmp(f->lval.numstr, "1") == 0);
+
+ TEST_CHECK(lex(f) == NUMSTR);
+ TEST_CHECK(strcmp(f->lval.numstr, "0") == 0);
+
+ TEST_CHECK(lex(f) == IDENTIFIER);
+ TEST_CHECK(strcmp(f->lval.str, "metric2") == 0);
+ cfl_sds_destroy(f->lval.str);
+
+ TEST_CHECK(lex(f) == '{');
+
+ TEST_CHECK(lex(f) == IDENTIFIER);
+ TEST_CHECK(strcmp(f->lval.str, "host") == 0);
+ cfl_sds_destroy(f->lval.str);
+
+ TEST_CHECK(lex(f) == '=');
+
+ TEST_CHECK(lex(f) == QUOTED);
+ TEST_CHECK(strcmp(f->lval.str, "calyptia.com") == 0);
+ cfl_sds_destroy(f->lval.str);
+
+ TEST_CHECK(lex(f) == ',');
+
+ TEST_CHECK(lex(f) == IDENTIFIER);
+ TEST_CHECK(strcmp(f->lval.str, "app") == 0);
+ cfl_sds_destroy(f->lval.str);
+
+ TEST_CHECK(lex(f) == '=');
+
+ TEST_CHECK(lex(f) == QUOTED);
+ TEST_CHECK(strcmp(f->lval.str, "cmetrics \n \\ \"") == 0);
+ cfl_sds_destroy(f->lval.str);
+
+ TEST_CHECK(lex(f) == '}');
+
+ TEST_CHECK(lex(f) == NUMSTR);
+ TEST_CHECK(strcmp(f->lval.numstr, "2.5") == 0);
+
+ TEST_CHECK(lex(f) == NUMSTR);
+ TEST_CHECK(strcmp(f->lval.numstr, "0") == 0);
+
+ TEST_CHECK(lex(f) == HELP);
+ TEST_CHECK(strcmp(f->lval.str, "metric1") == 0);
+ cfl_sds_destroy(f->lval.str);
+
+ TEST_CHECK(lex(f) == METRIC_DOC);
+ TEST_CHECK(strcmp(f->lval.str, "Second HELP tag") == 0);
+ cfl_sds_destroy(f->lval.str);
+
+ TEST_CHECK(lex(f) == IDENTIFIER);
+ TEST_CHECK(strcmp(f->lval.str, "metric1") == 0);
+ cfl_sds_destroy(f->lval.str);
+
+ TEST_CHECK(lex(f) == '{');
+
+ TEST_CHECK(lex(f) == IDENTIFIER);
+ TEST_CHECK(strcmp(f->lval.str, "escapes") == 0);
+ cfl_sds_destroy(f->lval.str);
+
+ TEST_CHECK(lex(f) == '=');
+
+ TEST_CHECK(lex(f) == QUOTED);
+ TEST_CHECK(strcmp(f->lval.str, "\n \\ \"") == 0);
+ cfl_sds_destroy(f->lval.str);
+
+ TEST_CHECK(lex(f) == '}');
+
+ TEST_CHECK(lex(f) == NUMSTR);
+ TEST_CHECK(strcmp(f->lval.numstr, "4.12") == 0);
+
+ TEST_CHECK(lex(f) == NUMSTR);
+ TEST_CHECK(strcmp(f->lval.numstr, "5") == 0);
+
+ destroy(f);
+}
+
+
+TEST_LIST = {
+ {"test_comment", test_comment},
+ {"test_help", test_help},
+ {"test_type", test_type},
+ {"test_simple", test_simple},
+ { 0 }
+};
diff --git a/src/fluent-bit/lib/cmetrics/tests/prometheus_parser.c b/src/fluent-bit/lib/cmetrics/tests/prometheus_parser.c
new file mode 100644
index 000000000..8cdef466a
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/tests/prometheus_parser.c
@@ -0,0 +1,1701 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_map.h>
+#include <cmetrics/cmt_decode_prometheus.h>
+#include <cmetrics/cmt_encode_prometheus.h>
+#include <stdio.h>
+
+#include "cmetrics/cmt_counter.h"
+#include "cmetrics/cmt_summary.h"
+#include "cmt_decode_prometheus_parser.h"
+#include "cmt_tests.h"
+#include "lib/acutest/acutest.h"
+#include "tests/cmt_tests_config.h"
+
+struct fixture {
+ yyscan_t scanner;
+ YY_BUFFER_STATE buf;
+ YYSTYPE lval;
+ struct cmt_decode_prometheus_context context;
+ const char *text;
+};
+
+struct fixture *init(int start_token, const char *test)
+{
+ cmt_initialize();
+ struct fixture *f = malloc(sizeof(*f));
+ memset(f, 0, sizeof(*f));
+ f->context.cmt = cmt_create();
+ f->context.opts.start_token = start_token;
+ cfl_list_init(&(f->context.metric.samples));
+ cmt_decode_prometheus_lex_init(&f->scanner);
+ f->buf = cmt_decode_prometheus__scan_string(test, f->scanner);
+ return f;
+}
+
+void destroy(struct fixture *f)
+{
+ cmt_decode_prometheus__delete_buffer(f->buf, f->scanner);
+ cmt_decode_prometheus_lex_destroy(f->scanner);
+ cmt_destroy(f->context.cmt);
+ free(f);
+}
+
+int parse(struct fixture *f)
+{
+ return cmt_decode_prometheus_parse(f->scanner, &f->context);
+}
+
+void test_header_help()
+{
+ struct fixture *f = init(START_HEADER,
+ "# HELP cmt_labels_test Static labels test\n"
+ );
+
+ TEST_CHECK(parse(f) == 0);
+
+ TEST_CHECK(strcmp(f->context.metric.ns, "cmt") == 0);
+ TEST_CHECK(strcmp(f->context.metric.subsystem, "labels") == 0);
+ TEST_CHECK(strcmp(f->context.metric.name, "test") == 0);
+ TEST_CHECK(strcmp(f->context.metric.docstring, "Static labels test") == 0);
+ TEST_CHECK(f->context.metric.type == 0);
+ cfl_sds_destroy(f->context.metric.name_orig);
+ cfl_sds_destroy(f->context.metric.docstring);
+ free(f->context.metric.ns);
+
+ destroy(f);
+}
+
+void test_header_type()
+{
+ struct fixture *f = init(START_HEADER,
+ "# TYPE cmt_labels_test counter\n"
+ );
+ TEST_CHECK(parse(f) == 0);
+
+ TEST_CHECK(strcmp(f->context.metric.ns, "cmt") == 0);
+ TEST_CHECK(strcmp(f->context.metric.subsystem, "labels") == 0);
+ TEST_CHECK(strcmp(f->context.metric.name, "test") == 0);
+ TEST_CHECK(f->context.metric.type == COUNTER);
+ TEST_CHECK(f->context.metric.docstring == NULL);
+ cfl_sds_destroy(f->context.metric.name_orig);
+ free(f->context.metric.ns);
+
+ destroy(f);
+}
+
+void test_header_help_type()
+{
+ struct fixture *f = init(START_HEADER,
+ "# HELP cmt_labels_test Static labels test\n"
+ "# TYPE cmt_labels_test summary\n"
+ );
+
+ TEST_CHECK(parse(f) == 0);
+
+ TEST_CHECK(strcmp(f->context.metric.docstring, "Static labels test") == 0);
+ TEST_CHECK(strcmp(f->context.metric.ns, "cmt") == 0);
+ TEST_CHECK(strcmp(f->context.metric.subsystem, "labels") == 0);
+ TEST_CHECK(strcmp(f->context.metric.name, "test") == 0);
+ TEST_CHECK(f->context.metric.type == SUMMARY);
+ cfl_sds_destroy(f->context.metric.name_orig);
+ cfl_sds_destroy(f->context.metric.docstring);
+ free(f->context.metric.ns);
+
+ destroy(f);
+}
+
+void test_header_type_help()
+{
+ struct fixture *f = init(START_HEADER,
+ "# TYPE cmt_labels_test gauge\n"
+ "# HELP cmt_labels_test Static labels test\n"
+ );
+
+ TEST_CHECK(parse(f) == 0);
+
+ TEST_CHECK(strcmp(f->context.metric.docstring, "Static labels test") == 0);
+ TEST_CHECK(strcmp(f->context.metric.ns, "cmt") == 0);
+ TEST_CHECK(strcmp(f->context.metric.subsystem, "labels") == 0);
+ TEST_CHECK(strcmp(f->context.metric.name, "test") == 0);
+ TEST_CHECK(f->context.metric.type == GAUGE);
+ cfl_sds_destroy(f->context.metric.name_orig);
+ cfl_sds_destroy(f->context.metric.docstring);
+ free(f->context.metric.ns);
+
+ destroy(f);
+}
+
+struct cmt_decode_prometheus_context_sample *add_empty_sample(struct fixture *f)
+{
+ struct cmt_decode_prometheus_context_sample *sample;
+ sample = malloc(sizeof(*sample));
+ memset(sample, 0, sizeof(*sample));
+ cfl_list_add(&sample->_head, &f->context.metric.samples);
+ return sample;
+}
+
+void test_labels()
+{
+ struct fixture *f = init(START_LABELS, "dev=\"Calyptia\",lang=\"C\"");
+ struct cmt_decode_prometheus_context_sample *sample = add_empty_sample(f);
+ TEST_CHECK(parse(f) == 0);
+ TEST_CHECK(f->context.metric.label_count == 2);
+ TEST_CHECK(strcmp(f->context.metric.labels[0], "dev") == 0);
+ TEST_CHECK(strcmp(sample->label_values[0], "Calyptia") == 0);
+ TEST_CHECK(strcmp(f->context.metric.labels[1], "lang") == 0);
+ TEST_CHECK(strcmp(sample->label_values[1], "C") == 0);
+ cfl_sds_destroy(f->context.metric.labels[0]);
+ cfl_sds_destroy(sample->label_values[0]);
+ cfl_sds_destroy(f->context.metric.labels[1]);
+ cfl_sds_destroy(sample->label_values[1]);
+ free(sample);
+ destroy(f);
+}
+
+void test_labels_trailing_comma()
+{
+ struct fixture *f = init(START_LABELS, "dev=\"Calyptia\",lang=\"C\",");
+ struct cmt_decode_prometheus_context_sample *sample = add_empty_sample(f);
+ TEST_CHECK(parse(f) == 0);
+ TEST_CHECK(f->context.metric.label_count == 2);
+ TEST_CHECK(strcmp(f->context.metric.labels[0], "dev") == 0);
+ TEST_CHECK(strcmp(sample->label_values[0], "Calyptia") == 0);
+ TEST_CHECK(strcmp(f->context.metric.labels[1], "lang") == 0);
+ TEST_CHECK(strcmp(sample->label_values[1], "C") == 0);
+ cfl_sds_destroy(f->context.metric.labels[0]);
+ cfl_sds_destroy(sample->label_values[0]);
+ cfl_sds_destroy(f->context.metric.labels[1]);
+ cfl_sds_destroy(sample->label_values[1]);
+ free(sample);
+ destroy(f);
+}
+
+void test_sample()
+{
+ cfl_sds_t result;
+ const char expected[] = (
+ "# HELP cmt_labels_test some docstring\n"
+ "# TYPE cmt_labels_test counter\n"
+ "cmt_labels_test{dev=\"Calyptia\",lang=\"C\"} 1 0\n"
+ );
+
+ struct fixture *f = init(0,
+ "# HELP cmt_labels_test some docstring\n"
+ "# TYPE cmt_labels_test counter\n"
+ "cmt_labels_test{dev=\"Calyptia\",lang=\"C\",} 1 0\n"
+ );
+
+ TEST_CHECK(parse(f) == 0);
+ result = cmt_encode_prometheus_create(f->context.cmt, CMT_TRUE);
+ TEST_CHECK(strcmp(result, expected) == 0);
+ cfl_sds_destroy(result);
+
+ destroy(f);
+}
+
+void test_samples()
+{
+ cfl_sds_t result;
+ const char expected[] = (
+ "# HELP cmt_labels_test some docstring\n"
+ "# TYPE cmt_labels_test gauge\n"
+ "cmt_labels_test{dev=\"Calyptia\",lang=\"C\"} 5 999999\n"
+ "cmt_labels_test{dev=\"Calyptia\",lang=\"C++\"} 6 7777\n"
+
+ );
+
+ struct fixture *f = init(0,
+ "# HELP cmt_labels_test some docstring\n"
+ "# TYPE cmt_labels_test gauge\n"
+ "cmt_labels_test{dev=\"Calyptia\",lang=\"C\",} 5 999999\n"
+ "cmt_labels_test{dev=\"Calyptia\",lang=\"C++\"} 6 7777\n"
+ );
+
+ TEST_CHECK(parse(f) == 0);
+ result = cmt_encode_prometheus_create(f->context.cmt, CMT_TRUE);
+ TEST_CHECK(strcmp(result, expected) == 0);
+ cfl_sds_destroy(result);
+
+ destroy(f);
+}
+
+void test_escape_sequences()
+{
+ cfl_sds_t result;
+ const char expected[] = (
+ "# HELP msdos_file_access_time_seconds\n"
+ "# TYPE msdos_file_access_time_seconds untyped\n"
+ "msdos_file_access_time_seconds{path=\"C:\\\\DIR\\\\FILE.TXT\",error=\"Cannot find file:\\n\\\"FILE.TXT\\\"\"} 1458255915 0\n"
+ );
+
+ struct fixture *f = init(0,
+ "# Escaping in label values:\n"
+ "msdos_file_access_time_seconds{path=\"C:\\\\DIR\\\\FILE.TXT\",error=\"Cannot find file:\\n\\\"FILE.TXT\\\"\"} 1.458255915e9\n"
+ );
+
+ TEST_CHECK(parse(f) == 0);
+ result = cmt_encode_prometheus_create(f->context.cmt, CMT_TRUE);
+ TEST_CHECK(strcmp(result, expected) == 0);
+ cfl_sds_destroy(result);
+
+ destroy(f);
+}
+
+void test_metric_without_labels()
+{
+ cfl_sds_t result;
+
+ const char expected[] =
+ "# HELP metric_without_timestamp_and_labels\n"
+ "# TYPE metric_without_timestamp_and_labels untyped\n"
+ "metric_without_timestamp_and_labels 12.470000000000001 0\n"
+ ;
+
+ struct fixture *f = init(0,
+ "# Minimalistic line:\n"
+ "metric_without_timestamp_and_labels 12.47\n"
+ );
+
+ TEST_CHECK(parse(f) == 0);
+ result = cmt_encode_prometheus_create(f->context.cmt, CMT_TRUE);
+ TEST_CHECK(strcmp(result, expected) == 0);
+ cfl_sds_destroy(result);
+
+ destroy(f);
+}
+
+void test_prometheus_spec_example()
+{
+ char errbuf[256];
+ int status;
+ cfl_sds_t result;
+ struct cmt *cmt;
+ struct cmt_decode_prometheus_parse_opts opts;
+ memset(&opts, 0, sizeof(opts));
+ opts.errbuf = errbuf;
+ opts.errbuf_size = sizeof(errbuf);
+ const char in_buf[] =
+ "# TYPE http_requests_total counter\n"
+ "# HELP http_requests_total The total number of HTTP requests.\n"
+ "http_requests_total{method=\"post\",code=\"200\"} 1027 1395066363000\n"
+ "http_requests_total{method=\"post\",code=\"400\"} 3 1395066363000\n"
+ "\n"
+ "# Escaping in label values:\n"
+ "msdos_file_access_time_seconds{path=\"C:\\\\DIR\\\\FILE.TXT\",error=\"Cannot find file:\\n\\\"FILE.TXT\\\"\"} 1.458255915e9\n"
+ "\n"
+ "# Minimalistic line:\n"
+ "metric_without_timestamp_and_labels 12.47\n"
+ "\n"
+ "# A weird metric from before the epoch:\n"
+ "something_weird{problem=\"division by zero\"} +Inf -3982045\n"
+ "\n"
+ "# A histogram, which has a pretty complex representation in the text format:\n"
+ "# HELP http_request_duration_seconds_bucket A histogram of the request duration.\n"
+ "# TYPE http_request_duration_seconds_bucket counter\n"
+ "http_request_duration_seconds_bucket{le=\"0.05\"} 24054\n"
+ "http_request_duration_seconds_bucket{le=\"0.1\"} 33444\n"
+ "http_request_duration_seconds_bucket{le=\"0.2\"} 100392\n"
+ "http_request_duration_seconds_bucket{le=\"0.5\"} 129389\n"
+ "http_request_duration_seconds_bucket{le=\"1\"} 133988\n"
+ "http_request_duration_seconds_bucket{le=\"+Inf\"} 144320\n"
+ "http_request_duration_seconds_sum 53423\n"
+ "http_request_duration_seconds_count 144320\n"
+ "\n"
+ "# Finally a summary, which has a complex representation, too:\n"
+ "# HELP rpc_duration_seconds A summary of the RPC duration in seconds.\n"
+ "# TYPE rpc_duration_seconds gauge\n"
+ "rpc_duration_seconds{quantile=\"0.01\"} 3102\n"
+ "rpc_duration_seconds{quantile=\"0.05\"} 3272\n"
+ "rpc_duration_seconds{quantile=\"0.5\"} 4773\n"
+ "rpc_duration_seconds{quantile=\"0.9\"} 9001\n"
+ "rpc_duration_seconds{quantile=\"0.99\"} 76656\n"
+ "rpc_duration_seconds_sum 1.7560473e+07\n"
+ "rpc_duration_seconds_count 2693\n"
+ ;
+ const char expected[] =
+ "# HELP http_requests_total The total number of HTTP requests.\n"
+ "# TYPE http_requests_total counter\n"
+ "http_requests_total{method=\"post\",code=\"200\"} 1027 1395066363000\n"
+ "http_requests_total{method=\"post\",code=\"400\"} 3 1395066363000\n"
+ "# HELP http_request_duration_seconds_bucket A histogram of the request duration.\n"
+ "# TYPE http_request_duration_seconds_bucket counter\n"
+ "http_request_duration_seconds_bucket{le=\"0.05\"} 24054 0\n"
+ "http_request_duration_seconds_bucket{le=\"0.1\"} 33444 0\n"
+ "http_request_duration_seconds_bucket{le=\"0.2\"} 100392 0\n"
+ "http_request_duration_seconds_bucket{le=\"0.5\"} 129389 0\n"
+ "http_request_duration_seconds_bucket{le=\"1\"} 133988 0\n"
+ "http_request_duration_seconds_bucket{le=\"+Inf\"} 144320 0\n"
+ "# HELP rpc_duration_seconds A summary of the RPC duration in seconds.\n"
+ "# TYPE rpc_duration_seconds gauge\n"
+ "rpc_duration_seconds{quantile=\"0.01\"} 3102 0\n"
+ "rpc_duration_seconds{quantile=\"0.05\"} 3272 0\n"
+ "rpc_duration_seconds{quantile=\"0.5\"} 4773 0\n"
+ "rpc_duration_seconds{quantile=\"0.9\"} 9001 0\n"
+ "rpc_duration_seconds{quantile=\"0.99\"} 76656 0\n"
+ "# HELP msdos_file_access_time_seconds\n"
+ "# TYPE msdos_file_access_time_seconds untyped\n"
+ "msdos_file_access_time_seconds{path=\"C:\\\\DIR\\\\FILE.TXT\",error=\"Cannot find file:\\n\\\"FILE.TXT\\\"\"} 1458255915 0\n"
+ "# HELP metric_without_timestamp_and_labels\n"
+ "# TYPE metric_without_timestamp_and_labels untyped\n"
+ "metric_without_timestamp_and_labels 12.470000000000001 0\n"
+ "# HELP something_weird\n"
+ "# TYPE something_weird untyped\n"
+ "something_weird{problem=\"division by zero\"} inf 0\n"
+ "# HELP http_request_duration_seconds_sum\n"
+ "# TYPE http_request_duration_seconds_sum untyped\n"
+ "http_request_duration_seconds_sum 53423 0\n"
+ "# HELP http_request_duration_seconds_count\n"
+ "# TYPE http_request_duration_seconds_count untyped\n"
+ "http_request_duration_seconds_count 144320 0\n"
+ "# HELP rpc_duration_seconds_sum\n"
+ "# TYPE rpc_duration_seconds_sum untyped\n"
+ "rpc_duration_seconds_sum 17560473 0\n"
+ "# HELP rpc_duration_seconds_count\n"
+ "# TYPE rpc_duration_seconds_count untyped\n"
+ "rpc_duration_seconds_count 2693 0\n"
+ ;
+
+ cmt_initialize();
+ status = cmt_decode_prometheus_create(&cmt, in_buf, 0, &opts);
+ TEST_CHECK(status == 0);
+ result = cmt_encode_prometheus_create(cmt, CMT_TRUE);
+ TEST_CHECK(strcmp(result, expected) == 0);
+ cfl_sds_destroy(result);
+ cmt_decode_prometheus_destroy(cmt);
+}
+
+void test_bison_parsing_error()
+{
+ // Note that in this test I commented checks for the error message. The
+ // reason is that the message is different depending on which bison
+ // version is used to generate the parser, so not fully deterministic.
+ int status;
+ char errbuf[256];
+ struct cmt *cmt;
+ struct cmt_decode_prometheus_parse_opts opts;
+ memset(&opts, 0, sizeof(opts));
+ opts.errbuf = errbuf;
+ opts.errbuf_size = sizeof(errbuf);
+
+ status = cmt_decode_prometheus_create(&cmt, "", 0, &opts);
+ TEST_CHECK(status == CMT_DECODE_PROMETHEUS_SYNTAX_ERROR);
+ // TEST_CHECK(strcmp(errbuf,
+ // "syntax error, unexpected end of file") == 0);
+
+ status = cmt_decode_prometheus_create(&cmt,
+ "# HELP metric_name some docstring\n"
+ "# TYPE metric_name counter\n"
+ "metric_name", 0, &opts);
+ TEST_CHECK(status == CMT_DECODE_PROMETHEUS_SYNTAX_ERROR);
+ // TEST_CHECK(strcmp(errbuf,
+ // "syntax error, unexpected end of file, expecting '{' "
+ // "or FPOINT or INTEGER") == 0);
+
+ status = cmt_decode_prometheus_create(&cmt,
+ "# HELP metric_name some docstring\n"
+ "# TYPE metric_name counter\n"
+ "metric_name {key", 0, &opts);
+ TEST_CHECK(status == CMT_DECODE_PROMETHEUS_SYNTAX_ERROR);
+ // TEST_CHECK(strcmp(errbuf,
+ // "syntax error, unexpected end of file, expecting '='") == 0);
+
+ status = cmt_decode_prometheus_create(&cmt,
+ "# HELP metric_name some docstring\n"
+ "# TYPE metric_name counter\n"
+ "metric_name {key=", 0, &opts);
+ TEST_CHECK(status == CMT_DECODE_PROMETHEUS_SYNTAX_ERROR);
+ // TEST_CHECK(strcmp(errbuf,
+ // "syntax error, unexpected end of file, expecting QUOTED") == 0);
+
+ status = cmt_decode_prometheus_create(&cmt,
+ "# HELP metric_name some docstring\n"
+ "# TYPE metric_name counter\n"
+ "metric_name {key=\"abc\"", 0, &opts);
+ TEST_CHECK(status == CMT_DECODE_PROMETHEUS_SYNTAX_ERROR);
+ // TEST_CHECK(strcmp(errbuf,
+ // "syntax error, unexpected end of file, expecting '}'") == 0);
+
+ status = cmt_decode_prometheus_create(&cmt,
+ "# HELP metric_name some docstring\n"
+ "# TYPE metric_name counter\n"
+ "metric_name {key=\"abc\"}", 0, &opts);
+ TEST_CHECK(status == CMT_DECODE_PROMETHEUS_SYNTAX_ERROR);
+ // TEST_CHECK(strcmp(errbuf,
+ // "syntax error, unexpected end of file, expecting "
+ // "FPOINT or INTEGER") == 0);
+}
+
+void test_label_limits()
+{
+ int i;
+ int status;
+ struct cmt_counter *counter;
+ char errbuf[256];
+ struct cmt *cmt;
+ char inbuf[65535];
+ int pos;
+ struct cmt_decode_prometheus_parse_opts opts;
+ memset(&opts, 0, sizeof(opts));
+ opts.errbuf = errbuf;
+ opts.errbuf_size = sizeof(errbuf);
+
+ pos = snprintf(inbuf, sizeof(inbuf),
+ "# HELP many_labels_metric reaches maximum number labels\n"
+ "# TYPE many_labels_metric counter\n"
+ "many_labels_metric {");
+ for (i = 0; i < CMT_DECODE_PROMETHEUS_MAX_LABEL_COUNT && pos < sizeof(inbuf); i++) {
+ pos += snprintf(inbuf + pos, sizeof(inbuf) - pos, "l%d=\"%d\",", i, i);
+ }
+ snprintf(inbuf + pos, sizeof(inbuf) - pos, "} 55 0\n");
+
+ status = cmt_decode_prometheus_create(&cmt, inbuf, 0, &opts);
+ TEST_CHECK(status == 0);
+ counter = cfl_list_entry_first(&cmt->counters, struct cmt_counter, _head);
+ TEST_CHECK(counter->map->label_count == CMT_DECODE_PROMETHEUS_MAX_LABEL_COUNT);
+ cmt_decode_prometheus_destroy(cmt);
+
+ // write one more label to exceed limit
+ snprintf(inbuf + pos, sizeof(inbuf) - pos, "last=\"val\"} 55 0\n");
+ status = cmt_decode_prometheus_create(&cmt, inbuf, 0, &opts);
+ TEST_CHECK(status == CMT_DECODE_PROMETHEUS_MAX_LABEL_COUNT_EXCEEDED);
+ TEST_CHECK(strcmp(errbuf, "maximum number of labels exceeded") == 0);
+}
+
+void test_invalid_value()
+{
+ int status;
+ char errbuf[256];
+ struct cmt *cmt;
+ struct cmt_decode_prometheus_parse_opts opts;
+ memset(&opts, 0, sizeof(opts));
+ opts.errbuf = errbuf;
+ opts.errbuf_size = sizeof(errbuf);
+
+ status = cmt_decode_prometheus_create(&cmt,
+ "# HELP metric_name some docstring\n"
+ "# TYPE metric_name counter\n"
+ "metric_name {key=\"abc\"} 10e", 0, &opts);
+ TEST_CHECK(status == CMT_DECODE_PROMETHEUS_PARSE_VALUE_FAILED);
+ TEST_CHECK(strcmp(errbuf,
+ "failed to parse sample: \"10e\" is not a valid value") == 0);
+}
+
+void test_invalid_timestamp()
+{
+ int status;
+ char errbuf[256];
+ struct cmt *cmt;
+ struct cmt_decode_prometheus_parse_opts opts;
+ memset(&opts, 0, sizeof(opts));
+ opts.errbuf = errbuf;
+ opts.errbuf_size = sizeof(errbuf);
+
+ status = cmt_decode_prometheus_create(&cmt,
+ "# HELP metric_name some docstring\n"
+ "# TYPE metric_name counter\n"
+ "metric_name {key=\"abc\"} 10 3e", 0, &opts);
+ TEST_CHECK(status == CMT_DECODE_PROMETHEUS_PARSE_TIMESTAMP_FAILED);
+ TEST_CHECK(strcmp(errbuf,
+ "failed to parse sample: \"3e\" is not a valid timestamp") == 0);
+}
+
+void test_default_timestamp()
+{
+ int status;
+ cfl_sds_t result;
+ struct cmt *cmt;
+ struct cmt_decode_prometheus_parse_opts opts;
+ memset(&opts, 0, sizeof(opts));
+ opts.default_timestamp = 557 * 10e5;
+
+
+ status = cmt_decode_prometheus_create(&cmt,
+ "# HELP metric_name some docstring\n"
+ "# TYPE metric_name counter\n"
+ "metric_name {key=\"abc\"} 10", 0, &opts);
+ TEST_CHECK(status == 0);
+ result = cmt_encode_prometheus_create(cmt, CMT_TRUE);
+ TEST_CHECK(strcmp(result,
+ "# HELP metric_name some docstring\n"
+ "# TYPE metric_name counter\n"
+ "metric_name{key=\"abc\"} 10 557\n") == 0);
+ cfl_sds_destroy(result);
+ cmt_decode_prometheus_destroy(cmt);
+}
+
+void test_values()
+{
+ int status = 0;
+ cfl_sds_t result = NULL;
+ struct cmt *cmt;
+
+ const char expected[] = "# HELP metric_name some docstring\n"
+ "# TYPE metric_name gauge\n"
+ "metric_name{key=\"simple integer\"} 54 0\n"
+ "metric_name{key=\"simple float\"} 12.470000000000001 0\n"
+ "metric_name{key=\"scientific notation 1\"} 17560473 0\n"
+ "metric_name{key=\"scientific notation 2\"} 1.7560473000000001 0\n"
+ "metric_name{key=\"Positive \\\"not a number\\\"\"} nan 0\n"
+ "metric_name{key=\"Positive infinity\"} inf 0\n"
+ "metric_name{key=\"Negative infinity\"} -inf 0\n";
+
+ status = cmt_decode_prometheus_create(&cmt,
+ "# HELP metric_name some docstring\n"
+ "# TYPE metric_name gauge\n"
+ "metric_name {key=\"simple integer\"} 54\n"
+ "metric_name {key=\"simple float\"} 12.47\n"
+ "metric_name {key=\"scientific notation 1\"} 1.7560473e+07\n"
+ "metric_name {key=\"scientific notation 2\"} 17560473e-07\n"
+ "metric_name {key=\"Positive \\\"not a number\\\"\"} +NAN\n"
+ "metric_name {key=\"Positive infinity\"} +INF\n"
+ "metric_name {key=\"Negative infinity\"} -iNf\n", 0, NULL);
+ TEST_CHECK(status == 0);
+ if (!status) {
+ result = cmt_encode_prometheus_create(cmt, CMT_TRUE);
+ status = strcmp(result, expected);
+ TEST_CHECK(status == 0);
+ if (status) {
+ fprintf(stderr, "EXPECTED:\n======\n%s\n======\nRESULT:\n======\n%s\n======\n", expected, result);
+ }
+ }
+
+ cfl_sds_destroy(result);
+ cmt_decode_prometheus_destroy(cmt);
+}
+
+void test_in_size()
+{
+ int status;
+ cfl_sds_t result;
+ struct cmt *cmt;
+ cfl_sds_t in_buf;
+ size_t in_size;
+
+ in_buf = cfl_sds_create("metric_name {key=\"1\"} 1\n");
+ in_size = cfl_sds_len(in_buf);
+ in_buf = cfl_sds_cat(in_buf, "metric_name {key=\"2\"} 2\n", in_size);
+
+ status = cmt_decode_prometheus_create(&cmt, in_buf, in_size, NULL);
+ TEST_CHECK(status == 0);
+ result = cmt_encode_prometheus_create(cmt, CMT_TRUE);
+ TEST_CHECK(strcmp(result,
+ "# HELP metric_name\n"
+ "# TYPE metric_name untyped\n"
+ "metric_name{key=\"1\"} 1 0\n") == 0);
+ cfl_sds_destroy(result);
+ cfl_sds_destroy(in_buf);
+ cmt_decode_prometheus_destroy(cmt);
+}
+
+// reproduces https://github.com/calyptia/cmetrics/issues/71
+void test_issue_71()
+{
+ int status;
+ struct cmt *cmt;
+ cfl_sds_t in_buf = read_file(CMT_TESTS_DATA_PATH "/issue_71.txt");
+ size_t in_size = cfl_sds_len(in_buf);
+
+ status = cmt_decode_prometheus_create(&cmt, in_buf, in_size, NULL);
+ TEST_CHECK(status == 0);
+ cfl_sds_destroy(in_buf);
+ cmt_decode_prometheus_destroy(cmt);
+}
+
+void test_histogram()
+{
+ int status;
+ struct cmt *cmt;
+ struct cmt_decode_prometheus_parse_opts opts;
+ cfl_sds_t result;
+ memset(&opts, 0, sizeof(opts));
+
+ status = cmt_decode_prometheus_create(&cmt,
+ "# HELP http_request_duration_seconds A histogram of the request duration.\n"
+ "# TYPE http_request_duration_seconds histogram\n"
+ "http_request_duration_seconds_bucket{le=\"0.05\"} 24054\n"
+ "http_request_duration_seconds_bucket{le=\"0.1\"} 33444\n"
+ "http_request_duration_seconds_bucket{le=\"0.2\"} 100392\n"
+ "http_request_duration_seconds_bucket{le=\"0.5\"} 129389\n"
+ "http_request_duration_seconds_bucket{le=\"1\"} 133988\n"
+ "http_request_duration_seconds_bucket{le=\"+Inf\"} 144320\n"
+ "http_request_duration_seconds_sum 53423\n"
+ "http_request_duration_seconds_count 144320\n", 0, &opts);
+ TEST_CHECK(status == 0);
+ result = cmt_encode_prometheus_create(cmt, CMT_FALSE);
+ TEST_CHECK(strcmp(result,
+ "# HELP http_request_duration_seconds A histogram of the request duration.\n"
+ "# TYPE http_request_duration_seconds histogram\n"
+ "http_request_duration_seconds_bucket{le=\"0.05\"} 24054\n"
+ "http_request_duration_seconds_bucket{le=\"0.1\"} 33444\n"
+ "http_request_duration_seconds_bucket{le=\"0.2\"} 100392\n"
+ "http_request_duration_seconds_bucket{le=\"0.5\"} 129389\n"
+ "http_request_duration_seconds_bucket{le=\"1.0\"} 133988\n"
+ "http_request_duration_seconds_bucket{le=\"+Inf\"} 144320\n"
+ "http_request_duration_seconds_sum 53423\n"
+ "http_request_duration_seconds_count 144320\n") == 0);
+ cfl_sds_destroy(result);
+ cmt_decode_prometheus_destroy(cmt);
+}
+
+void test_histogram_labels()
+{
+ int status;
+ struct cmt *cmt;
+ struct cmt_decode_prometheus_parse_opts opts;
+ memset(&opts, 0, sizeof(opts));
+ cfl_sds_t result;
+
+ status = cmt_decode_prometheus_create(&cmt,
+ "# HELP http_request_duration_seconds A histogram of the request duration.\n"
+ "# TYPE http_request_duration_seconds histogram\n"
+ "http_request_duration_seconds_bucket{label1=\"val1\",le=\"0.05\",label2=\"val2\"} 24054\n"
+ "http_request_duration_seconds_bucket{label1=\"val1\",le=\"0.1\",label2=\"val2\"} 33444\n"
+ "http_request_duration_seconds_bucket{label1=\"val1\",le=\"0.2\",label2=\"val2\"} 100392\n"
+ "http_request_duration_seconds_bucket{label1=\"val1\",le=\"0.5\",label2=\"val2\"} 129389\n"
+ "http_request_duration_seconds_bucket{label1=\"val1\",le=\"1\",label2=\"val2\"} 133988\n"
+ "http_request_duration_seconds_bucket{label1=\"val1\",le=\"+Inf\",label2=\"val2\"} 144320\n"
+ "http_request_duration_seconds_sum{label1=\"val1\",label2=\"val2\"} 53423\n"
+ "http_request_duration_seconds_count{label1=\"val1\",label2=\"val2\"}144320\n", 0, &opts);
+ TEST_CHECK(status == 0);
+ result = cmt_encode_prometheus_create(cmt, CMT_FALSE);
+ TEST_CHECK(strcmp(result,
+ "# HELP http_request_duration_seconds A histogram of the request duration.\n"
+ "# TYPE http_request_duration_seconds histogram\n"
+ "http_request_duration_seconds_bucket{le=\"0.05\",label1=\"val1\",label2=\"val2\"} 24054\n"
+ "http_request_duration_seconds_bucket{le=\"0.1\",label1=\"val1\",label2=\"val2\"} 33444\n"
+ "http_request_duration_seconds_bucket{le=\"0.2\",label1=\"val1\",label2=\"val2\"} 100392\n"
+ "http_request_duration_seconds_bucket{le=\"0.5\",label1=\"val1\",label2=\"val2\"} 129389\n"
+ "http_request_duration_seconds_bucket{le=\"1.0\",label1=\"val1\",label2=\"val2\"} 133988\n"
+ "http_request_duration_seconds_bucket{le=\"+Inf\",label1=\"val1\",label2=\"val2\"} 144320\n"
+ "http_request_duration_seconds_sum{label1=\"val1\",label2=\"val2\"} 53423\n"
+ "http_request_duration_seconds_count{label1=\"val1\",label2=\"val2\"} 144320\n") == 0);
+ cfl_sds_destroy(result);
+ cmt_decode_prometheus_destroy(cmt);
+}
+
+void test_summary()
+{
+ int status;
+ struct cmt *cmt;
+ struct cmt_decode_prometheus_parse_opts opts;
+ cfl_sds_t result;
+ memset(&opts, 0, sizeof(opts));
+
+ status = cmt_decode_prometheus_create(&cmt,
+ "# HELP rpc_duration_seconds A summary of the RPC duration in seconds.\n"
+ "# TYPE rpc_duration_seconds summary\n"
+ "rpc_duration_seconds{quantile=\"0.01\"} 3102\n"
+ "rpc_duration_seconds{quantile=\"0.05\"} 3272\n"
+ "rpc_duration_seconds{quantile=\"0.5\"} 4773\n"
+ "rpc_duration_seconds{quantile=\"0.9\"} 9001\n"
+ "rpc_duration_seconds{quantile=\"0.99\"} 76656\n"
+ "rpc_duration_seconds_sum 1.7560473e+07\n"
+ "rpc_duration_seconds_count 2693\n", 0, &opts);
+ TEST_CHECK(status == 0);
+ result = cmt_encode_prometheus_create(cmt, CMT_FALSE);
+ TEST_CHECK(strcmp(result,
+ "# HELP rpc_duration_seconds A summary of the RPC duration in seconds.\n"
+ "# TYPE rpc_duration_seconds summary\n"
+ "rpc_duration_seconds{quantile=\"0.01\"} 3102\n"
+ "rpc_duration_seconds{quantile=\"0.05\"} 3272\n"
+ "rpc_duration_seconds{quantile=\"0.5\"} 4773\n"
+ "rpc_duration_seconds{quantile=\"0.9\"} 9001\n"
+ "rpc_duration_seconds{quantile=\"0.99\"} 76656\n"
+ "rpc_duration_seconds_sum 17560473\n"
+ "rpc_duration_seconds_count 2693\n") == 0);
+ cfl_sds_destroy(result);
+ cmt_decode_prometheus_destroy(cmt);
+}
+
+void test_null_labels()
+{
+ int status;
+ cfl_sds_t result;
+ struct cmt *cmt;
+ struct cmt_decode_prometheus_parse_opts opts;
+ memset(&opts, 0, sizeof(opts));
+ const char in_buf[] =
+ "# TYPE ns_ss_name counter\n"
+ "# HELP ns_ss_name Example with null labels.\n"
+ "ns_ss_name{A=\"a\",B=\"b\",C=\"c\"} 1027 1395066363000\n"
+ "ns_ss_name{C=\"c\",D=\"d\",E=\"e\"} 1027 1395066363000\n"
+ ;
+ const char expected[] =
+ "# HELP ns_ss_name Example with null labels.\n"
+ "# TYPE ns_ss_name counter\n"
+ "ns_ss_name{A=\"a\",B=\"b\",C=\"c\"} 1027 1395066363000\n"
+ "ns_ss_name{C=\"c\",D=\"d\",E=\"e\"} 1027 1395066363000\n"
+ ;
+
+ cmt_initialize();
+ status = cmt_decode_prometheus_create(&cmt, in_buf, 0, &opts);
+ TEST_CHECK(status == 0);
+ result = cmt_encode_prometheus_create(cmt, CMT_TRUE);
+ TEST_CHECK(strcmp(result, expected) == 0);
+ cfl_sds_destroy(result);
+ cmt_decode_prometheus_destroy(cmt);
+}
+
+// reproduces https://github.com/fluent/fluent-bit/issues/5541
+void test_issue_fluent_bit_5541()
+{
+ int status;
+ char *result;
+ struct cmt *cmt;
+ cfl_sds_t in_buf = read_file(CMT_TESTS_DATA_PATH "/issue_fluent_bit_5541.txt");
+ size_t in_size = cfl_sds_len(in_buf);
+
+ const char expected[] =
+ "# HELP http_request_duration_seconds HTTP request latency (seconds)\n"
+ "# TYPE http_request_duration_seconds histogram\n"
+ "http_request_duration_seconds_bucket{le=\"0.005\"} 2 0\n"
+ "http_request_duration_seconds_bucket{le=\"0.01\"} 2 0\n"
+ "http_request_duration_seconds_bucket{le=\"0.025\"} 2 0\n"
+ "http_request_duration_seconds_bucket{le=\"0.05\"} 2 0\n"
+ "http_request_duration_seconds_bucket{le=\"0.075\"} 2 0\n"
+ "http_request_duration_seconds_bucket{le=\"0.1\"} 2 0\n"
+ "http_request_duration_seconds_bucket{le=\"0.25\"} 2 0\n"
+ "http_request_duration_seconds_bucket{le=\"0.5\"} 2 0\n"
+ "http_request_duration_seconds_bucket{le=\"0.75\"} 2 0\n"
+ "http_request_duration_seconds_bucket{le=\"1.0\"} 2 0\n"
+ "http_request_duration_seconds_bucket{le=\"2.5\"} 2 0\n"
+ "http_request_duration_seconds_bucket{le=\"5.0\"} 2 0\n"
+ "http_request_duration_seconds_bucket{le=\"7.5\"} 2 0\n"
+ "http_request_duration_seconds_bucket{le=\"10.0\"} 2 0\n"
+ "http_request_duration_seconds_bucket{le=\"+Inf\"} 2 0\n"
+ "http_request_duration_seconds_sum 0.00069131026975810528 0\n"
+ "http_request_duration_seconds_count 2 0\n"
+ ;
+
+ status = cmt_decode_prometheus_create(&cmt, in_buf, in_size, NULL);
+ TEST_CHECK(status == 0);
+
+ result = cmt_encode_prometheus_create(cmt, CMT_TRUE);
+ TEST_CHECK(strcmp(result, expected) == 0);
+
+ cfl_sds_destroy(in_buf);
+ cfl_sds_destroy(result);
+ cmt_decode_prometheus_destroy(cmt);
+}
+
+// reproduces https://github.com/fluent/fluent-bit/issues/5894
+void test_issue_fluent_bit_5894()
+{
+ char errbuf[256];
+ int status;
+ cfl_sds_t result = NULL;
+ struct cmt *cmt;
+ struct cmt_decode_prometheus_parse_opts opts;
+ memset(&opts, 0, sizeof(opts));
+ opts.errbuf = errbuf;
+ opts.errbuf_size = sizeof(errbuf);
+ cfl_sds_t in_buf = read_file(CMT_TESTS_DATA_PATH "/issue_fluent_bit_5894.txt");
+ size_t in_size = cfl_sds_len(in_buf);
+
+ const char expected[] =
+ "# HELP hikaricp_connections_timeout_total Connection timeout total count\n"
+ "# TYPE hikaricp_connections_timeout_total counter\n"
+ "hikaricp_connections_timeout_total{pool=\"mcadb\"} 0 0\n"
+ "# HELP rabbitmq_consumed_total\n"
+ "# TYPE rabbitmq_consumed_total counter\n"
+ "rabbitmq_consumed_total{name=\"rabbit\"} 0 0\n"
+ "# HELP rabbitmq_failed_to_publish_total\n"
+ "# TYPE rabbitmq_failed_to_publish_total counter\n"
+ "rabbitmq_failed_to_publish_total{name=\"rabbit\"} 0 0\n"
+ "# HELP rabbitmq_acknowledged_published_total\n"
+ "# TYPE rabbitmq_acknowledged_published_total counter\n"
+ "rabbitmq_acknowledged_published_total{name=\"rabbit\"} 0 0\n"
+ "# HELP tomcat_sessions_rejected_sessions_total\n"
+ "# TYPE tomcat_sessions_rejected_sessions_total counter\n"
+ "tomcat_sessions_rejected_sessions_total 0 0\n"
+
+ "# HELP process_start_time_seconds Start time of the process since unix epoch.\n"
+ "# TYPE process_start_time_seconds gauge\n"
+ "process_start_time_seconds 1660594096.832 0\n"
+ "# HELP spring_kafka_listener_seconds_max Kafka Listener Timer\n"
+ "# TYPE spring_kafka_listener_seconds_max gauge\n"
+ "spring_kafka_listener_seconds_max{exception=\"ListenerExecutionFailedException\",name=\"org.springframework.kafka.KafkaListenerEndpointContainer#0-0\",result=\"failure\"} 0 0\n"
+ "spring_kafka_listener_seconds_max{exception=\"none\",name=\"org.springframework.kafka.KafkaListenerEndpointContainer#0-0\",result=\"success\"} 0 0\n"
+ "# HELP process_files_max_files The maximum file descriptor count\n"
+ "# TYPE process_files_max_files gauge\n"
+ "process_files_max_files 1048576 0\n"
+ "# HELP hikaricp_connections_pending Pending threads\n"
+ "# TYPE hikaricp_connections_pending gauge\n"
+ "hikaricp_connections_pending{pool=\"mcadb\"} 0 0\n"
+ "# HELP jvm_memory_committed_bytes The amount of memory in bytes that is committed for the Java virtual machine to use\n"
+ "# TYPE jvm_memory_committed_bytes gauge\n"
+ "jvm_memory_committed_bytes{area=\"nonheap\",id=\"CodeHeap 'profiled nmethods'\"} 16056320 0\n"
+ "jvm_memory_committed_bytes{area=\"heap\",id=\"G1 Survivor Space\"} 20971520 0\n"
+ "jvm_memory_committed_bytes{area=\"heap\",id=\"G1 Old Gen\"} 232783872 0\n"
+ "jvm_memory_committed_bytes{area=\"nonheap\",id=\"Metaspace\"} 103374848 0\n"
+ "jvm_memory_committed_bytes{area=\"nonheap\",id=\"CodeHeap 'non-nmethods'\"} 4390912 0\n"
+ "jvm_memory_committed_bytes{area=\"heap\",id=\"G1 Eden Space\"} 373293056 0\n"
+ "jvm_memory_committed_bytes{area=\"nonheap\",id=\"Compressed Class Space\"} 13500416 0\n"
+ "jvm_memory_committed_bytes{area=\"nonheap\",id=\"CodeHeap 'non-profiled nmethods'\"} 4521984 0\n"
+ "# HELP process_files_open_files The open file descriptor count\n"
+ "# TYPE process_files_open_files gauge\n"
+ "process_files_open_files 290 0\n"
+ "# HELP kafka_consumer_sync_time_max_seconds The max time taken for a group sync.\n"
+ "# TYPE kafka_consumer_sync_time_max_seconds gauge\n"
+ "kafka_consumer_sync_time_max_seconds{client_id=\"consumer-1\"} nan 0\n"
+ "# HELP kafka_consumer_fetch_latency_avg_seconds The average time taken for a fetch request.\n"
+ "# TYPE kafka_consumer_fetch_latency_avg_seconds gauge\n"
+ "kafka_consumer_fetch_latency_avg_seconds{client_id=\"consumer-1\"} nan 0\n"
+ "# HELP rabbitmq_channels\n"
+ "# TYPE rabbitmq_channels gauge\n"
+ "rabbitmq_channels{name=\"rabbit\"} 0 0\n"
+ "# HELP kafka_consumer_sync_rate_syncs The number of group syncs per second. Group synchronization is the second and last phase of the rebalance protocol. A large value indicates group instability.\n"
+ "# TYPE kafka_consumer_sync_rate_syncs gauge\n"
+ "kafka_consumer_sync_rate_syncs{client_id=\"consumer-1\"} 0 0\n"
+ "# HELP jvm_classes_loaded_classes The number of classes that are currently loaded in the Java virtual machine\n"
+ "# TYPE jvm_classes_loaded_classes gauge\n"
+ "jvm_classes_loaded_classes 17220 0\n"
+ "# HELP jdbc_connections_min\n"
+ "# TYPE jdbc_connections_min gauge\n"
+ "jdbc_connections_min{name=\"dataSource\"} 10 0\n"
+ "# HELP kafka_consumer_fetch_throttle_time_avg_seconds The average throttle time. When quotas are enabled, the broker may delay fetch requests in order to throttle a consumer which has exceeded its limit. This metric indicates how throttling time has been added to fetch requests on average.\n"
+ "# TYPE kafka_consumer_fetch_throttle_time_avg_seconds gauge\n"
+ "kafka_consumer_fetch_throttle_time_avg_seconds{client_id=\"consumer-1\"} nan 0\n"
+ "# HELP tomcat_sessions_active_max_sessions\n"
+ "# TYPE tomcat_sessions_active_max_sessions gauge\n"
+ "tomcat_sessions_active_max_sessions 0 0\n"
+ "# HELP process_cpu_usage The \"recent cpu usage\" for the Java Virtual Machine process\n"
+ "# TYPE process_cpu_usage gauge\n"
+ "process_cpu_usage 0.00070793903055696016 0\n"
+ "# HELP jvm_buffer_total_capacity_bytes An estimate of the total capacity of the buffers in this pool\n"
+ "# TYPE jvm_buffer_total_capacity_bytes gauge\n"
+ "jvm_buffer_total_capacity_bytes{id=\"mapped\"} 0 0\n"
+ "jvm_buffer_total_capacity_bytes{id=\"direct\"} 81920 0\n"
+ "# HELP kafka_consumer_fetch_throttle_time_max_seconds The maximum throttle time.\n"
+ "# TYPE kafka_consumer_fetch_throttle_time_max_seconds gauge\n"
+ "kafka_consumer_fetch_throttle_time_max_seconds{client_id=\"consumer-1\"} nan 0\n"
+ "# HELP system_load_average_1m The sum of the number of runnable entities queued to available processors and the number of runnable entities running on the available processors averaged over a period of time\n"
+ "# TYPE system_load_average_1m gauge\n"
+ "system_load_average_1m 0.52000000000000002 0\n"
+ "# HELP kafka_consumer_join_time_avg_seconds The average time taken for a group rejoin. This value can get as high as the configured session timeout for the consumer, but should usually be lower.\n"
+ "# TYPE kafka_consumer_join_time_avg_seconds gauge\n"
+ "kafka_consumer_join_time_avg_seconds{client_id=\"consumer-1\"} nan 0\n"
+ "# HELP jdbc_connections_max\n"
+ "# TYPE jdbc_connections_max gauge\n"
+ "jdbc_connections_max{name=\"dataSource\"} 10 0\n"
+ "# HELP kafka_consumer_assigned_partitions The number of partitions currently assigned to this consumer.\n"
+ "# TYPE kafka_consumer_assigned_partitions gauge\n"
+ "kafka_consumer_assigned_partitions{client_id=\"consumer-1\"} 0 0\n"
+ "# HELP kafka_consumer_heartbeat_response_time_max_seconds The max time taken to receive a response to a heartbeat request.\n"
+ "# TYPE kafka_consumer_heartbeat_response_time_max_seconds gauge\n"
+ "kafka_consumer_heartbeat_response_time_max_seconds{client_id=\"consumer-1\"} nan 0\n"
+ "# HELP jvm_threads_daemon_threads The current number of live daemon threads\n"
+ "# TYPE jvm_threads_daemon_threads gauge\n"
+ "jvm_threads_daemon_threads 20 0\n"
+ "# HELP system_cpu_count The number of processors available to the Java virtual machine\n"
+ "# TYPE system_cpu_count gauge\n"
+ "system_cpu_count 16 0\n"
+ "# HELP jvm_buffer_count_buffers An estimate of the number of buffers in the pool\n"
+ "# TYPE jvm_buffer_count_buffers gauge\n"
+ "jvm_buffer_count_buffers{id=\"mapped\"} 0 0\n"
+ "jvm_buffer_count_buffers{id=\"direct\"} 10 0\n"
+ "# HELP kafka_consumer_io_wait_time_avg_seconds The average length of time the I/O thread spent waiting for a socket to be ready for reads or writes.\n"
+ "# TYPE kafka_consumer_io_wait_time_avg_seconds gauge\n"
+ "kafka_consumer_io_wait_time_avg_seconds{client_id=\"consumer-1\"} 0.047184790159065626 0\n"
+ "# HELP jvm_memory_max_bytes The maximum amount of memory in bytes that can be used for memory management\n"
+ "# TYPE jvm_memory_max_bytes gauge\n"
+ "jvm_memory_max_bytes{area=\"nonheap\",id=\"CodeHeap 'profiled nmethods'\"} 122028032 0\n"
+ "jvm_memory_max_bytes{area=\"heap\",id=\"G1 Survivor Space\"} -1 0\n"
+ "jvm_memory_max_bytes{area=\"heap\",id=\"G1 Old Gen\"} 8331984896 0\n"
+ "jvm_memory_max_bytes{area=\"nonheap\",id=\"Metaspace\"} -1 0\n"
+ "jvm_memory_max_bytes{area=\"nonheap\",id=\"CodeHeap 'non-nmethods'\"} 7598080 0\n"
+ "jvm_memory_max_bytes{area=\"heap\",id=\"G1 Eden Space\"} -1 0\n"
+ "jvm_memory_max_bytes{area=\"nonheap\",id=\"Compressed Class Space\"} 1073741824 0\n"
+ "jvm_memory_max_bytes{area=\"nonheap\",id=\"CodeHeap 'non-profiled nmethods'\"} 122032128 0\n"
+ "# HELP jvm_gc_pause_seconds_max Time spent in GC pause\n"
+ "# TYPE jvm_gc_pause_seconds_max gauge\n"
+ "jvm_gc_pause_seconds_max{action=\"end of minor GC\",cause=\"Metadata GC Threshold\"} 0.02 0\n"
+ "jvm_gc_pause_seconds_max{action=\"end of minor GC\",cause=\"G1 Evacuation Pause\"} 0 0\n"
+ "# HELP kafka_consumer_connection_count_connections The current number of active connections.\n"
+ "# TYPE kafka_consumer_connection_count_connections gauge\n"
+ "kafka_consumer_connection_count_connections{client_id=\"consumer-1\"} 0 0\n"
+ "# HELP jdbc_connections_active\n"
+ "# TYPE jdbc_connections_active gauge\n"
+ "jdbc_connections_active{name=\"dataSource\"} 0 0\n"
+
+ "# HELP spring_kafka_listener_seconds Kafka Listener Timer\n"
+ "# TYPE spring_kafka_listener_seconds summary\n"
+ "spring_kafka_listener_seconds_sum{exception=\"ListenerExecutionFailedException\",name=\"org.springframework.kafka.KafkaListenerEndpointContainer#0-0\",result=\"failure\"} 0 0\n"
+ "spring_kafka_listener_seconds_count{exception=\"ListenerExecutionFailedException\",name=\"org.springframework.kafka.KafkaListenerEndpointContainer#0-0\",result=\"failure\"} 0 0\n"
+ "spring_kafka_listener_seconds_sum{exception=\"none\",name=\"org.springframework.kafka.KafkaListenerEndpointContainer#0-0\",result=\"success\"} 0 0\n"
+ "spring_kafka_listener_seconds_count{exception=\"none\",name=\"org.springframework.kafka.KafkaListenerEndpointContainer#0-0\",result=\"success\"} 0 0\n"
+ "# HELP hikaricp_connections_usage_seconds Connection usage time\n"
+ "# TYPE hikaricp_connections_usage_seconds summary\n"
+ "hikaricp_connections_usage_seconds_sum{pool=\"mcadb\"} 0 0\n"
+ "hikaricp_connections_usage_seconds_count{pool=\"mcadb\"} 0 0\n"
+ "# HELP jvm_gc_pause_seconds Time spent in GC pause\n"
+ "# TYPE jvm_gc_pause_seconds summary\n"
+ "jvm_gc_pause_seconds_sum{action=\"end of minor GC\",cause=\"Metadata GC Threshold\"} 0.031 0\n"
+ "jvm_gc_pause_seconds_count{action=\"end of minor GC\",cause=\"Metadata GC Threshold\"} 2 0\n"
+ "jvm_gc_pause_seconds_sum{action=\"end of minor GC\",cause=\"G1 Evacuation Pause\"} 0.016 0\n"
+ "jvm_gc_pause_seconds_count{action=\"end of minor GC\",cause=\"G1 Evacuation Pause\"} 1 0\n"
+ ;
+
+ status = cmt_decode_prometheus_create(&cmt, in_buf, in_size, &opts);
+ TEST_CHECK(status == 0);
+ if (status) {
+ fprintf(stderr, "PARSE ERROR:\n======\n%s\n======\n", errbuf);
+ }
+ else {
+ result = cmt_encode_prometheus_create(cmt, CMT_TRUE);
+ status = strcmp(result, expected);
+ TEST_CHECK(status == 0);
+ if (status) {
+ fprintf(stderr, "EXPECTED:\n======\n%s\n======\nRESULT:\n======\n%s\n======\n", expected, result);
+ }
+ }
+
+ cfl_sds_destroy(in_buf);
+ cfl_sds_destroy(result);
+ cmt_decode_prometheus_destroy(cmt);
+}
+
+void test_empty_metrics()
+{
+ int status;
+ cfl_sds_t result;
+ struct cmt *cmt;
+ struct cmt_decode_prometheus_parse_opts opts;
+ memset(&opts, 0, sizeof(opts));
+ const char in_buf[] =
+ "# HELP kube_cronjob_annotations Kubernetes annotations converted to Prometheus labels.\n"
+ "# TYPE kube_cronjob_annotations gauge\n"
+ "# HELP kube_cronjob_labels Kubernetes labels converted to Prometheus labels.\n"
+ "# TYPE kube_cronjob_labels gauge\n"
+ "# HELP kube_cronjob_info Info about cronjob.\n"
+ "# TYPE kube_cronjob_info gauge\n"
+ "# HELP kube_cronjob_created Unix creation timestamp\n"
+ "# TYPE kube_cronjob_created gauge\n"
+ "# HELP kube_cronjob_status_active Active holds pointers to currently running jobs.\n"
+ "# TYPE kube_cronjob_status_active gauge\n"
+ "# HELP kube_cronjob_status_last_schedule_time LastScheduleTime keeps information of when was the last time the job was successfully scheduled.\n"
+ "# TYPE kube_cronjob_status_last_schedule_time gauge\n"
+ "# HELP kube_cronjob_status_last_successful_time LastSuccessfulTime keeps information of when was the last time the job was completed successfully.\n"
+ "# TYPE kube_cronjob_status_last_successful_time gauge\n"
+ "# HELP kube_cronjob_spec_suspend Suspend flag tells the controller to suspend subsequent executions.\n"
+ "# TYPE kube_cronjob_spec_suspend gauge\n"
+ "# HELP kube_cronjob_spec_starting_deadline_seconds Deadline in seconds for starting the job if it misses scheduled time for any reason.\n"
+ "# TYPE kube_cronjob_spec_starting_deadline_seconds gauge\n"
+ "# HELP kube_cronjob_next_schedule_time Next time the cronjob should be scheduled. The time after lastScheduleTime, or after the cron job's creation time if it's never been scheduled. Use this to determine if the job is delayed.\n"
+ "# TYPE kube_cronjob_next_schedule_time gauge\n"
+ "# HELP kube_cronjob_metadata_resource_version Resource version representing a specific version of the cronjob.\n"
+ "# TYPE kube_cronjob_metadata_resource_version gauge\n"
+ "# HELP kube_cronjob_spec_successful_job_history_limit Successful job history limit tells the controller how many completed jobs should be preserved.\n"
+ "# TYPE kube_cronjob_spec_successful_job_history_limit gauge\n"
+ "# HELP kube_cronjob_spec_failed_job_history_limit Failed job history limit tells the controller how many failed jobs should be preserved.\n"
+ "# TYPE kube_cronjob_spec_failed_job_history_limit gauge\n"
+ ;
+
+ const char expected[] = "";
+
+ cmt_initialize();
+ status = cmt_decode_prometheus_create(&cmt, in_buf, 0, &opts);
+ TEST_CHECK(status == 0);
+ result = cmt_encode_prometheus_create(cmt, CMT_TRUE);
+ TEST_CHECK(strcmp(result, expected) == 0);
+ cfl_sds_destroy(result);
+ cmt_decode_prometheus_destroy(cmt);
+}
+
+// reproduces https://github.com/fluent/fluent-bit/issues/5894
+void test_issue_fluent_bit_6021()
+{
+ char errbuf[256];
+ int status;
+ cfl_sds_t result = NULL;
+ struct cmt *cmt;
+ struct cmt_decode_prometheus_parse_opts opts;
+ memset(&opts, 0, sizeof(opts));
+ opts.errbuf = errbuf;
+ opts.errbuf_size = sizeof(errbuf);
+ cfl_sds_t in_buf = read_file(CMT_TESTS_DATA_PATH "/issue_fluent_bit_6021.txt");
+ size_t in_size = cfl_sds_len(in_buf);
+
+ const char expected[] =
+ "# HELP envoy_cluster_manager_cds_init_fetch_timeout\n"
+ "# TYPE envoy_cluster_manager_cds_init_fetch_timeout counter\n"
+ "envoy_cluster_manager_cds_init_fetch_timeout 0 0\n"
+ "# HELP envoy_cluster_manager_cds_update_attempt\n"
+ "# TYPE envoy_cluster_manager_cds_update_attempt counter\n"
+ "envoy_cluster_manager_cds_update_attempt 1 0\n"
+ "# HELP envoy_cluster_manager_cds_update_failure\n"
+ "# TYPE envoy_cluster_manager_cds_update_failure counter\n"
+ "envoy_cluster_manager_cds_update_failure 0 0\n"
+ "# HELP envoy_http_downstream_cx_length_ms\n"
+ "# TYPE envoy_http_downstream_cx_length_ms histogram\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"0.5\",envoy_http_conn_manager_prefix=\"admin\"} 0 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"1.0\",envoy_http_conn_manager_prefix=\"admin\"} 0 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"5.0\",envoy_http_conn_manager_prefix=\"admin\"} 0 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"10.0\",envoy_http_conn_manager_prefix=\"admin\"} 0 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"25.0\",envoy_http_conn_manager_prefix=\"admin\"} 1 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"50.0\",envoy_http_conn_manager_prefix=\"admin\"} 1 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"100.0\",envoy_http_conn_manager_prefix=\"admin\"} 1 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"250.0\",envoy_http_conn_manager_prefix=\"admin\"} 1 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"500.0\",envoy_http_conn_manager_prefix=\"admin\"} 1 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"1000.0\",envoy_http_conn_manager_prefix=\"admin\"} 1 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"2500.0\",envoy_http_conn_manager_prefix=\"admin\"} 1 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"5000.0\",envoy_http_conn_manager_prefix=\"admin\"} 1 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"10000.0\",envoy_http_conn_manager_prefix=\"admin\"} 1 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"30000.0\",envoy_http_conn_manager_prefix=\"admin\"} 1 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"60000.0\",envoy_http_conn_manager_prefix=\"admin\"} 1 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"300000.0\",envoy_http_conn_manager_prefix=\"admin\"} 1 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"600000.0\",envoy_http_conn_manager_prefix=\"admin\"} 1 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"1.8e+06\",envoy_http_conn_manager_prefix=\"admin\"} 1 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"3.6e+06\",envoy_http_conn_manager_prefix=\"admin\"} 1 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"+Inf\",envoy_http_conn_manager_prefix=\"admin\"} 1 0\n"
+ "envoy_http_downstream_cx_length_ms_sum{envoy_http_conn_manager_prefix=\"admin\"} 15.5 0\n"
+ "envoy_http_downstream_cx_length_ms_count{envoy_http_conn_manager_prefix=\"admin\"} 1 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"0.5\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"1.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"5.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"10.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"25.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"50.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"100.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"250.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"500.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"1000.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"2500.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"5000.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"10000.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"30000.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"60000.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"300000.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"600000.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"1.8e+06\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"3.6e+06\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_cx_length_ms_bucket{le=\"+Inf\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_cx_length_ms_sum{envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_cx_length_ms_count{envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "# HELP envoy_http_downstream_rq_time\n"
+ "# TYPE envoy_http_downstream_rq_time histogram\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"0.5\",envoy_http_conn_manager_prefix=\"admin\"} 0 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"1.0\",envoy_http_conn_manager_prefix=\"admin\"} 0 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"5.0\",envoy_http_conn_manager_prefix=\"admin\"} 10 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"10.0\",envoy_http_conn_manager_prefix=\"admin\"} 10 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"25.0\",envoy_http_conn_manager_prefix=\"admin\"} 10 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"50.0\",envoy_http_conn_manager_prefix=\"admin\"} 10 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"100.0\",envoy_http_conn_manager_prefix=\"admin\"} 10 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"250.0\",envoy_http_conn_manager_prefix=\"admin\"} 10 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"500.0\",envoy_http_conn_manager_prefix=\"admin\"} 10 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"1000.0\",envoy_http_conn_manager_prefix=\"admin\"} 10 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"2500.0\",envoy_http_conn_manager_prefix=\"admin\"} 10 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"5000.0\",envoy_http_conn_manager_prefix=\"admin\"} 10 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"10000.0\",envoy_http_conn_manager_prefix=\"admin\"} 10 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"30000.0\",envoy_http_conn_manager_prefix=\"admin\"} 10 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"60000.0\",envoy_http_conn_manager_prefix=\"admin\"} 10 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"300000.0\",envoy_http_conn_manager_prefix=\"admin\"} 10 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"600000.0\",envoy_http_conn_manager_prefix=\"admin\"} 10 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"1.8e+06\",envoy_http_conn_manager_prefix=\"admin\"} 10 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"3.6e+06\",envoy_http_conn_manager_prefix=\"admin\"} 10 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"+Inf\",envoy_http_conn_manager_prefix=\"admin\"} 10 0\n"
+ "envoy_http_downstream_rq_time_sum{envoy_http_conn_manager_prefix=\"admin\"} 25.5 0\n"
+ "envoy_http_downstream_rq_time_count{envoy_http_conn_manager_prefix=\"admin\"} 10 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"0.5\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"1.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"5.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"10.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"25.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"50.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"100.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"250.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"500.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"1000.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"2500.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"5000.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"10000.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"30000.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"60000.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"300000.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"600000.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"1.8e+06\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"3.6e+06\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_rq_time_bucket{le=\"+Inf\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_rq_time_sum{envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "envoy_http_downstream_rq_time_count{envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n"
+ "# HELP envoy_listener_admin_downstream_cx_length_ms\n"
+ "# TYPE envoy_listener_admin_downstream_cx_length_ms histogram\n"
+ "envoy_listener_admin_downstream_cx_length_ms_bucket{le=\"0.5\"} 0 0\n"
+ "envoy_listener_admin_downstream_cx_length_ms_bucket{le=\"1.0\"} 0 0\n"
+ "envoy_listener_admin_downstream_cx_length_ms_bucket{le=\"5.0\"} 0 0\n"
+ "envoy_listener_admin_downstream_cx_length_ms_bucket{le=\"10.0\"} 0 0\n"
+ "envoy_listener_admin_downstream_cx_length_ms_bucket{le=\"25.0\"} 1 0\n"
+ "envoy_listener_admin_downstream_cx_length_ms_bucket{le=\"50.0\"} 1 0\n"
+ "envoy_listener_admin_downstream_cx_length_ms_bucket{le=\"100.0\"} 1 0\n"
+ "envoy_listener_admin_downstream_cx_length_ms_bucket{le=\"250.0\"} 1 0\n"
+ "envoy_listener_admin_downstream_cx_length_ms_bucket{le=\"500.0\"} 1 0\n"
+ "envoy_listener_admin_downstream_cx_length_ms_bucket{le=\"1000.0\"} 1 0\n"
+ "envoy_listener_admin_downstream_cx_length_ms_bucket{le=\"2500.0\"} 1 0\n"
+ "envoy_listener_admin_downstream_cx_length_ms_bucket{le=\"5000.0\"} 1 0\n"
+ "envoy_listener_admin_downstream_cx_length_ms_bucket{le=\"10000.0\"} 1 0\n"
+ "envoy_listener_admin_downstream_cx_length_ms_bucket{le=\"30000.0\"} 1 0\n"
+ "envoy_listener_admin_downstream_cx_length_ms_bucket{le=\"60000.0\"} 1 0\n"
+ "envoy_listener_admin_downstream_cx_length_ms_bucket{le=\"300000.0\"} 1 0\n"
+ "envoy_listener_admin_downstream_cx_length_ms_bucket{le=\"600000.0\"} 1 0\n"
+ "envoy_listener_admin_downstream_cx_length_ms_bucket{le=\"1.8e+06\"} 1 0\n"
+ "envoy_listener_admin_downstream_cx_length_ms_bucket{le=\"3.6e+06\"} 1 0\n"
+ "envoy_listener_admin_downstream_cx_length_ms_bucket{le=\"+Inf\"} 1 0\n"
+ "envoy_listener_admin_downstream_cx_length_ms_sum 15.5 0\n"
+ "envoy_listener_admin_downstream_cx_length_ms_count 1 0\n"
+ "# HELP envoy_listener_downstream_cx_length_ms\n"
+ "# TYPE envoy_listener_downstream_cx_length_ms histogram\n"
+ "envoy_listener_downstream_cx_length_ms_bucket{le=\"0.5\",envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n"
+ "envoy_listener_downstream_cx_length_ms_bucket{le=\"1.0\",envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n"
+ "envoy_listener_downstream_cx_length_ms_bucket{le=\"5.0\",envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n"
+ "envoy_listener_downstream_cx_length_ms_bucket{le=\"10.0\",envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n"
+ "envoy_listener_downstream_cx_length_ms_bucket{le=\"25.0\",envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n"
+ "envoy_listener_downstream_cx_length_ms_bucket{le=\"50.0\",envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n"
+ "envoy_listener_downstream_cx_length_ms_bucket{le=\"100.0\",envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n"
+ "envoy_listener_downstream_cx_length_ms_bucket{le=\"250.0\",envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n"
+ "envoy_listener_downstream_cx_length_ms_bucket{le=\"500.0\",envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n"
+ "envoy_listener_downstream_cx_length_ms_bucket{le=\"1000.0\",envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n"
+ "envoy_listener_downstream_cx_length_ms_bucket{le=\"2500.0\",envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n"
+ "envoy_listener_downstream_cx_length_ms_bucket{le=\"5000.0\",envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n"
+ "envoy_listener_downstream_cx_length_ms_bucket{le=\"10000.0\",envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n"
+ "envoy_listener_downstream_cx_length_ms_bucket{le=\"30000.0\",envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n"
+ "envoy_listener_downstream_cx_length_ms_bucket{le=\"60000.0\",envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n"
+ "envoy_listener_downstream_cx_length_ms_bucket{le=\"300000.0\",envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n"
+ "envoy_listener_downstream_cx_length_ms_bucket{le=\"600000.0\",envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n"
+ "envoy_listener_downstream_cx_length_ms_bucket{le=\"1.8e+06\",envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n"
+ "envoy_listener_downstream_cx_length_ms_bucket{le=\"3.6e+06\",envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n"
+ "envoy_listener_downstream_cx_length_ms_bucket{le=\"+Inf\",envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n"
+ "envoy_listener_downstream_cx_length_ms_sum{envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n"
+ "envoy_listener_downstream_cx_length_ms_count{envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n"
+ "# HELP envoy_listener_manager_lds_update_duration\n"
+ "# TYPE envoy_listener_manager_lds_update_duration histogram\n"
+ "envoy_listener_manager_lds_update_duration_bucket{le=\"0.5\"} 0 0\n"
+ "envoy_listener_manager_lds_update_duration_bucket{le=\"1.0\"} 0 0\n"
+ "envoy_listener_manager_lds_update_duration_bucket{le=\"5.0\"} 0 0\n"
+ "envoy_listener_manager_lds_update_duration_bucket{le=\"10.0\"} 0 0\n"
+ "envoy_listener_manager_lds_update_duration_bucket{le=\"25.0\"} 0 0\n"
+ "envoy_listener_manager_lds_update_duration_bucket{le=\"50.0\"} 0 0\n"
+ "envoy_listener_manager_lds_update_duration_bucket{le=\"100.0\"} 0 0\n"
+ "envoy_listener_manager_lds_update_duration_bucket{le=\"250.0\"} 0 0\n"
+ "envoy_listener_manager_lds_update_duration_bucket{le=\"500.0\"} 0 0\n"
+ "envoy_listener_manager_lds_update_duration_bucket{le=\"1000.0\"} 0 0\n"
+ "envoy_listener_manager_lds_update_duration_bucket{le=\"2500.0\"} 0 0\n"
+ "envoy_listener_manager_lds_update_duration_bucket{le=\"5000.0\"} 0 0\n"
+ "envoy_listener_manager_lds_update_duration_bucket{le=\"10000.0\"} 0 0\n"
+ "envoy_listener_manager_lds_update_duration_bucket{le=\"30000.0\"} 0 0\n"
+ "envoy_listener_manager_lds_update_duration_bucket{le=\"60000.0\"} 0 0\n"
+ "envoy_listener_manager_lds_update_duration_bucket{le=\"300000.0\"} 0 0\n"
+ "envoy_listener_manager_lds_update_duration_bucket{le=\"600000.0\"} 0 0\n"
+ "envoy_listener_manager_lds_update_duration_bucket{le=\"1.8e+06\"} 0 0\n"
+ "envoy_listener_manager_lds_update_duration_bucket{le=\"3.6e+06\"} 0 0\n"
+ "envoy_listener_manager_lds_update_duration_bucket{le=\"+Inf\"} 0 0\n"
+ "envoy_listener_manager_lds_update_duration_sum 0 0\n"
+ "envoy_listener_manager_lds_update_duration_count 0 0\n"
+ "# HELP envoy_sds_tls_sds_update_duration\n"
+ "# TYPE envoy_sds_tls_sds_update_duration histogram\n"
+ "envoy_sds_tls_sds_update_duration_bucket{le=\"0.5\"} 0 0\n"
+ "envoy_sds_tls_sds_update_duration_bucket{le=\"1.0\"} 0 0\n"
+ "envoy_sds_tls_sds_update_duration_bucket{le=\"5.0\"} 0 0\n"
+ "envoy_sds_tls_sds_update_duration_bucket{le=\"10.0\"} 0 0\n"
+ "envoy_sds_tls_sds_update_duration_bucket{le=\"25.0\"} 0 0\n"
+ "envoy_sds_tls_sds_update_duration_bucket{le=\"50.0\"} 0 0\n"
+ "envoy_sds_tls_sds_update_duration_bucket{le=\"100.0\"} 0 0\n"
+ "envoy_sds_tls_sds_update_duration_bucket{le=\"250.0\"} 0 0\n"
+ "envoy_sds_tls_sds_update_duration_bucket{le=\"500.0\"} 0 0\n"
+ "envoy_sds_tls_sds_update_duration_bucket{le=\"1000.0\"} 0 0\n"
+ "envoy_sds_tls_sds_update_duration_bucket{le=\"2500.0\"} 0 0\n"
+ "envoy_sds_tls_sds_update_duration_bucket{le=\"5000.0\"} 0 0\n"
+ "envoy_sds_tls_sds_update_duration_bucket{le=\"10000.0\"} 0 0\n"
+ "envoy_sds_tls_sds_update_duration_bucket{le=\"30000.0\"} 0 0\n"
+ "envoy_sds_tls_sds_update_duration_bucket{le=\"60000.0\"} 0 0\n"
+ "envoy_sds_tls_sds_update_duration_bucket{le=\"300000.0\"} 0 0\n"
+ "envoy_sds_tls_sds_update_duration_bucket{le=\"600000.0\"} 0 0\n"
+ "envoy_sds_tls_sds_update_duration_bucket{le=\"1.8e+06\"} 0 0\n"
+ "envoy_sds_tls_sds_update_duration_bucket{le=\"3.6e+06\"} 0 0\n"
+ "envoy_sds_tls_sds_update_duration_bucket{le=\"+Inf\"} 0 0\n"
+ "envoy_sds_tls_sds_update_duration_sum 0 0\n"
+ "envoy_sds_tls_sds_update_duration_count 0 0\n"
+ "# HELP envoy_server_initialization_time_ms\n"
+ "# TYPE envoy_server_initialization_time_ms histogram\n"
+ "envoy_server_initialization_time_ms_bucket{le=\"0.5\"} 0 0\n"
+ "envoy_server_initialization_time_ms_bucket{le=\"1.0\"} 0 0\n"
+ "envoy_server_initialization_time_ms_bucket{le=\"5.0\"} 0 0\n"
+ "envoy_server_initialization_time_ms_bucket{le=\"10.0\"} 0 0\n"
+ "envoy_server_initialization_time_ms_bucket{le=\"25.0\"} 0 0\n"
+ "envoy_server_initialization_time_ms_bucket{le=\"50.0\"} 1 0\n"
+ "envoy_server_initialization_time_ms_bucket{le=\"100.0\"} 1 0\n"
+ "envoy_server_initialization_time_ms_bucket{le=\"250.0\"} 1 0\n"
+ "envoy_server_initialization_time_ms_bucket{le=\"500.0\"} 1 0\n"
+ "envoy_server_initialization_time_ms_bucket{le=\"1000.0\"} 1 0\n"
+ "envoy_server_initialization_time_ms_bucket{le=\"2500.0\"} 1 0\n"
+ "envoy_server_initialization_time_ms_bucket{le=\"5000.0\"} 1 0\n"
+ "envoy_server_initialization_time_ms_bucket{le=\"10000.0\"} 1 0\n"
+ "envoy_server_initialization_time_ms_bucket{le=\"30000.0\"} 1 0\n"
+ "envoy_server_initialization_time_ms_bucket{le=\"60000.0\"} 1 0\n"
+ "envoy_server_initialization_time_ms_bucket{le=\"300000.0\"} 1 0\n"
+ "envoy_server_initialization_time_ms_bucket{le=\"600000.0\"} 1 0\n"
+ "envoy_server_initialization_time_ms_bucket{le=\"1.8e+06\"} 1 0\n"
+ "envoy_server_initialization_time_ms_bucket{le=\"3.6e+06\"} 1 0\n"
+ "envoy_server_initialization_time_ms_bucket{le=\"+Inf\"} 1 0\n"
+ "envoy_server_initialization_time_ms_sum 30.5 0\n"
+ "envoy_server_initialization_time_ms_count 1 0\n"
+ ;
+
+ status = cmt_decode_prometheus_create(&cmt, in_buf, in_size, &opts);
+ TEST_CHECK(status == 0);
+ if (status) {
+ fprintf(stderr, "PARSE ERROR:\n======\n%s\n======\n", errbuf);
+ }
+ else {
+ result = cmt_encode_prometheus_create(cmt, CMT_TRUE);
+ status = strcmp(result, expected);
+ TEST_CHECK(status == 0);
+ if (status) {
+ fprintf(stderr, "EXPECTED:\n======\n%s\n======\nRESULT:\n======\n%s\n======\n", expected, result);
+ }
+ }
+
+ cfl_sds_destroy(in_buf);
+ cfl_sds_destroy(result);
+ cmt_decode_prometheus_destroy(cmt);
+}
+
+void test_override_timestamp()
+{
+ int status;
+ cfl_sds_t result = NULL;
+ struct cmt *cmt;
+ struct cmt_decode_prometheus_parse_opts opts;
+ memset(&opts, 0, sizeof(opts));
+ opts.override_timestamp = 123;
+ const char in_buf[] =
+ "# HELP hikaricp_connections_timeout_total Connection timeout total count\n"
+ "# TYPE hikaricp_connections_timeout_total counter\n"
+ "hikaricp_connections_timeout_total{pool=\"mcadb\"} 0 0\n"
+ "# HELP rabbitmq_consumed_total\n"
+ "# TYPE rabbitmq_consumed_total counter\n"
+ "rabbitmq_consumed_total{name=\"rabbit\"} 0 0\n"
+ "# HELP rabbitmq_failed_to_publish_total\n"
+ "# TYPE rabbitmq_failed_to_publish_total counter\n"
+ "rabbitmq_failed_to_publish_total{name=\"rabbit\"} 0 0\n"
+ "# HELP rabbitmq_acknowledged_published_total\n"
+ "# TYPE rabbitmq_acknowledged_published_total counter\n"
+ "rabbitmq_acknowledged_published_total{name=\"rabbit\"} 0 0\n"
+ "# HELP tomcat_sessions_rejected_sessions_total\n"
+ "# TYPE tomcat_sessions_rejected_sessions_total counter\n"
+ "tomcat_sessions_rejected_sessions_total 0 0\n"
+ "# A histogram, which has a pretty complex representation in the text format:\n"
+ "# HELP http_request_duration_seconds_bucket A histogram of the request duration.\n"
+ "# TYPE http_request_duration_seconds_bucket counter\n"
+ "http_request_duration_seconds_bucket{le=\"0.05\"} 24054\n"
+ "http_request_duration_seconds_bucket{le=\"0.1\"} 33444\n"
+ "http_request_duration_seconds_bucket{le=\"0.2\"} 100392\n"
+ "http_request_duration_seconds_bucket{le=\"0.5\"} 129389\n"
+ "http_request_duration_seconds_bucket{le=\"1\"} 133988\n"
+ "http_request_duration_seconds_bucket{le=\"+Inf\"} 144320\n"
+ "http_request_duration_seconds_sum 53423\n"
+ "http_request_duration_seconds_count 144320\n"
+ ;
+
+ const char expected[] =
+ "# HELP hikaricp_connections_timeout_total Connection timeout total count\n"
+ "# TYPE hikaricp_connections_timeout_total counter\n"
+ "hikaricp_connections_timeout_total{pool=\"mcadb\"} 0 123\n"
+ "# HELP http_request_duration_seconds_bucket A histogram of the request duration.\n"
+ "# TYPE http_request_duration_seconds_bucket counter\n"
+ "http_request_duration_seconds_bucket{le=\"0.05\"} 24054 123\n"
+ "http_request_duration_seconds_bucket{le=\"0.1\"} 33444 123\n"
+ "http_request_duration_seconds_bucket{le=\"0.2\"} 100392 123\n"
+ "http_request_duration_seconds_bucket{le=\"0.5\"} 129389 123\n"
+ "http_request_duration_seconds_bucket{le=\"1\"} 133988 123\n"
+ "http_request_duration_seconds_bucket{le=\"+Inf\"} 144320 123\n"
+ "# HELP rabbitmq_consumed_total\n"
+ "# TYPE rabbitmq_consumed_total untyped\n"
+ "rabbitmq_consumed_total{name=\"rabbit\"} 0 123\n"
+ "# HELP rabbitmq_failed_to_publish_total\n"
+ "# TYPE rabbitmq_failed_to_publish_total untyped\n"
+ "rabbitmq_failed_to_publish_total{name=\"rabbit\"} 0 123\n"
+ "# HELP rabbitmq_acknowledged_published_total\n"
+ "# TYPE rabbitmq_acknowledged_published_total untyped\n"
+ "rabbitmq_acknowledged_published_total{name=\"rabbit\"} 0 123\n"
+ "# HELP tomcat_sessions_rejected_sessions_total\n"
+ "# TYPE tomcat_sessions_rejected_sessions_total untyped\n"
+ "tomcat_sessions_rejected_sessions_total 0 123\n"
+ "# HELP http_request_duration_seconds_sum\n"
+ "# TYPE http_request_duration_seconds_sum untyped\n"
+ "http_request_duration_seconds_sum 53423 123\n"
+ "# HELP http_request_duration_seconds_count\n"
+ "# TYPE http_request_duration_seconds_count untyped\n"
+ "http_request_duration_seconds_count 144320 123\n"
+ ;
+
+ cmt_initialize();
+ status = cmt_decode_prometheus_create(&cmt, in_buf, 0, &opts);
+ TEST_CHECK(status == 0);
+ if (!status) {
+ result = cmt_encode_prometheus_create(cmt, CMT_TRUE);
+ status = strcmp(result, expected);
+ TEST_CHECK(status == 0);
+ if (status) {
+ fprintf(stderr, "EXPECTED:\n======\n%s\n======\nRESULT:\n======\n%s\n======\n", expected, result);
+ }
+ }
+ cfl_sds_destroy(result);
+ cmt_decode_prometheus_destroy(cmt);
+}
+
+// testing bug uncovered by https://github.com/fluent/cmetrics/pull/168
+void test_pr_168()
+{
+ char errbuf[256];
+ int status;
+ struct cmt *cmt;
+ cfl_sds_t result = NULL;
+ struct cmt_decode_prometheus_parse_opts opts;
+ memset(&opts, 0, sizeof(opts));
+ opts.errbuf = errbuf;
+ opts.errbuf_size = sizeof(errbuf);
+ cfl_sds_t in_buf = read_file(CMT_TESTS_DATA_PATH "/pr_168.txt");
+ const char expected[] =
+ "# HELP prometheus_engine_query_duration_seconds Query timings\n"
+ "# TYPE prometheus_engine_query_duration_seconds summary\n"
+ "prometheus_engine_query_duration_seconds{quantile=\"0.5\",slice=\"inner_eval\"} nan 0\n"
+ "prometheus_engine_query_duration_seconds{quantile=\"0.9\",slice=\"inner_eval\"} nan 0\n"
+ "prometheus_engine_query_duration_seconds{quantile=\"0.99\",slice=\"inner_eval\"} nan 0\n"
+ "prometheus_engine_query_duration_seconds_sum{slice=\"inner_eval\"} 0 0\n"
+ "prometheus_engine_query_duration_seconds_count{slice=\"inner_eval\"} 0 0\n"
+ "prometheus_engine_query_duration_seconds{quantile=\"0.5\",slice=\"prepare_time\"} 0 0\n"
+ "prometheus_engine_query_duration_seconds{quantile=\"0.9\",slice=\"prepare_time\"} 0 0\n"
+ "prometheus_engine_query_duration_seconds{quantile=\"0.99\",slice=\"prepare_time\"} 0 0\n"
+ "prometheus_engine_query_duration_seconds_sum{slice=\"prepare_time\"} 0 0\n"
+ "prometheus_engine_query_duration_seconds_count{slice=\"prepare_time\"} 0 0\n"
+ "prometheus_engine_query_duration_seconds{quantile=\"0.5\",slice=\"queue_time\"} 0 0\n"
+ "prometheus_engine_query_duration_seconds{quantile=\"0.9\",slice=\"queue_time\"} 0 0\n"
+ "prometheus_engine_query_duration_seconds{quantile=\"0.99\",slice=\"queue_time\"} 0 0\n"
+ "prometheus_engine_query_duration_seconds_sum{slice=\"queue_time\"} 0 0\n"
+ "prometheus_engine_query_duration_seconds_count{slice=\"queue_time\"} 0 0\n"
+ "prometheus_engine_query_duration_seconds{quantile=\"0.5\",slice=\"result_sort\"} 0 0\n"
+ "prometheus_engine_query_duration_seconds{quantile=\"0.9\",slice=\"result_sort\"} 0 0\n"
+ "prometheus_engine_query_duration_seconds{quantile=\"0.99\",slice=\"result_sort\"} 0 0\n"
+ "prometheus_engine_query_duration_seconds_sum{slice=\"result_sort\"} 0 0\n"
+ "prometheus_engine_query_duration_seconds_count{slice=\"result_sort\"} 0 0\n";
+
+ cmt_initialize();
+ status = cmt_decode_prometheus_create(&cmt, in_buf, cfl_sds_len(in_buf), &opts);
+ TEST_CHECK(status == 0);
+ if (!status) {
+ result = cmt_encode_prometheus_create(cmt, CMT_TRUE);
+ status = strcmp(result, expected);
+ TEST_CHECK(status == 0);
+ if (status) {
+ fprintf(stderr, "EXPECTED:\n======\n%s\n======\nRESULT:\n======\n%s\n======\n", expected, result);
+ }
+ cfl_sds_destroy(result);
+ }
+ cfl_sds_destroy(in_buf);
+ cmt_decode_prometheus_destroy(cmt);
+}
+
+void test_histogram_different_label_count()
+{
+ char errbuf[256];
+ int status;
+ struct cmt *cmt;
+ cfl_sds_t result = NULL;
+ struct cmt_decode_prometheus_parse_opts opts;
+ memset(&opts, 0, sizeof(opts));
+ opts.errbuf = errbuf;
+ opts.errbuf_size = sizeof(errbuf);
+ cfl_sds_t in_buf = read_file(CMT_TESTS_DATA_PATH "/histogram_different_label_count.txt");
+ const char expected[] =
+ "# HELP k8s_network_load Network load\n"
+ "# TYPE k8s_network_load histogram\n"
+ "k8s_network_load_bucket{le=\"0.05\"} 0 0\n"
+ "k8s_network_load_bucket{le=\"5.0\"} 1 0\n"
+ "k8s_network_load_bucket{le=\"10.0\"} 2 0\n"
+ "k8s_network_load_bucket{le=\"+Inf\"} 3 0\n"
+ "k8s_network_load_sum 1013 0\n"
+ "k8s_network_load_count 3 0\n"
+ "# HELP k8s_network_load Network load\n"
+ "# TYPE k8s_network_load histogram\n"
+ "k8s_network_load_bucket{le=\"0.05\",my_label=\"my_val\"} 0 0\n"
+ "k8s_network_load_bucket{le=\"5.0\",my_label=\"my_val\"} 1 0\n"
+ "k8s_network_load_bucket{le=\"10.0\",my_label=\"my_val\"} 2 0\n"
+ "k8s_network_load_bucket{le=\"+Inf\",my_label=\"my_val\"} 3 0\n"
+ "k8s_network_load_sum{my_label=\"my_val\"} 1013 0\n"
+ "k8s_network_load_count{my_label=\"my_val\"} 3 0\n"
+ ;
+
+ cmt_initialize();
+ status = cmt_decode_prometheus_create(&cmt, in_buf, cfl_sds_len(in_buf), &opts);
+ TEST_CHECK(status == 0);
+ if (!status) {
+ result = cmt_encode_prometheus_create(cmt, CMT_TRUE);
+ status = strcmp(result, expected);
+ TEST_CHECK(status == 0);
+ if (status) {
+ fprintf(stderr, "EXPECTED:\n======\n%s\n======\nRESULT:\n======\n%s\n======\n", expected, result);
+ }
+ cfl_sds_destroy(result);
+ }
+ cfl_sds_destroy(in_buf);
+ cmt_decode_prometheus_destroy(cmt);
+}
+
+// reproduces https://github.com/fluent/fluent-bit/issues/6534
+void test_issue_fluent_bit_6534()
+{
+ char errbuf[256];
+ int status;
+ cfl_sds_t result = NULL;
+ struct cmt *cmt;
+ struct cmt_decode_prometheus_parse_opts opts;
+ memset(&opts, 0, sizeof(opts));
+ opts.errbuf = errbuf;
+ opts.errbuf_size = sizeof(errbuf);
+ cfl_sds_t in_buf = read_file(CMT_TESTS_DATA_PATH "/issue_6534.txt");
+ size_t in_size = cfl_sds_len(in_buf);
+
+ const char expected[] =
+ "# HELP dotnet_jit_method_total Total number of methods compiled by the JIT compiler\n"
+ "# TYPE dotnet_jit_method_total counter\n"
+ "dotnet_jit_method_total 6476 0\n"
+ "# HELP dotnet_gc_collection_count_total Counts the number of garbage collections that have occurred, broken down by generation number and the reason for the collection.\n"
+ "# TYPE dotnet_gc_collection_count_total counter\n"
+ "dotnet_gc_collection_count_total{gc_generation=\"1\",gc_reason=\"alloc_small\"} 133 0\n"
+ "dotnet_gc_collection_count_total{gc_generation=\"0\",gc_reason=\"alloc_small\"} 618 0\n"
+ "dotnet_gc_collection_count_total{gc_generation=\"2\",gc_reason=\"alloc_small\"} 8 0\n"
+ "# HELP dotnet_collection_count_total GC collection count\n"
+ "# TYPE dotnet_collection_count_total counter\n"
+ "dotnet_collection_count_total{generation=\"0\"} 759 0\n"
+ "dotnet_collection_count_total{generation=\"2\"} 8 0\n"
+ "dotnet_collection_count_total{generation=\"1\"} 141 0\n"
+ "# HELP dotnet_threadpool_adjustments_total The total number of changes made to the size of the thread pool, labeled by the reason for change\n"
+ "# TYPE dotnet_threadpool_adjustments_total counter\n"
+ "dotnet_threadpool_adjustments_total{adjustment_reason=\"starvation\"} 957 0\n"
+ "dotnet_threadpool_adjustments_total{adjustment_reason=\"warmup\"} 4 0\n"
+ "dotnet_threadpool_adjustments_total{adjustment_reason=\"thread_timed_out\"} 2409 0\n"
+ "dotnet_threadpool_adjustments_total{adjustment_reason=\"climbing_move\"} 93458 0\n"
+ "# HELP process_cpu_seconds_total Total user and system CPU time spent in seconds.\n"
+ "# TYPE process_cpu_seconds_total counter\n"
+ "process_cpu_seconds_total 1450.8499999999999 0\n"
+ "# HELP dotnet_contention_total The number of locks contended\n"
+ "# TYPE dotnet_contention_total counter\n"
+ "dotnet_contention_total 6758 0\n"
+ "# HELP dotnet_contention_seconds_total The total amount of time spent contending locks\n"
+ "# TYPE dotnet_contention_seconds_total counter\n"
+ "dotnet_contention_seconds_total 1.0246322000000074 0\n"
+ "# HELP dotnet_internal_recycle_count prometheus-net.DotNetRuntime internal metric. Counts the number of times the underlying event listeners have been recycled\n"
+ "# TYPE dotnet_internal_recycle_count counter\n"
+ "dotnet_internal_recycle_count 3 0\n"
+ "# HELP dotnet_gc_allocated_bytes_total The total number of bytes allocated on the managed heap\n"
+ "# TYPE dotnet_gc_allocated_bytes_total counter\n"
+ "dotnet_gc_allocated_bytes_total{gc_heap=\"soh\"} 19853322336 0\n"
+ "# HELP dotnet_threadpool_throughput_total The total number of work items that have finished execution in the thread pool\n"
+ "# TYPE dotnet_threadpool_throughput_total counter\n"
+ "dotnet_threadpool_throughput_total 3381388 0\n"
+ "# HELP dotnet_exceptions_total Count of exceptions thrown, broken down by type\n"
+ "# TYPE dotnet_exceptions_total counter\n"
+ "dotnet_exceptions_total{type=\"System.Net.Http.HttpRequestException\"} 792 0\n"
+ "dotnet_exceptions_total{type=\"System.ObjectDisposedException\"} 11977 0\n"
+ "dotnet_exceptions_total{type=\"System.IO.DirectoryNotFoundException\"} 14 0\n"
+ "dotnet_exceptions_total{type=\"System.Net.Sockets.SocketException\"} 258287 0\n"
+ "dotnet_exceptions_total{type=\"Grpc.Core.RpcException\"} 72 0\n"
+ "# HELP dotnet_threadpool_num_threads The number of active threads in the thread pool\n"
+ "# TYPE dotnet_threadpool_num_threads gauge\n"
+ "dotnet_threadpool_num_threads 6 0\n"
+ "# HELP dotnet_gc_heap_size_bytes The current size of all heaps (only updated after a garbage collection)\n"
+ "# TYPE dotnet_gc_heap_size_bytes gauge\n"
+ "dotnet_gc_heap_size_bytes{gc_generation=\"0\"} 24 0\n"
+ "dotnet_gc_heap_size_bytes{gc_generation=\"loh\"} 550280 0\n"
+ "dotnet_gc_heap_size_bytes{gc_generation=\"2\"} 2625704 0\n"
+ "dotnet_gc_heap_size_bytes{gc_generation=\"1\"} 226944 0\n"
+ "# HELP dotnet_threadpool_timer_count The number of timers active\n"
+ "# TYPE dotnet_threadpool_timer_count gauge\n"
+ "dotnet_threadpool_timer_count 5 0\n"
+ "# HELP dotnet_gc_cpu_ratio The percentage of process CPU time spent running garbage collections\n"
+ "# TYPE dotnet_gc_cpu_ratio gauge\n"
+ "dotnet_gc_cpu_ratio 0 0\n"
+ "# HELP process_open_handles Number of open handles\n"
+ "# TYPE process_open_handles gauge\n"
+ "process_open_handles 264 0\n"
+ "# HELP dotnet_gc_pause_ratio The percentage of time the process spent paused for garbage collection\n"
+ "# TYPE dotnet_gc_pause_ratio gauge\n"
+ "dotnet_gc_pause_ratio 0 0\n"
+ "# HELP dotnet_jit_il_bytes Total bytes of IL compiled by the JIT compiler\n"
+ "# TYPE dotnet_jit_il_bytes gauge\n"
+ "dotnet_jit_il_bytes 487850 0\n"
+ "# HELP dotnet_gc_memory_total_available_bytes The upper limit on the amount of physical memory .NET can allocate to\n"
+ "# TYPE dotnet_gc_memory_total_available_bytes gauge\n"
+ "dotnet_gc_memory_total_available_bytes 805306368 0\n"
+ "# HELP dotnet_build_info Build information about prometheus-net.DotNetRuntime and the environment\n"
+ "# TYPE dotnet_build_info gauge\n"
+ "dotnet_build_info{version=\"4.2.4.0\",target_framework=\".NETCoreApp,Version=v6.0\",runtime_version=\".NET 6.0.11\",os_version=\"Linux 5.4.0-1094-azure #100~18.04.1-Ubuntu SMP Mon Oct 17 11:44:30 UTC 2022\",process_architecture=\"X64\",gc_mode=\"Workstation\"} 1 0\n"
+ "# HELP process_start_time_seconds Start time of the process since unix epoch in seconds.\n"
+ "# TYPE process_start_time_seconds gauge\n"
+ "process_start_time_seconds 1670526623.05 0\n"
+ "# HELP process_cpu_count The number of processor cores available to this process.\n"
+ "# TYPE process_cpu_count gauge\n"
+ "process_cpu_count 1 0\n"
+ "# HELP dotnet_gc_pinned_objects The number of pinned objects\n"
+ "# TYPE dotnet_gc_pinned_objects gauge\n"
+ "dotnet_gc_pinned_objects 0 0\n"
+ "# HELP dotnet_total_memory_bytes Total known allocated memory\n"
+ "# TYPE dotnet_total_memory_bytes gauge\n"
+ "dotnet_total_memory_bytes 20979896 0\n"
+ "# HELP process_virtual_memory_bytes Virtual memory size in bytes.\n"
+ "# TYPE process_virtual_memory_bytes gauge\n"
+ "process_virtual_memory_bytes 8562679808 0\n"
+ "# HELP process_working_set_bytes Process working set\n"
+ "# TYPE process_working_set_bytes gauge\n"
+ "process_working_set_bytes 135118848 0\n"
+ "# HELP process_num_threads Total number of threads\n"
+ "# TYPE process_num_threads gauge\n"
+ "process_num_threads 21 0\n"
+ "# HELP dotnet_gc_finalization_queue_length The number of objects waiting to be finalized\n"
+ "# TYPE dotnet_gc_finalization_queue_length gauge\n"
+ "dotnet_gc_finalization_queue_length 15 0\n"
+ "# HELP process_private_memory_bytes Process private memory size\n"
+ "# TYPE process_private_memory_bytes gauge\n"
+ "process_private_memory_bytes 247390208 0\n"
+ "# HELP dotnet_threadpool_queue_length Measures the queue length of the thread pool. Values greater than 0 indicate a backlog of work for the threadpool to process.\n"
+ "# TYPE dotnet_threadpool_queue_length histogram\n"
+ "dotnet_threadpool_queue_length_bucket{le=\"0.0\"} 321728 0\n"
+ "dotnet_threadpool_queue_length_bucket{le=\"1.0\"} 321733 0\n"
+ "dotnet_threadpool_queue_length_bucket{le=\"10.0\"} 321733 0\n"
+ "dotnet_threadpool_queue_length_bucket{le=\"100.0\"} 321733 0\n"
+ "dotnet_threadpool_queue_length_bucket{le=\"1000.0\"} 321733 0\n"
+ "dotnet_threadpool_queue_length_bucket{le=\"+Inf\"} 0 0\n"
+ "dotnet_threadpool_queue_length_sum 5 0\n"
+ "dotnet_threadpool_queue_length_count 321733 0\n"
+ "# HELP dotnet_gc_pause_seconds The amount of time execution was paused for garbage collection\n"
+ "# TYPE dotnet_gc_pause_seconds histogram\n"
+ "dotnet_gc_pause_seconds_bucket{le=\"0.001\"} 7 0\n"
+ "dotnet_gc_pause_seconds_bucket{le=\"0.01\"} 747 0\n"
+ "dotnet_gc_pause_seconds_bucket{le=\"0.05\"} 759 0\n"
+ "dotnet_gc_pause_seconds_bucket{le=\"0.1\"} 759 0\n"
+ "dotnet_gc_pause_seconds_bucket{le=\"0.5\"} 759 0\n"
+ "dotnet_gc_pause_seconds_bucket{le=\"1.0\"} 759 0\n"
+ "dotnet_gc_pause_seconds_bucket{le=\"10.0\"} 759 0\n"
+ "dotnet_gc_pause_seconds_bucket{le=\"+Inf\"} 0 0\n"
+ "dotnet_gc_pause_seconds_sum 1.3192573999999997 0\n"
+ "dotnet_gc_pause_seconds_count 759 0\n"
+ "# HELP dotnet_gc_collection_seconds The amount of time spent running garbage collections\n"
+ "# TYPE dotnet_gc_collection_seconds histogram\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"0.001\",gc_generation=\"1\",gc_type=\"non_concurrent_gc\"} 3 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"0.01\",gc_generation=\"1\",gc_type=\"non_concurrent_gc\"} 133 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"0.05\",gc_generation=\"1\",gc_type=\"non_concurrent_gc\"} 133 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"0.1\",gc_generation=\"1\",gc_type=\"non_concurrent_gc\"} 133 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"0.5\",gc_generation=\"1\",gc_type=\"non_concurrent_gc\"} 133 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"1.0\",gc_generation=\"1\",gc_type=\"non_concurrent_gc\"} 133 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"10.0\",gc_generation=\"1\",gc_type=\"non_concurrent_gc\"} 133 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"+Inf\",gc_generation=\"1\",gc_type=\"non_concurrent_gc\"} 0 0\n"
+ "dotnet_gc_collection_seconds_sum{gc_generation=\"1\",gc_type=\"non_concurrent_gc\"} 0.20421500000000006 0\n"
+ "dotnet_gc_collection_seconds_count{gc_generation=\"1\",gc_type=\"non_concurrent_gc\"} 133 0\n"
+ "# HELP dotnet_gc_collection_seconds The amount of time spent running garbage collections\n"
+ "# TYPE dotnet_gc_collection_seconds histogram\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"0.001\"} 0 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"0.01\"} 0 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"0.05\"} 0 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"0.1\"} 0 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"0.5\"} 0 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"1.0\"} 0 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"10.0\"} 0 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"+Inf\"} 0 0\n"
+ "dotnet_gc_collection_seconds_sum 0 0\n"
+ "dotnet_gc_collection_seconds_count 0 0\n"
+ "# HELP dotnet_gc_collection_seconds The amount of time spent running garbage collections\n"
+ "# TYPE dotnet_gc_collection_seconds histogram\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"0.001\",gc_generation=\"2\",gc_type=\"non_concurrent_gc\"} 0 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"0.01\",gc_generation=\"2\",gc_type=\"non_concurrent_gc\"} 0 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"0.05\",gc_generation=\"2\",gc_type=\"non_concurrent_gc\"} 8 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"0.1\",gc_generation=\"2\",gc_type=\"non_concurrent_gc\"} 8 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"0.5\",gc_generation=\"2\",gc_type=\"non_concurrent_gc\"} 8 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"1.0\",gc_generation=\"2\",gc_type=\"non_concurrent_gc\"} 8 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"10.0\",gc_generation=\"2\",gc_type=\"non_concurrent_gc\"} 8 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"+Inf\",gc_generation=\"2\",gc_type=\"non_concurrent_gc\"} 0 0\n"
+ "dotnet_gc_collection_seconds_sum{gc_generation=\"2\",gc_type=\"non_concurrent_gc\"} 0.093447800000000011 0\n"
+ "dotnet_gc_collection_seconds_count{gc_generation=\"2\",gc_type=\"non_concurrent_gc\"} 8 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"0.001\",gc_generation=\"0\",gc_type=\"non_concurrent_gc\"} 127 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"0.01\",gc_generation=\"0\",gc_type=\"non_concurrent_gc\"} 617 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"0.05\",gc_generation=\"0\",gc_type=\"non_concurrent_gc\"} 618 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"0.1\",gc_generation=\"0\",gc_type=\"non_concurrent_gc\"} 618 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"0.5\",gc_generation=\"0\",gc_type=\"non_concurrent_gc\"} 618 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"1.0\",gc_generation=\"0\",gc_type=\"non_concurrent_gc\"} 618 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"10.0\",gc_generation=\"0\",gc_type=\"non_concurrent_gc\"} 618 0\n"
+ "dotnet_gc_collection_seconds_bucket{le=\"+Inf\",gc_generation=\"0\",gc_type=\"non_concurrent_gc\"} 0 0\n"
+ "dotnet_gc_collection_seconds_sum{gc_generation=\"0\",gc_type=\"non_concurrent_gc\"} 0.85545190000000104 0\n"
+ "dotnet_gc_collection_seconds_count{gc_generation=\"0\",gc_type=\"non_concurrent_gc\"} 618 0\n"
+ ;
+
+ status = cmt_decode_prometheus_create(&cmt, in_buf, in_size, &opts);
+ TEST_CHECK(status == 0);
+ if (status) {
+ fprintf(stderr, "PARSE ERROR:\n======\n%s\n======\n", errbuf);
+ }
+ else {
+ result = cmt_encode_prometheus_create(cmt, CMT_TRUE);
+ status = strcmp(result, expected);
+ TEST_CHECK(status == 0);
+ if (status) {
+ fprintf(stderr, "EXPECTED:\n======\n%s\n======\nRESULT:\n======\n%s\n======\n", expected, result);
+ }
+ }
+
+ cfl_sds_destroy(in_buf);
+ cfl_sds_destroy(result);
+ cmt_decode_prometheus_destroy(cmt);
+}
+
+TEST_LIST = {
+ {"header_help", test_header_help},
+ {"header_type", test_header_type},
+ {"header_help_type", test_header_help_type},
+ {"header_type_help", test_header_type_help},
+ {"labels", test_labels},
+ {"labels_trailing_comma", test_labels_trailing_comma},
+ {"sample", test_sample},
+ {"samples", test_samples},
+ {"escape_sequences", test_escape_sequences},
+ {"metric_without_labels", test_metric_without_labels},
+ {"prometheus_spec_example", test_prometheus_spec_example},
+ {"bison_parsing_error", test_bison_parsing_error},
+ {"label_limits", test_label_limits},
+ {"invalid_value", test_invalid_value},
+ {"invalid_timestamp", test_invalid_timestamp},
+ {"default_timestamp", test_default_timestamp},
+ {"values", test_values},
+ {"in_size", test_in_size},
+ {"issue_71", test_issue_71},
+ {"histogram", test_histogram},
+ {"histogram_labels", test_histogram_labels},
+ {"summary", test_summary},
+ {"null_labels", test_null_labels},
+ {"issue_fluent_bit_5541", test_issue_fluent_bit_5541},
+ {"issue_fluent_bit_5894", test_issue_fluent_bit_5894},
+ {"empty_metrics", test_empty_metrics},
+ {"issue_fluent_bit_6021", test_issue_fluent_bit_6021},
+ {"override_timestamp", test_override_timestamp},
+ {"pr_168", test_pr_168},
+ {"histogram_different_label_count", test_histogram_different_label_count},
+ {"issue_fluent_bit_6534", test_issue_fluent_bit_6534},
+ { 0 }
+};
diff --git a/src/fluent-bit/lib/cmetrics/tests/summary.c b/src/fluent-bit/lib/cmetrics/tests/summary.c
new file mode 100644
index 000000000..99ffe5792
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/tests/summary.c
@@ -0,0 +1,172 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_summary.h>
+#include <cmetrics/cmt_encode_prometheus.h>
+#include <cmetrics/cmt_encode_text.h>
+
+#include "cmt_tests.h"
+
+static void prometheus_encode_test(struct cmt *cmt)
+{
+ cfl_sds_t buf;
+
+ buf = cmt_encode_prometheus_create(cmt, CMT_FALSE);
+ printf("\n%s\n", buf);
+ cmt_encode_prometheus_destroy(buf);
+
+ /* encode to all possible formats */
+ cmt_test_encode_all(cmt);
+}
+
+void test_set_defaults()
+{
+ double sum;
+ uint64_t count;
+ uint64_t ts;
+ double q[6];
+ double r[6];
+ struct cmt *cmt;
+ struct cmt_summary *s;
+
+ cmt_initialize();
+
+ /* Timestamp */
+ ts = cfl_time_now();
+
+ /* CMetrics context */
+ cmt = cmt_create();
+ TEST_CHECK(cmt != NULL);
+
+ /* set quantiles, no labels */
+ q[0] = 0.1;
+ q[1] = 0.2;
+ q[2] = 0.3;
+ q[3] = 0.4;
+ q[4] = 0.5;
+ q[5] = 1.0;
+
+ r[0] = 1;
+ r[1] = 2;
+ r[2] = 3;
+ r[3] = 4;
+ r[4] = 5;
+ r[5] = 6;
+
+ /* Create a gauge metric type */
+ s = cmt_summary_create(cmt,
+ "k8s", "network", "load", "Network load",
+ 6, q,
+ 1, (char *[]) {"my_label"});
+ TEST_CHECK(s != NULL);
+
+ count = 10;
+ sum = 51.612894511314444;
+
+ /* no quantiles, no labels */
+ cmt_summary_set_default(s, ts, NULL, sum, count, 0, NULL);
+ prometheus_encode_test(cmt);
+
+ cmt_summary_set_default(s, ts, r, sum, count, 0, NULL);
+ prometheus_encode_test(cmt);
+
+ /* static label: register static label for the context */
+ cmt_label_add(cmt, "static", "test");
+ prometheus_encode_test(cmt);
+
+ cmt_destroy(cmt);
+}
+
+/* ref: https://github.com/fluent/fluent-bit/issues/5894 */
+void fluentbit_bug_5894()
+{
+ double sum;
+ uint64_t count;
+ uint64_t ts;
+ double q[6];
+ double r[6];
+ struct cmt *cmt;
+ struct cmt_summary *s;
+
+ cmt_initialize();
+
+ /* Timestamp */
+ ts = cfl_time_now();
+
+ /* CMetrics context */
+ cmt = cmt_create();
+ TEST_CHECK(cmt != NULL);
+
+ /* set quantiles, no labels */
+ q[0] = 0.1;
+ q[1] = 0.2;
+ q[2] = 0.3;
+ q[3] = 0.4;
+ q[4] = 0.5;
+ q[5] = 1.0;
+
+ r[0] = 1;
+ r[1] = 2;
+ r[2] = 3;
+ r[3] = 4;
+ r[4] = 5;
+ r[5] = 6;
+
+ /* Create a gauge metric type */
+ s = cmt_summary_create(cmt,
+ "spring", "kafka_listener", "seconds", "Kafka Listener Timer",
+ 6, q,
+ 3, (char *[]) {"exception", "name", "result"});
+ TEST_CHECK(s != NULL);
+
+ /* no quantiles, labels */
+ sum = 0.0;
+ count = 1;
+
+ cmt_summary_set_default(s, ts, NULL, sum, count,
+ 3, (char *[]) {"ListenerExecutionFailedException",
+ "org.springframework.kafka.KafkaListenerEndpointContainer#0-0",
+ "failure"});
+
+ /* no quantiles, labels */
+ sum = 0.1;
+ count = 2;
+ cmt_summary_set_default(s, ts, NULL, sum, count,
+ 3, (char *[]) {"none",
+ "org.springframework.kafka.KafkaListenerEndpointContainer#0-0",
+ "success"});
+
+ /* quantiles, labels */
+ sum = 0.2;
+ count = 3;
+ cmt_summary_set_default(s, ts, r, sum, count,
+ 3, (char *[]) {"extra test",
+ "org.springframework.kafka.KafkaListenerEndpointContainer#0-0",
+ "success"});
+
+ prometheus_encode_test(cmt);
+ cmt_destroy(cmt);
+}
+
+TEST_LIST = {
+ {"set_defaults" , test_set_defaults},
+ {"fluentbit_bug_5894", fluentbit_bug_5894},
+ { 0 }
+};
diff --git a/src/fluent-bit/lib/cmetrics/tests/untyped.c b/src/fluent-bit/lib/cmetrics/tests/untyped.c
new file mode 100644
index 000000000..83c3647fe
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/tests/untyped.c
@@ -0,0 +1,122 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <cmetrics/cmetrics.h>
+#include <cmetrics/cmt_untyped.h>
+#include <cmetrics/cmt_encode_msgpack.h>
+#include <cmetrics/cmt_decode_msgpack.h>
+#include <cmetrics/cmt_encode_prometheus.h>
+#include <cmetrics/cmt_encode_text.h>
+#include <cmetrics/cmt_encode_influx.h>
+
+#include "cmt_tests.h"
+
+static struct cmt *generate_encoder_test_data()
+{
+ int ret;
+ double val;
+ uint64_t ts;
+ struct cmt *cmt;
+ struct cmt_untyped *u;
+
+ printf("version: %s\n\n", cmt_version());
+ cmt = cmt_create();
+
+ u = cmt_untyped_create(cmt, "kubernetes", "network", "load", "Network load",
+ 2, (char *[]) {"hostname", "app"});
+
+ ts = cfl_time_now();
+
+ ret = cmt_untyped_get_val(u, 0, NULL, &val);
+ TEST_CHECK(ret == -1);
+
+ cmt_untyped_set(u, ts, 2, 0, NULL);
+ cmt_untyped_get_val(u, 0, NULL, &val);
+ TEST_CHECK(val == 2.0);
+
+ cmt_untyped_get_val(u, 2, (char *[]) {"localhost", "cmetrics"}, &val);
+ cmt_untyped_get_val(u, 2, (char *[]) {"localhost", "test"}, &val);
+ cmt_untyped_set(u, ts, 12.15, 2, (char *[]) {"localhost", "test"});
+ cmt_untyped_set(u, ts, 1, 2, (char *[]) {"localhost", "test"});
+
+ return cmt;
+}
+
+void test_prometheus()
+{
+ struct cmt *cmt = NULL;
+ cfl_sds_t prom = NULL;
+
+ cmt_initialize();
+
+ cmt = generate_encoder_test_data();
+ TEST_CHECK(cmt != NULL);
+
+ prom = cmt_encode_prometheus_create(cmt, CMT_TRUE);
+ TEST_CHECK(prom != NULL);
+ printf("%s\n", prom);
+
+ cmt_encode_prometheus_destroy(prom);
+ cmt_destroy(cmt);
+}
+
+void test_text()
+{
+ struct cmt *cmt = NULL;
+ cfl_sds_t text = NULL;
+
+ cmt_initialize();
+
+ cmt = generate_encoder_test_data();
+ TEST_CHECK(cmt != NULL);
+
+ text = cmt_encode_text_create(cmt);
+ TEST_CHECK(text != NULL);
+
+ printf("%s\n", text);
+
+ cmt_encode_text_destroy(text);
+ cmt_destroy(cmt);
+}
+
+void test_influx()
+{
+ struct cmt *cmt = NULL;
+ cfl_sds_t text = NULL;
+
+ cmt_initialize();
+
+ cmt = generate_encoder_test_data();
+ TEST_CHECK(cmt != NULL);
+
+ text = cmt_encode_influx_create(cmt);
+ TEST_CHECK(text != NULL);
+
+ printf("%s\n", text);
+
+ cmt_encode_influx_destroy(text);
+ cmt_destroy(cmt);
+}
+
+TEST_LIST = {
+ {"prometheus", test_prometheus},
+ {"text" , test_text},
+ {"influx" , test_influx},
+ { 0 }
+};
diff --git a/src/fluent-bit/lib/cmetrics/tests/util.c b/src/fluent-bit/lib/cmetrics/tests/util.c
new file mode 100644
index 000000000..c8c9815e2
--- /dev/null
+++ b/src/fluent-bit/lib/cmetrics/tests/util.c
@@ -0,0 +1,68 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* CMetrics
+ * ========
+ * Copyright 2021-2022 The CMetrics Authors
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+
+#include <cmetrics/cmetrics.h>
+
+cfl_sds_t read_file(const char *path)
+{
+ long flen;
+ FILE *f = NULL;
+ cfl_sds_t result = NULL;
+
+ f = fopen(path, "rb");
+ if (!f) {
+ return NULL;
+ }
+
+ if (fseek(f, 0, SEEK_END) == -1) {
+ goto err;
+ }
+
+ flen = ftell(f);
+ if (flen < 0) {
+ goto err;
+ }
+
+ if (fseek(f, 0, SEEK_SET) == -1) {
+ goto err;
+ }
+
+ result = cfl_sds_create_size(flen);
+ if (!result) {
+ goto err;
+ }
+
+ if (flen > 0 && fread(result, flen, 1, f) != 1) {
+ goto err;
+ }
+
+ cfl_sds_set_len(result, flen);
+ fclose(f);
+ return result;
+
+err:
+ fclose(f);
+ if (result) {
+ cfl_sds_destroy(result);
+ }
+ return NULL;
+}
+