mirror of
https://github.com/outbackdingo/nDPId.git
synced 2026-01-28 02:19:37 +00:00
Compare commits
3 Commits
main
...
ndpi-examp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9678d33d0c | ||
|
|
b94bf63067 | ||
|
|
0c24bb5d04 |
@@ -1,19 +0,0 @@
|
|||||||
version: 2.1
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
docker:
|
|
||||||
- image: ubuntu:latest
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- run: export DEBIAN_FRONTEND=noninteractive
|
|
||||||
- run: apt-get update -qq
|
|
||||||
- run: |
|
|
||||||
env DEBIAN_FRONTEND=noninteractive \
|
|
||||||
apt-get install -y -qq \
|
|
||||||
coreutils wget git unzip make cmake binutils gcc g++ autoconf automake flex bison texinfo \
|
|
||||||
libtool pkg-config gettext libjson-c-dev flex bison libpcap-dev zlib1g-dev
|
|
||||||
- run: |
|
|
||||||
cmake -S . -B build -DENABLE_SYSTEMD=ON -DBUILD_EXAMPLES=ON -DBUILD_NDPI=ON
|
|
||||||
- run: |
|
|
||||||
cmake --build build --verbose
|
|
||||||
@@ -51,7 +51,7 @@ Cpp11BracedListStyle: true
|
|||||||
IndentWidth: 4
|
IndentWidth: 4
|
||||||
TabWidth: 4
|
TabWidth: 4
|
||||||
UseTab: Never
|
UseTab: Never
|
||||||
BreakBeforeBraces: Allman
|
BreakBeforeBraces: Linux
|
||||||
SpacesInParentheses: false
|
SpacesInParentheses: false
|
||||||
SpacesInSquareBrackets: false
|
SpacesInSquareBrackets: false
|
||||||
SpacesInAngles : false
|
SpacesInAngles : false
|
||||||
|
|||||||
38
.github/workflows/build-archlinux.yml
vendored
38
.github/workflows/build-archlinux.yml
vendored
@@ -1,38 +0,0 @@
|
|||||||
name: ArchLinux PKGBUILD
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- tmp
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
types: [opened, synchronize, reopened]
|
|
||||||
release:
|
|
||||||
types: [created]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
env:
|
|
||||||
CMAKE_C_FLAGS: -Werror
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: false
|
|
||||||
fetch-depth: 1
|
|
||||||
- name: Prepare for ArchLinux packaging
|
|
||||||
run: |
|
|
||||||
sudo chmod -R 0777 .
|
|
||||||
mv -v packages/archlinux packages/ndpid-testing
|
|
||||||
- uses: 2m/arch-pkgbuild-builder@v1.16
|
|
||||||
with:
|
|
||||||
debug: true
|
|
||||||
target: 'pkgbuild'
|
|
||||||
pkgname: 'packages/ndpid-testing'
|
|
||||||
- name: Upload PKG
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: nDPId-archlinux-packages
|
|
||||||
path: packages/ndpid-testing/*.pkg.tar.zst
|
|
||||||
59
.github/workflows/build-centos.yml
vendored
59
.github/workflows/build-centos.yml
vendored
@@ -1,59 +0,0 @@
|
|||||||
name: CentOs
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- tmp
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
types: [opened, synchronize, reopened]
|
|
||||||
release:
|
|
||||||
types: [created]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
centos8:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container: 'centos:8'
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: false
|
|
||||||
fetch-depth: 1
|
|
||||||
- name: Install CentOs Prerequisites
|
|
||||||
run: |
|
|
||||||
sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
|
|
||||||
sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
|
|
||||||
yum -y update
|
|
||||||
yum -y install curl gpg
|
|
||||||
curl 'https://packages.ntop.org/centos/ntop.repo' > /etc/yum.repos.d/ntop.repo
|
|
||||||
curl 'https://packages.ntop.org/centos/RPM-GPG-KEY-deri' | gpg --import
|
|
||||||
yum -y install yum-utils dnf-plugins-core epel-release
|
|
||||||
dnf config-manager --set-enabled powertools
|
|
||||||
yum -y update
|
|
||||||
yum -y install rpm-build gcc gcc-c++ autoconf automake make cmake flex bison gettext pkg-config libtool ndpi-dev libpcap-devel zlib-devel python3.8 git wget unzip /usr/lib64/libasan.so.5.0.0 /usr/lib64/libubsan.so.1.0.0
|
|
||||||
repoquery -l ndpi-dev
|
|
||||||
- name: Configure nDPId
|
|
||||||
run: |
|
|
||||||
mkdir build && cd build
|
|
||||||
cmake .. -DENABLE_SYSTEMD=ON -DBUILD_EXAMPLES=ON -DENABLE_SANITIZER=ON -DNDPI_NO_PKGCONFIG=ON -DSTATIC_LIBNDPI_INSTALLDIR=/usr
|
|
||||||
- name: Build nDPId
|
|
||||||
run: |
|
|
||||||
make -C build all VERBOSE=1
|
|
||||||
- name: CPack RPM
|
|
||||||
run: |
|
|
||||||
cd ./build && cpack -G RPM && cd ..
|
|
||||||
- name: Upload RPM
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: nDPId-centos-packages
|
|
||||||
path: build/*.rpm
|
|
||||||
- name: Upload on Failure
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
if: failure()
|
|
||||||
with:
|
|
||||||
name: autoconf-config-log
|
|
||||||
path: |
|
|
||||||
build/CMakeCache.txt
|
|
||||||
libnDPI/config.log
|
|
||||||
25
.github/workflows/build-docker.yml
vendored
25
.github/workflows/build-docker.yml
vendored
@@ -1,25 +0,0 @@
|
|||||||
name: Docker Build
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- 'main'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
docker:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v2
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v2
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
- name: Build and push
|
|
||||||
uses: docker/build-push-action@v4
|
|
||||||
with:
|
|
||||||
push: true
|
|
||||||
tags: utoni/ndpid:latest
|
|
||||||
39
.github/workflows/build-freebsd.yml
vendored
39
.github/workflows/build-freebsd.yml
vendored
@@ -1,39 +0,0 @@
|
|||||||
name: FreeBSD Build
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
# At the end of every day
|
|
||||||
- cron: '0 0 * * *'
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- tmp
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
types: [opened, synchronize, reopened]
|
|
||||||
release:
|
|
||||||
types: [created]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Build and Test
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Test in FreeBSD
|
|
||||||
id: test
|
|
||||||
uses: vmactions/freebsd-vm@main
|
|
||||||
with:
|
|
||||||
usesh: true
|
|
||||||
prepare: |
|
|
||||||
pkg install -y bash autoconf automake cmake gmake libtool gettext pkgconf gcc \
|
|
||||||
git wget unzip flock \
|
|
||||||
json-c flex bison libpcap curl openssl dbus
|
|
||||||
run: |
|
|
||||||
echo "Working Directory: $(pwd)"
|
|
||||||
echo "User.............: $(whoami)"
|
|
||||||
echo "FreeBSD Version..: $(freebsd-version)"
|
|
||||||
# TODO: Make examples I/O event agnostic i.e. use nio
|
|
||||||
cmake -S . -B build -DBUILD_NDPI=ON -DBUILD_EXAMPLES=OFF #-DENABLE_CURL=ON -DENABLE_DBUS=ON
|
|
||||||
cmake --build build
|
|
||||||
54
.github/workflows/build-openwrt.yml
vendored
54
.github/workflows/build-openwrt.yml
vendored
@@ -1,54 +0,0 @@
|
|||||||
name: OpenWrt Build
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
# At the end of every day
|
|
||||||
- cron: '0 0 * * *'
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- tmp
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
types: [opened, synchronize, reopened]
|
|
||||||
release:
|
|
||||||
types: [created]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: ${{ matrix.arch }} ${{ matrix.target }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- arch: arm_cortex-a9_vfpv3-d16
|
|
||||||
target: mvebu-cortexa9
|
|
||||||
|
|
||||||
- arch: arm_cortex-a15_neon-vfpv4
|
|
||||||
target: armvirt-32
|
|
||||||
|
|
||||||
- arch: x86_64
|
|
||||||
target: x86-64
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: false
|
|
||||||
fetch-depth: 1
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
uses: openwrt/gh-action-sdk@main
|
|
||||||
env:
|
|
||||||
ARCH: ${{ matrix.arch }}-snapshot
|
|
||||||
FEED_DIR: ${{ github.workspace }}/packages/openwrt
|
|
||||||
FEEDNAME: ndpid_openwrt_packages_ci
|
|
||||||
PACKAGES: nDPId-testing
|
|
||||||
V: s
|
|
||||||
|
|
||||||
- name: Store packages
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: nDPId-${{ matrix.arch}}-${{ matrix.target }}
|
|
||||||
path: bin/packages/${{ matrix.arch }}/ndpid_openwrt_packages_ci/*.ipk
|
|
||||||
45
.github/workflows/build-rpm.yml
vendored
45
.github/workflows/build-rpm.yml
vendored
@@ -1,45 +0,0 @@
|
|||||||
name: RPM Build
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
# At the end of every day
|
|
||||||
- cron: '0 0 * * *'
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- tmp
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
types: [opened, synchronize, reopened]
|
|
||||||
release:
|
|
||||||
types: [created]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Install Ubuntu Prerequisites
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install fakeroot alien autoconf automake cmake libtool pkg-config gettext libjson-c-dev flex bison libpcap-dev zlib1g-dev libcurl4-openssl-dev libdbus-1-dev
|
|
||||||
|
|
||||||
- name: Build RPM package
|
|
||||||
run: |
|
|
||||||
cmake -S . -B build-rpm -DBUILD_EXAMPLES=ON -DBUILD_NDPI=ON -DCMAKE_BUILD_TYPE=Release
|
|
||||||
cmake --build build-rpm --parallel
|
|
||||||
cd build-rpm
|
|
||||||
cpack -G RPM
|
|
||||||
cd ..
|
|
||||||
|
|
||||||
- name: Convert/Install RPM package
|
|
||||||
run: |
|
|
||||||
fakeroot alien --scripts --to-deb --verbose ./build-rpm/nDPId-*.rpm
|
|
||||||
sudo dpkg -i ./ndpid_*.deb
|
|
||||||
|
|
||||||
- name: Upload RPM
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: nDPId-rpm-packages
|
|
||||||
path: build-rpm/*.rpm
|
|
||||||
409
.github/workflows/build.yml
vendored
409
.github/workflows/build.yml
vendored
@@ -1,409 +0,0 @@
|
|||||||
name: Build
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
# At the end of every day
|
|
||||||
- cron: '0 0 * * *'
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- tmp
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
types: [opened, synchronize, reopened]
|
|
||||||
release:
|
|
||||||
types: [created]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
name: ${{ matrix.os }} ${{ matrix.compiler }}
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
env:
|
|
||||||
CMAKE_C_COMPILER: ${{ matrix.compiler }}
|
|
||||||
CMAKE_C_FLAGS: -Werror ${{ matrix.cflags }}
|
|
||||||
CMAKE_C_EXE_LINKER_FLAGS: ${{ matrix.ldflags }}
|
|
||||||
CMAKE_MODULE_LINKER_FLAGS: ${{ matrix.ldflags }}
|
|
||||||
DYLD_LIBRARY_PATH: /usr/local/lib
|
|
||||||
strategy:
|
|
||||||
fail-fast: true
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- compiler: "gcc"
|
|
||||||
os: "ubuntu-latest"
|
|
||||||
ndpi_build: "-DBUILD_NDPI=ON"
|
|
||||||
ndpid_examples: "-DBUILD_EXAMPLES=ON"
|
|
||||||
ndpid_rust_examples: "-DBUILD_RUST_EXAMPLES=ON"
|
|
||||||
ndpid_gcrypt: "-DNDPI_WITH_GCRYPT=OFF"
|
|
||||||
ndpid_zlib: "-DENABLE_ZLIB=ON"
|
|
||||||
ndpid_extras: "-DENABLE_CRYPTO=ON"
|
|
||||||
sanitizer: "-DENABLE_SANITIZER=OFF -DENABLE_SANITIZER_THREAD=OFF"
|
|
||||||
coverage: "-DENABLE_COVERAGE=OFF"
|
|
||||||
poll: "-DFORCE_POLL=OFF"
|
|
||||||
upload: true
|
|
||||||
upload_suffix: ""
|
|
||||||
ndpi_min_version: "5.0"
|
|
||||||
- compiler: "gcc"
|
|
||||||
os: "ubuntu-latest"
|
|
||||||
ndpi_build: "-DBUILD_NDPI=ON"
|
|
||||||
ndpid_examples: "-DBUILD_EXAMPLES=ON"
|
|
||||||
ndpid_rust_examples: ""
|
|
||||||
ndpid_gcrypt: "-DNDPI_WITH_GCRYPT=ON"
|
|
||||||
ndpid_zlib: "-DENABLE_ZLIB=ON"
|
|
||||||
ndpid_extras: "-DENABLE_CRYPTO=ON -DNDPI_WITH_MAXMINDDB=ON -DNDPI_WITH_PCRE=ON -DENABLE_MEMORY_PROFILING=ON"
|
|
||||||
sanitizer: "-DENABLE_SANITIZER=OFF -DENABLE_SANITIZER_THREAD=OFF"
|
|
||||||
coverage: "-DENABLE_COVERAGE=OFF"
|
|
||||||
poll: "-DFORCE_POLL=OFF"
|
|
||||||
upload: true
|
|
||||||
upload_suffix: "-host-gcrypt"
|
|
||||||
ndpi_min_version: "5.0"
|
|
||||||
- compiler: "clang"
|
|
||||||
os: "ubuntu-latest"
|
|
||||||
ndpi_build: "-DBUILD_NDPI=ON"
|
|
||||||
ndpid_examples: "-DBUILD_EXAMPLES=ON"
|
|
||||||
ndpid_rust_examples: ""
|
|
||||||
ndpid_gcrypt: "-DNDPI_WITH_GCRYPT=OFF"
|
|
||||||
ndpid_zlib: "-DENABLE_ZLIB=OFF"
|
|
||||||
ndpid_extras: ""
|
|
||||||
sanitizer: "-DENABLE_SANITIZER=OFF -DENABLE_SANITIZER_THREAD=OFF"
|
|
||||||
coverage: "-DENABLE_COVERAGE=OFF"
|
|
||||||
poll: "-DFORCE_POLL=OFF"
|
|
||||||
upload: true
|
|
||||||
upload_suffix: "-no-zlib"
|
|
||||||
ndpi_min_version: "5.0"
|
|
||||||
- compiler: "gcc"
|
|
||||||
os: "ubuntu-latest"
|
|
||||||
ndpi_build: "-DBUILD_NDPI=ON"
|
|
||||||
ndpid_examples: "-DBUILD_EXAMPLES=ON"
|
|
||||||
ndpid_rust_examples: ""
|
|
||||||
ndpid_gcrypt: "-DNDPI_WITH_GCRYPT=OFF"
|
|
||||||
ndpid_zlib: "-DENABLE_ZLIB=ON"
|
|
||||||
ndpid_extras: ""
|
|
||||||
sanitizer: "-DENABLE_SANITIZER=ON"
|
|
||||||
coverage: "-DENABLE_COVERAGE=ON"
|
|
||||||
poll: "-DFORCE_POLL=ON"
|
|
||||||
upload: false
|
|
||||||
ndpi_min_version: "5.0"
|
|
||||||
- compiler: "clang"
|
|
||||||
os: "ubuntu-latest"
|
|
||||||
ndpi_build: "-DBUILD_NDPI=ON"
|
|
||||||
ndpid_examples: "-DBUILD_EXAMPLES=ON"
|
|
||||||
ndpid_rust_examples: ""
|
|
||||||
ndpid_gcrypt: "-DNDPI_WITH_GCRYPT=OFF"
|
|
||||||
ndpid_zlib: "-DENABLE_ZLIB=ON"
|
|
||||||
ndpid_extras: "-DENABLE_CRYPTO=ON"
|
|
||||||
sanitizer: "-DENABLE_SANITIZER=ON"
|
|
||||||
coverage: "-DENABLE_COVERAGE=OFF"
|
|
||||||
poll: "-DFORCE_POLL=OFF"
|
|
||||||
upload: false
|
|
||||||
ndpi_min_version: "5.0"
|
|
||||||
- compiler: "clang-12"
|
|
||||||
os: "ubuntu-22.04"
|
|
||||||
ndpi_build: "-DBUILD_NDPI=ON"
|
|
||||||
ndpid_examples: "-DBUILD_EXAMPLES=ON"
|
|
||||||
ndpid_rust_examples: ""
|
|
||||||
ndpid_gcrypt: "-DNDPI_WITH_GCRYPT=OFF"
|
|
||||||
ndpid_zlib: "-DENABLE_ZLIB=ON"
|
|
||||||
ndpid_extras: ""
|
|
||||||
sanitizer: "-DENABLE_SANITIZER_THREAD=ON"
|
|
||||||
coverage: "-DENABLE_COVERAGE=OFF"
|
|
||||||
poll:
|
|
||||||
upload: false
|
|
||||||
ndpi_min_version: "5.0"
|
|
||||||
- compiler: "gcc-10"
|
|
||||||
os: "ubuntu-22.04"
|
|
||||||
ndpi_build: "-DBUILD_NDPI=ON"
|
|
||||||
ndpid_examples: "-DBUILD_EXAMPLES=ON"
|
|
||||||
ndpid_rust_examples: ""
|
|
||||||
ndpid_gcrypt: "-DNDPI_WITH_GCRYPT=OFF"
|
|
||||||
ndpid_zlib: "-DENABLE_ZLIB=OFF"
|
|
||||||
ndpid_extras: ""
|
|
||||||
sanitizer: "-DENABLE_SANITIZER=ON"
|
|
||||||
coverage: "-DENABLE_COVERAGE=OFF"
|
|
||||||
poll: "-DFORCE_POLL=ON"
|
|
||||||
upload: false
|
|
||||||
ndpi_min_version: "5.0"
|
|
||||||
- compiler: "gcc-9"
|
|
||||||
os: "ubuntu-22.04"
|
|
||||||
ndpi_build: "-DBUILD_NDPI=ON"
|
|
||||||
ndpid_examples: "-DBUILD_EXAMPLES=ON"
|
|
||||||
ndpid_rust_examples: ""
|
|
||||||
ndpid_gcrypt: "-DNDPI_WITH_GCRYPT=OFF"
|
|
||||||
ndpid_zlib: "-DENABLE_ZLIB=ON"
|
|
||||||
ndpid_extras: ""
|
|
||||||
sanitizer: "-DENABLE_SANITIZER=ON"
|
|
||||||
coverage: "-DENABLE_COVERAGE=OFF"
|
|
||||||
poll: "-DFORCE_POLL=OFF"
|
|
||||||
upload: false
|
|
||||||
ndpi_min_version: "5.0"
|
|
||||||
- compiler: "cc"
|
|
||||||
os: "macOS-13"
|
|
||||||
ndpi_build: "-DBUILD_NDPI=OFF"
|
|
||||||
ndpid_examples: "-DBUILD_EXAMPLES=OFF"
|
|
||||||
ndpid_rust_examples: ""
|
|
||||||
ndpid_gcrypt: "-DNDPI_WITH_GCRYPT=OFF"
|
|
||||||
ndpid_zlib: "-DENABLE_ZLIB=ON"
|
|
||||||
ndpid_extras: ""
|
|
||||||
examples: "-DBUILD_EXAMPLES=OFF"
|
|
||||||
sanitizer: "-DENABLE_SANITIZER=OFF"
|
|
||||||
coverage: "-DENABLE_COVERAGE=OFF"
|
|
||||||
poll:
|
|
||||||
upload: false
|
|
||||||
ndpi_min_version: "5.0"
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Print Matrix
|
|
||||||
run: |
|
|
||||||
echo '----------------------------------------'
|
|
||||||
echo '| OS.......: ${{ matrix.os }}'
|
|
||||||
echo '| CC.......: ${{ matrix.compiler }}'
|
|
||||||
echo "| CFLAGS...: $CMAKE_C_FLAGS"
|
|
||||||
echo "| LDFLAGS..: $CMAKE_C_EXE_LINKER_FLAGS"
|
|
||||||
echo '|---------------------------------------'
|
|
||||||
echo '| nDPI min.: ${{ matrix.ndpi_min_version }}'
|
|
||||||
echo '| GCRYPT...: ${{ matrix.ndpid_gcrypt }}'
|
|
||||||
echo '| ZLIB.....: ${{ matrix.ndpid_zlib }}'
|
|
||||||
echo '| Extras...: ${{ matrix.ndpid_extras }}'
|
|
||||||
echo '| ForcePoll: ${{ matrix.poll }}'
|
|
||||||
echo '|---------------------------------------'
|
|
||||||
echo '| SANITIZER: ${{ matrix.sanitizer }}'
|
|
||||||
echo '| COVERAGE.: ${{ matrix.coverage }}'
|
|
||||||
echo '|---------------------------------------'
|
|
||||||
echo '| UPLOAD...: ${{ matrix.upload }}'
|
|
||||||
echo '----------------------------------------'
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: false
|
|
||||||
fetch-depth: 1
|
|
||||||
- name: Install MacOS Prerequisites
|
|
||||||
if: startsWith(matrix.os, 'macOS')
|
|
||||||
run: |
|
|
||||||
brew install coreutils automake make unzip
|
|
||||||
wget 'https://www.tcpdump.org/release/libpcap-1.10.4.tar.gz'
|
|
||||||
tar -xzvf libpcap-1.10.4.tar.gz
|
|
||||||
cd libpcap-1.10.4
|
|
||||||
./configure && make install
|
|
||||||
cd ..
|
|
||||||
wget 'https://github.com/ntop/nDPI/archive/refs/heads/dev.zip' -O libndpi-dev.zip
|
|
||||||
unzip libndpi-dev.zip
|
|
||||||
cd nDPI-dev
|
|
||||||
./autogen.sh
|
|
||||||
./configure --prefix=/usr/local --with-only-libndpi && make install
|
|
||||||
- name: Fix kernel mmap rnd bits on Ubuntu
|
|
||||||
if: startsWith(matrix.os, 'ubuntu')
|
|
||||||
run: |
|
|
||||||
# Workaround for compatinility between latest kernel and sanitizer
|
|
||||||
# See https://github.com/actions/runner-images/issues/9491
|
|
||||||
sudo sysctl vm.mmap_rnd_bits=28
|
|
||||||
- name: Install Ubuntu Prerequisites
|
|
||||||
if: startsWith(matrix.os, 'ubuntu')
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install autoconf automake cmake libtool pkg-config gettext libjson-c-dev flex bison libpcap-dev zlib1g-dev libcurl4-openssl-dev libdbus-1-dev
|
|
||||||
sudo apt-get install ${{ matrix.compiler }} lcov iproute2
|
|
||||||
- name: Install Ubuntu Prerequisites (Rust/Cargo)
|
|
||||||
if: startsWith(matrix.os, 'ubuntu') && startsWith(matrix.ndpid_rust_examples, '-DBUILD_RUST_EXAMPLES=ON')
|
|
||||||
run: |
|
|
||||||
sudo apt-get install cargo
|
|
||||||
- name: Install Ubuntu Prerequisites (libgcrypt)
|
|
||||||
if: startsWith(matrix.os, 'ubuntu') && startsWith(matrix.ndpid_gcrypt, '-DNDPI_WITH_GCRYPT=ON')
|
|
||||||
run: |
|
|
||||||
sudo apt-get install libgcrypt20-dev
|
|
||||||
- name: Install Ubuntu Prerequisites (zlib)
|
|
||||||
if: startsWith(matrix.os, 'ubuntu') && startsWith(matrix.ndpid_zlib, '-DENABLE_ZLIB=ON')
|
|
||||||
run: |
|
|
||||||
sudo apt-get install zlib1g-dev
|
|
||||||
- name: Install Ubuntu Prerequisites (libmaxminddb, libpcre2)
|
|
||||||
if: startsWith(matrix.ndpid_extras, '-D')
|
|
||||||
run: |
|
|
||||||
sudo apt-get install libmaxminddb-dev libpcre2-dev
|
|
||||||
- name: Install Ubuntu Prerequisites (libnl-genl-3-dev)
|
|
||||||
if: startsWith(matrix.ndpi_build, '-DBUILD_NDPI=ON') && startsWith(matrix.coverage, '-DENABLE_COVERAGE=OFF') && startsWith(matrix.sanitizer, '-DENABLE_SANITIZER=ON') && startsWith(matrix.ndpid_gcrypt, '-DNDPI_WITH_GCRYPT=OFF') && startsWith(matrix.ndpid_zlib, '-DENABLE_ZLIB=ON')
|
|
||||||
run: |
|
|
||||||
sudo apt-get install libnl-genl-3-dev
|
|
||||||
- name: Checking Network Buffer Size
|
|
||||||
run: |
|
|
||||||
C_VAL=$(cat config.h | sed -n 's/^#define\s\+NETWORK_BUFFER_MAX_SIZE\s\+\([0-9]\+\).*$/\1/gp')
|
|
||||||
PY_VAL=$(cat dependencies/nDPIsrvd.py | sed -n 's/^NETWORK_BUFFER_MAX_SIZE = \([0-9]\+\).*$/\1/gp')
|
|
||||||
test ${C_VAL} = ${PY_VAL}
|
|
||||||
- name: Configure nDPId
|
|
||||||
run: |
|
|
||||||
cmake -S . -B build -Werror=dev -Werror=deprecated -DCMAKE_C_COMPILER="$CMAKE_C_COMPILER" -DCMAKE_C_FLAGS="$CMAKE_C_FLAGS" -DCMAKE_MODULE_LINKER_FLAGS="$CMAKE_MODULE_LINKER_FLAGS" -DCMAKE_C_EXE_LINKER_FLAGS="$CMAKE_C_EXE_LINKER_FLAGS" \
|
|
||||||
-DENABLE_DBUS=ON -DENABLE_CURL=ON -DENABLE_SYSTEMD=ON \
|
|
||||||
${{ matrix.poll }} ${{ matrix.coverage }} ${{ matrix.sanitizer }} ${{ matrix.ndpi_build }} \
|
|
||||||
${{ matrix.ndpid_examples }} ${{ matrix.ndpid_rust_examples }} ${{ matrix.ndpid_zlib }} ${{ matrix.ndpid_gcrypt }} ${{ matrix.ndpid_extras }}
|
|
||||||
- name: Build nDPId
|
|
||||||
run: |
|
|
||||||
cmake --build build --verbose
|
|
||||||
- name: Build single nDPId/nDPIsrvd executables (invoke CC directly - dynamic nDPI lib)
|
|
||||||
if: startsWith(matrix.ndpi_build, '-DBUILD_NDPI=OFF') && startsWith(matrix.coverage, '-DENABLE_COVERAGE=OFF') && startsWith(matrix.ndpid_gcrypt, '-DNDPI_WITH_GCRYPT=OFF')
|
|
||||||
run: |
|
|
||||||
pkg-config --cflags --libs libndpi
|
|
||||||
cc -Wall -Wextra -std=gnu99 \
|
|
||||||
${{ matrix.poll }} -DENABLE_MEMORY_PROFILING=1 \
|
|
||||||
nDPId.c nio.c utils.c \
|
|
||||||
$(pkg-config --cflags libndpi) -I. -I./dependencies -I./dependencies/jsmn -I./dependencies/uthash/include \
|
|
||||||
-o /tmp/a.out \
|
|
||||||
-lpcap $(pkg-config --libs libndpi) -pthread -lm
|
|
||||||
cc -Wall -Wextra -std=gnu99 \
|
|
||||||
${{ matrix.poll }} -DENABLE_MEMORY_PROFILING=1 \
|
|
||||||
nDPIsrvd.c nio.c utils.c \
|
|
||||||
-I. -I./dependencies -I./dependencies/jsmn -I./dependencies/uthash/include \
|
|
||||||
-o /tmp/a.out
|
|
||||||
- name: Build single nDPId/nDPIsrvd executables (invoke CC directly - static nDPI lib)
|
|
||||||
if: startsWith(matrix.ndpi_build, '-DBUILD_NDPI=ON') && startsWith(matrix.coverage, '-DENABLE_COVERAGE=OFF') && startsWith(matrix.sanitizer, '-DENABLE_SANITIZER=ON') && startsWith(matrix.ndpid_gcrypt, '-DNDPI_WITH_GCRYPT=OFF') && startsWith(matrix.ndpid_zlib, '-DENABLE_ZLIB=ON')
|
|
||||||
run: |
|
|
||||||
cc -Wall -Wextra -std=gnu99 ${{ matrix.poll }} -DENABLE_ZLIB=1 -DENABLE_MEMORY_PROFILING=1 \
|
|
||||||
-fsanitize=address -fsanitize=undefined -fno-sanitize=alignment -fsanitize=enum -fsanitize=leak \
|
|
||||||
nDPId.c nio.c utils.c \
|
|
||||||
-I./build/libnDPI/include/ndpi -I. -I./dependencies -I./dependencies/jsmn -I./dependencies/uthash/include \
|
|
||||||
-o /tmp/a.out \
|
|
||||||
-lpcap ./build/libnDPI/lib/libndpi.a -pthread -lm -lz
|
|
||||||
- name: Test EXEC
|
|
||||||
run: |
|
|
||||||
./build/nDPId-test
|
|
||||||
./build/nDPId -h || test $? -eq 1
|
|
||||||
./build/nDPIsrvd -h || test $? -eq 1
|
|
||||||
- name: Test DIFF
|
|
||||||
if: startsWith(matrix.os, 'macOS') == false && startsWith(matrix.ndpid_gcrypt, '-DNDPI_WITH_GCRYPT=OFF')
|
|
||||||
run: |
|
|
||||||
./test/run_tests.sh ./libnDPI ./build/nDPId-test
|
|
||||||
./test/run_config_tests.sh ./libnDPI ./build/nDPId-test
|
|
||||||
- name: Daemon
|
|
||||||
if: startsWith(matrix.compiler, 'gcc') || endsWith(matrix.compiler, 'clang')
|
|
||||||
run: |
|
|
||||||
make -C ./build daemon VERBOSE=1
|
|
||||||
make -C ./build daemon VERBOSE=1
|
|
||||||
- name: Coverage
|
|
||||||
if: startsWith(matrix.coverage, '-DENABLE_COVERAGE=ON')
|
|
||||||
run: |
|
|
||||||
make -C ./build coverage
|
|
||||||
- name: Dist
|
|
||||||
if: startsWith(matrix.os, 'macOS') == false && matrix.upload == false
|
|
||||||
run: |
|
|
||||||
make -C ./build dist
|
|
||||||
RAND_ID=$(( ( RANDOM ) + 1 ))
|
|
||||||
mkdir "nDPId-dist-${RAND_ID}"
|
|
||||||
cd "nDPId-dist-${RAND_ID}"
|
|
||||||
tar -xjf ../nDPId-*.tar.bz2
|
|
||||||
cd ./nDPId-*
|
|
||||||
cmake -S . -B ./build \
|
|
||||||
-DENABLE_DBUS=ON -DENABLE_CURL=ON -DENABLE_SYSTEMD=ON \
|
|
||||||
${{ matrix.poll }} ${{ matrix.coverage }} ${{ matrix.sanitizer }} ${{ matrix.ndpi_build }} \
|
|
||||||
${{ matrix.ndpid_examples }} ${{ matrix.ndpid_rust_examples }} ${{ matrix.ndpid_zlib }} ${{ matrix.ndpid_gcrypt }} ${{ matrix.ndpid_extras }}
|
|
||||||
cd ../..
|
|
||||||
rm -rf "nDPId-dist-${RAND_ID}"
|
|
||||||
- name: CPack DEB
|
|
||||||
if: startsWith(matrix.os, 'macOS') == false
|
|
||||||
run: |
|
|
||||||
cd ./build && cpack -G DEB && \
|
|
||||||
sudo dpkg -i nDPId-*.deb && \
|
|
||||||
sudo apt purge ndpid && \
|
|
||||||
sudo dpkg -i nDPId-*.deb && cd ..
|
|
||||||
- name: Upload DEB
|
|
||||||
if: startsWith(matrix.os, 'macOS') == false && matrix.upload
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: nDPId-debian-packages_${{ matrix.compiler }}${{ matrix.upload_suffix }}
|
|
||||||
path: build/*.deb
|
|
||||||
- name: Test systemd
|
|
||||||
if: startsWith(matrix.os, 'ubuntu') && startsWith(matrix.compiler, 'gcc')
|
|
||||||
run: |
|
|
||||||
ip -c address
|
|
||||||
sudo systemctl daemon-reload
|
|
||||||
sudo systemctl enable ndpid@lo
|
|
||||||
sudo systemctl start ndpid@lo
|
|
||||||
SYSTEMCTL_RET=3; while (( $SYSTEMCTL_RET == 3 )); do systemctl is-active ndpid@lo.service; SYSTEMCTL_RET=$?; sleep 1; done
|
|
||||||
sudo systemctl status ndpisrvd.service ndpid@lo.service
|
|
||||||
sudo systemctl show ndpisrvd.service ndpid@lo.service -p SubState,ActiveState
|
|
||||||
sudo dpkg -i ./build/nDPId-*.deb
|
|
||||||
sudo systemctl status ndpisrvd.service ndpid@lo.service
|
|
||||||
sudo systemctl show ndpisrvd.service ndpid@lo.service -p SubState,ActiveState
|
|
||||||
sudo systemctl stop ndpisrvd.service
|
|
||||||
journalctl --no-tail --no-pager -u ndpisrvd.service -u ndpid@lo.service
|
|
||||||
- name: Build PF_RING and nDPId (invoke CC directly - dynamic nDPI lib)
|
|
||||||
if: startsWith(matrix.ndpi_build, '-DBUILD_NDPI=ON') && startsWith(matrix.coverage, '-DENABLE_COVERAGE=OFF') && startsWith(matrix.sanitizer, '-DENABLE_SANITIZER=ON') && startsWith(matrix.ndpid_gcrypt, '-DNDPI_WITH_GCRYPT=OFF') && startsWith(matrix.ndpid_zlib, '-DENABLE_ZLIB=ON')
|
|
||||||
run: |
|
|
||||||
git clone --depth=1 https://github.com/ntop/PF_RING.git
|
|
||||||
cd PF_RING/userland && ./configure && make && sudo make install prefix=/usr
|
|
||||||
cd ../..
|
|
||||||
cc -Wall -Wextra -std=gnu99 ${{ matrix.poll }} -DENABLE_PFRING=1 -DENABLE_ZLIB=1 -DENABLE_MEMORY_PROFILING=1 \
|
|
||||||
-fsanitize=address -fsanitize=undefined -fno-sanitize=alignment -fsanitize=enum -fsanitize=leak \
|
|
||||||
nDPId.c npfring.c nio.c utils.c \
|
|
||||||
-I. -I./dependencies -I./dependencies/jsmn -I./dependencies/uthash/include \
|
|
||||||
-I./build/libnDPI/include/ndpi \
|
|
||||||
-I./PF_RING/userland/lib -I./PF_RING/kernel \
|
|
||||||
-o /tmp/a.out \
|
|
||||||
-ldl /usr/lib/libpfring.a -lpcap ./build/libnDPI/lib/libndpi.a -pthread -lm -lz
|
|
||||||
- name: Build against libnDPI-${{ matrix.ndpi_min_version }}
|
|
||||||
if: startsWith(matrix.os, 'ubuntu')
|
|
||||||
run: |
|
|
||||||
mkdir build-local-ndpi && cd build-local-ndpi
|
|
||||||
WGET_RET=0
|
|
||||||
wget 'https://github.com/ntop/nDPI/archive/refs/tags/${{ matrix.ndpi_min_version }}.tar.gz' || { WGET_RET=$?; true; }
|
|
||||||
echo "wget returned: ${WGET_RET}"
|
|
||||||
test $WGET_RET -ne 8 && { \
|
|
||||||
tar -xzvf ${{ matrix.ndpi_min_version }}.tar.gz; }
|
|
||||||
test $WGET_RET -ne 8 || { \
|
|
||||||
echo "::warning file=nDPId.c::New libnDPI release required to build against release tarball, falling back to dev branch."; \
|
|
||||||
wget 'http://github.com/ntop/nDPI/archive/refs/heads/dev.tar.gz'; \
|
|
||||||
WGET_RET=$?; \
|
|
||||||
tar -xzvf dev.tar.gz; \
|
|
||||||
mv -v 'nDPI-dev' 'nDPI-${{ matrix.ndpi_min_version }}'; }
|
|
||||||
test $WGET_RET -ne 0 || { cd nDPI-${{ matrix.ndpi_min_version }}; \
|
|
||||||
NDPI_CONFIGURE_ARGS=''; \
|
|
||||||
test 'x${{ matrix.ndpid_gcrypt }}' != 'x-DNDPI_WITH_GCRYPT=ON' || NDPI_CONFIGURE_ARGS="$NDPI_CONFIGURE_ARGS --with-local-libgcrypt"; \
|
|
||||||
test 'x${{ matrix.sanitizer }}' != 'x-DENABLE_SANITIZER=ON' || NDPI_CONFIGURE_ARGS="$NDPI_CONFIGURE_ARGS --with-sanitizer"; \
|
|
||||||
echo "Configure arguments: '$NDPI_CONFIGURE_ARGS'"; \
|
|
||||||
./autogen.sh; \
|
|
||||||
./configure --prefix=/usr --with-only-libndpi $NDPI_CONFIGURE_ARGS CC="${{ matrix.compiler }}" CXX=false \
|
|
||||||
CFLAGS="$CMAKE_C_FLAGS" && make && sudo make install; cd ..; }
|
|
||||||
ls -alhR /usr/include/ndpi
|
|
||||||
cd ..
|
|
||||||
test $WGET_RET -ne 0 || { echo "Running CMake.. (pkgconfig)"; \
|
|
||||||
cmake -S . -B ./build-local-pkgconfig \
|
|
||||||
-DCMAKE_C_COMPILER="$CMAKE_C_COMPILER" -DCMAKE_C_FLAGS="$CMAKE_C_FLAGS" \
|
|
||||||
-DCMAKE_C_EXE_LINKER_FLAGS="$CMAKE_C_EXE_LINKER_FLAGS" \
|
|
||||||
-DBUILD_NDPI=OFF -DBUILD_EXAMPLES=ON \
|
|
||||||
-DENABLE_DBUS=ON -DENABLE_CURL=ON -DENABLE_SYSTEMD=ON \
|
|
||||||
${{ matrix.poll }} ${{ matrix.coverage }} \
|
|
||||||
${{ matrix.sanitizer }} ${{ matrix.ndpid_examples }} ${{ matrix.ndpid_rust_examples }}; }
|
|
||||||
test $WGET_RET -ne 0 || { echo "Running Make.. (pkgconfig)"; \
|
|
||||||
cmake --build ./build-local-pkgconfig --verbose; }
|
|
||||||
test $WGET_RET -ne 0 || { echo "Testing Executable.. (pkgconfig)"; \
|
|
||||||
./build-local-pkgconfig/nDPId-test; \
|
|
||||||
./build-local-pkgconfig/nDPId -h || test $? -eq 1; \
|
|
||||||
./build-local-pkgconfig/nDPIsrvd -h || test $? -eq 1; }
|
|
||||||
test $WGET_RET -ne 0 || { echo "Running CMake.. (static)"; \
|
|
||||||
cmake -S . -B ./build-local-static \
|
|
||||||
-DCMAKE_C_COMPILER="$CMAKE_C_COMPILER" -DCMAKE_C_FLAGS="$CMAKE_C_FLAGS" \
|
|
||||||
-DCMAKE_C_EXE_LINKER_FLAGS="$CMAKE_C_EXE_LINKER_FLAGS" \
|
|
||||||
-DBUILD_NDPI=OFF -DBUILD_EXAMPLES=ON \
|
|
||||||
-DENABLE_DBUS=ON -DENABLE_CURL=ON -DENABLE_SYSTEMD=ON \
|
|
||||||
-DNDPI_NO_PKGCONFIG=ON -DSTATIC_LIBNDPI_INSTALLDIR=/usr \
|
|
||||||
${{ matrix.poll }} ${{ matrix.coverage }} ${{ matrix.ndpid_gcrypt }} \
|
|
||||||
${{ matrix.sanitizer }} ${{ matrix.ndpid_examples }} ${{ matrix.ndpid_rust_examples }}; }
|
|
||||||
test $WGET_RET -ne 0 || { echo "Running Make.. (static)"; \
|
|
||||||
cmake --build ./build-local-static --verbose; }
|
|
||||||
test $WGET_RET -ne 0 || { echo "Testing Executable.. (static)"; \
|
|
||||||
./build-local-static/nDPId-test; \
|
|
||||||
./build-local-static/nDPId -h || test $? -eq 1; \
|
|
||||||
./build-local-static/nDPIsrvd -h || test $? -eq 1; }
|
|
||||||
test $WGET_RET -ne 0 || test ! -d ./PF_RING || { echo "Running CMake.. (PF_RING)"; \
|
|
||||||
cmake -S . -B ./build-local-pfring \
|
|
||||||
-DCMAKE_C_COMPILER="$CMAKE_C_COMPILER" -DCMAKE_C_FLAGS="$CMAKE_C_FLAGS" \
|
|
||||||
-DCMAKE_C_EXE_LINKER_FLAGS="$CMAKE_C_EXE_LINKER_FLAGS" \
|
|
||||||
-DBUILD_NDPI=OFF -DBUILD_EXAMPLES=ON -DENABLE_PFRING=ON \
|
|
||||||
-DENABLE_DBUS=ON -DENABLE_CURL=ON -DENABLE_SYSTEMD=ON \
|
|
||||||
-DNDPI_NO_PKGCONFIG=ON -DSTATIC_LIBNDPI_INSTALLDIR=/usr \
|
|
||||||
-DPFRING_LINK_STATIC=OFF \
|
|
||||||
-DPFRING_INSTALLDIR=/usr -DPFRING_KERNEL_INC="$(realpath ./PF_RING/kernel)" \
|
|
||||||
${{ matrix.poll }} ${{ matrix.coverage }} ${{ matrix.ndpid_gcrypt }} \
|
|
||||||
${{ matrix.sanitizer }} ${{ matrix.ndpid_examples }} ${{ matrix.ndpid_rust_examples }}; }
|
|
||||||
test $WGET_RET -ne 0 || test ! -d ./PF_RING || { echo "Running Make.. (PF_RING)"; \
|
|
||||||
cmake --build ./build-local-pfring --verbose; }
|
|
||||||
test $WGET_RET -ne 0 || test ! -d ./PF_RING || { echo "Testing Executable.. (PF_RING)"; \
|
|
||||||
./build-local-pfring/nDPId-test; \
|
|
||||||
./build-local-pfring/nDPId -h || test $? -eq 1; \
|
|
||||||
./build-local-pfring/nDPIsrvd -h || test $? -eq 1; }
|
|
||||||
test $WGET_RET -eq 0 -o $WGET_RET -eq 8
|
|
||||||
66
.github/workflows/sonarcloud.yml
vendored
66
.github/workflows/sonarcloud.yml
vendored
@@ -1,66 +0,0 @@
|
|||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- tmp
|
|
||||||
pull_request:
|
|
||||||
types: [opened, synchronize, reopened]
|
|
||||||
|
|
||||||
name: Sonarcloud Scan
|
|
||||||
jobs:
|
|
||||||
sonarcloud:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
env:
|
|
||||||
BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
- name: Set up Python 3.8 for gcovr
|
|
||||||
uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: 3.8
|
|
||||||
- name: install gcovr 5.0
|
|
||||||
run: |
|
|
||||||
pip install gcovr==5.0 # 5.1 is not supported
|
|
||||||
- name: Install sonar-scanner and build-wrapper
|
|
||||||
uses: SonarSource/sonarcloud-github-c-cpp@v3.2.0
|
|
||||||
- name: Install Prerequisites
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install autoconf automake cmake lcov \
|
|
||||||
libtool pkg-config gettext \
|
|
||||||
libjson-c-dev flex bison \
|
|
||||||
libcurl4-openssl-dev libpcap-dev zlib1g-dev
|
|
||||||
- name: Run build-wrapper
|
|
||||||
run: |
|
|
||||||
build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} ./scripts/build-sonarcloud.sh
|
|
||||||
- name: Run tests
|
|
||||||
run: |
|
|
||||||
for file in $(ls libnDPI/tests/cfgs/*/pcap/*.pcap libnDPI/tests/cfgs/*/pcap/*.pcapng libnDPI/tests/cfgs/*/pcap/*.cap); do \
|
|
||||||
echo -n "${file} "; \
|
|
||||||
cd ./build-sonarcloud; \
|
|
||||||
./nDPId-test "../${file}" >/dev/null 2>/dev/null; \
|
|
||||||
cd ..; \
|
|
||||||
echo "[ok]"; \
|
|
||||||
done
|
|
||||||
mkdir -p gcov_report
|
|
||||||
cd gcov_report
|
|
||||||
gcov ../build-sonarcloud/CMakeFiles/nDPId-test.dir/nDPId-test.c.o
|
|
||||||
cd ..
|
|
||||||
- name: Run sonar-scanner
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
|
||||||
run: |
|
|
||||||
sonar-scanner \
|
|
||||||
--define sonar.projectName=nDPId \
|
|
||||||
--define sonar.projectVersion=1.7 \
|
|
||||||
--define sonar.sourceEncoding=UTF-8 \
|
|
||||||
--define sonar.branch.name=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}} \
|
|
||||||
--define sonar.organization=lnslbrty \
|
|
||||||
--define sonar.projectKey=lnslbrty_nDPId \
|
|
||||||
--define sonar.python.version=3.8 \
|
|
||||||
--define sonar.cfamily.compile-commands=${{ env.BUILD_WRAPPER_OUT_DIR }}/compile_commands.json \
|
|
||||||
--define sonar.cfamily.gcov.reportsPath=gcov_report \
|
|
||||||
--define sonar.exclusions=build-sonarcloud/**,libnDPI/**,test/results/**,dependencies/jsmn/**,dependencies/uthash/**,examples/js-rt-analyzer-frontend/**,examples/js-rt-analyzer/**,examples/c-collectd/www/**,examples/py-flow-dashboard/assets/**
|
|
||||||
12
.gitignore
vendored
12
.gitignore
vendored
@@ -1,12 +0,0 @@
|
|||||||
# python related
|
|
||||||
*.pyc
|
|
||||||
__pycache__
|
|
||||||
|
|
||||||
# go related
|
|
||||||
*.sum
|
|
||||||
|
|
||||||
# lockfiles generated by some shell scripts
|
|
||||||
*.lock
|
|
||||||
|
|
||||||
# building folder
|
|
||||||
build
|
|
||||||
135
.gitlab-ci.yml
135
.gitlab-ci.yml
@@ -1,135 +0,0 @@
|
|||||||
image: debian:stable
|
|
||||||
|
|
||||||
stages:
|
|
||||||
- build_and_test
|
|
||||||
|
|
||||||
variables:
|
|
||||||
GIT_CLONE_PATH: '$CI_BUILDS_DIR/$CI_JOB_ID/$CI_PROJECT_NAME'
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
- export DEBIAN_FRONTEND=noninteractive
|
|
||||||
- apt-get update -qq
|
|
||||||
- >
|
|
||||||
apt-get install -y -qq \
|
|
||||||
coreutils sudo \
|
|
||||||
build-essential make cmake binutils gcc clang autoconf automake \
|
|
||||||
libtool pkg-config git wget unzip \
|
|
||||||
libpcap-dev libgpg-error-dev libjson-c-dev zlib1g-dev \
|
|
||||||
netcat-openbsd python3 python3-jsonschema tree lcov iproute2
|
|
||||||
|
|
||||||
after_script:
|
|
||||||
- test -r /tmp/nDPIsrvd.log && cat /tmp/nDPIsrvd.log
|
|
||||||
- test -r /tmp/nDPId.log && cat /tmp/nDPId.log
|
|
||||||
|
|
||||||
build_and_test_static_libndpi_tsan:
|
|
||||||
script:
|
|
||||||
# test for NETWORK_BUFFER_MAX_SIZE C and Python value equality
|
|
||||||
- C_VAL=$(cat config.h | sed -n 's/^#define\s\+NETWORK_BUFFER_MAX_SIZE\s\+\([0-9]\+\).*$/\1/gp')
|
|
||||||
- PY_VAL=$(cat dependencies/nDPIsrvd.py | sed -n 's/^NETWORK_BUFFER_MAX_SIZE = \([0-9]\+\).*$/\1/gp')
|
|
||||||
- test ${C_VAL} = ${PY_VAL}
|
|
||||||
# test for nDPId_PACKETS_PLEN_MAX C and Python value equality
|
|
||||||
- C_VAL=$(cat config.h | sed -n 's/^#define\s\+nDPId_PACKETS_PLEN_MAX\s\+\([0-9]\+\).*$/\1/gp')
|
|
||||||
- PY_VAL=$(cat dependencies/nDPIsrvd.py | sed -n 's/^nDPId_PACKETS_PLEN_MAX = \([0-9]\+\).*$/\1/gp')
|
|
||||||
- test ${C_VAL} = ${PY_VAL}
|
|
||||||
# static linked build
|
|
||||||
- mkdir build-clang-tsan
|
|
||||||
- cd build-clang-tsan
|
|
||||||
- env CMAKE_C_FLAGS='-Werror' CMAKE_C_COMPILER='clang' cmake .. -DBUILD_EXAMPLES=ON -DBUILD_NDPI=ON -DBUILD_NDPI_FORCE_GIT_UPDATE=ON -DENABLE_SANITIZER_THREAD=ON -DENABLE_ZLIB=ON
|
|
||||||
- make clean-libnDPI
|
|
||||||
- make libnDPI
|
|
||||||
- tree libnDPI
|
|
||||||
- make install VERBOSE=1 DESTDIR="$(realpath ../_install)"
|
|
||||||
- cd ..
|
|
||||||
- ./_install/usr/local/bin/nDPId-test
|
|
||||||
- ./test/run_tests.sh ./libnDPI ./_install/usr/local/bin/nDPId-test
|
|
||||||
artifacts:
|
|
||||||
expire_in: 1 week
|
|
||||||
paths:
|
|
||||||
- _install/
|
|
||||||
stage: build_and_test
|
|
||||||
|
|
||||||
build_and_test_static_libndpi:
|
|
||||||
script:
|
|
||||||
- mkdir build-cmake-submodule
|
|
||||||
- cd build-cmake-submodule
|
|
||||||
- env CMAKE_C_FLAGS='-Werror' cmake .. -DENABLE_SYSTEMD=ON -DBUILD_EXAMPLES=ON -DBUILD_NDPI=ON -DBUILD_NDPI_FORCE_GIT_UPDATE=ON -DENABLE_ZLIB=ON
|
|
||||||
- make clean-libnDPI
|
|
||||||
- make libnDPI
|
|
||||||
- tree libnDPI
|
|
||||||
- make install VERBOSE=1 DESTDIR="$(realpath ../_install)"
|
|
||||||
- cpack -G DEB
|
|
||||||
- sudo dpkg -i nDPId-*.deb
|
|
||||||
- cd ..
|
|
||||||
- test -x /bin/systemctl && sudo systemctl daemon-reload
|
|
||||||
- test -x /bin/systemctl && sudo systemctl enable ndpid@lo
|
|
||||||
- test -x /bin/systemctl && sudo systemctl start ndpid@lo
|
|
||||||
- test -x /bin/systemctl && sudo systemctl status ndpisrvd.service ndpid@lo.service
|
|
||||||
- test -x /bin/systemctl && sudo systemctl stop ndpid@lo
|
|
||||||
- ./build-cmake-submodule/nDPId-test
|
|
||||||
- ./test/run_tests.sh ./libnDPI ./build-cmake-submodule/nDPId-test
|
|
||||||
- >
|
|
||||||
if ldd ./build-cmake-submodule/nDPId | grep -qoEi libndpi; then \
|
|
||||||
echo 'nDPId linked against a static libnDPI should not contain a shared linked libnDPI.' >&2; false; fi
|
|
||||||
- cc -Wall -Wextra -std=gnu99 nDPId.c nio.c utils.c -I./build-cmake-submodule/libnDPI/include/ndpi -I. -I./dependencies -I./dependencies/jsmn -I./dependencies/uthash/include -o /tmp/a.out -lpcap ./build-cmake-submodule/libnDPI/lib/libndpi.a -pthread -lm -lz
|
|
||||||
artifacts:
|
|
||||||
expire_in: 1 week
|
|
||||||
paths:
|
|
||||||
- build-cmake-submodule/*.deb
|
|
||||||
- _install/
|
|
||||||
stage: build_and_test
|
|
||||||
|
|
||||||
build_and_test_static_libndpi_coverage:
|
|
||||||
script:
|
|
||||||
- mkdir build-cmake-submodule
|
|
||||||
- cd build-cmake-submodule
|
|
||||||
- env CMAKE_C_FLAGS='-Werror' cmake .. -DENABLE_SYSTEMD=ON -DENABLE_COVERAGE=ON -DBUILD_EXAMPLES=ON -DBUILD_NDPI=ON -DBUILD_NDPI_FORCE_GIT_UPDATE=ON -DENABLE_SANITIZER=ON -DENABLE_ZLIB=ON
|
|
||||||
- make clean-libnDPI
|
|
||||||
- make libnDPI
|
|
||||||
- tree libnDPI
|
|
||||||
- make install VERBOSE=1 DESTDIR="$(realpath ../_install)"
|
|
||||||
- cd ..
|
|
||||||
- ./build-cmake-submodule/nDPId-test
|
|
||||||
- ./test/run_tests.sh ./libnDPI ./build-cmake-submodule/nDPId-test
|
|
||||||
# generate coverage report
|
|
||||||
- make -C ./build-cmake-submodule coverage || true
|
|
||||||
- >
|
|
||||||
if ldd build/nDPId | grep -qoEi libndpi; then \
|
|
||||||
echo 'nDPId linked against a static libnDPI should not contain a shared linked libnDPI.' >&2; false; fi
|
|
||||||
artifacts:
|
|
||||||
expire_in: 1 week
|
|
||||||
paths:
|
|
||||||
- build-cmake-submodule/coverage_report
|
|
||||||
- _install/
|
|
||||||
stage: build_and_test
|
|
||||||
|
|
||||||
build_dynamic_libndpi:
|
|
||||||
script:
|
|
||||||
# pkg-config dynamic linked build
|
|
||||||
- git clone https://github.com/ntop/nDPI.git
|
|
||||||
- cd nDPI
|
|
||||||
- ./autogen.sh
|
|
||||||
- ./configure --prefix="$(realpath ../_install)" --enable-option-checking=fatal
|
|
||||||
- make install V=s
|
|
||||||
- cd ..
|
|
||||||
- tree ./_install
|
|
||||||
- mkdir build
|
|
||||||
- cd build
|
|
||||||
- export CMAKE_PREFIX_PATH="$(realpath ../_install)"
|
|
||||||
- env CMAKE_C_FLAGS='-Werror' cmake .. -DBUILD_EXAMPLES=ON -DENABLE_SANITIZER=ON -DENABLE_MEMORY_PROFILING=ON -DENABLE_ZLIB=ON
|
|
||||||
- make all VERBOSE=1
|
|
||||||
- make install VERBOSE=1 DESTDIR="$(realpath ../_install)"
|
|
||||||
- cd ..
|
|
||||||
- tree ./_install
|
|
||||||
- ./build/nDPId-test
|
|
||||||
- ./build/nDPId -h || test $? -eq 1
|
|
||||||
- ./build/nDPIsrvd -h || test $? -eq 1
|
|
||||||
# dameon start/stop test
|
|
||||||
- NUSER=nobody make -C ./build daemon VERBOSE=1
|
|
||||||
- NUSER=nobody make -C ./build daemon VERBOSE=1
|
|
||||||
# make dist
|
|
||||||
- make -C ./build dist
|
|
||||||
artifacts:
|
|
||||||
expire_in: 1 week
|
|
||||||
paths:
|
|
||||||
- _install/
|
|
||||||
stage: build_and_test
|
|
||||||
14
.gitmodules
vendored
14
.gitmodules
vendored
@@ -1,14 +0,0 @@
|
|||||||
[submodule "libnDPI"]
|
|
||||||
path = libnDPI
|
|
||||||
url = https://github.com/ntop/nDPI
|
|
||||||
branch = dev
|
|
||||||
update = rebase
|
|
||||||
[submodule "examples/js-rt-analyzer"]
|
|
||||||
path = examples/js-rt-analyzer
|
|
||||||
url = https://gitlab.com/verzulli/ndpid-rt-analyzer.git
|
|
||||||
[submodule "examples/js-rt-analyzer-frontend"]
|
|
||||||
path = examples/js-rt-analyzer-frontend
|
|
||||||
url = https://gitlab.com/verzulli/ndpid-rt-analyzer-frontend.git
|
|
||||||
[submodule "examples/cxx-graph"]
|
|
||||||
path = examples/cxx-graph
|
|
||||||
url = https://github.com/utoni/nDPId-Graph.git
|
|
||||||
83
CHANGELOG.md
83
CHANGELOG.md
@@ -1,83 +0,0 @@
|
|||||||
# CHANGELOG
|
|
||||||
|
|
||||||
#### nDPId 1.7 (Oct 2024)
|
|
||||||
|
|
||||||
- Read and parse configuration files for nDPId (+ libnDPI) and nDPIsrvd
|
|
||||||
- Added loading risk domains from a file (`-R`, thanks to @UnveilTech)
|
|
||||||
- Added Filebeat configuration file
|
|
||||||
- Improved hostname handling; will now always be part of `analyse`/`end`/`idle` events (if dissected)
|
|
||||||
- Improved Documentation (INSTALL / Schema)
|
|
||||||
- Added PF\_RING support
|
|
||||||
- Improved nDPIsrvd-analyse to write global stats to a CSV
|
|
||||||
- Added global (heap) memory stats for daemon status events (if enabled)
|
|
||||||
- Fixed IPv6 address/netmask retrieval on some systems
|
|
||||||
- Improved nDPIsrvd-collect; gauges and counters are now handled the right way
|
|
||||||
- Added nDPId Grafana dashboard
|
|
||||||
- Fixed `detection-update` event bug; was thrown even if nothing changed
|
|
||||||
- Fixed `not-detected` event spam if detection not completed (in some rare cases)
|
|
||||||
- Improved InfluxDB push daemon (severity parsing / gauge handling)
|
|
||||||
- Improved zLib compression
|
|
||||||
- Fixed nDPIsrvd-collectd missing escape character
|
|
||||||
|
|
||||||
#### nDPId 1.6 (Nov 2023)
|
|
||||||
|
|
||||||
- Added Event I/O abstraction layer (supporting only poll/epoll by now)
|
|
||||||
- Support for OSX and *BSD systems
|
|
||||||
- Added proper DLT_RAW dissection for IPv4 and IPv6
|
|
||||||
- Improved TCP timeout handling if FIN/RST seen which caused Midstream TCP flows when there shouldn't be any
|
|
||||||
- Fixed a crash if `nDPId -o value=''` was used
|
|
||||||
- Added OpenWrt packaging
|
|
||||||
- Added new flow event "analyse" used to give some statistical information about active flows
|
|
||||||
- Added new analyse event daemon which generates CSV files from such events
|
|
||||||
- Fixed a crash in nDPIsrvd if a collector closes a connection
|
|
||||||
- Support `nDPId` to send it's data to a UDP endpoint instead of a nDPIsrvd collector
|
|
||||||
- Added events and flow states documentation
|
|
||||||
- Added basic systemd support
|
|
||||||
- Fixed a bug in base64 encoding which could lead to invalid base64 strings
|
|
||||||
- Added some machine learning examples
|
|
||||||
- Fixed various smaller bugs
|
|
||||||
- Fixed nDPIsrvd bug which causes invalid JSON messages sent to Distributors
|
|
||||||
|
|
||||||
#### nDPId 1.5 (Apr 2022)
|
|
||||||
|
|
||||||
- Improved nDPId cross compilation
|
|
||||||
- zLib flow memory compression (Experimental!)
|
|
||||||
- Memory profiling for nDPId-test
|
|
||||||
- JSMN with parent link support for subtoken iteration
|
|
||||||
- Refactored nDPIsrvd buffer and buffer bloat handling
|
|
||||||
- Upgraded JSMN/uthash
|
|
||||||
- Improved nDPIsrvd.(h|py) debugging capability for client apps
|
|
||||||
- Advanced flow usage logging usable for memory profiling
|
|
||||||
- Support for dissection additional layer2/layer3 protocols
|
|
||||||
- Serialize more JSON information
|
|
||||||
- Add TCP/IP support for nDPIsrvd
|
|
||||||
- Improved nDPIsrvd connection lost behaviour
|
|
||||||
- Reworked Python/C distributor API
|
|
||||||
- Support read()/recv() timeouts and nonblocking I/O
|
|
||||||
|
|
||||||
|
|
||||||
#### nDPId 1.4 (Jun 2021)
|
|
||||||
|
|
||||||
- Use layer4 specific flow timeouts for nDPId
|
|
||||||
- Reworked layer4 flow length names and calculations (use only layer4 payload w/o any previous headers) for nDPId
|
|
||||||
- Build system cleanup and cosmetics
|
|
||||||
|
|
||||||
|
|
||||||
#### nDPId 1.3 (May 2021)
|
|
||||||
|
|
||||||
- Added missing datalink layer types
|
|
||||||
|
|
||||||
|
|
||||||
#### nDPId 1.2 (May 2021)
|
|
||||||
|
|
||||||
- OpenWrt compatible build system
|
|
||||||
|
|
||||||
|
|
||||||
#### nDPId 1.1 (May 2021)
|
|
||||||
|
|
||||||
- Added License information
|
|
||||||
|
|
||||||
|
|
||||||
#### nDPId 1.0 (May 2021)
|
|
||||||
|
|
||||||
- First public release
|
|
||||||
686
CMakeLists.txt
686
CMakeLists.txt
@@ -1,686 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 3.12.4)
|
|
||||||
project(nDPId C)
|
|
||||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
|
||||||
execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION)
|
|
||||||
if (GCC_VERSION VERSION_GREATER 4.7 OR GCC_VERSION VERSION_EQUAL 4.7)
|
|
||||||
message(STATUS "${CMAKE_C_COMPILER} supports C11 standard.")
|
|
||||||
else ()
|
|
||||||
message(FATAL_ERROR "C Compiler with C11 standard needed. Therefore a gcc compiler with a version equal or higher than 4.7 is needed.")
|
|
||||||
endif()
|
|
||||||
endif(CMAKE_COMPILER_IS_GNUCXX)
|
|
||||||
set(CMAKE_C_STANDARD 11)
|
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -D_DEFAULT_SOURCE=1 -D_GNU_SOURCE=1")
|
|
||||||
if("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}")
|
|
||||||
message(FATAL_ERROR "In-source builds are not allowed.\n"
|
|
||||||
"Please remove ${PROJECT_SOURCE_DIR}/CMakeCache.txt\n"
|
|
||||||
"and\n"
|
|
||||||
"${PROJECT_SOURCE_DIR}/CMakeFiles\n"
|
|
||||||
"Create a build directory somewhere and run CMake again.\n"
|
|
||||||
"Or run: 'cmake -S ${PROJECT_SOURCE_DIR} -B ./your-custom-build-dir [CMAKE-OPTIONS]'")
|
|
||||||
endif()
|
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
|
|
||||||
find_package(PkgConfig REQUIRED)
|
|
||||||
|
|
||||||
set(CPACK_PACKAGE_CONTACT "toni@impl.cc")
|
|
||||||
set(CPACK_DEBIAN_PACKAGE_NAME "nDPId")
|
|
||||||
set(CPACK_DEBIAN_PACKAGE_SECTION "network")
|
|
||||||
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "nDPId is a set of daemons and tools to capture, process and classify network traffic.")
|
|
||||||
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Toni Uhlig <toni@impl.cc>")
|
|
||||||
set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_SOURCE_DIR}/packages/debian/preinst;${CMAKE_SOURCE_DIR}/packages/debian/prerm;${CMAKE_SOURCE_DIR}/packages/debian/postrm")
|
|
||||||
set(CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION TRUE)
|
|
||||||
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
|
|
||||||
set(CPACK_DEBIAN_DEBUGINFO_PACKAGE ON)
|
|
||||||
set(CPACK_RPM_PACKAGE_LICENSE "GPL-3")
|
|
||||||
set(CPACK_RPM_PACKAGE_VENDOR "Toni Uhlig")
|
|
||||||
set(CPACK_RPM_PACKAGE_URL "https://www.github.com/utoni/nDPId.git")
|
|
||||||
set(CPACK_RPM_PACKAGE_DESCRIPTION "nDPId is a set of daemons and tools to capture, process and classify network traffic.")
|
|
||||||
set(CPACK_RPM_PRE_INSTALL_SCRIPT_FILE "${CMAKE_SOURCE_DIR}/packages/redhat/pre_install")
|
|
||||||
set(CPACK_RPM_PRE_UNINSTALL_SCRIPT_FILE "${CMAKE_SOURCE_DIR}/packages/redhat/pre_uninstall")
|
|
||||||
set(CPACK_RPM_POST_UNINSTALL_SCRIPT_FILE "${CMAKE_SOURCE_DIR}/packages/redhat/post_uninstall")
|
|
||||||
set(CPACK_STRIP_FILES ON)
|
|
||||||
set(CPACK_PACKAGE_VERSION_MAJOR 1)
|
|
||||||
set(CPACK_PACKAGE_VERSION_MINOR 7)
|
|
||||||
set(CPACK_PACKAGE_VERSION_PATCH 0)
|
|
||||||
# Note: CPACK_PACKAGING_INSTALL_PREFIX and CMAKE_INSTALL_PREFIX are *not* the same.
|
|
||||||
# It is used only to ease environment file loading via systemd.
|
|
||||||
set(CPACK_PACKAGING_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}")
|
|
||||||
set(CMAKE_MACOSX_RPATH 1)
|
|
||||||
|
|
||||||
include(CPack)
|
|
||||||
include(CheckFunctionExists)
|
|
||||||
include(CheckLibraryExists)
|
|
||||||
include(CheckEpoll)
|
|
||||||
|
|
||||||
check_epoll(HAS_EPOLL)
|
|
||||||
if(HAS_EPOLL)
|
|
||||||
option(FORCE_POLL "Force the use of poll() instead of epoll()." OFF)
|
|
||||||
if(NOT FORCE_POLL)
|
|
||||||
set(EPOLL_DEFS "-DENABLE_EPOLL=1")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
if(BUILD_EXAMPLES)
|
|
||||||
message(FATAL_ERROR "Examples are using epoll event I/O. Without epoll available, you can not build/run those.")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT MATH_FUNCTION_EXISTS AND NOT NEED_LINKING_AGAINST_LIBM)
|
|
||||||
CHECK_FUNCTION_EXISTS(log2f MATH_FUNCTION_EXISTS)
|
|
||||||
if(NOT MATH_FUNCTION_EXISTS)
|
|
||||||
unset(MATH_FUNCTION_EXISTS CACHE)
|
|
||||||
list(APPEND CMAKE_REQUIRED_LIBRARIES m)
|
|
||||||
CHECK_FUNCTION_EXISTS(log2f MATH_FUNCTION_EXISTS)
|
|
||||||
if(MATH_FUNCTION_EXISTS)
|
|
||||||
set(NEED_LINKING_AGAINST_LIBM TRUE CACHE BOOL "" FORCE)
|
|
||||||
else()
|
|
||||||
check_library_exists(m sqrt "" NEED_LINKING_AGAINST_LIBM)
|
|
||||||
if(NOT NEED_LINKING_AGAINST_LIBM)
|
|
||||||
# Was not able to figure out if explicit linkage against libm is required.
|
|
||||||
# Forcing libm linkage. Good idea?
|
|
||||||
set(NEED_LINKING_AGAINST_LIBM TRUE CACHE BOOL "" FORCE)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NEED_LINKING_AGAINST_LIBM)
|
|
||||||
set(LIBM_LIB "-lm")
|
|
||||||
else()
|
|
||||||
set(LIBM_LIB "")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
option(ENABLE_COVERAGE "Generate a code coverage report using lcov/genhtml." OFF)
|
|
||||||
option(ENABLE_SANITIZER "Enable ASAN/LSAN/UBSAN." OFF)
|
|
||||||
option(ENABLE_SANITIZER_THREAD "Enable TSAN (does not work together with ASAN)." OFF)
|
|
||||||
option(ENABLE_MEMORY_PROFILING "Enable dynamic memory tracking." OFF)
|
|
||||||
option(ENABLE_ZLIB "Enable zlib support for nDPId (experimental)." OFF)
|
|
||||||
option(ENABLE_SYSTEMD "Install systemd components." OFF)
|
|
||||||
option(ENABLE_CRYPTO "Enable OpenSSL cryptographic support in nDPId/nDPIsrvd." OFF)
|
|
||||||
option(BUILD_EXAMPLES "Build C examples." ON)
|
|
||||||
option(BUILD_RUST_EXAMPLES "Build Rust examples." OFF)
|
|
||||||
if(BUILD_EXAMPLES)
|
|
||||||
option(ENABLE_DBUS "Build DBus notification example." OFF)
|
|
||||||
option(ENABLE_CURL "Build influxdb data write example." OFF)
|
|
||||||
endif()
|
|
||||||
option(ENABLE_PFRING "Enable PF_RING support for nDPId (experimental)" OFF)
|
|
||||||
option(BUILD_NDPI "Clone and build nDPI from github." OFF)
|
|
||||||
|
|
||||||
if(ENABLE_PFRING)
|
|
||||||
option(PFRING_LINK_STATIC "Link against a static version of pfring." ON)
|
|
||||||
set(PFRING_KERNEL_INC "" CACHE STRING "Path to PFRING kernel module include directory.")
|
|
||||||
set(PFRING_DEFS "-DENABLE_PFRING=1")
|
|
||||||
|
|
||||||
if(PFRING_KERNEL_INC STREQUAL "")
|
|
||||||
message(FATAL_ERROR "PFRING_KERNEL_INC needs to be set to the PFRING kernel module include directory.")
|
|
||||||
endif()
|
|
||||||
if(NOT EXISTS "${PFRING_KERNEL_INC}/linux/pf_ring.h")
|
|
||||||
message(FATAL_ERROR "Expected to find <linux/pf_ring.h> below ${PFRING_KERNEL_INC}, but none found.")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(PFRING_INSTALLDIR "/opt/PF_RING/usr" CACHE STRING "")
|
|
||||||
set(PFRING_INC "${PFRING_INSTALLDIR}/include")
|
|
||||||
|
|
||||||
if(NOT EXISTS "${PFRING_INC}")
|
|
||||||
message(FATAL_ERROR "Include directory \"${PFRING_INC}\" does not exist!")
|
|
||||||
endif()
|
|
||||||
if(PFRING_LINK_STATIC)
|
|
||||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
|
||||||
if(EXISTS "${PFRING_INSTALLDIR}/lib64")
|
|
||||||
set(STATIC_PFRING_LIB "${PFRING_INSTALLDIR}/lib64/libpfring.a")
|
|
||||||
else()
|
|
||||||
set(STATIC_PFRING_LIB "${PFRING_INSTALLDIR}/lib/libpfring.a")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
if(EXISTS "${PFRING_INSTALLDIR}/lib32")
|
|
||||||
set(STATIC_PFRING_LIB "${PFRING_INSTALLDIR}/lib32/libpfring.a")
|
|
||||||
else()
|
|
||||||
set(STATIC_PFRING_LIB "${PFRING_INSTALLDIR}/lib/libpfring.a")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT EXISTS "${STATIC_PFRING_LIB}")
|
|
||||||
message(FATAL_ERROR "Static library \"${STATIC_PFRING_LIB}\" does not exist!")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
|
||||||
if(EXISTS "${PFRING_INSTALLDIR}/lib64")
|
|
||||||
find_library(PF_RING_LIB pfring PATHS "${PFRING_INSTALLDIR}/lib64")
|
|
||||||
else()
|
|
||||||
find_library(PF_RING_LIB pfring PATHS "${PFRING_INSTALLDIR}/lib")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
if(EXISTS "${PFRING_INSTALLDIR}/lib32")
|
|
||||||
find_library(PF_RING_LIB pfring PATHS "${PFRING_INSTALLDIR}/lib32")
|
|
||||||
else()
|
|
||||||
find_library(PF_RING_LIB pfring PATHS "${PFRING_INSTALLDIR}/lib")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT PF_RING_LIB)
|
|
||||||
message(FATAL_ERROR "libpfring.so not found below ${PFRING_INSTALLDIR}/{lib,lib32,lib64}")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT EXISTS "${PFRING_INSTALLDIR}/include/pfring.h")
|
|
||||||
message(FATAL_ERROR "Expected to find <include/pfring.h> inside ${PFRING_INSTALLDIR}, but none found.")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
unset(PFRING_INSTALLDIR CACHE)
|
|
||||||
unset(PFRING_INC CACHE)
|
|
||||||
unset(STATIC_PFRING_LIB CACHE)
|
|
||||||
unset(PFRING_LINK_STATIC CACHE)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(BUILD_NDPI)
|
|
||||||
option(BUILD_NDPI_FORCE_GIT_UPDATE "Forcefully instruments nDPI build script to update the git submodule." OFF)
|
|
||||||
unset(NDPI_NO_PKGCONFIG CACHE)
|
|
||||||
unset(STATIC_LIBNDPI_INSTALLDIR CACHE)
|
|
||||||
else()
|
|
||||||
option(NDPI_NO_PKGCONFIG "Do not use pkgconfig to search for libnDPI." OFF)
|
|
||||||
if(NDPI_NO_PKGCONFIG)
|
|
||||||
set(STATIC_LIBNDPI_INSTALLDIR "/opt/libnDPI/usr" CACHE STRING "Path to a installation directory of libnDPI e.g. /opt/libnDPI/usr")
|
|
||||||
if(STATIC_LIBNDPI_INSTALLDIR STREQUAL "")
|
|
||||||
message(FATAL_ERROR "STATIC_LIBNDPI_INSTALLDIR can not be an empty string within your configuration!")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
unset(STATIC_LIBNDPI_INSTALLDIR CACHE)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(STATIC_LIBNDPI_INSTALLDIR OR BUILD_NDPI OR NDPI_NO_PKGCONFIG)
|
|
||||||
option(NDPI_WITH_GCRYPT "Link static libndpi library against libgcrypt." OFF)
|
|
||||||
option(NDPI_WITH_PCRE "Link static libndpi library against libpcre." OFF)
|
|
||||||
option(NDPI_WITH_MAXMINDDB "Link static libndpi library against libmaxminddb." OFF)
|
|
||||||
else()
|
|
||||||
unset(NDPI_WITH_GCRYPT CACHE)
|
|
||||||
unset(NDPI_WITH_PCRE CACHE)
|
|
||||||
unset(NDPI_WITH_MAXMINDDB CACHE)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(ENABLE_PFRING)
|
|
||||||
set(NDPID_PFRING_SRCS npfring.c)
|
|
||||||
endif()
|
|
||||||
if(ENABLE_CRYPTO)
|
|
||||||
set(CRYPTO_SRCS ncrypt.c)
|
|
||||||
endif()
|
|
||||||
add_executable(nDPId nDPId.c ${NDPID_PFRING_SRCS} ${CRYPTO_SRCS} nio.c utils.c)
|
|
||||||
add_executable(nDPIsrvd nDPIsrvd.c nio.c utils.c)
|
|
||||||
add_executable(nDPId-test nDPId-test.c ${NDPID_PFRING_SRCS} ${CRYPTO_SRCS})
|
|
||||||
|
|
||||||
add_custom_target(umask_check)
|
|
||||||
add_custom_command(
|
|
||||||
TARGET umask_check
|
|
||||||
PRE_BUILD
|
|
||||||
COMMAND ${CMAKE_SOURCE_DIR}/scripts/umask-check.sh
|
|
||||||
)
|
|
||||||
add_dependencies(nDPId umask_check)
|
|
||||||
|
|
||||||
add_custom_target(dist)
|
|
||||||
add_custom_command(
|
|
||||||
TARGET dist
|
|
||||||
PRE_BUILD
|
|
||||||
COMMAND "${CMAKE_SOURCE_DIR}/scripts/make-dist.sh"
|
|
||||||
)
|
|
||||||
|
|
||||||
add_custom_target(daemon)
|
|
||||||
add_custom_command(
|
|
||||||
TARGET daemon
|
|
||||||
POST_BUILD
|
|
||||||
COMMAND env nDPIsrvd_ARGS='-C 1024' "${CMAKE_SOURCE_DIR}/scripts/daemon.sh" "$<TARGET_FILE:nDPId>" "$<TARGET_FILE:nDPIsrvd>"
|
|
||||||
)
|
|
||||||
add_dependencies(daemon nDPId nDPIsrvd)
|
|
||||||
|
|
||||||
if(CMAKE_CROSSCOMPILING)
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(BUILD_NDPI)
|
|
||||||
enable_testing()
|
|
||||||
add_test(NAME run_tests
|
|
||||||
COMMAND "${CMAKE_SOURCE_DIR}/test/run_tests.sh"
|
|
||||||
"${CMAKE_SOURCE_DIR}/libnDPI"
|
|
||||||
"$<TARGET_FILE:nDPId-test>")
|
|
||||||
if(NDPI_WITH_PCRE OR NDPI_WITH_MAXMINDDB)
|
|
||||||
message(WARNING "NDPI_WITH_PCRE or NDPI_WITH_MAXMINDDB enabled.\n"
|
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/test/run_tests.sh or ctest will fail!")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(ENABLE_COVERAGE)
|
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage")
|
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")
|
|
||||||
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} --coverage")
|
|
||||||
add_custom_target(coverage DEPENDS "${CMAKE_BINARY_DIR}/coverage_report/nDPId/index.html")
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT "${CMAKE_BINARY_DIR}/coverage_report/nDPId/index.html"
|
|
||||||
COMMAND lcov --directory "${CMAKE_BINARY_DIR}" --directory "${CMAKE_SOURCE_DIR}/libnDPI" --capture --output-file "${CMAKE_BINARY_DIR}/lcov.info"
|
|
||||||
COMMAND genhtml -o "${CMAKE_BINARY_DIR}/coverage_report" "${CMAKE_BINARY_DIR}/lcov.info"
|
|
||||||
DEPENDS nDPId nDPId-test nDPIsrvd
|
|
||||||
)
|
|
||||||
add_custom_target(coverage-clean)
|
|
||||||
add_custom_command(
|
|
||||||
TARGET coverage-clean
|
|
||||||
COMMAND find "${CMAKE_BINARY_DIR}" "${CMAKE_SOURCE_DIR}/libnDPI" -name "*.gcda" -delete
|
|
||||||
POST_BUILD
|
|
||||||
)
|
|
||||||
add_custom_target(coverage-view)
|
|
||||||
add_custom_command(
|
|
||||||
TARGET coverage-view
|
|
||||||
COMMAND cd "${CMAKE_BINARY_DIR}/coverage_report" && python3 -m http.server
|
|
||||||
POST_BUILD
|
|
||||||
)
|
|
||||||
add_dependencies(coverage-view coverage)
|
|
||||||
endif()
|
|
||||||
if(ENABLE_SANITIZER)
|
|
||||||
# TODO: Check for `-fsanitize-memory-track-origins` and add if available?
|
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fsanitize=undefined -fno-sanitize=alignment -fsanitize=enum -fsanitize=leak")
|
|
||||||
endif()
|
|
||||||
if(ENABLE_SANITIZER_THREAD)
|
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined -fno-sanitize=alignment -fsanitize=enum -fsanitize=thread")
|
|
||||||
endif()
|
|
||||||
if(ENABLE_ZLIB)
|
|
||||||
set(ZLIB_DEFS "-DENABLE_ZLIB=1")
|
|
||||||
pkg_check_modules(ZLIB REQUIRED zlib)
|
|
||||||
endif()
|
|
||||||
if(BUILD_EXAMPLES)
|
|
||||||
if(ENABLE_DBUS)
|
|
||||||
pkg_check_modules(DBUS REQUIRED dbus-1)
|
|
||||||
endif()
|
|
||||||
if(ENABLE_CURL)
|
|
||||||
pkg_check_modules(CURL REQUIRED libcurl)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
if(BUILD_NDPI)
|
|
||||||
if(NDPI_WITH_GCRYPT)
|
|
||||||
message(STATUS "nDPI: Enable GCRYPT")
|
|
||||||
set(NDPI_ADDITIONAL_ARGS "${NDPI_ADDITIONAL_ARGS} --with-local-libgcrypt")
|
|
||||||
endif()
|
|
||||||
if(NDPI_WITH_PCRE)
|
|
||||||
message(STATUS "nDPI: Enable PCRE")
|
|
||||||
set(NDPI_ADDITIONAL_ARGS "${NDPI_ADDITIONAL_ARGS} --with-pcre2")
|
|
||||||
endif()
|
|
||||||
if(NDPI_WITH_MAXMINDDB)
|
|
||||||
message(STATUS "nDPI: Enable MAXMINDDB")
|
|
||||||
set(NDPI_ADDITIONAL_ARGS "${NDPI_ADDITIONAL_ARGS} --with-maxminddb")
|
|
||||||
endif()
|
|
||||||
if(ENABLE_COVERAGE)
|
|
||||||
message(STATUS "nDPI: Enable Coverage")
|
|
||||||
set(NDPI_ADDITIONAL_ARGS "${NDPI_ADDITIONAL_ARGS} --enable-code-coverage")
|
|
||||||
endif()
|
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "")
|
|
||||||
message(STATUS "nDPI: Enable Debug Build")
|
|
||||||
set(NDPI_ADDITIONAL_ARGS "${NDPI_ADDITIONAL_ARGS} --enable-debug-build --enable-debug-messages")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
execute_process(
|
|
||||||
COMMAND git describe --tags
|
|
||||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
|
||||||
OUTPUT_VARIABLE GIT_VERSION ERROR_QUIET)
|
|
||||||
string(STRIP "${GIT_VERSION}" GIT_VERSION)
|
|
||||||
if(GIT_VERSION STREQUAL "" OR NOT IS_DIRECTORY "${CMAKE_SOURCE_DIR}/.git")
|
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "")
|
|
||||||
set(GIT_VERSION "${CPACK_PACKAGE_VERSION}-pre")
|
|
||||||
else()
|
|
||||||
set(GIT_VERSION "${CPACK_PACKAGE_VERSION}-release")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
set(PKG_VERSION "${CPACK_PACKAGE_VERSION}")
|
|
||||||
|
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra")
|
|
||||||
set(NDPID_DEFS -DJSMN_STATIC=1 -DJSMN_STRICT=1 -DJSMN_PARENT_LINKS=1)
|
|
||||||
set(NDPID_DEPS_INC "${CMAKE_SOURCE_DIR}"
|
|
||||||
"${CMAKE_SOURCE_DIR}/dependencies"
|
|
||||||
"${CMAKE_SOURCE_DIR}/dependencies/jsmn"
|
|
||||||
"${CMAKE_SOURCE_DIR}/dependencies/uthash/src")
|
|
||||||
if(CMAKE_CROSSCOMPILING)
|
|
||||||
add_definitions("-DCROSS_COMPILATION=1")
|
|
||||||
endif()
|
|
||||||
if(ENABLE_MEMORY_PROFILING)
|
|
||||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT CMAKE_BUILD_TYPE STREQUAL "")
|
|
||||||
message(WARNING "ENABLE_MEMORY_PROFILING should not be used in production environments.")
|
|
||||||
endif()
|
|
||||||
add_definitions("-DENABLE_MEMORY_PROFILING=1"
|
|
||||||
"-Duthash_malloc=nDPIsrvd_uthash_malloc"
|
|
||||||
"-Duthash_free=nDPIsrvd_uthash_free")
|
|
||||||
else()
|
|
||||||
set(NDPID_TEST_MPROF_DEFS "-DENABLE_MEMORY_PROFILING=1")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "")
|
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -g3 -fno-omit-frame-pointer -fno-inline")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(ENABLE_SANITIZER AND ENABLE_SANITIZER_THREAD)
|
|
||||||
message(FATAL_ERROR "ENABLE_SANITIZER and ENABLE_SANITIZER_THREAD can not be used together!")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(BUILD_NDPI)
|
|
||||||
include(ExternalProject)
|
|
||||||
ExternalProject_Add(
|
|
||||||
libnDPI
|
|
||||||
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libnDPI
|
|
||||||
DOWNLOAD_COMMAND ""
|
|
||||||
CONFIGURE_COMMAND env
|
|
||||||
CC=${CMAKE_C_COMPILER}
|
|
||||||
CXX=false
|
|
||||||
AR=${CMAKE_AR}
|
|
||||||
RANLIB=${CMAKE_RANLIB}
|
|
||||||
PKG_CONFIG=${PKG_CONFIG_EXECUTABLE}
|
|
||||||
CFLAGS=${CMAKE_C_FLAGS}
|
|
||||||
LDFLAGS=${CMAKE_MODULE_LINKER_FLAGS}
|
|
||||||
ADDITIONAL_ARGS=${NDPI_ADDITIONAL_ARGS}
|
|
||||||
MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}
|
|
||||||
DEST_INSTALL=${CMAKE_BINARY_DIR}/libnDPI
|
|
||||||
FORCE_GIT_UPDATE=${BUILD_NDPI_FORCE_GIT_UPDATE}
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/scripts/get-and-build-libndpi.sh
|
|
||||||
BUILD_BYPRODUCTS ${CMAKE_BINARY_DIR}/libnDPI/lib/libndpi.a
|
|
||||||
BUILD_COMMAND ""
|
|
||||||
INSTALL_COMMAND ""
|
|
||||||
BUILD_IN_SOURCE 1)
|
|
||||||
|
|
||||||
add_custom_target(clean-libnDPI
|
|
||||||
COMMAND ${CMAKE_BUILD_TOOL} clean
|
|
||||||
COMMAND rm -rf ${CMAKE_BINARY_DIR}/libnDPI
|
|
||||||
COMMAND rm -f ${CMAKE_BINARY_DIR}/libnDPI-prefix/src/libnDPI-stamp/libnDPI-configure
|
|
||||||
)
|
|
||||||
|
|
||||||
set(STATIC_LIBNDPI_INSTALLDIR "${CMAKE_BINARY_DIR}/libnDPI")
|
|
||||||
add_dependencies(nDPId libnDPI)
|
|
||||||
add_dependencies(nDPId-test libnDPI)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(ENABLE_CRYPTO)
|
|
||||||
find_package(OpenSSL REQUIRED)
|
|
||||||
set(OSSL_DEFS "-DENABLE_CRYPTO=1")
|
|
||||||
set(OSSL_LIBRARY "${OPENSSL_SSL_LIBRARY}" "${OPENSSL_CRYPTO_LIBRARY}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(STATIC_LIBNDPI_INSTALLDIR OR BUILD_NDPI OR NDPI_NO_PKGCONFIG)
|
|
||||||
if(NDPI_WITH_GCRYPT)
|
|
||||||
find_package(GCRYPT "1.4.2" REQUIRED)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NDPI_WITH_PCRE)
|
|
||||||
pkg_check_modules(PCRE REQUIRED libpcre2-8)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NDPI_WITH_MAXMINDDB)
|
|
||||||
pkg_check_modules(MAXMINDDB REQUIRED libmaxminddb)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(STATIC_LIBNDPI_INSTALLDIR OR BUILD_NDPI)
|
|
||||||
add_definitions("-DLIBNDPI_STATIC=1")
|
|
||||||
set(STATIC_LIBNDPI_INC "${STATIC_LIBNDPI_INSTALLDIR}/include/ndpi")
|
|
||||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
|
||||||
if(EXISTS "${STATIC_LIBNDPI_INSTALLDIR}/lib64/libndpi.a")
|
|
||||||
set(STATIC_LIBNDPI_LIB "${STATIC_LIBNDPI_INSTALLDIR}/lib64/libndpi.a")
|
|
||||||
else()
|
|
||||||
set(STATIC_LIBNDPI_LIB "${STATIC_LIBNDPI_INSTALLDIR}/lib/libndpi.a")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
if(EXISTS "${STATIC_LIBNDPI_INSTALLDIR}/lib32/libndpi.a")
|
|
||||||
set(STATIC_LIBNDPI_LIB "${STATIC_LIBNDPI_INSTALLDIR}/lib32/libndpi.a")
|
|
||||||
else()
|
|
||||||
set(STATIC_LIBNDPI_LIB "${STATIC_LIBNDPI_INSTALLDIR}/lib/libndpi.a")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(STATIC_LIBNDPI_INSTALLDIR AND NOT BUILD_NDPI)
|
|
||||||
if(NOT EXISTS "${STATIC_LIBNDPI_INC}" OR NOT EXISTS "${STATIC_LIBNDPI_LIB}")
|
|
||||||
message(FATAL_ERROR "Include directory \"${STATIC_LIBNDPI_INC}\" or\n"
|
|
||||||
"static library \"${STATIC_LIBNDPI_LIB}\" does not exist!")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
unset(DEFAULT_NDPI_INCLUDE CACHE)
|
|
||||||
unset(pkgcfg_lib_NDPI_ndpi CACHE)
|
|
||||||
else()
|
|
||||||
if(NOT NDPI_NO_PKGCONFIG)
|
|
||||||
pkg_check_modules(NDPI REQUIRED libndpi>=5.0.0)
|
|
||||||
if(NOT pkgcfg_lib_NDPI_ndpi)
|
|
||||||
find_package(NDPI "5.0.0" REQUIRED)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
unset(STATIC_LIBNDPI_INC CACHE)
|
|
||||||
unset(STATIC_LIBNDPI_LIB CACHE)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(DEFAULT_NDPI_INCLUDE ${NDPI_INCLUDE_DIRS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
pkg_check_modules(PCAP libpcap>=1.9.0) # no *.pc file before 1.9.0
|
|
||||||
if(NOT pkgcfg_lib_PCAP_pcap)
|
|
||||||
pkg_check_modules(PCAP libpcap>=1.8.1) # seems like some distributions provide their own *.pc file for 1.8.1 (e.g. Ubuntu-18.04)
|
|
||||||
endif()
|
|
||||||
if(NOT pkgcfg_lib_PCAP_pcap)
|
|
||||||
find_package(PCAP "1.9.0" REQUIRED)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_compile_options(nDPId PRIVATE "-pthread")
|
|
||||||
target_compile_definitions(nDPId PRIVATE -D_GNU_SOURCE=1 -DPKG_VERSION=\"${PKG_VERSION}\" -DGIT_VERSION=\"${GIT_VERSION}\"
|
|
||||||
${NDPID_DEFS} ${EPOLL_DEFS} ${ZLIB_DEFS} ${PFRING_DEFS} ${OSSL_DEFS})
|
|
||||||
target_include_directories(nDPId PRIVATE "${STATIC_LIBNDPI_INC}" "${DEFAULT_NDPI_INCLUDE}" ${NDPID_DEPS_INC} ${PFRING_KERNEL_INC} ${PFRING_INC})
|
|
||||||
target_link_libraries(nDPId "${STATIC_LIBNDPI_LIB}" "${STATIC_PFRING_LIB}" "${pkgcfg_lib_PCAP_pcap}" "${pkgcfg_lib_NDPI_ndpi}"
|
|
||||||
"${pkgcfg_lib_PCRE_pcre2-8}" "${pkgcfg_lib_MAXMINDDB_maxminddb}" "${pkgcfg_lib_ZLIB_z}"
|
|
||||||
"${GCRYPT_LIBRARY}" "${GCRYPT_ERROR_LIBRARY}" "${PCAP_LIBRARY}" "${LIBM_LIB}" "${PF_RING_LIB}"
|
|
||||||
"${OSSL_LIBRARY}" "-pthread")
|
|
||||||
|
|
||||||
target_compile_definitions(nDPIsrvd PRIVATE -D_GNU_SOURCE=1 -DPKG_VERSION=\"${PKG_VERSION}\" -DGIT_VERSION=\"${GIT_VERSION}\" ${NDPID_DEFS} ${EPOLL_DEFS})
|
|
||||||
target_include_directories(nDPIsrvd PRIVATE ${NDPID_DEPS_INC})
|
|
||||||
|
|
||||||
target_compile_options(nDPId-test PRIVATE "-Wno-unused-function" "-pthread")
|
|
||||||
target_compile_definitions(nDPId-test PRIVATE -D_GNU_SOURCE=1 -DNO_MAIN=1 -DPKG_VERSION=\"${PKG_VERSION}\" -DGIT_VERSION=\"${GIT_VERSION}\"
|
|
||||||
${NDPID_DEFS} ${EPOLL_DEFS} ${ZLIB_DEFS} ${PFRING_DEFS} ${OSSL_DEFS} ${NDPID_TEST_MPROF_DEFS})
|
|
||||||
target_include_directories(nDPId-test PRIVATE
|
|
||||||
"${STATIC_LIBNDPI_INC}" "${DEFAULT_NDPI_INCLUDE}" ${NDPID_DEPS_INC} ${PFRING_KERNEL_INC} ${PFRING_INC})
|
|
||||||
target_link_libraries(nDPId-test "${STATIC_LIBNDPI_LIB}" "${STATIC_PFRING_LIB}" "${pkgcfg_lib_PCAP_pcap}" "${pkgcfg_lib_NDPI_ndpi}"
|
|
||||||
"${pkgcfg_lib_PCRE_pcre2-8}" "${pkgcfg_lib_MAXMINDDB_maxminddb}" "${pkgcfg_lib_ZLIB_z}"
|
|
||||||
"${GCRYPT_LIBRARY}" "${GCRYPT_ERROR_LIBRARY}" "${PCAP_LIBRARY}" "${LIBM_LIB}" "${PF_RING_LIB}"
|
|
||||||
"${OSSL_LIBRARY}" "-pthread")
|
|
||||||
|
|
||||||
if(CMAKE_C_COMPILER_ID STREQUAL "Clang")
|
|
||||||
add_executable(fuzz_ndpi_process_packet test/fuzz_ndpi_process_packet.c)
|
|
||||||
if(BUILD_NDPI)
|
|
||||||
add_dependencies(fuzz_ndpi_process_packet libnDPI)
|
|
||||||
endif()
|
|
||||||
target_compile_options(fuzz_ndpi_process_packet PRIVATE "-Wno-unused-function" "-fsanitize=fuzzer" "-pthread")
|
|
||||||
target_compile_definitions(fuzz_ndpi_process_packet PRIVATE -D_GNU_SOURCE=1
|
|
||||||
-DPKG_VERSION=\"${PKG_VERSION}\" -DGIT_VERSION=\"${GIT_VERSION}\"
|
|
||||||
${NDPID_DEFS} ${EPOLL_DEFS} ${ZLIB_DEFS} ${PFRING_DEFS})
|
|
||||||
target_include_directories(fuzz_ndpi_process_packet PRIVATE "${STATIC_LIBNDPI_INC}" "${DEFAULT_NDPI_INCLUDE}"
|
|
||||||
${NDPID_DEPS_INC} ${PFRING_KERNEL_INC} ${PFRING_INC})
|
|
||||||
target_link_libraries(fuzz_ndpi_process_packet "${STATIC_LIBNDPI_LIB}" "${STATIC_PFRING_LIB}" "${pkgcfg_lib_PCAP_pcap}" "${pkgcfg_lib_NDPI_ndpi}"
|
|
||||||
"${pkgcfg_lib_PCRE_pcre2-8}" "${pkgcfg_lib_MAXMINDDB_maxminddb}" "${pkgcfg_lib_ZLIB_z}"
|
|
||||||
"${GCRYPT_LIBRARY}" "${GCRYPT_ERROR_LIBRARY}" "${PCAP_LIBRARY}" "${LIBM_LIB}" "${PF_RING_LIB}"
|
|
||||||
"-pthread")
|
|
||||||
target_link_options(fuzz_ndpi_process_packet PRIVATE "-fsanitize=fuzzer")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(BUILD_EXAMPLES)
|
|
||||||
add_executable(nDPIsrvd-collectd examples/c-collectd/c-collectd.c utils.c)
|
|
||||||
if(BUILD_NDPI)
|
|
||||||
add_dependencies(nDPIsrvd-collectd libnDPI)
|
|
||||||
endif()
|
|
||||||
target_compile_definitions(nDPIsrvd-collectd PRIVATE ${NDPID_DEFS})
|
|
||||||
target_include_directories(nDPIsrvd-collectd PRIVATE
|
|
||||||
"${STATIC_LIBNDPI_INC}" "${DEFAULT_NDPI_INCLUDE}" "${CMAKE_SOURCE_DIR}" ${NDPID_DEPS_INC})
|
|
||||||
|
|
||||||
add_executable(nDPIsrvd-captured examples/c-captured/c-captured.c utils.c)
|
|
||||||
if(BUILD_NDPI)
|
|
||||||
add_dependencies(nDPIsrvd-captured libnDPI)
|
|
||||||
endif()
|
|
||||||
target_compile_definitions(nDPIsrvd-captured PRIVATE ${NDPID_DEFS})
|
|
||||||
target_include_directories(nDPIsrvd-captured PRIVATE
|
|
||||||
"${STATIC_LIBNDPI_INC}" "${DEFAULT_NDPI_INCLUDE}" "${CMAKE_SOURCE_DIR}" ${NDPID_DEPS_INC})
|
|
||||||
target_link_libraries(nDPIsrvd-captured "${pkgcfg_lib_PCAP_pcap}" "${pkgcfg_lib_NDPI_ndpi}"
|
|
||||||
"${pkgcfg_lib_PCRE_pcre2-8}" "${pkgcfg_lib_MAXMINDDB_maxminddb}"
|
|
||||||
"${GCRYPT_LIBRARY}" "${GCRYPT_ERROR_LIBRARY}" "${PCAP_LIBRARY}")
|
|
||||||
|
|
||||||
add_executable(nDPIsrvd-analysed examples/c-analysed/c-analysed.c utils.c)
|
|
||||||
if(BUILD_NDPI)
|
|
||||||
add_dependencies(nDPIsrvd-analysed libnDPI)
|
|
||||||
endif()
|
|
||||||
target_compile_definitions(nDPIsrvd-analysed PRIVATE ${NDPID_DEFS})
|
|
||||||
target_include_directories(nDPIsrvd-analysed PRIVATE
|
|
||||||
"${STATIC_LIBNDPI_INC}" "${DEFAULT_NDPI_INCLUDE}" "${CMAKE_SOURCE_DIR}" ${NDPID_DEPS_INC})
|
|
||||||
|
|
||||||
add_executable(nDPIsrvd-simple examples/c-simple/c-simple.c)
|
|
||||||
target_compile_definitions(nDPIsrvd-simple PRIVATE ${NDPID_DEFS})
|
|
||||||
target_include_directories(nDPIsrvd-simple PRIVATE ${NDPID_DEPS_INC})
|
|
||||||
|
|
||||||
if(ENABLE_COVERAGE)
|
|
||||||
add_dependencies(coverage nDPIsrvd-analysed nDPIsrvd-collectd nDPIsrvd-captured nDPIsrvd-simple)
|
|
||||||
if(BUILD_NDPI)
|
|
||||||
add_dependencies(coverage libnDPI)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(ENABLE_DBUS)
|
|
||||||
add_executable(nDPIsrvd-notifyd examples/c-notifyd/c-notifyd.c utils.c)
|
|
||||||
if(BUILD_NDPI)
|
|
||||||
add_dependencies(nDPIsrvd-notifyd libnDPI)
|
|
||||||
endif()
|
|
||||||
target_compile_definitions(nDPIsrvd-notifyd PRIVATE ${NDPID_DEFS})
|
|
||||||
target_include_directories(nDPIsrvd-notifyd PRIVATE
|
|
||||||
"${STATIC_LIBNDPI_INC}" "${DEFAULT_NDPI_INCLUDE}" "${CMAKE_SOURCE_DIR}" "${NDPID_DEPS_INC}"
|
|
||||||
"${DBUS_INCLUDE_DIRS}")
|
|
||||||
target_link_libraries(nDPIsrvd-notifyd "${DBUS_LIBRARIES}")
|
|
||||||
install(TARGETS nDPIsrvd-notifyd DESTINATION bin)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(ENABLE_CURL)
|
|
||||||
add_executable(nDPIsrvd-influxd examples/c-influxd/c-influxd.c utils.c)
|
|
||||||
if(BUILD_NDPI)
|
|
||||||
add_dependencies(nDPIsrvd-influxd libnDPI)
|
|
||||||
endif()
|
|
||||||
target_compile_definitions(nDPIsrvd-influxd PRIVATE ${NDPID_DEFS})
|
|
||||||
target_include_directories(nDPIsrvd-influxd PRIVATE
|
|
||||||
"${STATIC_LIBNDPI_INC}" "${DEFAULT_NDPI_INCLUDE}" "${CMAKE_SOURCE_DIR}" "${NDPID_DEPS_INC}"
|
|
||||||
"${CURL_INCLUDE_DIRS}")
|
|
||||||
target_link_libraries(nDPIsrvd-influxd "${CURL_LIBRARIES}")
|
|
||||||
install(TARGETS nDPIsrvd-influxd DESTINATION bin)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
install(TARGETS nDPIsrvd-analysed nDPIsrvd-collectd nDPIsrvd-captured nDPIsrvd-simple DESTINATION bin)
|
|
||||||
install(FILES examples/c-collectd/plugin_nDPIsrvd.conf examples/c-collectd/rrdgraph.sh DESTINATION share/nDPId/nDPIsrvd-collectd)
|
|
||||||
install(DIRECTORY examples/c-collectd/www DESTINATION share/nDPId/nDPIsrvd-collectd)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(BUILD_RUST_EXAMPLES)
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/target/release/rs-simple
|
|
||||||
COMMAND cargo build --release
|
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/examples/rs-simple
|
|
||||||
COMMENT "Build Rust executable with cargo: rs-simple"
|
|
||||||
)
|
|
||||||
add_custom_target(rs-simple ALL
|
|
||||||
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/target/release/rs-simple
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(ENABLE_SYSTEMD)
|
|
||||||
configure_file(packages/systemd/ndpisrvd.service.in ndpisrvd.service @ONLY)
|
|
||||||
configure_file(packages/systemd/ndpid@.service.in ndpid@.service @ONLY)
|
|
||||||
|
|
||||||
install(DIRECTORY DESTINATION etc/nDPId)
|
|
||||||
install(FILES "ndpid.conf.example" DESTINATION share/nDPId)
|
|
||||||
install(FILES "ndpisrvd.conf.example" DESTINATION share/nDPId)
|
|
||||||
install(FILES "${CMAKE_BINARY_DIR}/ndpisrvd.service" DESTINATION lib/systemd/system)
|
|
||||||
install(FILES "${CMAKE_BINARY_DIR}/ndpid@.service" DESTINATION lib/systemd/system)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
install(FILES config.h
|
|
||||||
dependencies/nDPIsrvd.h
|
|
||||||
dependencies/jsmn/jsmn.h
|
|
||||||
dependencies/uthash/src/utarray.h
|
|
||||||
dependencies/uthash/src/uthash.h
|
|
||||||
dependencies/uthash/src/utlist.h
|
|
||||||
dependencies/uthash/src/utringbuffer.h
|
|
||||||
dependencies/uthash/src/utstack.h
|
|
||||||
dependencies/uthash/src/utstring.h
|
|
||||||
DESTINATION include/nDPId)
|
|
||||||
install(TARGETS nDPId DESTINATION sbin)
|
|
||||||
install(TARGETS nDPIsrvd nDPId-test DESTINATION bin)
|
|
||||||
if(BUILD_EXAMPLES)
|
|
||||||
install(FILES dependencies/nDPIsrvd.py
|
|
||||||
DESTINATION share/nDPId)
|
|
||||||
install(FILES examples/py-flow-info/flow-info.py
|
|
||||||
DESTINATION bin RENAME nDPIsrvd-flow-info.py
|
|
||||||
PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
|
||||||
install(FILES examples/py-json-stdout/json-stdout.py
|
|
||||||
DESTINATION bin RENAME nDPIsrvd-json-stdout.py
|
|
||||||
PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
|
||||||
install(FILES examples/py-schema-validation/py-schema-validation.py
|
|
||||||
DESTINATION bin RENAME nDPIsrvd-schema-validation.py
|
|
||||||
PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
|
||||||
install(FILES examples/py-semantic-validation/py-semantic-validation.py
|
|
||||||
DESTINATION bin RENAME nDPIsrvd-semantic-validation.py
|
|
||||||
PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
|
||||||
install(FILES examples/py-machine-learning/sklearn-random-forest.py
|
|
||||||
DESTINATION bin RENAME nDPIsrvd-sklearn.py
|
|
||||||
PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
|
||||||
endif()
|
|
||||||
install(FILES schema/error_event_schema.json schema/daemon_event_schema.json
|
|
||||||
schema/flow_event_schema.json schema/packet_event_schema.json DESTINATION share/nDPId/json-schema)
|
|
||||||
|
|
||||||
message(STATUS "--------------------------")
|
|
||||||
message(STATUS "nDPId GIT_VERSION........: ${GIT_VERSION}")
|
|
||||||
message(STATUS "Cross Compilation........: ${CMAKE_CROSSCOMPILING}")
|
|
||||||
message(STATUS "CMAKE_BUILD_TYPE.........: ${CMAKE_BUILD_TYPE}")
|
|
||||||
message(STATUS "CMAKE_C_FLAGS............: ${CMAKE_C_FLAGS}")
|
|
||||||
message(STATUS "NDPID_DEFS...............: ${NDPID_DEFS}")
|
|
||||||
message(STATUS "FORCE_POLL...............: ${FORCE_POLL}")
|
|
||||||
message(STATUS "ENABLE_PFRING............: ${ENABLE_PFRING}")
|
|
||||||
if(ENABLE_PFRING)
|
|
||||||
message(STATUS "PFRING_LINK_STATIC.......: ${PFRING_LINK_STATIC}")
|
|
||||||
endif()
|
|
||||||
message(STATUS "ENABLE_CRYPTO............: ${ENABLE_CRYPTO}")
|
|
||||||
message(STATUS "ENABLE_COVERAGE..........: ${ENABLE_COVERAGE}")
|
|
||||||
message(STATUS "ENABLE_SANITIZER.........: ${ENABLE_SANITIZER}")
|
|
||||||
message(STATUS "ENABLE_SANITIZER_THREAD..: ${ENABLE_SANITIZER_THREAD}")
|
|
||||||
message(STATUS "ENABLE_MEMORY_PROFILING..: ${ENABLE_MEMORY_PROFILING}")
|
|
||||||
message(STATUS "ENABLE_ZLIB..............: ${ENABLE_ZLIB}")
|
|
||||||
message(STATUS "BUILD_NDPI...............: ${BUILD_NDPI}")
|
|
||||||
message(STATUS "BUILD_EXAMPLES...........: ${BUILD_EXAMPLES}")
|
|
||||||
message(STATUS "BUILD_RUST_EXAMPLES......: ${BUILD_RUST_EXAMPLES}")
|
|
||||||
if(BUILD_EXAMPLES)
|
|
||||||
message(STATUS "ENABLE_DBUS..............: ${ENABLE_DBUS}")
|
|
||||||
message(STATUS "ENABLE_CURL..............: ${ENABLE_CURL}")
|
|
||||||
endif()
|
|
||||||
if(BUILD_NDPI)
|
|
||||||
message(STATUS "NDPI_ADDITIONAL_ARGS.....: ${NDPI_ADDITIONAL_ARGS}")
|
|
||||||
endif()
|
|
||||||
message(STATUS "NDPI_NO_PKGCONFIG........: ${NDPI_NO_PKGCONFIG}")
|
|
||||||
message(STATUS "--------------------------")
|
|
||||||
if(PFRING_INSTALLDIR)
|
|
||||||
message(STATUS "PFRING_INSTALLDIR........: ${PFRING_INSTALLDIR}")
|
|
||||||
message(STATUS "- PFRING_INC.............: ${PFRING_INC}")
|
|
||||||
message(STATUS "- PFRING_KERNEL_INC......: ${PFRING_KERNEL_INC}")
|
|
||||||
message(STATUS "- STATIC_PFRING_LIB......: ${STATIC_PFRING_LIB}")
|
|
||||||
message(STATUS "- SHARED_PFRING_LIB......: ${PF_RING_LIB}")
|
|
||||||
message(STATUS "--------------------------")
|
|
||||||
endif()
|
|
||||||
if(STATIC_LIBNDPI_INSTALLDIR)
|
|
||||||
message(STATUS "STATIC_LIBNDPI_INSTALLDIR: ${STATIC_LIBNDPI_INSTALLDIR}")
|
|
||||||
endif()
|
|
||||||
if(STATIC_LIBNDPI_INSTALLDIR OR BUILD_NDPI OR NDPI_NO_PKGCONFIG)
|
|
||||||
message(STATUS "- STATIC_LIBNDPI_INC.....: ${STATIC_LIBNDPI_INC}")
|
|
||||||
message(STATUS "- STATIC_LIBNDPI_LIB.....: ${STATIC_LIBNDPI_LIB}")
|
|
||||||
message(STATUS "- NDPI_WITH_GCRYPT.......: ${NDPI_WITH_GCRYPT}")
|
|
||||||
message(STATUS "- NDPI_WITH_PCRE.........: ${NDPI_WITH_PCRE}")
|
|
||||||
message(STATUS "- NDPI_WITH_MAXMINDDB....: ${NDPI_WITH_MAXMINDDB}")
|
|
||||||
endif()
|
|
||||||
if(NOT STATIC_LIBNDPI_INSTALLDIR AND NOT BUILD_NDPI)
|
|
||||||
message(STATUS "- DEFAULT_NDPI_INCLUDE...: ${DEFAULT_NDPI_INCLUDE}")
|
|
||||||
endif()
|
|
||||||
if(NOT NDPI_NO_PKGCONFIG)
|
|
||||||
message(STATUS "- pkgcfg_lib_NDPI_ndpi...: ${pkgcfg_lib_NDPI_ndpi}")
|
|
||||||
endif()
|
|
||||||
message(STATUS "--------------------------")
|
|
||||||
if(CMAKE_C_COMPILER_ID STREQUAL "Clang")
|
|
||||||
message(STATUS "Fuzzing enabled")
|
|
||||||
endif()
|
|
||||||
674
COPYING
674
COPYING
@@ -1,674 +0,0 @@
|
|||||||
GNU GENERAL PUBLIC LICENSE
|
|
||||||
Version 3, 29 June 2007
|
|
||||||
|
|
||||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
|
||||||
of this license document, but changing it is not allowed.
|
|
||||||
|
|
||||||
Preamble
|
|
||||||
|
|
||||||
The GNU General Public License is a free, copyleft license for
|
|
||||||
software and other kinds of works.
|
|
||||||
|
|
||||||
The licenses for most software and other practical works are designed
|
|
||||||
to take away your freedom to share and change the works. By contrast,
|
|
||||||
the GNU General Public License is intended to guarantee your freedom to
|
|
||||||
share and change all versions of a program--to make sure it remains free
|
|
||||||
software for all its users. We, the Free Software Foundation, use the
|
|
||||||
GNU General Public License for most of our software; it applies also to
|
|
||||||
any other work released this way by its authors. You can apply it to
|
|
||||||
your programs, too.
|
|
||||||
|
|
||||||
When we speak of free software, we are referring to freedom, not
|
|
||||||
price. Our General Public Licenses are designed to make sure that you
|
|
||||||
have the freedom to distribute copies of free software (and charge for
|
|
||||||
them if you wish), that you receive source code or can get it if you
|
|
||||||
want it, that you can change the software or use pieces of it in new
|
|
||||||
free programs, and that you know you can do these things.
|
|
||||||
|
|
||||||
To protect your rights, we need to prevent others from denying you
|
|
||||||
these rights or asking you to surrender the rights. Therefore, you have
|
|
||||||
certain responsibilities if you distribute copies of the software, or if
|
|
||||||
you modify it: responsibilities to respect the freedom of others.
|
|
||||||
|
|
||||||
For example, if you distribute copies of such a program, whether
|
|
||||||
gratis or for a fee, you must pass on to the recipients the same
|
|
||||||
freedoms that you received. You must make sure that they, too, receive
|
|
||||||
or can get the source code. And you must show them these terms so they
|
|
||||||
know their rights.
|
|
||||||
|
|
||||||
Developers that use the GNU GPL protect your rights with two steps:
|
|
||||||
(1) assert copyright on the software, and (2) offer you this License
|
|
||||||
giving you legal permission to copy, distribute and/or modify it.
|
|
||||||
|
|
||||||
For the developers' and authors' protection, the GPL clearly explains
|
|
||||||
that there is no warranty for this free software. For both users' and
|
|
||||||
authors' sake, the GPL requires that modified versions be marked as
|
|
||||||
changed, so that their problems will not be attributed erroneously to
|
|
||||||
authors of previous versions.
|
|
||||||
|
|
||||||
Some devices are designed to deny users access to install or run
|
|
||||||
modified versions of the software inside them, although the manufacturer
|
|
||||||
can do so. This is fundamentally incompatible with the aim of
|
|
||||||
protecting users' freedom to change the software. The systematic
|
|
||||||
pattern of such abuse occurs in the area of products for individuals to
|
|
||||||
use, which is precisely where it is most unacceptable. Therefore, we
|
|
||||||
have designed this version of the GPL to prohibit the practice for those
|
|
||||||
products. If such problems arise substantially in other domains, we
|
|
||||||
stand ready to extend this provision to those domains in future versions
|
|
||||||
of the GPL, as needed to protect the freedom of users.
|
|
||||||
|
|
||||||
Finally, every program is threatened constantly by software patents.
|
|
||||||
States should not allow patents to restrict development and use of
|
|
||||||
software on general-purpose computers, but in those that do, we wish to
|
|
||||||
avoid the special danger that patents applied to a free program could
|
|
||||||
make it effectively proprietary. To prevent this, the GPL assures that
|
|
||||||
patents cannot be used to render the program non-free.
|
|
||||||
|
|
||||||
The precise terms and conditions for copying, distribution and
|
|
||||||
modification follow.
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
0. Definitions.
|
|
||||||
|
|
||||||
"This License" refers to version 3 of the GNU General Public License.
|
|
||||||
|
|
||||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
|
||||||
works, such as semiconductor masks.
|
|
||||||
|
|
||||||
"The Program" refers to any copyrightable work licensed under this
|
|
||||||
License. Each licensee is addressed as "you". "Licensees" and
|
|
||||||
"recipients" may be individuals or organizations.
|
|
||||||
|
|
||||||
To "modify" a work means to copy from or adapt all or part of the work
|
|
||||||
in a fashion requiring copyright permission, other than the making of an
|
|
||||||
exact copy. The resulting work is called a "modified version" of the
|
|
||||||
earlier work or a work "based on" the earlier work.
|
|
||||||
|
|
||||||
A "covered work" means either the unmodified Program or a work based
|
|
||||||
on the Program.
|
|
||||||
|
|
||||||
To "propagate" a work means to do anything with it that, without
|
|
||||||
permission, would make you directly or secondarily liable for
|
|
||||||
infringement under applicable copyright law, except executing it on a
|
|
||||||
computer or modifying a private copy. Propagation includes copying,
|
|
||||||
distribution (with or without modification), making available to the
|
|
||||||
public, and in some countries other activities as well.
|
|
||||||
|
|
||||||
To "convey" a work means any kind of propagation that enables other
|
|
||||||
parties to make or receive copies. Mere interaction with a user through
|
|
||||||
a computer network, with no transfer of a copy, is not conveying.
|
|
||||||
|
|
||||||
An interactive user interface displays "Appropriate Legal Notices"
|
|
||||||
to the extent that it includes a convenient and prominently visible
|
|
||||||
feature that (1) displays an appropriate copyright notice, and (2)
|
|
||||||
tells the user that there is no warranty for the work (except to the
|
|
||||||
extent that warranties are provided), that licensees may convey the
|
|
||||||
work under this License, and how to view a copy of this License. If
|
|
||||||
the interface presents a list of user commands or options, such as a
|
|
||||||
menu, a prominent item in the list meets this criterion.
|
|
||||||
|
|
||||||
1. Source Code.
|
|
||||||
|
|
||||||
The "source code" for a work means the preferred form of the work
|
|
||||||
for making modifications to it. "Object code" means any non-source
|
|
||||||
form of a work.
|
|
||||||
|
|
||||||
A "Standard Interface" means an interface that either is an official
|
|
||||||
standard defined by a recognized standards body, or, in the case of
|
|
||||||
interfaces specified for a particular programming language, one that
|
|
||||||
is widely used among developers working in that language.
|
|
||||||
|
|
||||||
The "System Libraries" of an executable work include anything, other
|
|
||||||
than the work as a whole, that (a) is included in the normal form of
|
|
||||||
packaging a Major Component, but which is not part of that Major
|
|
||||||
Component, and (b) serves only to enable use of the work with that
|
|
||||||
Major Component, or to implement a Standard Interface for which an
|
|
||||||
implementation is available to the public in source code form. A
|
|
||||||
"Major Component", in this context, means a major essential component
|
|
||||||
(kernel, window system, and so on) of the specific operating system
|
|
||||||
(if any) on which the executable work runs, or a compiler used to
|
|
||||||
produce the work, or an object code interpreter used to run it.
|
|
||||||
|
|
||||||
The "Corresponding Source" for a work in object code form means all
|
|
||||||
the source code needed to generate, install, and (for an executable
|
|
||||||
work) run the object code and to modify the work, including scripts to
|
|
||||||
control those activities. However, it does not include the work's
|
|
||||||
System Libraries, or general-purpose tools or generally available free
|
|
||||||
programs which are used unmodified in performing those activities but
|
|
||||||
which are not part of the work. For example, Corresponding Source
|
|
||||||
includes interface definition files associated with source files for
|
|
||||||
the work, and the source code for shared libraries and dynamically
|
|
||||||
linked subprograms that the work is specifically designed to require,
|
|
||||||
such as by intimate data communication or control flow between those
|
|
||||||
subprograms and other parts of the work.
|
|
||||||
|
|
||||||
The Corresponding Source need not include anything that users
|
|
||||||
can regenerate automatically from other parts of the Corresponding
|
|
||||||
Source.
|
|
||||||
|
|
||||||
The Corresponding Source for a work in source code form is that
|
|
||||||
same work.
|
|
||||||
|
|
||||||
2. Basic Permissions.
|
|
||||||
|
|
||||||
All rights granted under this License are granted for the term of
|
|
||||||
copyright on the Program, and are irrevocable provided the stated
|
|
||||||
conditions are met. This License explicitly affirms your unlimited
|
|
||||||
permission to run the unmodified Program. The output from running a
|
|
||||||
covered work is covered by this License only if the output, given its
|
|
||||||
content, constitutes a covered work. This License acknowledges your
|
|
||||||
rights of fair use or other equivalent, as provided by copyright law.
|
|
||||||
|
|
||||||
You may make, run and propagate covered works that you do not
|
|
||||||
convey, without conditions so long as your license otherwise remains
|
|
||||||
in force. You may convey covered works to others for the sole purpose
|
|
||||||
of having them make modifications exclusively for you, or provide you
|
|
||||||
with facilities for running those works, provided that you comply with
|
|
||||||
the terms of this License in conveying all material for which you do
|
|
||||||
not control copyright. Those thus making or running the covered works
|
|
||||||
for you must do so exclusively on your behalf, under your direction
|
|
||||||
and control, on terms that prohibit them from making any copies of
|
|
||||||
your copyrighted material outside their relationship with you.
|
|
||||||
|
|
||||||
Conveying under any other circumstances is permitted solely under
|
|
||||||
the conditions stated below. Sublicensing is not allowed; section 10
|
|
||||||
makes it unnecessary.
|
|
||||||
|
|
||||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
|
||||||
|
|
||||||
No covered work shall be deemed part of an effective technological
|
|
||||||
measure under any applicable law fulfilling obligations under article
|
|
||||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
|
||||||
similar laws prohibiting or restricting circumvention of such
|
|
||||||
measures.
|
|
||||||
|
|
||||||
When you convey a covered work, you waive any legal power to forbid
|
|
||||||
circumvention of technological measures to the extent such circumvention
|
|
||||||
is effected by exercising rights under this License with respect to
|
|
||||||
the covered work, and you disclaim any intention to limit operation or
|
|
||||||
modification of the work as a means of enforcing, against the work's
|
|
||||||
users, your or third parties' legal rights to forbid circumvention of
|
|
||||||
technological measures.
|
|
||||||
|
|
||||||
4. Conveying Verbatim Copies.
|
|
||||||
|
|
||||||
You may convey verbatim copies of the Program's source code as you
|
|
||||||
receive it, in any medium, provided that you conspicuously and
|
|
||||||
appropriately publish on each copy an appropriate copyright notice;
|
|
||||||
keep intact all notices stating that this License and any
|
|
||||||
non-permissive terms added in accord with section 7 apply to the code;
|
|
||||||
keep intact all notices of the absence of any warranty; and give all
|
|
||||||
recipients a copy of this License along with the Program.
|
|
||||||
|
|
||||||
You may charge any price or no price for each copy that you convey,
|
|
||||||
and you may offer support or warranty protection for a fee.
|
|
||||||
|
|
||||||
5. Conveying Modified Source Versions.
|
|
||||||
|
|
||||||
You may convey a work based on the Program, or the modifications to
|
|
||||||
produce it from the Program, in the form of source code under the
|
|
||||||
terms of section 4, provided that you also meet all of these conditions:
|
|
||||||
|
|
||||||
a) The work must carry prominent notices stating that you modified
|
|
||||||
it, and giving a relevant date.
|
|
||||||
|
|
||||||
b) The work must carry prominent notices stating that it is
|
|
||||||
released under this License and any conditions added under section
|
|
||||||
7. This requirement modifies the requirement in section 4 to
|
|
||||||
"keep intact all notices".
|
|
||||||
|
|
||||||
c) You must license the entire work, as a whole, under this
|
|
||||||
License to anyone who comes into possession of a copy. This
|
|
||||||
License will therefore apply, along with any applicable section 7
|
|
||||||
additional terms, to the whole of the work, and all its parts,
|
|
||||||
regardless of how they are packaged. This License gives no
|
|
||||||
permission to license the work in any other way, but it does not
|
|
||||||
invalidate such permission if you have separately received it.
|
|
||||||
|
|
||||||
d) If the work has interactive user interfaces, each must display
|
|
||||||
Appropriate Legal Notices; however, if the Program has interactive
|
|
||||||
interfaces that do not display Appropriate Legal Notices, your
|
|
||||||
work need not make them do so.
|
|
||||||
|
|
||||||
A compilation of a covered work with other separate and independent
|
|
||||||
works, which are not by their nature extensions of the covered work,
|
|
||||||
and which are not combined with it such as to form a larger program,
|
|
||||||
in or on a volume of a storage or distribution medium, is called an
|
|
||||||
"aggregate" if the compilation and its resulting copyright are not
|
|
||||||
used to limit the access or legal rights of the compilation's users
|
|
||||||
beyond what the individual works permit. Inclusion of a covered work
|
|
||||||
in an aggregate does not cause this License to apply to the other
|
|
||||||
parts of the aggregate.
|
|
||||||
|
|
||||||
6. Conveying Non-Source Forms.
|
|
||||||
|
|
||||||
You may convey a covered work in object code form under the terms
|
|
||||||
of sections 4 and 5, provided that you also convey the
|
|
||||||
machine-readable Corresponding Source under the terms of this License,
|
|
||||||
in one of these ways:
|
|
||||||
|
|
||||||
a) Convey the object code in, or embodied in, a physical product
|
|
||||||
(including a physical distribution medium), accompanied by the
|
|
||||||
Corresponding Source fixed on a durable physical medium
|
|
||||||
customarily used for software interchange.
|
|
||||||
|
|
||||||
b) Convey the object code in, or embodied in, a physical product
|
|
||||||
(including a physical distribution medium), accompanied by a
|
|
||||||
written offer, valid for at least three years and valid for as
|
|
||||||
long as you offer spare parts or customer support for that product
|
|
||||||
model, to give anyone who possesses the object code either (1) a
|
|
||||||
copy of the Corresponding Source for all the software in the
|
|
||||||
product that is covered by this License, on a durable physical
|
|
||||||
medium customarily used for software interchange, for a price no
|
|
||||||
more than your reasonable cost of physically performing this
|
|
||||||
conveying of source, or (2) access to copy the
|
|
||||||
Corresponding Source from a network server at no charge.
|
|
||||||
|
|
||||||
c) Convey individual copies of the object code with a copy of the
|
|
||||||
written offer to provide the Corresponding Source. This
|
|
||||||
alternative is allowed only occasionally and noncommercially, and
|
|
||||||
only if you received the object code with such an offer, in accord
|
|
||||||
with subsection 6b.
|
|
||||||
|
|
||||||
d) Convey the object code by offering access from a designated
|
|
||||||
place (gratis or for a charge), and offer equivalent access to the
|
|
||||||
Corresponding Source in the same way through the same place at no
|
|
||||||
further charge. You need not require recipients to copy the
|
|
||||||
Corresponding Source along with the object code. If the place to
|
|
||||||
copy the object code is a network server, the Corresponding Source
|
|
||||||
may be on a different server (operated by you or a third party)
|
|
||||||
that supports equivalent copying facilities, provided you maintain
|
|
||||||
clear directions next to the object code saying where to find the
|
|
||||||
Corresponding Source. Regardless of what server hosts the
|
|
||||||
Corresponding Source, you remain obligated to ensure that it is
|
|
||||||
available for as long as needed to satisfy these requirements.
|
|
||||||
|
|
||||||
e) Convey the object code using peer-to-peer transmission, provided
|
|
||||||
you inform other peers where the object code and Corresponding
|
|
||||||
Source of the work are being offered to the general public at no
|
|
||||||
charge under subsection 6d.
|
|
||||||
|
|
||||||
A separable portion of the object code, whose source code is excluded
|
|
||||||
from the Corresponding Source as a System Library, need not be
|
|
||||||
included in conveying the object code work.
|
|
||||||
|
|
||||||
A "User Product" is either (1) a "consumer product", which means any
|
|
||||||
tangible personal property which is normally used for personal, family,
|
|
||||||
or household purposes, or (2) anything designed or sold for incorporation
|
|
||||||
into a dwelling. In determining whether a product is a consumer product,
|
|
||||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
|
||||||
product received by a particular user, "normally used" refers to a
|
|
||||||
typical or common use of that class of product, regardless of the status
|
|
||||||
of the particular user or of the way in which the particular user
|
|
||||||
actually uses, or expects or is expected to use, the product. A product
|
|
||||||
is a consumer product regardless of whether the product has substantial
|
|
||||||
commercial, industrial or non-consumer uses, unless such uses represent
|
|
||||||
the only significant mode of use of the product.
|
|
||||||
|
|
||||||
"Installation Information" for a User Product means any methods,
|
|
||||||
procedures, authorization keys, or other information required to install
|
|
||||||
and execute modified versions of a covered work in that User Product from
|
|
||||||
a modified version of its Corresponding Source. The information must
|
|
||||||
suffice to ensure that the continued functioning of the modified object
|
|
||||||
code is in no case prevented or interfered with solely because
|
|
||||||
modification has been made.
|
|
||||||
|
|
||||||
If you convey an object code work under this section in, or with, or
|
|
||||||
specifically for use in, a User Product, and the conveying occurs as
|
|
||||||
part of a transaction in which the right of possession and use of the
|
|
||||||
User Product is transferred to the recipient in perpetuity or for a
|
|
||||||
fixed term (regardless of how the transaction is characterized), the
|
|
||||||
Corresponding Source conveyed under this section must be accompanied
|
|
||||||
by the Installation Information. But this requirement does not apply
|
|
||||||
if neither you nor any third party retains the ability to install
|
|
||||||
modified object code on the User Product (for example, the work has
|
|
||||||
been installed in ROM).
|
|
||||||
|
|
||||||
The requirement to provide Installation Information does not include a
|
|
||||||
requirement to continue to provide support service, warranty, or updates
|
|
||||||
for a work that has been modified or installed by the recipient, or for
|
|
||||||
the User Product in which it has been modified or installed. Access to a
|
|
||||||
network may be denied when the modification itself materially and
|
|
||||||
adversely affects the operation of the network or violates the rules and
|
|
||||||
protocols for communication across the network.
|
|
||||||
|
|
||||||
Corresponding Source conveyed, and Installation Information provided,
|
|
||||||
in accord with this section must be in a format that is publicly
|
|
||||||
documented (and with an implementation available to the public in
|
|
||||||
source code form), and must require no special password or key for
|
|
||||||
unpacking, reading or copying.
|
|
||||||
|
|
||||||
7. Additional Terms.
|
|
||||||
|
|
||||||
"Additional permissions" are terms that supplement the terms of this
|
|
||||||
License by making exceptions from one or more of its conditions.
|
|
||||||
Additional permissions that are applicable to the entire Program shall
|
|
||||||
be treated as though they were included in this License, to the extent
|
|
||||||
that they are valid under applicable law. If additional permissions
|
|
||||||
apply only to part of the Program, that part may be used separately
|
|
||||||
under those permissions, but the entire Program remains governed by
|
|
||||||
this License without regard to the additional permissions.
|
|
||||||
|
|
||||||
When you convey a copy of a covered work, you may at your option
|
|
||||||
remove any additional permissions from that copy, or from any part of
|
|
||||||
it. (Additional permissions may be written to require their own
|
|
||||||
removal in certain cases when you modify the work.) You may place
|
|
||||||
additional permissions on material, added by you to a covered work,
|
|
||||||
for which you have or can give appropriate copyright permission.
|
|
||||||
|
|
||||||
Notwithstanding any other provision of this License, for material you
|
|
||||||
add to a covered work, you may (if authorized by the copyright holders of
|
|
||||||
that material) supplement the terms of this License with terms:
|
|
||||||
|
|
||||||
a) Disclaiming warranty or limiting liability differently from the
|
|
||||||
terms of sections 15 and 16 of this License; or
|
|
||||||
|
|
||||||
b) Requiring preservation of specified reasonable legal notices or
|
|
||||||
author attributions in that material or in the Appropriate Legal
|
|
||||||
Notices displayed by works containing it; or
|
|
||||||
|
|
||||||
c) Prohibiting misrepresentation of the origin of that material, or
|
|
||||||
requiring that modified versions of such material be marked in
|
|
||||||
reasonable ways as different from the original version; or
|
|
||||||
|
|
||||||
d) Limiting the use for publicity purposes of names of licensors or
|
|
||||||
authors of the material; or
|
|
||||||
|
|
||||||
e) Declining to grant rights under trademark law for use of some
|
|
||||||
trade names, trademarks, or service marks; or
|
|
||||||
|
|
||||||
f) Requiring indemnification of licensors and authors of that
|
|
||||||
material by anyone who conveys the material (or modified versions of
|
|
||||||
it) with contractual assumptions of liability to the recipient, for
|
|
||||||
any liability that these contractual assumptions directly impose on
|
|
||||||
those licensors and authors.
|
|
||||||
|
|
||||||
All other non-permissive additional terms are considered "further
|
|
||||||
restrictions" within the meaning of section 10. If the Program as you
|
|
||||||
received it, or any part of it, contains a notice stating that it is
|
|
||||||
governed by this License along with a term that is a further
|
|
||||||
restriction, you may remove that term. If a license document contains
|
|
||||||
a further restriction but permits relicensing or conveying under this
|
|
||||||
License, you may add to a covered work material governed by the terms
|
|
||||||
of that license document, provided that the further restriction does
|
|
||||||
not survive such relicensing or conveying.
|
|
||||||
|
|
||||||
If you add terms to a covered work in accord with this section, you
|
|
||||||
must place, in the relevant source files, a statement of the
|
|
||||||
additional terms that apply to those files, or a notice indicating
|
|
||||||
where to find the applicable terms.
|
|
||||||
|
|
||||||
Additional terms, permissive or non-permissive, may be stated in the
|
|
||||||
form of a separately written license, or stated as exceptions;
|
|
||||||
the above requirements apply either way.
|
|
||||||
|
|
||||||
8. Termination.
|
|
||||||
|
|
||||||
You may not propagate or modify a covered work except as expressly
|
|
||||||
provided under this License. Any attempt otherwise to propagate or
|
|
||||||
modify it is void, and will automatically terminate your rights under
|
|
||||||
this License (including any patent licenses granted under the third
|
|
||||||
paragraph of section 11).
|
|
||||||
|
|
||||||
However, if you cease all violation of this License, then your
|
|
||||||
license from a particular copyright holder is reinstated (a)
|
|
||||||
provisionally, unless and until the copyright holder explicitly and
|
|
||||||
finally terminates your license, and (b) permanently, if the copyright
|
|
||||||
holder fails to notify you of the violation by some reasonable means
|
|
||||||
prior to 60 days after the cessation.
|
|
||||||
|
|
||||||
Moreover, your license from a particular copyright holder is
|
|
||||||
reinstated permanently if the copyright holder notifies you of the
|
|
||||||
violation by some reasonable means, this is the first time you have
|
|
||||||
received notice of violation of this License (for any work) from that
|
|
||||||
copyright holder, and you cure the violation prior to 30 days after
|
|
||||||
your receipt of the notice.
|
|
||||||
|
|
||||||
Termination of your rights under this section does not terminate the
|
|
||||||
licenses of parties who have received copies or rights from you under
|
|
||||||
this License. If your rights have been terminated and not permanently
|
|
||||||
reinstated, you do not qualify to receive new licenses for the same
|
|
||||||
material under section 10.
|
|
||||||
|
|
||||||
9. Acceptance Not Required for Having Copies.
|
|
||||||
|
|
||||||
You are not required to accept this License in order to receive or
|
|
||||||
run a copy of the Program. Ancillary propagation of a covered work
|
|
||||||
occurring solely as a consequence of using peer-to-peer transmission
|
|
||||||
to receive a copy likewise does not require acceptance. However,
|
|
||||||
nothing other than this License grants you permission to propagate or
|
|
||||||
modify any covered work. These actions infringe copyright if you do
|
|
||||||
not accept this License. Therefore, by modifying or propagating a
|
|
||||||
covered work, you indicate your acceptance of this License to do so.
|
|
||||||
|
|
||||||
10. Automatic Licensing of Downstream Recipients.
|
|
||||||
|
|
||||||
Each time you convey a covered work, the recipient automatically
|
|
||||||
receives a license from the original licensors, to run, modify and
|
|
||||||
propagate that work, subject to this License. You are not responsible
|
|
||||||
for enforcing compliance by third parties with this License.
|
|
||||||
|
|
||||||
An "entity transaction" is a transaction transferring control of an
|
|
||||||
organization, or substantially all assets of one, or subdividing an
|
|
||||||
organization, or merging organizations. If propagation of a covered
|
|
||||||
work results from an entity transaction, each party to that
|
|
||||||
transaction who receives a copy of the work also receives whatever
|
|
||||||
licenses to the work the party's predecessor in interest had or could
|
|
||||||
give under the previous paragraph, plus a right to possession of the
|
|
||||||
Corresponding Source of the work from the predecessor in interest, if
|
|
||||||
the predecessor has it or can get it with reasonable efforts.
|
|
||||||
|
|
||||||
You may not impose any further restrictions on the exercise of the
|
|
||||||
rights granted or affirmed under this License. For example, you may
|
|
||||||
not impose a license fee, royalty, or other charge for exercise of
|
|
||||||
rights granted under this License, and you may not initiate litigation
|
|
||||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
|
||||||
any patent claim is infringed by making, using, selling, offering for
|
|
||||||
sale, or importing the Program or any portion of it.
|
|
||||||
|
|
||||||
11. Patents.
|
|
||||||
|
|
||||||
A "contributor" is a copyright holder who authorizes use under this
|
|
||||||
License of the Program or a work on which the Program is based. The
|
|
||||||
work thus licensed is called the contributor's "contributor version".
|
|
||||||
|
|
||||||
A contributor's "essential patent claims" are all patent claims
|
|
||||||
owned or controlled by the contributor, whether already acquired or
|
|
||||||
hereafter acquired, that would be infringed by some manner, permitted
|
|
||||||
by this License, of making, using, or selling its contributor version,
|
|
||||||
but do not include claims that would be infringed only as a
|
|
||||||
consequence of further modification of the contributor version. For
|
|
||||||
purposes of this definition, "control" includes the right to grant
|
|
||||||
patent sublicenses in a manner consistent with the requirements of
|
|
||||||
this License.
|
|
||||||
|
|
||||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
|
||||||
patent license under the contributor's essential patent claims, to
|
|
||||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
|
||||||
propagate the contents of its contributor version.
|
|
||||||
|
|
||||||
In the following three paragraphs, a "patent license" is any express
|
|
||||||
agreement or commitment, however denominated, not to enforce a patent
|
|
||||||
(such as an express permission to practice a patent or covenant not to
|
|
||||||
sue for patent infringement). To "grant" such a patent license to a
|
|
||||||
party means to make such an agreement or commitment not to enforce a
|
|
||||||
patent against the party.
|
|
||||||
|
|
||||||
If you convey a covered work, knowingly relying on a patent license,
|
|
||||||
and the Corresponding Source of the work is not available for anyone
|
|
||||||
to copy, free of charge and under the terms of this License, through a
|
|
||||||
publicly available network server or other readily accessible means,
|
|
||||||
then you must either (1) cause the Corresponding Source to be so
|
|
||||||
available, or (2) arrange to deprive yourself of the benefit of the
|
|
||||||
patent license for this particular work, or (3) arrange, in a manner
|
|
||||||
consistent with the requirements of this License, to extend the patent
|
|
||||||
license to downstream recipients. "Knowingly relying" means you have
|
|
||||||
actual knowledge that, but for the patent license, your conveying the
|
|
||||||
covered work in a country, or your recipient's use of the covered work
|
|
||||||
in a country, would infringe one or more identifiable patents in that
|
|
||||||
country that you have reason to believe are valid.
|
|
||||||
|
|
||||||
If, pursuant to or in connection with a single transaction or
|
|
||||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
|
||||||
covered work, and grant a patent license to some of the parties
|
|
||||||
receiving the covered work authorizing them to use, propagate, modify
|
|
||||||
or convey a specific copy of the covered work, then the patent license
|
|
||||||
you grant is automatically extended to all recipients of the covered
|
|
||||||
work and works based on it.
|
|
||||||
|
|
||||||
A patent license is "discriminatory" if it does not include within
|
|
||||||
the scope of its coverage, prohibits the exercise of, or is
|
|
||||||
conditioned on the non-exercise of one or more of the rights that are
|
|
||||||
specifically granted under this License. You may not convey a covered
|
|
||||||
work if you are a party to an arrangement with a third party that is
|
|
||||||
in the business of distributing software, under which you make payment
|
|
||||||
to the third party based on the extent of your activity of conveying
|
|
||||||
the work, and under which the third party grants, to any of the
|
|
||||||
parties who would receive the covered work from you, a discriminatory
|
|
||||||
patent license (a) in connection with copies of the covered work
|
|
||||||
conveyed by you (or copies made from those copies), or (b) primarily
|
|
||||||
for and in connection with specific products or compilations that
|
|
||||||
contain the covered work, unless you entered into that arrangement,
|
|
||||||
or that patent license was granted, prior to 28 March 2007.
|
|
||||||
|
|
||||||
Nothing in this License shall be construed as excluding or limiting
|
|
||||||
any implied license or other defenses to infringement that may
|
|
||||||
otherwise be available to you under applicable patent law.
|
|
||||||
|
|
||||||
12. No Surrender of Others' Freedom.
|
|
||||||
|
|
||||||
If conditions are imposed on you (whether by court order, agreement or
|
|
||||||
otherwise) that contradict the conditions of this License, they do not
|
|
||||||
excuse you from the conditions of this License. If you cannot convey a
|
|
||||||
covered work so as to satisfy simultaneously your obligations under this
|
|
||||||
License and any other pertinent obligations, then as a consequence you may
|
|
||||||
not convey it at all. For example, if you agree to terms that obligate you
|
|
||||||
to collect a royalty for further conveying from those to whom you convey
|
|
||||||
the Program, the only way you could satisfy both those terms and this
|
|
||||||
License would be to refrain entirely from conveying the Program.
|
|
||||||
|
|
||||||
13. Use with the GNU Affero General Public License.
|
|
||||||
|
|
||||||
Notwithstanding any other provision of this License, you have
|
|
||||||
permission to link or combine any covered work with a work licensed
|
|
||||||
under version 3 of the GNU Affero General Public License into a single
|
|
||||||
combined work, and to convey the resulting work. The terms of this
|
|
||||||
License will continue to apply to the part which is the covered work,
|
|
||||||
but the special requirements of the GNU Affero General Public License,
|
|
||||||
section 13, concerning interaction through a network will apply to the
|
|
||||||
combination as such.
|
|
||||||
|
|
||||||
14. Revised Versions of this License.
|
|
||||||
|
|
||||||
The Free Software Foundation may publish revised and/or new versions of
|
|
||||||
the GNU General Public License from time to time. Such new versions will
|
|
||||||
be similar in spirit to the present version, but may differ in detail to
|
|
||||||
address new problems or concerns.
|
|
||||||
|
|
||||||
Each version is given a distinguishing version number. If the
|
|
||||||
Program specifies that a certain numbered version of the GNU General
|
|
||||||
Public License "or any later version" applies to it, you have the
|
|
||||||
option of following the terms and conditions either of that numbered
|
|
||||||
version or of any later version published by the Free Software
|
|
||||||
Foundation. If the Program does not specify a version number of the
|
|
||||||
GNU General Public License, you may choose any version ever published
|
|
||||||
by the Free Software Foundation.
|
|
||||||
|
|
||||||
If the Program specifies that a proxy can decide which future
|
|
||||||
versions of the GNU General Public License can be used, that proxy's
|
|
||||||
public statement of acceptance of a version permanently authorizes you
|
|
||||||
to choose that version for the Program.
|
|
||||||
|
|
||||||
Later license versions may give you additional or different
|
|
||||||
permissions. However, no additional obligations are imposed on any
|
|
||||||
author or copyright holder as a result of your choosing to follow a
|
|
||||||
later version.
|
|
||||||
|
|
||||||
15. Disclaimer of Warranty.
|
|
||||||
|
|
||||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
|
||||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
|
||||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
|
||||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
|
||||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
|
||||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
|
||||||
|
|
||||||
16. Limitation of Liability.
|
|
||||||
|
|
||||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
|
||||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
|
||||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
|
||||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
|
||||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
|
||||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
|
||||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
|
||||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
|
||||||
SUCH DAMAGES.
|
|
||||||
|
|
||||||
17. Interpretation of Sections 15 and 16.
|
|
||||||
|
|
||||||
If the disclaimer of warranty and limitation of liability provided
|
|
||||||
above cannot be given local legal effect according to their terms,
|
|
||||||
reviewing courts shall apply local law that most closely approximates
|
|
||||||
an absolute waiver of all civil liability in connection with the
|
|
||||||
Program, unless a warranty or assumption of liability accompanies a
|
|
||||||
copy of the Program in return for a fee.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
How to Apply These Terms to Your New Programs
|
|
||||||
|
|
||||||
If you develop a new program, and you want it to be of the greatest
|
|
||||||
possible use to the public, the best way to achieve this is to make it
|
|
||||||
free software which everyone can redistribute and change under these terms.
|
|
||||||
|
|
||||||
To do so, attach the following notices to the program. It is safest
|
|
||||||
to attach them to the start of each source file to most effectively
|
|
||||||
state the exclusion of warranty; and each file should have at least
|
|
||||||
the "copyright" line and a pointer to where the full notice is found.
|
|
||||||
|
|
||||||
<one line to give the program's name and a brief idea of what it does.>
|
|
||||||
Copyright (C) <year> <name of author>
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
|
||||||
|
|
||||||
If the program does terminal interaction, make it output a short
|
|
||||||
notice like this when it starts in an interactive mode:
|
|
||||||
|
|
||||||
<program> Copyright (C) <year> <name of author>
|
|
||||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
|
||||||
This is free software, and you are welcome to redistribute it
|
|
||||||
under certain conditions; type `show c' for details.
|
|
||||||
|
|
||||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
|
||||||
parts of the General Public License. Of course, your program's commands
|
|
||||||
might be different; for a GUI interface, you would use an "about box".
|
|
||||||
|
|
||||||
You should also get your employer (if you work as a programmer) or school,
|
|
||||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
|
||||||
For more information on this, and how to apply and follow the GNU GPL, see
|
|
||||||
<http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
The GNU General Public License does not permit incorporating your program
|
|
||||||
into proprietary programs. If your program is a subroutine library, you
|
|
||||||
may consider it more useful to permit linking proprietary applications with
|
|
||||||
the library. If this is what you want to do, use the GNU Lesser General
|
|
||||||
Public License instead of this License. But first, please read
|
|
||||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
|
||||||
46
Dockerfile
46
Dockerfile
@@ -1,46 +0,0 @@
|
|||||||
FROM ubuntu:22.04 AS builder-ubuntu-2204
|
|
||||||
|
|
||||||
WORKDIR /root
|
|
||||||
RUN apt-get -y update \
|
|
||||||
&& apt-get install -y --no-install-recommends \
|
|
||||||
autoconf automake build-essential ca-certificates cmake git \
|
|
||||||
libpcap-dev libcurl4-openssl-dev libdbus-1-dev libtool make pkg-config unzip wget \
|
|
||||||
&& apt-get clean \
|
|
||||||
&& git clone https://github.com/utoni/nDPId.git
|
|
||||||
|
|
||||||
WORKDIR /root/nDPId
|
|
||||||
RUN cmake -S . -B build -DBUILD_NDPI=ON -DBUILD_EXAMPLES=ON \
|
|
||||||
-DENABLE_DBUS=ON -DENABLE_CURL=ON \
|
|
||||||
&& cmake --build build --verbose
|
|
||||||
|
|
||||||
FROM ubuntu:22.04
|
|
||||||
USER root
|
|
||||||
WORKDIR /
|
|
||||||
|
|
||||||
COPY --from=builder-ubuntu-2204 /root/nDPId/build/nDPId /usr/sbin/nDPId
|
|
||||||
COPY --from=builder-ubuntu-2204 /root/nDPId/build/nDPIsrvd /usr/bin/nDPIsrvd
|
|
||||||
COPY --from=builder-ubuntu-2204 /root/nDPId/build/nDPId-test /usr/bin/nDPId-test
|
|
||||||
COPY --from=builder-ubuntu-2204 /root/nDPId/build/nDPIsrvd-collectd /usr/bin/nDPIsrvd-collectd
|
|
||||||
COPY --from=builder-ubuntu-2204 /root/nDPId/build/nDPIsrvd-captured /usr/bin/nDPIsrvd-captured
|
|
||||||
COPY --from=builder-ubuntu-2204 /root/nDPId/build/nDPIsrvd-analysed /usr/bin/nDPIsrvd-anaylsed
|
|
||||||
COPY --from=builder-ubuntu-2204 /root/nDPId/build/nDPIsrvd-analysed /usr/bin/nDPIsrvd-anaylsed
|
|
||||||
COPY --from=builder-ubuntu-2204 /root/nDPId/build/nDPIsrvd-notifyd /usr/bin/nDPIsrvd-notifyd
|
|
||||||
COPY --from=builder-ubuntu-2204 /root/nDPId/build/nDPIsrvd-influxd /usr/bin/nDPIsrvd-influxd
|
|
||||||
COPY --from=builder-ubuntu-2204 /root/nDPId/build/nDPIsrvd-simple /usr/bin/nDPIsrvd-simple
|
|
||||||
|
|
||||||
RUN apt-get -y update \
|
|
||||||
&& apt-get install -y --no-install-recommends libpcap-dev \
|
|
||||||
&& apt-get clean
|
|
||||||
|
|
||||||
USER nobody
|
|
||||||
RUN /usr/bin/nDPIsrvd -h || { RC=$?; test ${RC} -eq 1; }; \
|
|
||||||
/usr/sbin/nDPId -h || { RC=$?; test ${RC} -eq 1; }
|
|
||||||
|
|
||||||
FROM archlinux:base-devel AS builder-archlinux
|
|
||||||
|
|
||||||
WORKDIR /root
|
|
||||||
RUN pacman --noconfirm -Sy cmake git unzip wget && mkdir /build && chown nobody /build && cd /build \
|
|
||||||
&& runuser -u nobody git clone https://github.com/utoni/nDPId.git
|
|
||||||
|
|
||||||
WORKDIR /build/nDPId/packages/archlinux
|
|
||||||
RUN runuser -u nobody makepkg
|
|
||||||
34
Makefile
Normal file
34
Makefile
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
CC = gcc
|
||||||
|
CFLAGS += -Wall -Wextra $(EXTRA_CFLAGS)
|
||||||
|
LIBS += -pthread -lpcap -lm
|
||||||
|
|
||||||
|
ifneq ($(CUSTOM_LIBNDPI),)
|
||||||
|
LIBS += '$(CUSTOM_LIBNDPI)'
|
||||||
|
CFLAGS += '-I$(shell dirname $(CUSTOM_LIBNDPI))/../include'
|
||||||
|
ifeq ($(findstring $*.so, $(CUSTOM_LIBNDPI)),.so)
|
||||||
|
CFLAGS += -Wl,-rpath='$(shell dirname $(CUSTOM_LIBNDPI))'
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
LIBS += -lndpi
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(ENABLE_DEBUG),yes)
|
||||||
|
CFLAGS += -Og -g3
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(ENABLE_SANITIZER),yes)
|
||||||
|
CFLAGS += -fsanitize=address -fsanitize=undefined -fsanitize=leak
|
||||||
|
LIBS += -lasan -lubsan
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(VERBOSE),yes)
|
||||||
|
CFLAGS += -DVERBOSE
|
||||||
|
endif
|
||||||
|
|
||||||
|
RM = rm -f
|
||||||
|
|
||||||
|
main: main.c
|
||||||
|
$(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) $(LIBS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) main
|
||||||
349
README.md
349
README.md
@@ -1,349 +0,0 @@
|
|||||||
[](https://github.com/utoni/nDPId/actions/workflows/build.yml)
|
|
||||||
[](https://gitlab.com/utoni/nDPId/-/pipelines)
|
|
||||||
[](https://app.circleci.com/pipelines/github/utoni/nDPId)
|
|
||||||
[](https://sonarcloud.io/summary/new_code?id=lnslbrty_nDPId)
|
|
||||||
[](https://sonarcloud.io/summary/new_code?id=lnslbrty_nDPId)
|
|
||||||
[](https://sonarcloud.io/summary/new_code?id=lnslbrty_nDPId)
|
|
||||||
[](https://sonarcloud.io/summary/new_code?id=lnslbrty_nDPId)
|
|
||||||
[](https://sonarcloud.io/summary/new_code?id=lnslbrty_nDPId)
|
|
||||||
[](https://sonarcloud.io/summary/new_code?id=lnslbrty_nDPId)
|
|
||||||

|
|
||||||
|
|
||||||
# References
|
|
||||||
|
|
||||||
[ntop Webinar 2022](https://www.ntop.org/webinar/ntop-webinar-on-dec-14th-community-meeting-and-future-plans/)
|
|
||||||
[ntopconf 2023](https://www.ntop.org/ntopconf2023/)
|
|
||||||
|
|
||||||
# Disclaimer
|
|
||||||
|
|
||||||
Please respect & protect the privacy of others.
|
|
||||||
|
|
||||||
The purpose of this software is not to spy on others, but to detect network anomalies and malicious traffic.
|
|
||||||
|
|
||||||
# Abstract
|
|
||||||
|
|
||||||
nDPId is a set of daemons and tools to capture, process and classify network traffic.
|
|
||||||
Its minimal dependencies (besides a to some extent modern C library and POSIX threads) are libnDPI (>=5.0.0 or current github dev branch) and libpcap.
|
|
||||||
|
|
||||||
The daemon `nDPId` is capable of multithreading for packet processing, but w/o mutexes for performance reasons.
|
|
||||||
Instead, synchronization is achieved by a packet distribution mechanism.
|
|
||||||
To balance the workload to all threads (more or less) equally, a unique identifier represented as hash value is calculated using a 3-tuple consisting of: IPv4/IPv6 src/dst address; IP header value of the layer4 protocol; and (for TCP/UDP) src/dst port. Other protocols e.g. ICMP/ICMPv6 lack relevance for DPI, thus nDPId does not distinguish between different ICMP/ICMPv6 flows coming from the same host. This saves memory and performance, but might change in the future.
|
|
||||||
|
|
||||||
`nDPId` uses libnDPI's JSON serialization interface to generate a JSON messages for each event it receives from the library and which it then sends out to a UNIX-socket (default: `/tmp/ndpid-collector.sock` ). From such a socket, `nDPIsrvd` (or other custom applications) can retrieve incoming JSON-messages and further proceed working/distributing messages to higher-level applications.
|
|
||||||
|
|
||||||
Unfortunately, `nDPIsrvd` does not yet support any encryption/authentication for TCP connections (TODO!).
|
|
||||||
|
|
||||||
# Architecture
|
|
||||||
|
|
||||||
This project uses a kind of microservice architecture.
|
|
||||||
|
|
||||||
```text
|
|
||||||
connect to UNIX socket [1] connect to UNIX/TCP socket [2]
|
|
||||||
_______________________ | | __________________________
|
|
||||||
| "producer" |___| |___| "consumer" |
|
|
||||||
|---------------------| _____________________________ |------------------------|
|
|
||||||
| | | nDPIsrvd | | |
|
|
||||||
| nDPId --- Thread 1 >| ---> |> | <| ---> |< example/c-json-stdout |
|
|
||||||
| (eth0) `- Thread 2 >| ---> |> collector | distributor <| ---> |________________________|
|
|
||||||
| `- Thread N >| ---> |> >>> forward >>> <| ---> | |
|
|
||||||
|_____________________| ^ |____________|______________| ^ |< example/py-flow-info |
|
|
||||||
| | | | |________________________|
|
|
||||||
| nDPId --- Thread 1 >| `- send serialized data [1] | | |
|
|
||||||
| (eth1) `- Thread 2 >| | |< example/... |
|
|
||||||
| `- Thread N >| receive serialized data [2] -' |________________________|
|
|
||||||
|_____________________|
|
|
||||||
|
|
||||||
```
|
|
||||||
where:
|
|
||||||
|
|
||||||
* `nDPId` capture traffic, extract traffic data (with libnDPI) and send a JSON-serialized output stream to an already existing UNIX-socket;
|
|
||||||
* `nDPIsrvd`:
|
|
||||||
|
|
||||||
* create and manage an "incoming" UNIX-socket (ref [1] above), to fetch data from a local `nDPId`;
|
|
||||||
* apply a buffering logic to received data;
|
|
||||||
* create and manage an "outgoing" UNIX or TCP socket (ref [2] above) to relay matched events
|
|
||||||
to connected clients
|
|
||||||
|
|
||||||
* `consumers` are common/custom applications being able to receive selected flows/events, via both UNIX-socket or TCP-socket.
|
|
||||||
|
|
||||||
|
|
||||||
# JSON stream format
|
|
||||||
|
|
||||||
JSON messages streamed by both `nDPId` and `nDPIsrvd` are presented with:
|
|
||||||
|
|
||||||
* a 5-digit-number describing (as decimal number) the **entire** JSON message including the newline `\n` at the end;
|
|
||||||
* the JSON messages
|
|
||||||
|
|
||||||
```text
|
|
||||||
[5-digit-number][JSON message]
|
|
||||||
```
|
|
||||||
|
|
||||||
as with the following example:
|
|
||||||
|
|
||||||
```text
|
|
||||||
01223{"flow_event_id":7,"flow_event_name":"detection-update","thread_id":12,"packet_id":307,"source":"wlan0", ...snip...}
|
|
||||||
00458{"packet_event_id":2,"packet_event_name":"packet-flow","thread_id":11,"packet_id":324,"source":"wlan0", ...snip...}
|
|
||||||
00572{"flow_event_id":1,"flow_event_name":"new","thread_id":11,"packet_id":324,"source":"wlan0", ...snip...}
|
|
||||||
```
|
|
||||||
|
|
||||||
The full stream of `nDPId` generated JSON-events can be retrieved directly from `nDPId`, without relying on `nDPIsrvd`, by providing a properly managed UNIX-socket.
|
|
||||||
|
|
||||||
Technical details about the JSON-message format can be obtained from the related `.schema` file included in the `schema` directory
|
|
||||||
|
|
||||||
|
|
||||||
# Events
|
|
||||||
|
|
||||||
`nDPId` generates JSON messages whereby each string is assigned to a certain event.
|
|
||||||
Those events specify the contents (key-value-pairs) of the JSON message.
|
|
||||||
They are divided into four categories, each with a number of subevents.
|
|
||||||
|
|
||||||
## Error Events
|
|
||||||
They are 17 distinct events, indicating that layer2 or layer3 packet processing failed or not enough flow memory available:
|
|
||||||
|
|
||||||
1. Unknown datalink layer packet
|
|
||||||
2. Unknown L3 protocol
|
|
||||||
3. Unsupported datalink layer
|
|
||||||
4. Packet too short
|
|
||||||
5. Unknown packet type
|
|
||||||
6. Packet header invalid
|
|
||||||
7. IP4 packet too short
|
|
||||||
8. Packet smaller than IP4 header:
|
|
||||||
9. nDPI IPv4/L4 payload detection failed
|
|
||||||
10. IP6 packet too short
|
|
||||||
11. Packet smaller than IP6 header
|
|
||||||
12. nDPI IPv6/L4 payload detection failed
|
|
||||||
13. TCP packet smaller than expected
|
|
||||||
14. UDP packet smaller than expected
|
|
||||||
15. Captured packet size is smaller than expected packet size
|
|
||||||
16. Max flows to track reached
|
|
||||||
17. Flow memory allocation failed
|
|
||||||
|
|
||||||
Detailed JSON-schema is available [here](schema/error_event_schema.json)
|
|
||||||
|
|
||||||
## Daemon Events
|
|
||||||
There are 4 distinct events indicating startup/shutdown or status events as well as a reconnect event if there was a previous connection failure (collector):
|
|
||||||
|
|
||||||
1. init: `nDPId` startup
|
|
||||||
2. reconnect: (UNIX) socket connection lost previously and was established again
|
|
||||||
3. shutdown: `nDPId` terminates gracefully
|
|
||||||
4. status: statistics about the daemon itself e.g. memory consumption, zLib compressions (if enabled)
|
|
||||||
|
|
||||||
Detailed JSON-schema is available [here](schema/daemon_event_schema.json)
|
|
||||||
|
|
||||||
|
|
||||||
## Packet Events
|
|
||||||
There are 2 events containing base64 encoded packet payloads either belonging to a flow or not:
|
|
||||||
|
|
||||||
1. packet: does not belong to any flow
|
|
||||||
2. packet-flow: belongs to a flow e.g. TCP/UDP or ICMP
|
|
||||||
|
|
||||||
Detailed JSON-schema is available [here](schema/packet_event_schema.json)
|
|
||||||
|
|
||||||
## Flow Events
|
|
||||||
There are 9 distinct events related to a flow:
|
|
||||||
|
|
||||||
1. new: a new TCP/UDP/ICMP flow seen which will be tracked
|
|
||||||
2. end: a TCP connection terminates
|
|
||||||
3. idle: a flow timed out, because there was no packet on the wire for a certain amount of time
|
|
||||||
4. update: inform nDPIsrvd or other apps about a long-lasting flow, whose detection was finished a long time ago but is still active
|
|
||||||
5. analyse: provide some information about extracted features of a flow (Experimental; disabled per default, enable with `-A`)
|
|
||||||
6. guessed: `libnDPI` was not able to reliably detect a layer7 protocol and falls back to IP/Port based detection
|
|
||||||
7. detected: `libnDPI` sucessfully detected a layer7 protocol
|
|
||||||
8. detection-update: `libnDPI` dissected more layer7 protocol data (after detection already done)
|
|
||||||
9. not-detected: neither detected nor guessed
|
|
||||||
|
|
||||||
Detailed JSON-schema is available [here](schema/flow_event_schema.json). Also, a graphical representation of *Flow Events* timeline is available [here](schema/flow_events_diagram.png).
|
|
||||||
|
|
||||||
# Flow States
|
|
||||||
|
|
||||||
A flow can have three different states while it is been tracked by `nDPId`.
|
|
||||||
|
|
||||||
1. skipped: the flow will be tracked, but no detection will happen to reduce memory usage.
|
|
||||||
See command line argument `-I` and `-E`
|
|
||||||
2. finished: detection finished and the memory used for the detection is freed
|
|
||||||
3. info: detection is in progress and all flow memory required for `libnDPI` is allocated (this state consumes most memory)
|
|
||||||
|
|
||||||
# Build (CMake)
|
|
||||||
|
|
||||||
`nDPId` build system is based on [CMake](https://cmake.org/)
|
|
||||||
|
|
||||||
```shell
|
|
||||||
git clone https://github.com/utoni/nDPId.git
|
|
||||||
[...]
|
|
||||||
cd ndpid
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake ..
|
|
||||||
[...]
|
|
||||||
make
|
|
||||||
```
|
|
||||||
|
|
||||||
see below for a full/test live-session
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Based on your build environment and/or desiderata, you could need:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
ccmake ..
|
|
||||||
```
|
|
||||||
|
|
||||||
or to build with a staticially linked libnDPI:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
cmake -S . -B ./build \
|
|
||||||
-DSTATIC_LIBNDPI_INSTALLDIR=[path/to/your/libnDPI/installdir] \
|
|
||||||
-DNDPI_NO_PKGCONFIG=ON
|
|
||||||
cmake --build ./build
|
|
||||||
```
|
|
||||||
|
|
||||||
If you use the latter, make sure that you've configured libnDPI with `./configure --prefix=[path/to/your/libnDPI/installdir]`
|
|
||||||
and remember to set the all-necessary CMake variables to link against shared libraries used by your nDPI build.
|
|
||||||
You'll also need to use `-DNDPI_NO_PKGCONFIG=ON` if `STATIC_LIBNDPI_INSTALLDIR` does not contain a pkg-config file.
|
|
||||||
|
|
||||||
e.g.:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
cmake -S . -B ./build \
|
|
||||||
-DSTATIC_LIBNDPI_INSTALLDIR=[path/to/your/libnDPI/installdir] \
|
|
||||||
-DNDPI_NO_PKGCONFIG=ON \
|
|
||||||
-DNDPI_WITH_GCRYPT=ON -DNDPI_WITH_PCRE=OFF -DNDPI_WITH_MAXMINDDB=OFF
|
|
||||||
cmake --build ./build
|
|
||||||
```
|
|
||||||
|
|
||||||
Or let a shell script do the work for you:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
cmake -S . -B ./build \
|
|
||||||
-DBUILD_NDPI=ON
|
|
||||||
cmake --build ./build
|
|
||||||
```
|
|
||||||
|
|
||||||
The CMake cache variable `-DBUILD_NDPI=ON` builds a version of `libnDPI` residing as a git submodule in this repository.
|
|
||||||
|
|
||||||
# run
|
|
||||||
|
|
||||||
As mentioned above, in order to run `nDPId`, a UNIX-socket needs to be provided in order to stream our related JSON-data.
|
|
||||||
|
|
||||||
Such a UNIX-socket can be provided by both the included `nDPIsrvd` daemon, or, if you simply need a quick check, with the [ncat](https://nmap.org/book/ncat-man.html) utility, with a simple `ncat -U /tmp/listen.sock -l -k`. Remember that OpenBSD `netcat` is not able to handle multiple connections reliably.
|
|
||||||
|
|
||||||
Once the socket is ready, you can run `nDPId` capturing and analyzing your own traffic, with something similar to: `sudo nDPId -c /tmp/listen.sock`
|
|
||||||
If you're using OpenBSD `netcat`, you need to run: `sudo nDPId -c /tmp/listen.sock -o max-reader-threads=1`
|
|
||||||
Make sure that the UNIX socket is accessible by the user (see -u) to whom nDPId changes to, default: nobody.
|
|
||||||
|
|
||||||
Of course, both `ncat` and `nDPId` need to point to the same UNIX-socket (`nDPId` provides the `-c` option, exactly for this. By default, `nDPId` refers to `/tmp/ndpid-collector.sock`, and the same default-path is also used by `nDPIsrvd` for the incoming socket).
|
|
||||||
|
|
||||||
Give `nDPId` some real-traffic. You can capture your own traffic, with something similar to:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
socat -u UNIX-Listen:/tmp/listen.sock,fork - # does the same as `ncat`
|
|
||||||
sudo chown nobody:nobody /tmp/listen.sock # default `nDPId` user/group, see `-u` and `-g`
|
|
||||||
sudo ./nDPId -c /tmp/listen.sock -l
|
|
||||||
```
|
|
||||||
|
|
||||||
`nDPId` supports also UDP collector endpoints:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
nc -d -u 127.0.0.1 7000 -l -k
|
|
||||||
sudo ./nDPId -c 127.0.0.1:7000 -l
|
|
||||||
```
|
|
||||||
|
|
||||||
or you can generate a nDPId-compatible JSON dump with:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
./nDPId-test [path-to-a-PCAP-file]
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also automatically fire both `nDPId` and `nDPIsrvd` automatically, with:
|
|
||||||
|
|
||||||
Daemons:
|
|
||||||
```shell
|
|
||||||
make -C [path-to-a-build-dir] daemon
|
|
||||||
```
|
|
||||||
|
|
||||||
Or a manual approach with:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
./nDPIsrvd -d
|
|
||||||
sudo ./nDPId -d
|
|
||||||
```
|
|
||||||
|
|
||||||
or for a usage printout:
|
|
||||||
```shell
|
|
||||||
./nDPIsrvd -h
|
|
||||||
./nDPId -h
|
|
||||||
```
|
|
||||||
|
|
||||||
And why not a flow-info example?
|
|
||||||
```shell
|
|
||||||
./examples/py-flow-info/flow-info.py
|
|
||||||
```
|
|
||||||
|
|
||||||
or anything below `./examples`.
|
|
||||||
|
|
||||||
# nDPId tuning
|
|
||||||
|
|
||||||
It is possible to change `nDPId` internals w/o recompiling by using `-o subopt=value`.
|
|
||||||
But be careful: changing the default values may render `nDPId` useless and is not well tested.
|
|
||||||
|
|
||||||
Suboptions for `-o`:
|
|
||||||
|
|
||||||
Format: `subopt` (unit, comment): description
|
|
||||||
|
|
||||||
* `max-flows-per-thread` (N, caution advised): affects max. memory usage
|
|
||||||
* `max-idle-flows-per-thread` (N, safe): max. allowed idle flows whose memory gets freed after `flow-scan-interval`
|
|
||||||
* `max-reader-threads` (N, safe): amount of packet processing threads, every thread can have a max. of `max-flows-per-thread` flows
|
|
||||||
* `daemon-status-interval` (ms, safe): specifies how often daemon event `status` is generated
|
|
||||||
* `compression-scan-interval` (ms, untested): specifies how often `nDPId` scans for inactive flows ready for compression
|
|
||||||
* `compression-flow-inactivity` (ms, untested): the shortest period of time elapsed before `nDPId` considers compressing a flow (e.g. nDPI flow struct) that neither sent nor received any data
|
|
||||||
* `flow-scan-interval` (ms, safe): min. amount of time after which `nDPId` scans for idle or long-lasting flows
|
|
||||||
* `generic-max-idle-time` (ms, untested): time after which a non TCP/UDP/ICMP flow times out
|
|
||||||
* `icmp-max-idle-time` (ms, untested): time after which an ICMP flow times out
|
|
||||||
* `udp-max-idle-time` (ms, caution advised): time after which an UDP flow times out
|
|
||||||
* `tcp-max-idle-time` (ms, caution advised): time after which a TCP flow times out
|
|
||||||
* `tcp-max-post-end-flow-time` (ms, caution advised): a TCP flow that received a FIN or RST waits this amount of time before flow tracking stops and the flow memory is freed
|
|
||||||
* `max-packets-per-flow-to-send` (N, safe): max. `packet-flow` events generated for the first N packets of each flow
|
|
||||||
* `max-packets-per-flow-to-process` (N, caution advised): max. amount of packets processed by `libnDPI`
|
|
||||||
* `max-packets-per-flow-to-analyze` (N, safe): max. packets to analyze before sending an `analyse` event, requires `-A`
|
|
||||||
* `error-event-threshold-n` (N, safe): max. error events to send until threshold time has passed
|
|
||||||
* `error-event-threshold-time` (N, safe): time after which the error event threshold resets
|
|
||||||
|
|
||||||
# test
|
|
||||||
|
|
||||||
The recommended way to run regression / diff tests:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
cmake -S . -B ./build-like-ci \
|
|
||||||
-DBUILD_NDPI=ON -DENABLE_ZLIB=ON -DBUILD_EXAMPLES=ON
|
|
||||||
# optional: -DENABLE_CURL=ON -DENABLE_SANITIZER=ON
|
|
||||||
./test/run_tests.sh ./libnDPI ./build-like-ci/nDPId-test
|
|
||||||
# or: make -C ./build-like-ci test
|
|
||||||
```
|
|
||||||
|
|
||||||
Run `./test/run_tests.sh` to see some usage information.
|
|
||||||
|
|
||||||
Remember that all test results are tied to a specific libnDPI commit hash
|
|
||||||
as part of the `git submodule`. Using `test/run_tests.sh` for other commit hashes
|
|
||||||
will most likely result in PCAP diffs.
|
|
||||||
|
|
||||||
# Code Coverage
|
|
||||||
|
|
||||||
You may generate code coverage by using:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
cmake -S . -B ./build-coverage \
|
|
||||||
-DENABLE_COVERAGE=ON -DENABLE_ZLIB=ON
|
|
||||||
# optional: -DBUILD_NDPI=ON
|
|
||||||
make -C ./build-coverage coverage-clean
|
|
||||||
make -C ./build-coverage clean
|
|
||||||
make -C ./build-coverage all
|
|
||||||
./test/run_tests.sh ./libnDPI ./build-coverage/nDPId-test
|
|
||||||
make -C ./build-coverage coverage
|
|
||||||
make -C ./build-coverage coverage-view
|
|
||||||
```
|
|
||||||
|
|
||||||
# Contributors
|
|
||||||
|
|
||||||
Special thanks to Damiano Verzulli ([@verzulli](https://github.com/verzulli)) from [GARRLab](https://www.garrlab.it) for providing server and test infrastructure.
|
|
||||||
18
SECURITY.md
18
SECURITY.md
@@ -1,18 +0,0 @@
|
|||||||
# Security Policy
|
|
||||||
|
|
||||||
I encourage you to submit a pull request if you have a solution or fix for anything even security vulnerabilities.
|
|
||||||
Your contributions help advance and enhance safety for all users :star:.
|
|
||||||
|
|
||||||
## Reporting a Bug :bug: :bug:
|
|
||||||
|
|
||||||
Simply use GitHub issues to report a bug with related information to debug the issue :pencil:.
|
|
||||||
|
|
||||||
## Reporting a Vulnerability :closed_lock_with_key: :eyes:
|
|
||||||
|
|
||||||
For sensitive security issues, please email <toni@impl.cc> including the following information:
|
|
||||||
|
|
||||||
- Description of the vulnerability
|
|
||||||
- Steps to reproduce the issue
|
|
||||||
- Affected versions i.e. release tags, git commit hashes or git branch
|
|
||||||
- If applicable, a data sample (preferably `pcap/pcapng`) to reproduce
|
|
||||||
- If known, any mitigations or fixes for the issue
|
|
||||||
20
TODO.md
20
TODO.md
@@ -1,20 +0,0 @@
|
|||||||
# TODOs
|
|
||||||
|
|
||||||
1.8:
|
|
||||||
|
|
||||||
* let nDPIsrvd (collector) connect to other nDPIsrvd instances (as distributor)
|
|
||||||
* nDPIsrvd GnuTLS support for TCP/IP distributor connections
|
|
||||||
* provide nDPId-exportd daemon which will only send captured packets to an nDPId instance running on a different machine
|
|
||||||
|
|
||||||
2.0.0:
|
|
||||||
|
|
||||||
* switch to semantic versioning for the greater good ;)
|
|
||||||
|
|
||||||
no release plan:
|
|
||||||
|
|
||||||
* merge flow end/idle event into idle event (end is not really useful..)
|
|
||||||
* provide a shared library for C / C++ for distributor application developers
|
|
||||||
* improve UDP/TCP timeout handling by reading netfilter conntrack timeouts from /proc (or just read conntrack table entries)
|
|
||||||
* detect interface / timeout changes and apply them to nDPId
|
|
||||||
* switch to MIT or BSD License
|
|
||||||
* libdaq integration
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
# - Check if the system supports epoll.
|
|
||||||
# CHECK_EPOLL(<var>)
|
|
||||||
# <var> - variable to store the result
|
|
||||||
# (1 for success, empty for failure)
|
|
||||||
|
|
||||||
#=============================================================================
|
|
||||||
# This software is in the public domain, furnished "as is", without technical
|
|
||||||
# support, and with no warranty, express or implied, as to its usefulness for
|
|
||||||
# any purpose.
|
|
||||||
#=============================================================================
|
|
||||||
|
|
||||||
macro(CHECK_EPOLL VARIABLE)
|
|
||||||
if(UNIX)
|
|
||||||
if("${VARIABLE}" MATCHES "^${VARIABLE}$")
|
|
||||||
message(STATUS "Check if the system supports epoll")
|
|
||||||
include(CheckSymbolExists)
|
|
||||||
check_symbol_exists(epoll_create "sys/epoll.h" EPOLL_PROTOTYPE_EXISTS)
|
|
||||||
|
|
||||||
if(EPOLL_PROTOTYPE_EXISTS)
|
|
||||||
message(STATUS "Check if the system supports epoll - yes")
|
|
||||||
set(${VARIABLE} 1 CACHE INTERNAL "Result of CHECK_EPOLL" FORCE)
|
|
||||||
else(EPOLL_PROTOTYPE_EXISTS)
|
|
||||||
message(STATUS "Check if the system supports epoll - no")
|
|
||||||
set(${VARIABLE} "" CACHE INTERNAL "Result of CHECK_EPOLL" FORCE)
|
|
||||||
endif(EPOLL_PROTOTYPE_EXISTS)
|
|
||||||
endif("${VARIABLE}" MATCHES "^${VARIABLE}$")
|
|
||||||
endif(UNIX)
|
|
||||||
endmacro(CHECK_EPOLL)
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
#
|
|
||||||
# - Find gcrypt
|
|
||||||
# Find the native GCRYPT includes and library
|
|
||||||
#
|
|
||||||
# GCRYPT_INCLUDE_DIRS - where to find gcrypt.h, etc.
|
|
||||||
# GCRYPT_LIBRARIES - List of libraries when using gcrypt.
|
|
||||||
# GCRYPT_FOUND - True if gcrypt found.
|
|
||||||
# GCRYPT_DLL_DIR - (Windows) Path to the Libgcrypt DLLs.
|
|
||||||
# GCRYPT_DLLS - (Windows) List of required Libgcrypt DLLs.
|
|
||||||
|
|
||||||
|
|
||||||
if(GCRYPT_INCLUDE_DIRS)
|
|
||||||
# Already in cache, be silent
|
|
||||||
set(GCRYPT_FIND_QUIETLY TRUE)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
find_path(GCRYPT_INCLUDE_DIR gcrypt.h
|
|
||||||
HINTS
|
|
||||||
"${GCRYPT_HINTS}/include"
|
|
||||||
)
|
|
||||||
|
|
||||||
find_library(GCRYPT_LIBRARY
|
|
||||||
NAMES gcrypt libgcrypt-20
|
|
||||||
HINTS "${GCRYPT_HINTS}/bin")
|
|
||||||
|
|
||||||
# libgpg-error6-0 is used in libgcrypt-1.7.6-win??ws (built from source).
|
|
||||||
# libgpg-error-0 is used in libgcrypt-1.8.3-win??ws (from Debian).
|
|
||||||
find_library(GCRYPT_ERROR_LIBRARY
|
|
||||||
NAMES gpg-error libgpg-error-0 libgpg-error6-0
|
|
||||||
HINTS "${GCRYPT_HINTS}/bin")
|
|
||||||
|
|
||||||
# Try to retrieve version from header if found (available since libgcrypt 1.3.0)
|
|
||||||
if(GCRYPT_INCLUDE_DIR)
|
|
||||||
set(_version_regex "^#define[ \t]+GCRYPT_VERSION[ \t]+\"([^\"]+)\".*")
|
|
||||||
file(STRINGS "${GCRYPT_INCLUDE_DIR}/gcrypt.h" GCRYPT_VERSION REGEX "${_version_regex}")
|
|
||||||
string(REGEX REPLACE "${_version_regex}" "\\1" GCRYPT_VERSION "${GCRYPT_VERSION}")
|
|
||||||
unset(_version_regex)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
include(FindPackageHandleStandardArgs)
|
|
||||||
find_package_handle_standard_args(GCRYPT
|
|
||||||
REQUIRED_VARS GCRYPT_LIBRARY GCRYPT_INCLUDE_DIR
|
|
||||||
VERSION_VAR GCRYPT_VERSION)
|
|
||||||
|
|
||||||
if(GCRYPT_FOUND)
|
|
||||||
set(GCRYPT_LIBRARIES ${GCRYPT_LIBRARY} ${GCRYPT_ERROR_LIBRARY})
|
|
||||||
set(GCRYPT_INCLUDE_DIRS ${GCRYPT_INCLUDE_DIR})
|
|
||||||
if(WIN32)
|
|
||||||
set(GCRYPT_DLL_DIR "${GCRYPT_HINTS}/bin"
|
|
||||||
CACHE PATH "Path to the Libgcrypt DLLs"
|
|
||||||
)
|
|
||||||
file(GLOB _gcrypt_dlls RELATIVE "${GCRYPT_DLL_DIR}"
|
|
||||||
"${GCRYPT_DLL_DIR}/libgcrypt-*.dll"
|
|
||||||
"${GCRYPT_DLL_DIR}/libgpg-error*.dll"
|
|
||||||
)
|
|
||||||
set(GCRYPT_DLLS ${_gcrypt_dlls}
|
|
||||||
# We're storing filenames only. Should we use STRING instead?
|
|
||||||
CACHE FILEPATH "Libgcrypt DLL list"
|
|
||||||
)
|
|
||||||
mark_as_advanced(GCRYPT_DLL_DIR GCRYPT_DLLS)
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
set(GCRYPT_LIBRARIES)
|
|
||||||
set(GCRYPT_INCLUDE_DIRS)
|
|
||||||
set(GCRYPT_DLL_DIR)
|
|
||||||
set(GCRYPT_DLLS)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
mark_as_advanced(GCRYPT_LIBRARIES GCRYPT_INCLUDE_DIRS)
|
|
||||||
@@ -1,273 +0,0 @@
|
|||||||
#
|
|
||||||
# - Find libpcap
|
|
||||||
# Find the native PCAP includes and library
|
|
||||||
#
|
|
||||||
# PCAP_INCLUDE_DIRS - where to find pcap.h, etc.
|
|
||||||
# PCAP_LIBRARIES - List of libraries when using pcap.
|
|
||||||
# PCAP_FOUND - True if pcap found.
|
|
||||||
|
|
||||||
#
|
|
||||||
# First, try pkg-config on platforms other than Windows.
|
|
||||||
#
|
|
||||||
if(NOT WIN32)
|
|
||||||
find_package(PkgConfig)
|
|
||||||
pkg_search_module(PC_PCAP libpcap)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT PC_PCAP_FOUND AND NOT WIN32)
|
|
||||||
#
|
|
||||||
# That didn't work. Try to retrieve hints from pcap-config.
|
|
||||||
# Do not use it on Windows as pcap-config is a shell script.
|
|
||||||
#
|
|
||||||
find_program(PCAP_CONFIG pcap-config)
|
|
||||||
if(PCAP_CONFIG)
|
|
||||||
#
|
|
||||||
# We have pcap-config; use it.
|
|
||||||
#
|
|
||||||
# First, get the include directory.
|
|
||||||
#
|
|
||||||
execute_process(COMMAND "${PCAP_CONFIG}" "--cflags"
|
|
||||||
RESULT_VARIABLE PCAP_CONFIG_RESULT
|
|
||||||
OUTPUT_VARIABLE PCAP_CONFIG_OUTPUT
|
|
||||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
||||||
)
|
|
||||||
if(NOT PCAP_CONFIG_RESULT EQUAL 0)
|
|
||||||
message(FATAL_ERROR "pcap-config --cflags failed")
|
|
||||||
endif()
|
|
||||||
#
|
|
||||||
# Assumes there's exactly one -I flag in the output
|
|
||||||
# of pcap-config --cflags. That *should* be the case.
|
|
||||||
# Note that the hint might be bogus, on macOS it could be
|
|
||||||
# -I/usr/local/include even though the header isn't
|
|
||||||
# there (it may be under /usr/include or it may be
|
|
||||||
# buried in the Xcode app bundle).
|
|
||||||
#
|
|
||||||
string(REGEX REPLACE "^-I" "" PCAP_CONFIG_INCLUDE_DIRS "${PCAP_CONFIG_OUTPUT}")
|
|
||||||
|
|
||||||
# Now, get the library search path.
|
|
||||||
execute_process(COMMAND "${PCAP_CONFIG}" "--libs"
|
|
||||||
RESULT_VARIABLE PCAP_CONFIG_RESULT
|
|
||||||
OUTPUT_VARIABLE PCAP_CONFIG_OUTPUT
|
|
||||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
||||||
)
|
|
||||||
if(NOT PCAP_CONFIG_RESULT EQUAL 0)
|
|
||||||
message(FATAL_ERROR "pcap-config --libs failed")
|
|
||||||
endif()
|
|
||||||
separate_arguments(LIBS_LIST UNIX_COMMAND ${PCAP_CONFIG_OUTPUT})
|
|
||||||
set(PCAP_CONFIG_LIBRARY_DIRS "")
|
|
||||||
foreach(_arg IN LISTS LIBS_LIST)
|
|
||||||
# At most one -L path is expected for -lpcap.
|
|
||||||
if(_arg MATCHES "^-L")
|
|
||||||
string(REGEX REPLACE "^-L" "" _dir ${_arg})
|
|
||||||
list(APPEND PCAP_CONFIG_LIBRARY_DIRS ${_dir})
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
if(UNIX AND CMAKE_FIND_LIBRARY_SUFFIXES STREQUAL ".a")
|
|
||||||
# Now, get the library directories and libraries for static linking.
|
|
||||||
# (XXX - what about AIX?)
|
|
||||||
execute_process(COMMAND "${PCAP_CONFIG}" "--libs" "--static"
|
|
||||||
RESULT_VARIABLE PCAP_CONFIG_RESULT
|
|
||||||
OUTPUT_VARIABLE PCAP_CONFIG_OUTPUT
|
|
||||||
)
|
|
||||||
if(NOT PCAP_CONFIG_RESULT EQUAL 0)
|
|
||||||
message(FATAL_ERROR "pcap-config --libs --static failed")
|
|
||||||
endif()
|
|
||||||
separate_arguments(LIBS_LIST UNIX_COMMAND ${PCAP_CONFIG_OUTPUT})
|
|
||||||
set(PCAP_CONFIG_STATIC_LIBRARY_DIRS "")
|
|
||||||
set(PCAP_CONFIG_STATIC_LIBRARIES "")
|
|
||||||
foreach(_arg IN LISTS LIBS_LIST)
|
|
||||||
if(_arg MATCHES "^-L")
|
|
||||||
# Add this directory to the library directory hints.
|
|
||||||
string(REGEX REPLACE "^-L" "" _dir ${_arg})
|
|
||||||
list(APPEND PCAP_CONFIG_STATIC_LIBRARY_DIRS ${_dir})
|
|
||||||
elseif(_arg MATCHES "^-l")
|
|
||||||
# Add this library to the requirements for static linking.
|
|
||||||
string(REGEX REPLACE "^-l" "" _lib ${_arg})
|
|
||||||
list(APPEND PCAP_CONFIG_STATIC_LIBRARIES ${_lib})
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
#
|
|
||||||
# Locate the actual include directory. For pkg-config the
|
|
||||||
# PC_PCAP_INCLUDE_DIRS variable could be empty if the default
|
|
||||||
# header search path is sufficient to locate the header file.
|
|
||||||
# For macOS, the directory returned by pcap-config is wrong, so
|
|
||||||
# this will make sure to find a valid path.
|
|
||||||
#
|
|
||||||
find_path(PCAP_INCLUDE_DIR
|
|
||||||
NAMES
|
|
||||||
pcap/pcap.h
|
|
||||||
pcap.h
|
|
||||||
HINTS
|
|
||||||
${PC_PCAP_INCLUDE_DIRS}
|
|
||||||
${PCAP_CONFIG_INCLUDE_DIRS}
|
|
||||||
"${PCAP_HINTS}/Include"
|
|
||||||
)
|
|
||||||
|
|
||||||
# On Windows we load wpcap.dll explicitly and probe its functions in
|
|
||||||
# caputils\capture-wpcap.c. We don't want to link with pcap.lib since
|
|
||||||
# that would bring in the non-capturing (null) pcap.dll from the vcpkg
|
|
||||||
# library.
|
|
||||||
if(WIN32)
|
|
||||||
set(_pkg_required_vars PCAP_INCLUDE_DIR)
|
|
||||||
else()
|
|
||||||
find_library(PCAP_LIBRARY
|
|
||||||
NAMES
|
|
||||||
pcap
|
|
||||||
HINTS
|
|
||||||
${PC_PCAP_LIBRARY_DIRS}
|
|
||||||
${PCAP_CONFIG_LIBRARY_DIRS}
|
|
||||||
)
|
|
||||||
set(_pkg_required_vars PCAP_LIBRARY PCAP_INCLUDE_DIR)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(UNIX AND CMAKE_FIND_LIBRARY_SUFFIXES STREQUAL ".a")
|
|
||||||
# Try to find the static library (XXX - what about AIX?)
|
|
||||||
if(PC_PCAP_FOUND)
|
|
||||||
set(_pcap_static_libraries ${PC_PCAP_STATIC_LIBRARIES})
|
|
||||||
elseif(PCAP_CONFIG)
|
|
||||||
set(_pcap_static_libraries ${PCAP_CONFIG_STATIC_LIBRARIES})
|
|
||||||
else()
|
|
||||||
#
|
|
||||||
# No pkg-config nor pcap-config found, hope that this single library is
|
|
||||||
# sufficient for static linking.
|
|
||||||
#
|
|
||||||
set(_pcap_static_libraries pcap)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(PCAP_STATIC_LIBRARIES "")
|
|
||||||
foreach(_lib IN LISTS _pcap_static_libraries)
|
|
||||||
#
|
|
||||||
# Try to find that library, so we get its full path, as
|
|
||||||
# we do with dynamic libraries.
|
|
||||||
#
|
|
||||||
string(MAKE_C_IDENTIFIER "PCAP_STATIC_${_lib}_LIBRARY" _libvar)
|
|
||||||
find_library(${_libvar} ${_lib}
|
|
||||||
HINTS
|
|
||||||
${PC_PCAP_STATIC_LIBRARY_DIRS}
|
|
||||||
${PCAP_CONFIG_STATIC_LIBRARY_DIRS}
|
|
||||||
)
|
|
||||||
set(_libpath ${${_libvar}})
|
|
||||||
if(_libpath)
|
|
||||||
list(APPEND PCAP_STATIC_LIBRARIES ${_libpath})
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
include(FindPackageHandleStandardArgs)
|
|
||||||
find_package_handle_standard_args(PCAP DEFAULT_MSG ${_pkg_required_vars})
|
|
||||||
mark_as_advanced(${_pkg_required_vars})
|
|
||||||
|
|
||||||
if(PCAP_FOUND)
|
|
||||||
set(PCAP_INCLUDE_DIRS ${PCAP_INCLUDE_DIR})
|
|
||||||
if(UNIX AND CMAKE_FIND_LIBRARY_SUFFIXES STREQUAL ".a")
|
|
||||||
# Link with static libpcap and its transitive dependencies.
|
|
||||||
set(PCAP_LIBRARIES ${PCAP_STATIC_LIBRARIES})
|
|
||||||
else()
|
|
||||||
set(PCAP_LIBRARIES ${PCAP_LIBRARY})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
#Functions
|
|
||||||
include( CMakePushCheckState )
|
|
||||||
include( CheckFunctionExists )
|
|
||||||
include( CheckVariableExists )
|
|
||||||
|
|
||||||
cmake_push_check_state()
|
|
||||||
set( CMAKE_REQUIRED_INCLUDES ${PCAP_INCLUDE_DIRS} )
|
|
||||||
set( CMAKE_REQUIRED_LIBRARIES ${PCAP_LIBRARIES} )
|
|
||||||
|
|
||||||
include(CheckSymbolExists)
|
|
||||||
|
|
||||||
if(WIN32)
|
|
||||||
#
|
|
||||||
# Prepopulate some values. WinPcap 3.1 and later, and Npcap, have these
|
|
||||||
# in their SDK, and compilation checks on Windows can be slow. We check
|
|
||||||
# whether they're present at run time, when we load wpcap.dll, and work
|
|
||||||
# around their absence or report an error.
|
|
||||||
#
|
|
||||||
set(HAVE_PCAP_FREECODE TRUE)
|
|
||||||
set(HAVE_PCAP_CREATE TRUE)
|
|
||||||
set(HAVE_PCAP_FREE_DATALINKS TRUE)
|
|
||||||
set(HAVE_PCAP_OPEN TRUE)
|
|
||||||
set(HAVE_PCAP_SETSAMPLING TRUE)
|
|
||||||
set(HAVE_PCAP_SET_TSTAMP_PRECISION TRUE)
|
|
||||||
set(HAVE_PCAP_SET_TSTAMP_TYPE TRUE)
|
|
||||||
else(WIN32)
|
|
||||||
#
|
|
||||||
# Make sure we have at least libpcap 0.8, because we we require at
|
|
||||||
# least libpcap 0.8's APIs.
|
|
||||||
#
|
|
||||||
# We check whether pcap_lib_version is defined in the pcap header,
|
|
||||||
# using it as a proxy for all the 0.8 API's. if not, we fail.
|
|
||||||
#
|
|
||||||
check_symbol_exists( pcap_lib_version ${PCAP_INCLUDE_DIR}/pcap.h HAVE_PCAP_LIB_VERSION )
|
|
||||||
if( NOT HAVE_PCAP_LIB_VERSION )
|
|
||||||
message(FATAL_ERROR "You need libpcap 0.8 or later")
|
|
||||||
endif( NOT HAVE_PCAP_LIB_VERSION )
|
|
||||||
|
|
||||||
check_function_exists( "pcap_freecode" HAVE_PCAP_FREECODE )
|
|
||||||
check_function_exists( "pcap_create" HAVE_PCAP_CREATE )
|
|
||||||
check_function_exists( "pcap_free_datalinks" HAVE_PCAP_FREE_DATALINKS )
|
|
||||||
check_function_exists( "pcap_open" HAVE_PCAP_OPEN )
|
|
||||||
if( HAVE_PCAP_OPEN )
|
|
||||||
#
|
|
||||||
# XXX - this *should* be checked for independently of checking
|
|
||||||
# for pcap_open(), as you might have pcap_setsampling() without
|
|
||||||
# remote capture support.
|
|
||||||
#
|
|
||||||
# However, 1) the sampling options are treated as remote options
|
|
||||||
# in the GUI and and 2) having pcap_setsampling() doesn't mean
|
|
||||||
# you have sampling support. libpcap needs a way to indicate
|
|
||||||
# whether a given device supports sampling, and the GUI should
|
|
||||||
# be changed to decouple them.
|
|
||||||
#
|
|
||||||
# (Actually, libpcap needs a general mechanism to offer options
|
|
||||||
# for particular devices, and Wireshark needs to use that
|
|
||||||
# mechanism. The former is a work in progress.)
|
|
||||||
#
|
|
||||||
# (Note: another work in progress is support for remote
|
|
||||||
# capturing using pcap_create()/pcap_activate(), which we
|
|
||||||
# also need to support once it's available.)
|
|
||||||
#
|
|
||||||
check_function_exists( "pcap_setsampling" HAVE_PCAP_SETSAMPLING )
|
|
||||||
endif( HAVE_PCAP_OPEN )
|
|
||||||
endif(WIN32)
|
|
||||||
|
|
||||||
if( HAVE_PCAP_CREATE )
|
|
||||||
#
|
|
||||||
# If we have pcap_create(), we have pcap_set_buffer_size(), and
|
|
||||||
# can set the capture buffer size.
|
|
||||||
#
|
|
||||||
# Otherwise, if this is Windows, we have pcap_setbuff(), and can
|
|
||||||
# set the capture buffer size.
|
|
||||||
#
|
|
||||||
set( CAN_SET_CAPTURE_BUFFER_SIZE TRUE )
|
|
||||||
endif()
|
|
||||||
check_function_exists( "pcap_set_tstamp_precision" HAVE_PCAP_SET_TSTAMP_PRECISION )
|
|
||||||
check_function_exists( "pcap_set_tstamp_type" HAVE_PCAP_SET_TSTAMP_TYPE )
|
|
||||||
# Remote pcap checks
|
|
||||||
if( HAVE_PCAP_OPEN )
|
|
||||||
set( HAVE_PCAP_REMOTE 1 )
|
|
||||||
endif()
|
|
||||||
|
|
||||||
cmake_pop_check_state()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(PCAP_FOUND AND NOT TARGET pcap::pcap)
|
|
||||||
if(WIN32)
|
|
||||||
add_library(pcap::pcap INTERFACE IMPORTED)
|
|
||||||
set_target_properties(pcap::pcap PROPERTIES
|
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${PCAP_INCLUDE_DIRS}"
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
add_library(pcap::pcap UNKNOWN IMPORTED)
|
|
||||||
set_target_properties(pcap::pcap PROPERTIES
|
|
||||||
IMPORTED_LOCATION "${PCAP_LIBRARIES}"
|
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${PCAP_INCLUDE_DIRS}"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
55
config.h
55
config.h
@@ -1,55 +0,0 @@
|
|||||||
#ifndef CONFIG_H
|
|
||||||
#define CONFIG_H 1
|
|
||||||
|
|
||||||
/* macros shared across multiple executables */
|
|
||||||
#define DEFAULT_CHUSER "nobody"
|
|
||||||
#define COLLECTOR_UNIX_SOCKET "/tmp/ndpid-collector.sock"
|
|
||||||
#define DISTRIBUTOR_UNIX_SOCKET "/tmp/ndpid-distributor.sock"
|
|
||||||
#define DISTRIBUTOR_HOST "127.0.0.1"
|
|
||||||
#define DISTRIBUTOR_PORT 7000u
|
|
||||||
|
|
||||||
/*
|
|
||||||
* NOTE: Buffer size needs to keep in sync with other implementations
|
|
||||||
* e.g. dependencies/nDPIsrvd.py
|
|
||||||
*/
|
|
||||||
#define NETWORK_BUFFER_MAX_SIZE 33792u /* 8192 + 8192 + 8192 + 8192 + 1024 */
|
|
||||||
#define NETWORK_BUFFER_LENGTH_DIGITS 5u
|
|
||||||
#define NETWORK_BUFFER_LENGTH_DIGITS_STR "5"
|
|
||||||
|
|
||||||
#define PFRING_BUFFER_SIZE 65536u
|
|
||||||
|
|
||||||
#define TIME_S_TO_US(s) (s * 1000llu * 1000llu)
|
|
||||||
|
|
||||||
/* nDPId default config options */
|
|
||||||
#define nDPId_PIDFILE "/tmp/ndpid.pid"
|
|
||||||
#define nDPId_MAX_FLOWS_PER_THREAD 65536u
|
|
||||||
#define nDPId_MAX_IDLE_FLOWS_PER_THREAD (nDPId_MAX_FLOWS_PER_THREAD / 32u)
|
|
||||||
#define nDPId_MAX_READER_THREADS 32u
|
|
||||||
#define nDPId_ERROR_EVENT_THRESHOLD_N 16u
|
|
||||||
#define nDPId_ERROR_EVENT_THRESHOLD_TIME TIME_S_TO_US(10u) /* 10 sec */
|
|
||||||
#define nDPId_DAEMON_STATUS_INTERVAL TIME_S_TO_US(600u) /* 600 sec */
|
|
||||||
#define nDPId_MEMORY_PROFILING_LOG_INTERVAL TIME_S_TO_US(5u) /* 5 sec */
|
|
||||||
#define nDPId_COMPRESSION_SCAN_INTERVAL TIME_S_TO_US(20u) /* 20 sec */
|
|
||||||
#define nDPId_COMPRESSION_FLOW_INACTIVITY TIME_S_TO_US(30u) /* 30 sec */
|
|
||||||
#define nDPId_FLOW_SCAN_INTERVAL TIME_S_TO_US(10u) /* 10 sec */
|
|
||||||
#define nDPId_GENERIC_IDLE_TIME TIME_S_TO_US(600u) /* 600 sec */
|
|
||||||
#define nDPId_ICMP_IDLE_TIME TIME_S_TO_US(120u) /* 120 sec */
|
|
||||||
#define nDPId_TCP_IDLE_TIME TIME_S_TO_US(7440u) /* 7440 sec */
|
|
||||||
#define nDPId_UDP_IDLE_TIME TIME_S_TO_US(180u) /* 180 sec */
|
|
||||||
#define nDPId_TCP_POST_END_FLOW_TIME TIME_S_TO_US(120u) /* 120 sec */
|
|
||||||
#define nDPId_THREAD_DISTRIBUTION_SEED 0x03dd018b
|
|
||||||
#define nDPId_PACKETS_PLEN_MAX 8192u /* 8kB */
|
|
||||||
#define nDPId_PACKETS_PER_FLOW_TO_SEND 15u
|
|
||||||
#define nDPId_PACKETS_PER_FLOW_TO_PROCESS 32u
|
|
||||||
#define nDPId_PACKETS_PER_FLOW_TO_ANALYZE 32u
|
|
||||||
#define nDPId_ANALYZE_PLEN_MAX 1504u
|
|
||||||
#define nDPId_ANALYZE_PLEN_BIN_LEN 32u
|
|
||||||
#define nDPId_ANALYZE_PLEN_NUM_BINS 48u
|
|
||||||
#define nDPId_FLOW_STRUCT_SEED 0x5defc104
|
|
||||||
|
|
||||||
/* nDPIsrvd default config options */
|
|
||||||
#define nDPIsrvd_PIDFILE "/tmp/ndpisrvd.pid"
|
|
||||||
#define nDPIsrvd_MAX_REMOTE_DESCRIPTORS 128
|
|
||||||
#define nDPIsrvd_MAX_WRITE_BUFFERS 1024
|
|
||||||
|
|
||||||
#endif
|
|
||||||
90
dependencies/jsmn/.clang-format
vendored
90
dependencies/jsmn/.clang-format
vendored
@@ -1,90 +0,0 @@
|
|||||||
---
|
|
||||||
Language: Cpp
|
|
||||||
# BasedOnStyle: LLVM
|
|
||||||
AccessModifierOffset: -2
|
|
||||||
AlignAfterOpenBracket: Align
|
|
||||||
AlignConsecutiveAssignments: false
|
|
||||||
AlignConsecutiveDeclarations: false
|
|
||||||
AlignEscapedNewlinesLeft: false
|
|
||||||
AlignOperands: true
|
|
||||||
AlignTrailingComments: true
|
|
||||||
AllowAllParametersOfDeclarationOnNextLine: true
|
|
||||||
AllowShortBlocksOnASingleLine: false
|
|
||||||
AllowShortCaseLabelsOnASingleLine: false
|
|
||||||
AllowShortFunctionsOnASingleLine: All
|
|
||||||
AllowShortIfStatementsOnASingleLine: false
|
|
||||||
AllowShortLoopsOnASingleLine: false
|
|
||||||
AlwaysBreakAfterDefinitionReturnType: None
|
|
||||||
AlwaysBreakAfterReturnType: None
|
|
||||||
AlwaysBreakBeforeMultilineStrings: false
|
|
||||||
AlwaysBreakTemplateDeclarations: false
|
|
||||||
BinPackArguments: true
|
|
||||||
BinPackParameters: true
|
|
||||||
BraceWrapping:
|
|
||||||
AfterClass: false
|
|
||||||
AfterControlStatement: false
|
|
||||||
AfterEnum: false
|
|
||||||
AfterFunction: false
|
|
||||||
AfterNamespace: false
|
|
||||||
AfterObjCDeclaration: false
|
|
||||||
AfterStruct: false
|
|
||||||
AfterUnion: false
|
|
||||||
BeforeCatch: false
|
|
||||||
BeforeElse: false
|
|
||||||
IndentBraces: false
|
|
||||||
BreakBeforeBinaryOperators: None
|
|
||||||
BreakBeforeBraces: Attach
|
|
||||||
BreakBeforeTernaryOperators: true
|
|
||||||
BreakConstructorInitializersBeforeComma: false
|
|
||||||
ColumnLimit: 80
|
|
||||||
CommentPragmas: '^ IWYU pragma:'
|
|
||||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
|
||||||
ConstructorInitializerIndentWidth: 4
|
|
||||||
ContinuationIndentWidth: 4
|
|
||||||
Cpp11BracedListStyle: true
|
|
||||||
DerivePointerAlignment: false
|
|
||||||
DisableFormat: false
|
|
||||||
ExperimentalAutoDetectBinPacking: false
|
|
||||||
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
|
|
||||||
IncludeCategories:
|
|
||||||
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
|
||||||
Priority: 2
|
|
||||||
- Regex: '^(<|"(gtest|isl|json)/)'
|
|
||||||
Priority: 3
|
|
||||||
- Regex: '.*'
|
|
||||||
Priority: 1
|
|
||||||
IndentCaseLabels: false
|
|
||||||
IndentWidth: 2
|
|
||||||
IndentWrappedFunctionNames: false
|
|
||||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
|
||||||
MacroBlockBegin: ''
|
|
||||||
MacroBlockEnd: ''
|
|
||||||
MaxEmptyLinesToKeep: 1
|
|
||||||
NamespaceIndentation: None
|
|
||||||
ObjCBlockIndentWidth: 2
|
|
||||||
ObjCSpaceAfterProperty: false
|
|
||||||
ObjCSpaceBeforeProtocolList: true
|
|
||||||
PenaltyBreakBeforeFirstCallParameter: 19
|
|
||||||
PenaltyBreakComment: 300
|
|
||||||
PenaltyBreakFirstLessLess: 120
|
|
||||||
PenaltyBreakString: 1000
|
|
||||||
PenaltyExcessCharacter: 1000000
|
|
||||||
PenaltyReturnTypeOnItsOwnLine: 60
|
|
||||||
PointerAlignment: Right
|
|
||||||
ReflowComments: true
|
|
||||||
SortIncludes: true
|
|
||||||
SpaceAfterCStyleCast: false
|
|
||||||
SpaceBeforeAssignmentOperators: true
|
|
||||||
SpaceBeforeParens: ControlStatements
|
|
||||||
SpaceInEmptyParentheses: false
|
|
||||||
SpacesBeforeTrailingComments: 1
|
|
||||||
SpacesInAngles: false
|
|
||||||
SpacesInContainerLiterals: true
|
|
||||||
SpacesInCStyleCastParentheses: false
|
|
||||||
SpacesInParentheses: false
|
|
||||||
SpacesInSquareBrackets: false
|
|
||||||
Standard: Cpp11
|
|
||||||
TabWidth: 8
|
|
||||||
UseTab: Never
|
|
||||||
...
|
|
||||||
|
|
||||||
4
dependencies/jsmn/.travis.yml
vendored
4
dependencies/jsmn/.travis.yml
vendored
@@ -1,4 +0,0 @@
|
|||||||
language: c
|
|
||||||
sudo: false
|
|
||||||
script:
|
|
||||||
- make test
|
|
||||||
20
dependencies/jsmn/LICENSE
vendored
20
dependencies/jsmn/LICENSE
vendored
@@ -1,20 +0,0 @@
|
|||||||
Copyright (c) 2010 Serge A. Zaitsev
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
36
dependencies/jsmn/Makefile
vendored
36
dependencies/jsmn/Makefile
vendored
@@ -1,36 +0,0 @@
|
|||||||
# You can put your build options here
|
|
||||||
-include config.mk
|
|
||||||
|
|
||||||
test: test_default test_strict test_links test_strict_links
|
|
||||||
test_default: test/tests.c jsmn.h
|
|
||||||
$(CC) $(CFLAGS) $(LDFLAGS) $< -o test/$@
|
|
||||||
./test/$@
|
|
||||||
test_strict: test/tests.c jsmn.h
|
|
||||||
$(CC) -DJSMN_STRICT=1 $(CFLAGS) $(LDFLAGS) $< -o test/$@
|
|
||||||
./test/$@
|
|
||||||
test_links: test/tests.c jsmn.h
|
|
||||||
$(CC) -DJSMN_PARENT_LINKS=1 $(CFLAGS) $(LDFLAGS) $< -o test/$@
|
|
||||||
./test/$@
|
|
||||||
test_strict_links: test/tests.c jsmn.h
|
|
||||||
$(CC) -DJSMN_STRICT=1 -DJSMN_PARENT_LINKS=1 $(CFLAGS) $(LDFLAGS) $< -o test/$@
|
|
||||||
./test/$@
|
|
||||||
|
|
||||||
simple_example: example/simple.c jsmn.h
|
|
||||||
$(CC) $(LDFLAGS) $< -o $@
|
|
||||||
|
|
||||||
jsondump: example/jsondump.c jsmn.h
|
|
||||||
$(CC) $(LDFLAGS) $< -o $@
|
|
||||||
|
|
||||||
fmt:
|
|
||||||
clang-format -i jsmn.h test/*.[ch] example/*.[ch]
|
|
||||||
|
|
||||||
lint:
|
|
||||||
clang-tidy jsmn.h --checks='*'
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f *.o example/*.o
|
|
||||||
rm -f simple_example
|
|
||||||
rm -f jsondump
|
|
||||||
|
|
||||||
.PHONY: clean test
|
|
||||||
|
|
||||||
182
dependencies/jsmn/README.md
vendored
182
dependencies/jsmn/README.md
vendored
@@ -1,182 +0,0 @@
|
|||||||
JSMN
|
|
||||||
====
|
|
||||||
|
|
||||||
[](https://travis-ci.org/zserge/jsmn)
|
|
||||||
|
|
||||||
jsmn (pronounced like 'jasmine') is a minimalistic JSON parser in C. It can be
|
|
||||||
easily integrated into resource-limited or embedded projects.
|
|
||||||
|
|
||||||
You can find more information about JSON format at [json.org][1]
|
|
||||||
|
|
||||||
Library sources are available at https://github.com/zserge/jsmn
|
|
||||||
|
|
||||||
The web page with some information about jsmn can be found at
|
|
||||||
[http://zserge.com/jsmn.html][2]
|
|
||||||
|
|
||||||
Philosophy
|
|
||||||
----------
|
|
||||||
|
|
||||||
Most JSON parsers offer you a bunch of functions to load JSON data, parse it
|
|
||||||
and extract any value by its name. jsmn proves that checking the correctness of
|
|
||||||
every JSON packet or allocating temporary objects to store parsed JSON fields
|
|
||||||
often is an overkill.
|
|
||||||
|
|
||||||
JSON format itself is extremely simple, so why should we complicate it?
|
|
||||||
|
|
||||||
jsmn is designed to be **robust** (it should work fine even with erroneous
|
|
||||||
data), **fast** (it should parse data on the fly), **portable** (no superfluous
|
|
||||||
dependencies or non-standard C extensions). And of course, **simplicity** is a
|
|
||||||
key feature - simple code style, simple algorithm, simple integration into
|
|
||||||
other projects.
|
|
||||||
|
|
||||||
Features
|
|
||||||
--------
|
|
||||||
|
|
||||||
* compatible with C89
|
|
||||||
* no dependencies (even libc!)
|
|
||||||
* highly portable (tested on x86/amd64, ARM, AVR)
|
|
||||||
* about 200 lines of code
|
|
||||||
* extremely small code footprint
|
|
||||||
* API contains only 2 functions
|
|
||||||
* no dynamic memory allocation
|
|
||||||
* incremental single-pass parsing
|
|
||||||
* library code is covered with unit-tests
|
|
||||||
|
|
||||||
Design
|
|
||||||
------
|
|
||||||
|
|
||||||
The rudimentary jsmn object is a **token**. Let's consider a JSON string:
|
|
||||||
|
|
||||||
'{ "name" : "Jack", "age" : 27 }'
|
|
||||||
|
|
||||||
It holds the following tokens:
|
|
||||||
|
|
||||||
* Object: `{ "name" : "Jack", "age" : 27}` (the whole object)
|
|
||||||
* Strings: `"name"`, `"Jack"`, `"age"` (keys and some values)
|
|
||||||
* Number: `27`
|
|
||||||
|
|
||||||
In jsmn, tokens do not hold any data, but point to token boundaries in JSON
|
|
||||||
string instead. In the example above jsmn will create tokens like: Object
|
|
||||||
[0..31], String [3..7], String [12..16], String [20..23], Number [27..29].
|
|
||||||
|
|
||||||
Every jsmn token has a type, which indicates the type of corresponding JSON
|
|
||||||
token. jsmn supports the following token types:
|
|
||||||
|
|
||||||
* Object - a container of key-value pairs, e.g.:
|
|
||||||
`{ "foo":"bar", "x":0.3 }`
|
|
||||||
* Array - a sequence of values, e.g.:
|
|
||||||
`[ 1, 2, 3 ]`
|
|
||||||
* String - a quoted sequence of chars, e.g.: `"foo"`
|
|
||||||
* Primitive - a number, a boolean (`true`, `false`) or `null`
|
|
||||||
|
|
||||||
Besides start/end positions, jsmn tokens for complex types (like arrays
|
|
||||||
or objects) also contain a number of child items, so you can easily follow
|
|
||||||
object hierarchy.
|
|
||||||
|
|
||||||
This approach provides enough information for parsing any JSON data and makes
|
|
||||||
it possible to use zero-copy techniques.
|
|
||||||
|
|
||||||
Usage
|
|
||||||
-----
|
|
||||||
|
|
||||||
Download `jsmn.h`, include it, done.
|
|
||||||
|
|
||||||
```
|
|
||||||
#include "jsmn.h"
|
|
||||||
|
|
||||||
...
|
|
||||||
jsmn_parser p;
|
|
||||||
jsmntok_t t[128]; /* We expect no more than 128 JSON tokens */
|
|
||||||
|
|
||||||
jsmn_init(&p);
|
|
||||||
r = jsmn_parse(&p, s, strlen(s), t, 128); // "s" is the char array holding the json content
|
|
||||||
```
|
|
||||||
|
|
||||||
Since jsmn is a single-header, header-only library, for more complex use cases
|
|
||||||
you might need to define additional macros. `#define JSMN_STATIC` hides all
|
|
||||||
jsmn API symbols by making them static. Also, if you want to include `jsmn.h`
|
|
||||||
from multiple C files, to avoid duplication of symbols you may define `JSMN_HEADER` macro.
|
|
||||||
|
|
||||||
```
|
|
||||||
/* In every .c file that uses jsmn include only declarations: */
|
|
||||||
#define JSMN_HEADER
|
|
||||||
#include "jsmn.h"
|
|
||||||
|
|
||||||
/* Additionally, create one jsmn.c file for jsmn implementation: */
|
|
||||||
#include "jsmn.h"
|
|
||||||
```
|
|
||||||
|
|
||||||
API
|
|
||||||
---
|
|
||||||
|
|
||||||
Token types are described by `jsmntype_t`:
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
JSMN_UNDEFINED = 0,
|
|
||||||
JSMN_OBJECT = 1 << 0,
|
|
||||||
JSMN_ARRAY = 1 << 1,
|
|
||||||
JSMN_STRING = 1 << 2,
|
|
||||||
JSMN_PRIMITIVE = 1 << 3
|
|
||||||
} jsmntype_t;
|
|
||||||
|
|
||||||
**Note:** Unlike JSON data types, primitive tokens are not divided into
|
|
||||||
numbers, booleans and null, because one can easily tell the type using the
|
|
||||||
first character:
|
|
||||||
|
|
||||||
* <code>'t', 'f'</code> - boolean
|
|
||||||
* <code>'n'</code> - null
|
|
||||||
* <code>'-', '0'..'9'</code> - number
|
|
||||||
|
|
||||||
Token is an object of `jsmntok_t` type:
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
jsmntype_t type; // Token type
|
|
||||||
int start; // Token start position
|
|
||||||
int end; // Token end position
|
|
||||||
int size; // Number of child (nested) tokens
|
|
||||||
} jsmntok_t;
|
|
||||||
|
|
||||||
**Note:** string tokens point to the first character after
|
|
||||||
the opening quote and the previous symbol before final quote. This was made
|
|
||||||
to simplify string extraction from JSON data.
|
|
||||||
|
|
||||||
All job is done by `jsmn_parser` object. You can initialize a new parser using:
|
|
||||||
|
|
||||||
jsmn_parser parser;
|
|
||||||
jsmntok_t tokens[10];
|
|
||||||
|
|
||||||
jsmn_init(&parser);
|
|
||||||
|
|
||||||
// js - pointer to JSON string
|
|
||||||
// tokens - an array of tokens available
|
|
||||||
// 10 - number of tokens available
|
|
||||||
jsmn_parse(&parser, js, strlen(js), tokens, 10);
|
|
||||||
|
|
||||||
This will create a parser, and then it tries to parse up to 10 JSON tokens from
|
|
||||||
the `js` string.
|
|
||||||
|
|
||||||
A non-negative return value of `jsmn_parse` is the number of tokens actually
|
|
||||||
used by the parser.
|
|
||||||
Passing NULL instead of the tokens array would not store parsing results, but
|
|
||||||
instead the function will return the number of tokens needed to parse the given
|
|
||||||
string. This can be useful if you don't know yet how many tokens to allocate.
|
|
||||||
|
|
||||||
If something goes wrong, you will get an error. Error will be one of these:
|
|
||||||
|
|
||||||
* `JSMN_ERROR_INVAL` - bad token, JSON string is corrupted
|
|
||||||
* `JSMN_ERROR_NOMEM` - not enough tokens, JSON string is too large
|
|
||||||
* `JSMN_ERROR_PART` - JSON string is too short, expecting more JSON data
|
|
||||||
|
|
||||||
If you get `JSMN_ERROR_NOMEM`, you can re-allocate more tokens and call
|
|
||||||
`jsmn_parse` once more. If you read json data from the stream, you can
|
|
||||||
periodically call `jsmn_parse` and check if return value is `JSMN_ERROR_PART`.
|
|
||||||
You will get this error until you reach the end of JSON data.
|
|
||||||
|
|
||||||
Other info
|
|
||||||
----------
|
|
||||||
|
|
||||||
This software is distributed under [MIT license](http://www.opensource.org/licenses/mit-license.php),
|
|
||||||
so feel free to integrate it in your commercial products.
|
|
||||||
|
|
||||||
[1]: http://www.json.org/
|
|
||||||
[2]: http://zserge.com/jsmn.html
|
|
||||||
134
dependencies/jsmn/example/jsondump.c
vendored
134
dependencies/jsmn/example/jsondump.c
vendored
@@ -1,134 +0,0 @@
|
|||||||
#include "../jsmn.h"
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
/* Function realloc_it() is a wrapper function for standard realloc()
|
|
||||||
* with one difference - it frees old memory pointer in case of realloc
|
|
||||||
* failure. Thus, DO NOT use old data pointer in anyway after call to
|
|
||||||
* realloc_it(). If your code has some kind of fallback algorithm if
|
|
||||||
* memory can't be re-allocated - use standard realloc() instead.
|
|
||||||
*/
|
|
||||||
static inline void *realloc_it(void *ptrmem, size_t size) {
|
|
||||||
void *p = realloc(ptrmem, size);
|
|
||||||
if (!p) {
|
|
||||||
free(ptrmem);
|
|
||||||
fprintf(stderr, "realloc(): errno=%d\n", errno);
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* An example of reading JSON from stdin and printing its content to stdout.
|
|
||||||
* The output looks like YAML, but I'm not sure if it's really compatible.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int dump(const char *js, jsmntok_t *t, size_t count, int indent) {
|
|
||||||
int i, j, k;
|
|
||||||
jsmntok_t *key;
|
|
||||||
if (count == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (t->type == JSMN_PRIMITIVE) {
|
|
||||||
printf("%.*s", t->end - t->start, js + t->start);
|
|
||||||
return 1;
|
|
||||||
} else if (t->type == JSMN_STRING) {
|
|
||||||
printf("'%.*s'", t->end - t->start, js + t->start);
|
|
||||||
return 1;
|
|
||||||
} else if (t->type == JSMN_OBJECT) {
|
|
||||||
printf("\n");
|
|
||||||
j = 0;
|
|
||||||
for (i = 0; i < t->size; i++) {
|
|
||||||
for (k = 0; k < indent; k++) {
|
|
||||||
printf(" ");
|
|
||||||
}
|
|
||||||
key = t + 1 + j;
|
|
||||||
j += dump(js, key, count - j, indent + 1);
|
|
||||||
if (key->size > 0) {
|
|
||||||
printf(": ");
|
|
||||||
j += dump(js, t + 1 + j, count - j, indent + 1);
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
return j + 1;
|
|
||||||
} else if (t->type == JSMN_ARRAY) {
|
|
||||||
j = 0;
|
|
||||||
printf("\n");
|
|
||||||
for (i = 0; i < t->size; i++) {
|
|
||||||
for (k = 0; k < indent - 1; k++) {
|
|
||||||
printf(" ");
|
|
||||||
}
|
|
||||||
printf(" - ");
|
|
||||||
j += dump(js, t + 1 + j, count - j, indent + 1);
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
return j + 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
int r;
|
|
||||||
int eof_expected = 0;
|
|
||||||
char *js = NULL;
|
|
||||||
size_t jslen = 0;
|
|
||||||
char buf[BUFSIZ];
|
|
||||||
|
|
||||||
jsmn_parser p;
|
|
||||||
jsmntok_t *tok;
|
|
||||||
size_t tokcount = 2;
|
|
||||||
|
|
||||||
/* Prepare parser */
|
|
||||||
jsmn_init(&p);
|
|
||||||
|
|
||||||
/* Allocate some tokens as a start */
|
|
||||||
tok = malloc(sizeof(*tok) * tokcount);
|
|
||||||
if (tok == NULL) {
|
|
||||||
fprintf(stderr, "malloc(): errno=%d\n", errno);
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
/* Read another chunk */
|
|
||||||
r = fread(buf, 1, sizeof(buf), stdin);
|
|
||||||
if (r < 0) {
|
|
||||||
fprintf(stderr, "fread(): %d, errno=%d\n", r, errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (r == 0) {
|
|
||||||
if (eof_expected != 0) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "fread(): unexpected EOF\n");
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
js = realloc_it(js, jslen + r + 1);
|
|
||||||
if (js == NULL) {
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
strncpy(js + jslen, buf, r);
|
|
||||||
jslen = jslen + r;
|
|
||||||
|
|
||||||
again:
|
|
||||||
r = jsmn_parse(&p, js, jslen, tok, tokcount);
|
|
||||||
if (r < 0) {
|
|
||||||
if (r == JSMN_ERROR_NOMEM) {
|
|
||||||
tokcount = tokcount * 2;
|
|
||||||
tok = realloc_it(tok, sizeof(*tok) * tokcount);
|
|
||||||
if (tok == NULL) {
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
goto again;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
dump(js, tok, p.toknext, 0);
|
|
||||||
eof_expected = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
77
dependencies/jsmn/example/simple.c
vendored
77
dependencies/jsmn/example/simple.c
vendored
@@ -1,77 +0,0 @@
|
|||||||
#include "../jsmn.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* A small example of jsmn parsing when JSON structure is known and number of
|
|
||||||
* tokens is predictable.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static const char *JSON_STRING =
|
|
||||||
"{\"user\": \"johndoe\", \"admin\": false, \"uid\": 1000,\n "
|
|
||||||
"\"groups\": [\"users\", \"wheel\", \"audio\", \"video\"]}";
|
|
||||||
|
|
||||||
static int jsoneq(const char *json, jsmntok_t *tok, const char *s) {
|
|
||||||
if (tok->type == JSMN_STRING && (int)strlen(s) == tok->end - tok->start &&
|
|
||||||
strncmp(json + tok->start, s, tok->end - tok->start) == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
int i;
|
|
||||||
int r;
|
|
||||||
jsmn_parser p;
|
|
||||||
jsmntok_t t[128]; /* We expect no more than 128 tokens */
|
|
||||||
|
|
||||||
jsmn_init(&p);
|
|
||||||
r = jsmn_parse(&p, JSON_STRING, strlen(JSON_STRING), t,
|
|
||||||
sizeof(t) / sizeof(t[0]));
|
|
||||||
if (r < 0) {
|
|
||||||
printf("Failed to parse JSON: %d\n", r);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Assume the top-level element is an object */
|
|
||||||
if (r < 1 || t[0].type != JSMN_OBJECT) {
|
|
||||||
printf("Object expected\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Loop over all keys of the root object */
|
|
||||||
for (i = 1; i < r; i++) {
|
|
||||||
if (jsoneq(JSON_STRING, &t[i], "user") == 0) {
|
|
||||||
/* We may use strndup() to fetch string value */
|
|
||||||
printf("- User: %.*s\n", t[i + 1].end - t[i + 1].start,
|
|
||||||
JSON_STRING + t[i + 1].start);
|
|
||||||
i++;
|
|
||||||
} else if (jsoneq(JSON_STRING, &t[i], "admin") == 0) {
|
|
||||||
/* We may additionally check if the value is either "true" or "false" */
|
|
||||||
printf("- Admin: %.*s\n", t[i + 1].end - t[i + 1].start,
|
|
||||||
JSON_STRING + t[i + 1].start);
|
|
||||||
i++;
|
|
||||||
} else if (jsoneq(JSON_STRING, &t[i], "uid") == 0) {
|
|
||||||
/* We may want to do strtol() here to get numeric value */
|
|
||||||
printf("- UID: %.*s\n", t[i + 1].end - t[i + 1].start,
|
|
||||||
JSON_STRING + t[i + 1].start);
|
|
||||||
i++;
|
|
||||||
} else if (jsoneq(JSON_STRING, &t[i], "groups") == 0) {
|
|
||||||
int j;
|
|
||||||
printf("- Groups:\n");
|
|
||||||
if (t[i + 1].type != JSMN_ARRAY) {
|
|
||||||
continue; /* We expect groups to be an array of strings */
|
|
||||||
}
|
|
||||||
for (j = 0; j < t[i + 1].size; j++) {
|
|
||||||
jsmntok_t *g = &t[i + j + 2];
|
|
||||||
printf(" * %.*s\n", g->end - g->start, JSON_STRING + g->start);
|
|
||||||
}
|
|
||||||
i += t[i + 1].size + 1;
|
|
||||||
} else {
|
|
||||||
printf("Unexpected key: %.*s\n", t[i].end - t[i].start,
|
|
||||||
JSON_STRING + t[i].start);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
471
dependencies/jsmn/jsmn.h
vendored
471
dependencies/jsmn/jsmn.h
vendored
@@ -1,471 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2010 Serge Zaitsev
|
|
||||||
*
|
|
||||||
* 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 JSMN_H
|
|
||||||
#define JSMN_H
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef JSMN_STATIC
|
|
||||||
#define JSMN_API static
|
|
||||||
#else
|
|
||||||
#define JSMN_API extern
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JSON type identifier. Basic types are:
|
|
||||||
* o Object
|
|
||||||
* o Array
|
|
||||||
* o String
|
|
||||||
* o Other primitive: number, boolean (true/false) or null
|
|
||||||
*/
|
|
||||||
typedef enum {
|
|
||||||
JSMN_UNDEFINED = 0,
|
|
||||||
JSMN_OBJECT = 1 << 0,
|
|
||||||
JSMN_ARRAY = 1 << 1,
|
|
||||||
JSMN_STRING = 1 << 2,
|
|
||||||
JSMN_PRIMITIVE = 1 << 3
|
|
||||||
} jsmntype_t;
|
|
||||||
|
|
||||||
enum jsmnerr {
|
|
||||||
/* Not enough tokens were provided */
|
|
||||||
JSMN_ERROR_NOMEM = -1,
|
|
||||||
/* Invalid character inside JSON string */
|
|
||||||
JSMN_ERROR_INVAL = -2,
|
|
||||||
/* The string is not a full JSON packet, more bytes expected */
|
|
||||||
JSMN_ERROR_PART = -3
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JSON token description.
|
|
||||||
* type type (object, array, string etc.)
|
|
||||||
* start start position in JSON data string
|
|
||||||
* end end position in JSON data string
|
|
||||||
*/
|
|
||||||
typedef struct jsmntok {
|
|
||||||
jsmntype_t type;
|
|
||||||
int start;
|
|
||||||
int end;
|
|
||||||
int size;
|
|
||||||
#ifdef JSMN_PARENT_LINKS
|
|
||||||
int parent;
|
|
||||||
#endif
|
|
||||||
} jsmntok_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JSON parser. Contains an array of token blocks available. Also stores
|
|
||||||
* the string being parsed now and current position in that string.
|
|
||||||
*/
|
|
||||||
typedef struct jsmn_parser {
|
|
||||||
unsigned int pos; /* offset in the JSON string */
|
|
||||||
unsigned int toknext; /* next token to allocate */
|
|
||||||
int toksuper; /* superior token node, e.g. parent object or array */
|
|
||||||
} jsmn_parser;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create JSON parser over an array of tokens
|
|
||||||
*/
|
|
||||||
JSMN_API void jsmn_init(jsmn_parser *parser);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run JSON parser. It parses a JSON data string into and array of tokens, each
|
|
||||||
* describing
|
|
||||||
* a single JSON object.
|
|
||||||
*/
|
|
||||||
JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
|
|
||||||
jsmntok_t *tokens, const unsigned int num_tokens);
|
|
||||||
|
|
||||||
#ifndef JSMN_HEADER
|
|
||||||
/**
|
|
||||||
* Allocates a fresh unused token from the token pool.
|
|
||||||
*/
|
|
||||||
static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens,
|
|
||||||
const size_t num_tokens) {
|
|
||||||
jsmntok_t *tok;
|
|
||||||
if (parser->toknext >= num_tokens) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
tok = &tokens[parser->toknext++];
|
|
||||||
tok->start = tok->end = -1;
|
|
||||||
tok->size = 0;
|
|
||||||
#ifdef JSMN_PARENT_LINKS
|
|
||||||
tok->parent = -1;
|
|
||||||
#endif
|
|
||||||
return tok;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fills token type and boundaries.
|
|
||||||
*/
|
|
||||||
static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type,
|
|
||||||
const int start, const int end) {
|
|
||||||
token->type = type;
|
|
||||||
token->start = start;
|
|
||||||
token->end = end;
|
|
||||||
token->size = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fills next available token with JSON primitive.
|
|
||||||
*/
|
|
||||||
static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
|
|
||||||
const size_t len, jsmntok_t *tokens,
|
|
||||||
const size_t num_tokens) {
|
|
||||||
jsmntok_t *token;
|
|
||||||
int start;
|
|
||||||
|
|
||||||
start = parser->pos;
|
|
||||||
|
|
||||||
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
|
|
||||||
switch (js[parser->pos]) {
|
|
||||||
#ifndef JSMN_STRICT
|
|
||||||
/* In strict mode primitive must be followed by "," or "}" or "]" */
|
|
||||||
case ':':
|
|
||||||
#endif
|
|
||||||
case '\t':
|
|
||||||
case '\r':
|
|
||||||
case '\n':
|
|
||||||
case ' ':
|
|
||||||
case ',':
|
|
||||||
case ']':
|
|
||||||
case '}':
|
|
||||||
goto found;
|
|
||||||
default:
|
|
||||||
/* to quiet a warning from gcc*/
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
|
|
||||||
parser->pos = start;
|
|
||||||
return JSMN_ERROR_INVAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#ifdef JSMN_STRICT
|
|
||||||
/* In strict mode primitive must be followed by a comma/object/array */
|
|
||||||
parser->pos = start;
|
|
||||||
return JSMN_ERROR_PART;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
found:
|
|
||||||
if (tokens == NULL) {
|
|
||||||
parser->pos--;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
token = jsmn_alloc_token(parser, tokens, num_tokens);
|
|
||||||
if (token == NULL) {
|
|
||||||
parser->pos = start;
|
|
||||||
return JSMN_ERROR_NOMEM;
|
|
||||||
}
|
|
||||||
jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
|
|
||||||
#ifdef JSMN_PARENT_LINKS
|
|
||||||
token->parent = parser->toksuper;
|
|
||||||
#endif
|
|
||||||
parser->pos--;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fills next token with JSON string.
|
|
||||||
*/
|
|
||||||
static int jsmn_parse_string(jsmn_parser *parser, const char *js,
|
|
||||||
const size_t len, jsmntok_t *tokens,
|
|
||||||
const size_t num_tokens) {
|
|
||||||
jsmntok_t *token;
|
|
||||||
|
|
||||||
int start = parser->pos;
|
|
||||||
|
|
||||||
/* Skip starting quote */
|
|
||||||
parser->pos++;
|
|
||||||
|
|
||||||
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
|
|
||||||
char c = js[parser->pos];
|
|
||||||
|
|
||||||
/* Quote: end of string */
|
|
||||||
if (c == '\"') {
|
|
||||||
if (tokens == NULL) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
token = jsmn_alloc_token(parser, tokens, num_tokens);
|
|
||||||
if (token == NULL) {
|
|
||||||
parser->pos = start;
|
|
||||||
return JSMN_ERROR_NOMEM;
|
|
||||||
}
|
|
||||||
jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos);
|
|
||||||
#ifdef JSMN_PARENT_LINKS
|
|
||||||
token->parent = parser->toksuper;
|
|
||||||
#endif
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Backslash: Quoted symbol expected */
|
|
||||||
if (c == '\\' && parser->pos + 1 < len) {
|
|
||||||
int i;
|
|
||||||
parser->pos++;
|
|
||||||
switch (js[parser->pos]) {
|
|
||||||
/* Allowed escaped symbols */
|
|
||||||
case '\"':
|
|
||||||
case '/':
|
|
||||||
case '\\':
|
|
||||||
case 'b':
|
|
||||||
case 'f':
|
|
||||||
case 'r':
|
|
||||||
case 'n':
|
|
||||||
case 't':
|
|
||||||
break;
|
|
||||||
/* Allows escaped symbol \uXXXX */
|
|
||||||
case 'u':
|
|
||||||
parser->pos++;
|
|
||||||
for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0';
|
|
||||||
i++) {
|
|
||||||
/* If it isn't a hex character we have an error */
|
|
||||||
if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
|
|
||||||
(js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
|
|
||||||
(js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
|
|
||||||
parser->pos = start;
|
|
||||||
return JSMN_ERROR_INVAL;
|
|
||||||
}
|
|
||||||
parser->pos++;
|
|
||||||
}
|
|
||||||
parser->pos--;
|
|
||||||
break;
|
|
||||||
/* Unexpected symbol */
|
|
||||||
default:
|
|
||||||
parser->pos = start;
|
|
||||||
return JSMN_ERROR_INVAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
parser->pos = start;
|
|
||||||
return JSMN_ERROR_PART;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse JSON string and fill tokens.
|
|
||||||
*/
|
|
||||||
JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
|
|
||||||
jsmntok_t *tokens, const unsigned int num_tokens) {
|
|
||||||
int r;
|
|
||||||
int i;
|
|
||||||
jsmntok_t *token;
|
|
||||||
int count = parser->toknext;
|
|
||||||
|
|
||||||
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
|
|
||||||
char c;
|
|
||||||
jsmntype_t type;
|
|
||||||
|
|
||||||
c = js[parser->pos];
|
|
||||||
switch (c) {
|
|
||||||
case '{':
|
|
||||||
case '[':
|
|
||||||
count++;
|
|
||||||
if (tokens == NULL) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
token = jsmn_alloc_token(parser, tokens, num_tokens);
|
|
||||||
if (token == NULL) {
|
|
||||||
return JSMN_ERROR_NOMEM;
|
|
||||||
}
|
|
||||||
if (parser->toksuper != -1) {
|
|
||||||
jsmntok_t *t = &tokens[parser->toksuper];
|
|
||||||
#ifdef JSMN_STRICT
|
|
||||||
/* In strict mode an object or array can't become a key */
|
|
||||||
if (t->type == JSMN_OBJECT) {
|
|
||||||
return JSMN_ERROR_INVAL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
t->size++;
|
|
||||||
#ifdef JSMN_PARENT_LINKS
|
|
||||||
token->parent = parser->toksuper;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
|
|
||||||
token->start = parser->pos;
|
|
||||||
parser->toksuper = parser->toknext - 1;
|
|
||||||
break;
|
|
||||||
case '}':
|
|
||||||
case ']':
|
|
||||||
if (tokens == NULL) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
|
|
||||||
#ifdef JSMN_PARENT_LINKS
|
|
||||||
if (parser->toknext < 1) {
|
|
||||||
return JSMN_ERROR_INVAL;
|
|
||||||
}
|
|
||||||
token = &tokens[parser->toknext - 1];
|
|
||||||
for (;;) {
|
|
||||||
if (token->start != -1 && token->end == -1) {
|
|
||||||
if (token->type != type) {
|
|
||||||
return JSMN_ERROR_INVAL;
|
|
||||||
}
|
|
||||||
token->end = parser->pos + 1;
|
|
||||||
parser->toksuper = token->parent;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (token->parent == -1) {
|
|
||||||
if (token->type != type || parser->toksuper == -1) {
|
|
||||||
return JSMN_ERROR_INVAL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
token = &tokens[token->parent];
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
for (i = parser->toknext - 1; i >= 0; i--) {
|
|
||||||
token = &tokens[i];
|
|
||||||
if (token->start != -1 && token->end == -1) {
|
|
||||||
if (token->type != type) {
|
|
||||||
return JSMN_ERROR_INVAL;
|
|
||||||
}
|
|
||||||
parser->toksuper = -1;
|
|
||||||
token->end = parser->pos + 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Error if unmatched closing bracket */
|
|
||||||
if (i == -1) {
|
|
||||||
return JSMN_ERROR_INVAL;
|
|
||||||
}
|
|
||||||
for (; i >= 0; i--) {
|
|
||||||
token = &tokens[i];
|
|
||||||
if (token->start != -1 && token->end == -1) {
|
|
||||||
parser->toksuper = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
case '\"':
|
|
||||||
r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
|
|
||||||
if (r < 0) {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
count++;
|
|
||||||
if (parser->toksuper != -1 && tokens != NULL) {
|
|
||||||
tokens[parser->toksuper].size++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case '\t':
|
|
||||||
case '\r':
|
|
||||||
case '\n':
|
|
||||||
case ' ':
|
|
||||||
break;
|
|
||||||
case ':':
|
|
||||||
parser->toksuper = parser->toknext - 1;
|
|
||||||
break;
|
|
||||||
case ',':
|
|
||||||
if (tokens != NULL && parser->toksuper != -1 &&
|
|
||||||
tokens[parser->toksuper].type != JSMN_ARRAY &&
|
|
||||||
tokens[parser->toksuper].type != JSMN_OBJECT) {
|
|
||||||
#ifdef JSMN_PARENT_LINKS
|
|
||||||
parser->toksuper = tokens[parser->toksuper].parent;
|
|
||||||
#else
|
|
||||||
for (i = parser->toknext - 1; i >= 0; i--) {
|
|
||||||
if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
|
|
||||||
if (tokens[i].start != -1 && tokens[i].end == -1) {
|
|
||||||
parser->toksuper = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
#ifdef JSMN_STRICT
|
|
||||||
/* In strict mode primitives are: numbers and booleans */
|
|
||||||
case '-':
|
|
||||||
case '0':
|
|
||||||
case '1':
|
|
||||||
case '2':
|
|
||||||
case '3':
|
|
||||||
case '4':
|
|
||||||
case '5':
|
|
||||||
case '6':
|
|
||||||
case '7':
|
|
||||||
case '8':
|
|
||||||
case '9':
|
|
||||||
case 't':
|
|
||||||
case 'f':
|
|
||||||
case 'n':
|
|
||||||
/* And they must not be keys of the object */
|
|
||||||
if (tokens != NULL && parser->toksuper != -1) {
|
|
||||||
const jsmntok_t *t = &tokens[parser->toksuper];
|
|
||||||
if (t->type == JSMN_OBJECT ||
|
|
||||||
(t->type == JSMN_STRING && t->size != 0)) {
|
|
||||||
return JSMN_ERROR_INVAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
/* In non-strict mode every unquoted value is a primitive */
|
|
||||||
default:
|
|
||||||
#endif
|
|
||||||
r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
|
|
||||||
if (r < 0) {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
count++;
|
|
||||||
if (parser->toksuper != -1 && tokens != NULL) {
|
|
||||||
tokens[parser->toksuper].size++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
#ifdef JSMN_STRICT
|
|
||||||
/* Unexpected char in strict mode */
|
|
||||||
default:
|
|
||||||
return JSMN_ERROR_INVAL;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tokens != NULL) {
|
|
||||||
for (i = parser->toknext - 1; i >= 0; i--) {
|
|
||||||
/* Unmatched opened object or array */
|
|
||||||
if (tokens[i].start != -1 && tokens[i].end == -1) {
|
|
||||||
return JSMN_ERROR_PART;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new parser based over a given buffer with an array of tokens
|
|
||||||
* available.
|
|
||||||
*/
|
|
||||||
JSMN_API void jsmn_init(jsmn_parser *parser) {
|
|
||||||
parser->pos = 0;
|
|
||||||
parser->toknext = 0;
|
|
||||||
parser->toksuper = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* JSMN_HEADER */
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* JSMN_H */
|
|
||||||
16
dependencies/jsmn/library.json
vendored
16
dependencies/jsmn/library.json
vendored
@@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "jsmn",
|
|
||||||
"keywords": "json",
|
|
||||||
"description": "Minimalistic JSON parser/tokenizer in C. It can be easily integrated into resource-limited or embedded projects",
|
|
||||||
"repository":
|
|
||||||
{
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/zserge/jsmn.git"
|
|
||||||
},
|
|
||||||
"frameworks": "*",
|
|
||||||
"platforms": "*",
|
|
||||||
"examples": [
|
|
||||||
"example/*.c"
|
|
||||||
],
|
|
||||||
"exclude": "test"
|
|
||||||
}
|
|
||||||
31
dependencies/jsmn/test/test.h
vendored
31
dependencies/jsmn/test/test.h
vendored
@@ -1,31 +0,0 @@
|
|||||||
#ifndef __TEST_H__
|
|
||||||
#define __TEST_H__
|
|
||||||
|
|
||||||
static int test_passed = 0;
|
|
||||||
static int test_failed = 0;
|
|
||||||
|
|
||||||
/* Terminate current test with error */
|
|
||||||
#define fail() return __LINE__
|
|
||||||
|
|
||||||
/* Successful end of the test case */
|
|
||||||
#define done() return 0
|
|
||||||
|
|
||||||
/* Check single condition */
|
|
||||||
#define check(cond) \
|
|
||||||
do { \
|
|
||||||
if (!(cond)) \
|
|
||||||
fail(); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
/* Test runner */
|
|
||||||
static void test(int (*func)(void), const char *name) {
|
|
||||||
int r = func();
|
|
||||||
if (r == 0) {
|
|
||||||
test_passed++;
|
|
||||||
} else {
|
|
||||||
test_failed++;
|
|
||||||
printf("FAILED: %s (at line %d)\n", name, r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* __TEST_H__ */
|
|
||||||
359
dependencies/jsmn/test/tests.c
vendored
359
dependencies/jsmn/test/tests.c
vendored
@@ -1,359 +0,0 @@
|
|||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "test.h"
|
|
||||||
#include "testutil.h"
|
|
||||||
|
|
||||||
int test_empty(void) {
|
|
||||||
check(parse("{}", 1, 1, JSMN_OBJECT, 0, 2, 0));
|
|
||||||
check(parse("[]", 1, 1, JSMN_ARRAY, 0, 2, 0));
|
|
||||||
check(parse("[{},{}]", 3, 3, JSMN_ARRAY, 0, 7, 2, JSMN_OBJECT, 1, 3, 0,
|
|
||||||
JSMN_OBJECT, 4, 6, 0));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_object(void) {
|
|
||||||
check(parse("{\"a\":0}", 3, 3, JSMN_OBJECT, 0, 7, 1, JSMN_STRING, "a", 1,
|
|
||||||
JSMN_PRIMITIVE, "0"));
|
|
||||||
check(parse("{\"a\":[]}", 3, 3, JSMN_OBJECT, 0, 8, 1, JSMN_STRING, "a", 1,
|
|
||||||
JSMN_ARRAY, 5, 7, 0));
|
|
||||||
check(parse("{\"a\":{},\"b\":{}}", 5, 5, JSMN_OBJECT, -1, -1, 2, JSMN_STRING,
|
|
||||||
"a", 1, JSMN_OBJECT, -1, -1, 0, JSMN_STRING, "b", 1, JSMN_OBJECT,
|
|
||||||
-1, -1, 0));
|
|
||||||
check(parse("{\n \"Day\": 26,\n \"Month\": 9,\n \"Year\": 12\n }", 7, 7,
|
|
||||||
JSMN_OBJECT, -1, -1, 3, JSMN_STRING, "Day", 1, JSMN_PRIMITIVE,
|
|
||||||
"26", JSMN_STRING, "Month", 1, JSMN_PRIMITIVE, "9", JSMN_STRING,
|
|
||||||
"Year", 1, JSMN_PRIMITIVE, "12"));
|
|
||||||
check(parse("{\"a\": 0, \"b\": \"c\"}", 5, 5, JSMN_OBJECT, -1, -1, 2,
|
|
||||||
JSMN_STRING, "a", 1, JSMN_PRIMITIVE, "0", JSMN_STRING, "b", 1,
|
|
||||||
JSMN_STRING, "c", 0));
|
|
||||||
|
|
||||||
#ifdef JSMN_STRICT
|
|
||||||
check(parse("{\"a\"\n0}", JSMN_ERROR_INVAL, 3));
|
|
||||||
check(parse("{\"a\", 0}", JSMN_ERROR_INVAL, 3));
|
|
||||||
check(parse("{\"a\": {2}}", JSMN_ERROR_INVAL, 3));
|
|
||||||
check(parse("{\"a\": {2: 3}}", JSMN_ERROR_INVAL, 3));
|
|
||||||
check(parse("{\"a\": {\"a\": 2 3}}", JSMN_ERROR_INVAL, 5));
|
|
||||||
/* FIXME */
|
|
||||||
/*check(parse("{\"a\"}", JSMN_ERROR_INVAL, 2));*/
|
|
||||||
/*check(parse("{\"a\": 1, \"b\"}", JSMN_ERROR_INVAL, 4));*/
|
|
||||||
/*check(parse("{\"a\",\"b\":1}", JSMN_ERROR_INVAL, 4));*/
|
|
||||||
/*check(parse("{\"a\":1,}", JSMN_ERROR_INVAL, 4));*/
|
|
||||||
/*check(parse("{\"a\":\"b\":\"c\"}", JSMN_ERROR_INVAL, 4));*/
|
|
||||||
/*check(parse("{,}", JSMN_ERROR_INVAL, 4));*/
|
|
||||||
#endif
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_array(void) {
|
|
||||||
/* FIXME */
|
|
||||||
/*check(parse("[10}", JSMN_ERROR_INVAL, 3));*/
|
|
||||||
/*check(parse("[1,,3]", JSMN_ERROR_INVAL, 3)*/
|
|
||||||
check(parse("[10]", 2, 2, JSMN_ARRAY, -1, -1, 1, JSMN_PRIMITIVE, "10"));
|
|
||||||
check(parse("{\"a\": 1]", JSMN_ERROR_INVAL, 3));
|
|
||||||
/* FIXME */
|
|
||||||
/*check(parse("[\"a\": 1]", JSMN_ERROR_INVAL, 3));*/
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_primitive(void) {
|
|
||||||
check(parse("{\"boolVar\" : true }", 3, 3, JSMN_OBJECT, -1, -1, 1,
|
|
||||||
JSMN_STRING, "boolVar", 1, JSMN_PRIMITIVE, "true"));
|
|
||||||
check(parse("{\"boolVar\" : false }", 3, 3, JSMN_OBJECT, -1, -1, 1,
|
|
||||||
JSMN_STRING, "boolVar", 1, JSMN_PRIMITIVE, "false"));
|
|
||||||
check(parse("{\"nullVar\" : null }", 3, 3, JSMN_OBJECT, -1, -1, 1,
|
|
||||||
JSMN_STRING, "nullVar", 1, JSMN_PRIMITIVE, "null"));
|
|
||||||
check(parse("{\"intVar\" : 12}", 3, 3, JSMN_OBJECT, -1, -1, 1, JSMN_STRING,
|
|
||||||
"intVar", 1, JSMN_PRIMITIVE, "12"));
|
|
||||||
check(parse("{\"floatVar\" : 12.345}", 3, 3, JSMN_OBJECT, -1, -1, 1,
|
|
||||||
JSMN_STRING, "floatVar", 1, JSMN_PRIMITIVE, "12.345"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_string(void) {
|
|
||||||
check(parse("{\"strVar\" : \"hello world\"}", 3, 3, JSMN_OBJECT, -1, -1, 1,
|
|
||||||
JSMN_STRING, "strVar", 1, JSMN_STRING, "hello world", 0));
|
|
||||||
check(parse("{\"strVar\" : \"escapes: \\/\\r\\n\\t\\b\\f\\\"\\\\\"}", 3, 3,
|
|
||||||
JSMN_OBJECT, -1, -1, 1, JSMN_STRING, "strVar", 1, JSMN_STRING,
|
|
||||||
"escapes: \\/\\r\\n\\t\\b\\f\\\"\\\\", 0));
|
|
||||||
check(parse("{\"strVar\": \"\"}", 3, 3, JSMN_OBJECT, -1, -1, 1, JSMN_STRING,
|
|
||||||
"strVar", 1, JSMN_STRING, "", 0));
|
|
||||||
check(parse("{\"a\":\"\\uAbcD\"}", 3, 3, JSMN_OBJECT, -1, -1, 1, JSMN_STRING,
|
|
||||||
"a", 1, JSMN_STRING, "\\uAbcD", 0));
|
|
||||||
check(parse("{\"a\":\"str\\u0000\"}", 3, 3, JSMN_OBJECT, -1, -1, 1,
|
|
||||||
JSMN_STRING, "a", 1, JSMN_STRING, "str\\u0000", 0));
|
|
||||||
check(parse("{\"a\":\"\\uFFFFstr\"}", 3, 3, JSMN_OBJECT, -1, -1, 1,
|
|
||||||
JSMN_STRING, "a", 1, JSMN_STRING, "\\uFFFFstr", 0));
|
|
||||||
check(parse("{\"a\":[\"\\u0280\"]}", 4, 4, JSMN_OBJECT, -1, -1, 1,
|
|
||||||
JSMN_STRING, "a", 1, JSMN_ARRAY, -1, -1, 1, JSMN_STRING,
|
|
||||||
"\\u0280", 0));
|
|
||||||
|
|
||||||
check(parse("{\"a\":\"str\\uFFGFstr\"}", JSMN_ERROR_INVAL, 3));
|
|
||||||
check(parse("{\"a\":\"str\\u@FfF\"}", JSMN_ERROR_INVAL, 3));
|
|
||||||
check(parse("{{\"a\":[\"\\u028\"]}", JSMN_ERROR_INVAL, 4));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_partial_string(void) {
|
|
||||||
int r;
|
|
||||||
unsigned long i;
|
|
||||||
jsmn_parser p;
|
|
||||||
jsmntok_t tok[5];
|
|
||||||
const char *js = "{\"x\": \"va\\\\ue\", \"y\": \"value y\"}";
|
|
||||||
|
|
||||||
jsmn_init(&p);
|
|
||||||
for (i = 1; i <= strlen(js); i++) {
|
|
||||||
r = jsmn_parse(&p, js, i, tok, sizeof(tok) / sizeof(tok[0]));
|
|
||||||
if (i == strlen(js)) {
|
|
||||||
check(r == 5);
|
|
||||||
check(tokeq(js, tok, 5, JSMN_OBJECT, -1, -1, 2, JSMN_STRING, "x", 1,
|
|
||||||
JSMN_STRING, "va\\\\ue", 0, JSMN_STRING, "y", 1, JSMN_STRING,
|
|
||||||
"value y", 0));
|
|
||||||
} else {
|
|
||||||
check(r == JSMN_ERROR_PART);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_partial_array(void) {
|
|
||||||
#ifdef JSMN_STRICT
|
|
||||||
int r;
|
|
||||||
unsigned long i;
|
|
||||||
jsmn_parser p;
|
|
||||||
jsmntok_t tok[10];
|
|
||||||
const char *js = "[ 1, true, [123, \"hello\"]]";
|
|
||||||
|
|
||||||
jsmn_init(&p);
|
|
||||||
for (i = 1; i <= strlen(js); i++) {
|
|
||||||
r = jsmn_parse(&p, js, i, tok, sizeof(tok) / sizeof(tok[0]));
|
|
||||||
if (i == strlen(js)) {
|
|
||||||
check(r == 6);
|
|
||||||
check(tokeq(js, tok, 6, JSMN_ARRAY, -1, -1, 3, JSMN_PRIMITIVE, "1",
|
|
||||||
JSMN_PRIMITIVE, "true", JSMN_ARRAY, -1, -1, 2, JSMN_PRIMITIVE,
|
|
||||||
"123", JSMN_STRING, "hello", 0));
|
|
||||||
} else {
|
|
||||||
check(r == JSMN_ERROR_PART);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_array_nomem(void) {
|
|
||||||
int i;
|
|
||||||
int r;
|
|
||||||
jsmn_parser p;
|
|
||||||
jsmntok_t toksmall[10], toklarge[10];
|
|
||||||
const char *js;
|
|
||||||
|
|
||||||
js = " [ 1, true, [123, \"hello\"]]";
|
|
||||||
|
|
||||||
for (i = 0; i < 6; i++) {
|
|
||||||
jsmn_init(&p);
|
|
||||||
memset(toksmall, 0, sizeof(toksmall));
|
|
||||||
memset(toklarge, 0, sizeof(toklarge));
|
|
||||||
r = jsmn_parse(&p, js, strlen(js), toksmall, i);
|
|
||||||
check(r == JSMN_ERROR_NOMEM);
|
|
||||||
|
|
||||||
memcpy(toklarge, toksmall, sizeof(toksmall));
|
|
||||||
|
|
||||||
r = jsmn_parse(&p, js, strlen(js), toklarge, 10);
|
|
||||||
check(r >= 0);
|
|
||||||
check(tokeq(js, toklarge, 4, JSMN_ARRAY, -1, -1, 3, JSMN_PRIMITIVE, "1",
|
|
||||||
JSMN_PRIMITIVE, "true", JSMN_ARRAY, -1, -1, 2, JSMN_PRIMITIVE,
|
|
||||||
"123", JSMN_STRING, "hello", 0));
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_unquoted_keys(void) {
|
|
||||||
#ifndef JSMN_STRICT
|
|
||||||
int r;
|
|
||||||
jsmn_parser p;
|
|
||||||
jsmntok_t tok[10];
|
|
||||||
const char *js;
|
|
||||||
|
|
||||||
jsmn_init(&p);
|
|
||||||
js = "key1: \"value\"\nkey2 : 123";
|
|
||||||
|
|
||||||
r = jsmn_parse(&p, js, strlen(js), tok, 10);
|
|
||||||
check(r >= 0);
|
|
||||||
check(tokeq(js, tok, 4, JSMN_PRIMITIVE, "key1", JSMN_STRING, "value", 0,
|
|
||||||
JSMN_PRIMITIVE, "key2", JSMN_PRIMITIVE, "123"));
|
|
||||||
#endif
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_issue_22(void) {
|
|
||||||
int r;
|
|
||||||
jsmn_parser p;
|
|
||||||
jsmntok_t tokens[128];
|
|
||||||
const char *js;
|
|
||||||
|
|
||||||
js =
|
|
||||||
"{ \"height\":10, \"layers\":[ { \"data\":[6,6], \"height\":10, "
|
|
||||||
"\"name\":\"Calque de Tile 1\", \"opacity\":1, \"type\":\"tilelayer\", "
|
|
||||||
"\"visible\":true, \"width\":10, \"x\":0, \"y\":0 }], "
|
|
||||||
"\"orientation\":\"orthogonal\", \"properties\": { }, \"tileheight\":32, "
|
|
||||||
"\"tilesets\":[ { \"firstgid\":1, \"image\":\"..\\/images\\/tiles.png\", "
|
|
||||||
"\"imageheight\":64, \"imagewidth\":160, \"margin\":0, "
|
|
||||||
"\"name\":\"Tiles\", "
|
|
||||||
"\"properties\":{}, \"spacing\":0, \"tileheight\":32, \"tilewidth\":32 "
|
|
||||||
"}], "
|
|
||||||
"\"tilewidth\":32, \"version\":1, \"width\":10 }";
|
|
||||||
jsmn_init(&p);
|
|
||||||
r = jsmn_parse(&p, js, strlen(js), tokens, 128);
|
|
||||||
check(r >= 0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_issue_27(void) {
|
|
||||||
const char *js =
|
|
||||||
"{ \"name\" : \"Jack\", \"age\" : 27 } { \"name\" : \"Anna\", ";
|
|
||||||
check(parse(js, JSMN_ERROR_PART, 8));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_input_length(void) {
|
|
||||||
const char *js;
|
|
||||||
int r;
|
|
||||||
jsmn_parser p;
|
|
||||||
jsmntok_t tokens[10];
|
|
||||||
|
|
||||||
js = "{\"a\": 0}garbage";
|
|
||||||
|
|
||||||
jsmn_init(&p);
|
|
||||||
r = jsmn_parse(&p, js, 8, tokens, 10);
|
|
||||||
check(r == 3);
|
|
||||||
check(tokeq(js, tokens, 3, JSMN_OBJECT, -1, -1, 1, JSMN_STRING, "a", 1,
|
|
||||||
JSMN_PRIMITIVE, "0"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_count(void) {
|
|
||||||
jsmn_parser p;
|
|
||||||
const char *js;
|
|
||||||
|
|
||||||
js = "{}";
|
|
||||||
jsmn_init(&p);
|
|
||||||
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 1);
|
|
||||||
|
|
||||||
js = "[]";
|
|
||||||
jsmn_init(&p);
|
|
||||||
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 1);
|
|
||||||
|
|
||||||
js = "[[]]";
|
|
||||||
jsmn_init(&p);
|
|
||||||
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 2);
|
|
||||||
|
|
||||||
js = "[[], []]";
|
|
||||||
jsmn_init(&p);
|
|
||||||
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 3);
|
|
||||||
|
|
||||||
js = "[[], []]";
|
|
||||||
jsmn_init(&p);
|
|
||||||
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 3);
|
|
||||||
|
|
||||||
js = "[[], [[]], [[], []]]";
|
|
||||||
jsmn_init(&p);
|
|
||||||
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 7);
|
|
||||||
|
|
||||||
js = "[\"a\", [[], []]]";
|
|
||||||
jsmn_init(&p);
|
|
||||||
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 5);
|
|
||||||
|
|
||||||
js = "[[], \"[], [[]]\", [[]]]";
|
|
||||||
jsmn_init(&p);
|
|
||||||
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 5);
|
|
||||||
|
|
||||||
js = "[1, 2, 3]";
|
|
||||||
jsmn_init(&p);
|
|
||||||
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 4);
|
|
||||||
|
|
||||||
js = "[1, 2, [3, \"a\"], null]";
|
|
||||||
jsmn_init(&p);
|
|
||||||
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 7);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_nonstrict(void) {
|
|
||||||
#ifndef JSMN_STRICT
|
|
||||||
const char *js;
|
|
||||||
js = "a: 0garbage";
|
|
||||||
check(parse(js, 2, 2, JSMN_PRIMITIVE, "a", JSMN_PRIMITIVE, "0garbage"));
|
|
||||||
|
|
||||||
js = "Day : 26\nMonth : Sep\n\nYear: 12";
|
|
||||||
check(parse(js, 6, 6, JSMN_PRIMITIVE, "Day", JSMN_PRIMITIVE, "26",
|
|
||||||
JSMN_PRIMITIVE, "Month", JSMN_PRIMITIVE, "Sep", JSMN_PRIMITIVE,
|
|
||||||
"Year", JSMN_PRIMITIVE, "12"));
|
|
||||||
|
|
||||||
/* nested {s don't cause a parse error. */
|
|
||||||
js = "\"key {1\": 1234";
|
|
||||||
check(parse(js, 2, 2, JSMN_STRING, "key {1", 1, JSMN_PRIMITIVE, "1234"));
|
|
||||||
|
|
||||||
#endif
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_unmatched_brackets(void) {
|
|
||||||
const char *js;
|
|
||||||
js = "\"key 1\": 1234}";
|
|
||||||
check(parse(js, JSMN_ERROR_INVAL, 2));
|
|
||||||
js = "{\"key 1\": 1234";
|
|
||||||
check(parse(js, JSMN_ERROR_PART, 3));
|
|
||||||
js = "{\"key 1\": 1234}}";
|
|
||||||
check(parse(js, JSMN_ERROR_INVAL, 3));
|
|
||||||
js = "\"key 1\"}: 1234";
|
|
||||||
check(parse(js, JSMN_ERROR_INVAL, 3));
|
|
||||||
js = "{\"key {1\": 1234}";
|
|
||||||
check(parse(js, 3, 3, JSMN_OBJECT, 0, 16, 1, JSMN_STRING, "key {1", 1,
|
|
||||||
JSMN_PRIMITIVE, "1234"));
|
|
||||||
js = "{\"key 1\":{\"key 2\": 1234}";
|
|
||||||
check(parse(js, JSMN_ERROR_PART, 5));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_object_key(void) {
|
|
||||||
const char *js;
|
|
||||||
|
|
||||||
js = "{\"key\": 1}";
|
|
||||||
check(parse(js, 3, 3, JSMN_OBJECT, 0, 10, 1, JSMN_STRING, "key", 1,
|
|
||||||
JSMN_PRIMITIVE, "1"));
|
|
||||||
#ifdef JSMN_STRICT
|
|
||||||
js = "{true: 1}";
|
|
||||||
check(parse(js, JSMN_ERROR_INVAL, 3));
|
|
||||||
js = "{1: 1}";
|
|
||||||
check(parse(js, JSMN_ERROR_INVAL, 3));
|
|
||||||
js = "{{\"key\": 1}: 2}";
|
|
||||||
check(parse(js, JSMN_ERROR_INVAL, 5));
|
|
||||||
js = "{[1,2]: 2}";
|
|
||||||
check(parse(js, JSMN_ERROR_INVAL, 5));
|
|
||||||
#endif
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
test(test_empty, "test for a empty JSON objects/arrays");
|
|
||||||
test(test_object, "test for a JSON objects");
|
|
||||||
test(test_array, "test for a JSON arrays");
|
|
||||||
test(test_primitive, "test primitive JSON data types");
|
|
||||||
test(test_string, "test string JSON data types");
|
|
||||||
|
|
||||||
test(test_partial_string, "test partial JSON string parsing");
|
|
||||||
test(test_partial_array, "test partial array reading");
|
|
||||||
test(test_array_nomem, "test array reading with a smaller number of tokens");
|
|
||||||
test(test_unquoted_keys, "test unquoted keys (like in JavaScript)");
|
|
||||||
test(test_input_length, "test strings that are not null-terminated");
|
|
||||||
test(test_issue_22, "test issue #22");
|
|
||||||
test(test_issue_27, "test issue #27");
|
|
||||||
test(test_count, "test tokens count estimation");
|
|
||||||
test(test_nonstrict, "test for non-strict mode");
|
|
||||||
test(test_unmatched_brackets, "test for unmatched brackets");
|
|
||||||
test(test_object_key, "test for key type");
|
|
||||||
printf("\nPASSED: %d\nFAILED: %d\n", test_passed, test_failed);
|
|
||||||
return (test_failed > 0);
|
|
||||||
}
|
|
||||||
96
dependencies/jsmn/test/testutil.h
vendored
96
dependencies/jsmn/test/testutil.h
vendored
@@ -1,96 +0,0 @@
|
|||||||
#ifndef __TEST_UTIL_H__
|
|
||||||
#define __TEST_UTIL_H__
|
|
||||||
|
|
||||||
#include "../jsmn.h"
|
|
||||||
|
|
||||||
static int vtokeq(const char *s, jsmntok_t *t, unsigned long numtok,
|
|
||||||
va_list ap) {
|
|
||||||
if (numtok > 0) {
|
|
||||||
unsigned long i;
|
|
||||||
int start, end, size;
|
|
||||||
jsmntype_t type;
|
|
||||||
char *value;
|
|
||||||
|
|
||||||
size = -1;
|
|
||||||
value = NULL;
|
|
||||||
for (i = 0; i < numtok; i++) {
|
|
||||||
type = va_arg(ap, jsmntype_t);
|
|
||||||
if (type == JSMN_STRING) {
|
|
||||||
value = va_arg(ap, char *);
|
|
||||||
size = va_arg(ap, int);
|
|
||||||
start = end = -1;
|
|
||||||
} else if (type == JSMN_PRIMITIVE) {
|
|
||||||
value = va_arg(ap, char *);
|
|
||||||
start = end = size = -1;
|
|
||||||
} else {
|
|
||||||
start = va_arg(ap, int);
|
|
||||||
end = va_arg(ap, int);
|
|
||||||
size = va_arg(ap, int);
|
|
||||||
value = NULL;
|
|
||||||
}
|
|
||||||
if (t[i].type != type) {
|
|
||||||
printf("token %lu type is %d, not %d\n", i, t[i].type, type);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (start != -1 && end != -1) {
|
|
||||||
if (t[i].start != start) {
|
|
||||||
printf("token %lu start is %d, not %d\n", i, t[i].start, start);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (t[i].end != end) {
|
|
||||||
printf("token %lu end is %d, not %d\n", i, t[i].end, end);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (size != -1 && t[i].size != size) {
|
|
||||||
printf("token %lu size is %d, not %d\n", i, t[i].size, size);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s != NULL && value != NULL) {
|
|
||||||
const char *p = s + t[i].start;
|
|
||||||
if (strlen(value) != (unsigned long)(t[i].end - t[i].start) ||
|
|
||||||
strncmp(p, value, t[i].end - t[i].start) != 0) {
|
|
||||||
printf("token %lu value is %.*s, not %s\n", i, t[i].end - t[i].start,
|
|
||||||
s + t[i].start, value);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int tokeq(const char *s, jsmntok_t *tokens, unsigned long numtok, ...) {
|
|
||||||
int ok;
|
|
||||||
va_list args;
|
|
||||||
va_start(args, numtok);
|
|
||||||
ok = vtokeq(s, tokens, numtok, args);
|
|
||||||
va_end(args);
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int parse(const char *s, int status, unsigned long numtok, ...) {
|
|
||||||
int r;
|
|
||||||
int ok = 1;
|
|
||||||
va_list args;
|
|
||||||
jsmn_parser p;
|
|
||||||
jsmntok_t *t = malloc(numtok * sizeof(jsmntok_t));
|
|
||||||
|
|
||||||
jsmn_init(&p);
|
|
||||||
r = jsmn_parse(&p, s, strlen(s), t, numtok);
|
|
||||||
if (r != status) {
|
|
||||||
printf("status is %d, not %d\n", r, status);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status >= 0) {
|
|
||||||
va_start(args, numtok);
|
|
||||||
ok = vtokeq(s, t, numtok, args);
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
free(t);
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* __TEST_UTIL_H__ */
|
|
||||||
1712
dependencies/nDPIsrvd.h
vendored
1712
dependencies/nDPIsrvd.h
vendored
File diff suppressed because it is too large
Load Diff
635
dependencies/nDPIsrvd.py
vendored
635
dependencies/nDPIsrvd.py
vendored
@@ -1,635 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import array
|
|
||||||
import json
|
|
||||||
import re
|
|
||||||
import os
|
|
||||||
import stat
|
|
||||||
import socket
|
|
||||||
import sys
|
|
||||||
|
|
||||||
try:
|
|
||||||
from colorama import Back, Fore, Style
|
|
||||||
USE_COLORAMA=True
|
|
||||||
except ImportError:
|
|
||||||
sys.stderr.write('Python module colorama not found, using fallback.\n')
|
|
||||||
USE_COLORAMA=False
|
|
||||||
|
|
||||||
DEFAULT_HOST = '127.0.0.1'
|
|
||||||
DEFAULT_PORT = 7000
|
|
||||||
DEFAULT_UNIX = '/tmp/ndpid-distributor.sock'
|
|
||||||
|
|
||||||
NETWORK_BUFFER_MIN_SIZE = 6 # NETWORK_BUFFER_LENGTH_DIGITS + 1
|
|
||||||
NETWORK_BUFFER_MAX_SIZE = 33792 # Please keep this value in sync with the one in config.h
|
|
||||||
nDPId_PACKETS_PLEN_MAX = 8192 # Please keep this value in sync with the one in config.h
|
|
||||||
|
|
||||||
PKT_TYPE_ETH_IP4 = 0x0800
|
|
||||||
PKT_TYPE_ETH_IP6 = 0x86DD
|
|
||||||
|
|
||||||
|
|
||||||
class TermColor:
|
|
||||||
HINT = '\033[33m'
|
|
||||||
WARNING = '\033[93m'
|
|
||||||
FAIL = '\033[91m'
|
|
||||||
BOLD = '\033[1m'
|
|
||||||
END = '\033[0m'
|
|
||||||
BLINK = '\x1b[5m'
|
|
||||||
|
|
||||||
if USE_COLORAMA is True:
|
|
||||||
COLOR_TUPLES = [ (Fore.BLUE, [Back.RED, Back.MAGENTA, Back.WHITE]),
|
|
||||||
(Fore.CYAN, [Back.MAGENTA, Back.RED, Back.WHITE]),
|
|
||||||
(Fore.GREEN, [Back.YELLOW, Back.RED, Back.MAGENTA, Back.WHITE]),
|
|
||||||
(Fore.MAGENTA, [Back.CYAN, Back.BLUE, Back.WHITE]),
|
|
||||||
(Fore.RED, [Back.GREEN, Back.BLUE, Back.WHITE]),
|
|
||||||
(Fore.WHITE, [Back.BLACK, Back.MAGENTA, Back.RED, Back.BLUE]),
|
|
||||||
(Fore.YELLOW, [Back.RED, Back.CYAN, Back.BLUE, Back.WHITE]),
|
|
||||||
(Fore.LIGHTBLUE_EX, [Back.LIGHTRED_EX, Back.RED]),
|
|
||||||
(Fore.LIGHTCYAN_EX, [Back.LIGHTMAGENTA_EX, Back.MAGENTA]),
|
|
||||||
(Fore.LIGHTGREEN_EX, [Back.LIGHTYELLOW_EX, Back.YELLOW]),
|
|
||||||
(Fore.LIGHTMAGENTA_EX, [Back.LIGHTCYAN_EX, Back.CYAN]),
|
|
||||||
(Fore.LIGHTRED_EX, [Back.LIGHTGREEN_EX, Back.GREEN]),
|
|
||||||
(Fore.LIGHTWHITE_EX, [Back.LIGHTBLACK_EX, Back.BLACK]),
|
|
||||||
(Fore.LIGHTYELLOW_EX, [Back.LIGHTRED_EX, Back.RED]) ]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def disableColor():
|
|
||||||
TermColor.HINT = ''
|
|
||||||
TermColor.WARNING = ''
|
|
||||||
TermColor.FAIL = ''
|
|
||||||
TermColor.BOLD = ''
|
|
||||||
TermColor.END = ''
|
|
||||||
TermColor.BLINK = ''
|
|
||||||
global USE_COLORAMA
|
|
||||||
USE_COLORAMA = False
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def disableBlink():
|
|
||||||
TermColor.BLINK = ''
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def calcColorHash(string):
|
|
||||||
h = 0
|
|
||||||
for char in string:
|
|
||||||
h += ord(char)
|
|
||||||
return h
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def getColorsByHash(string):
|
|
||||||
h = TermColor.calcColorHash(string)
|
|
||||||
tuple_index = h % len(TermColor.COLOR_TUPLES)
|
|
||||||
bg_tuple_index = h % len(TermColor.COLOR_TUPLES[tuple_index][1])
|
|
||||||
return (TermColor.COLOR_TUPLES[tuple_index][0],
|
|
||||||
TermColor.COLOR_TUPLES[tuple_index][1][bg_tuple_index])
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def setColorByString(string):
|
|
||||||
global USE_COLORAMA
|
|
||||||
if USE_COLORAMA is True:
|
|
||||||
fg_color, bg_color = TermColor.getColorsByHash(string)
|
|
||||||
return '{}{}{}{}{}'.format(Style.BRIGHT, fg_color, bg_color, string, Style.RESET_ALL)
|
|
||||||
else:
|
|
||||||
return '{}{}{}'.format(TermColor.BOLD, string, TermColor.END)
|
|
||||||
|
|
||||||
class ThreadData:
|
|
||||||
pass
|
|
||||||
|
|
||||||
class Instance:
|
|
||||||
|
|
||||||
def __init__(self, alias, source):
|
|
||||||
self.alias = str(alias)
|
|
||||||
self.source = str(source)
|
|
||||||
self.flows = dict()
|
|
||||||
self.thread_data = dict()
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return '<%s.%s object at %s with alias %s, source %s>' % (
|
|
||||||
self.__class__.__module__,
|
|
||||||
self.__class__.__name__,
|
|
||||||
hex(id(self)),
|
|
||||||
self.alias,
|
|
||||||
self.source
|
|
||||||
)
|
|
||||||
|
|
||||||
def getThreadData(self, thread_id):
|
|
||||||
if thread_id not in self.thread_data:
|
|
||||||
return None
|
|
||||||
return self.thread_data[thread_id]
|
|
||||||
|
|
||||||
def getThreadDataFromJSON(self, json_dict):
|
|
||||||
if 'thread_id' not in json_dict:
|
|
||||||
return None
|
|
||||||
return self.getThreadData(json_dict['thread_id'])
|
|
||||||
|
|
||||||
def getMostRecentFlowTime(self, thread_id):
|
|
||||||
return self.thread_data[thread_id].most_recent_flow_time
|
|
||||||
|
|
||||||
def setMostRecentFlowTime(self, thread_id, most_recent_flow_time):
|
|
||||||
if thread_id in self.thread_data:
|
|
||||||
return self.thread_data[thread_id]
|
|
||||||
|
|
||||||
self.thread_data[thread_id] = ThreadData()
|
|
||||||
self.thread_data[thread_id].most_recent_flow_time = most_recent_flow_time
|
|
||||||
return self.thread_data[thread_id]
|
|
||||||
|
|
||||||
def getMostRecentFlowTimeFromJSON(self, json_dict):
|
|
||||||
if 'thread_id' not in json_dict:
|
|
||||||
return 0
|
|
||||||
return self.getThreadData(json_dict['thread_id']).most_recent_flow_time
|
|
||||||
|
|
||||||
def setMostRecentFlowTimeFromJSON(self, json_dict):
|
|
||||||
if 'thread_id' not in json_dict:
|
|
||||||
return
|
|
||||||
thread_id = json_dict['thread_id']
|
|
||||||
if 'thread_ts_usec' in json_dict:
|
|
||||||
mrtf = self.getMostRecentFlowTime(thread_id) if thread_id in self.thread_data else 0
|
|
||||||
self.setMostRecentFlowTime(thread_id, max(json_dict['thread_ts_usec'], mrtf))
|
|
||||||
|
|
||||||
class Flow:
|
|
||||||
|
|
||||||
def __init__(self, flow_id, thread_id):
|
|
||||||
self.flow_id = flow_id
|
|
||||||
self.thread_id = thread_id
|
|
||||||
self.flow_last_seen = -1
|
|
||||||
self.flow_idle_time = -1
|
|
||||||
self.cleanup_reason = -1
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return '<%s.%s object at %s with flow id %d>' % (
|
|
||||||
self.__class__.__module__,
|
|
||||||
self.__class__.__name__,
|
|
||||||
hex(id(self)),
|
|
||||||
self.flow_id
|
|
||||||
)
|
|
||||||
|
|
||||||
class FlowManager:
|
|
||||||
CLEANUP_REASON_INVALID = 0
|
|
||||||
CLEANUP_REASON_DAEMON_INIT = 1 # can happen if kill -SIGKILL $(pidof nDPId) or restart after SIGSEGV
|
|
||||||
CLEANUP_REASON_DAEMON_SHUTDOWN = 2 # graceful shutdown e.g. kill -SIGTERM $(pidof nDPId)
|
|
||||||
CLEANUP_REASON_FLOW_END = 3
|
|
||||||
CLEANUP_REASON_FLOW_IDLE = 4
|
|
||||||
CLEANUP_REASON_FLOW_TIMEOUT = 5 # nDPId died a long time ago w/o restart?
|
|
||||||
CLEANUP_REASON_APP_SHUTDOWN = 6 # your python app called FlowManager.doShutdown()
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.instances = dict()
|
|
||||||
|
|
||||||
def getInstance(self, json_dict):
|
|
||||||
if 'alias' not in json_dict or \
|
|
||||||
'source' not in json_dict:
|
|
||||||
return None
|
|
||||||
|
|
||||||
alias = json_dict['alias']
|
|
||||||
source = json_dict['source']
|
|
||||||
|
|
||||||
if alias not in self.instances:
|
|
||||||
self.instances[alias] = dict()
|
|
||||||
if source not in self.instances[alias]:
|
|
||||||
self.instances[alias][source] = Instance(alias, source)
|
|
||||||
|
|
||||||
self.instances[alias][source].setMostRecentFlowTimeFromJSON(json_dict)
|
|
||||||
|
|
||||||
return self.instances[alias][source]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def getLastPacketTime(instance, flow_id, json_dict):
|
|
||||||
return max(int(json_dict['flow_src_last_pkt_time']), int(json_dict['flow_dst_last_pkt_time']), instance.flows[flow_id].flow_last_seen)
|
|
||||||
|
|
||||||
def getFlow(self, instance, json_dict):
|
|
||||||
if 'flow_id' not in json_dict:
|
|
||||||
return None
|
|
||||||
|
|
||||||
flow_id = int(json_dict['flow_id'])
|
|
||||||
|
|
||||||
if flow_id in instance.flows:
|
|
||||||
instance.flows[flow_id].flow_last_seen = FlowManager.getLastPacketTime(instance, flow_id, json_dict)
|
|
||||||
instance.flows[flow_id].flow_idle_time = int(json_dict['flow_idle_time'])
|
|
||||||
return instance.flows[flow_id]
|
|
||||||
|
|
||||||
thread_id = int(json_dict['thread_id'])
|
|
||||||
instance.flows[flow_id] = Flow(flow_id, thread_id)
|
|
||||||
instance.flows[flow_id].flow_last_seen = FlowManager.getLastPacketTime(instance, flow_id, json_dict)
|
|
||||||
instance.flows[flow_id].flow_idle_time = int(json_dict['flow_idle_time'])
|
|
||||||
instance.flows[flow_id].cleanup_reason = FlowManager.CLEANUP_REASON_INVALID
|
|
||||||
|
|
||||||
return instance.flows[flow_id]
|
|
||||||
|
|
||||||
def getFlowsToCleanup(self, instance, json_dict):
|
|
||||||
flows = dict()
|
|
||||||
|
|
||||||
if 'daemon_event_name' in json_dict:
|
|
||||||
if json_dict['daemon_event_name'].lower() == 'init' or \
|
|
||||||
json_dict['daemon_event_name'].lower() == 'shutdown':
|
|
||||||
# invalidate all existing flows with that alias/source/thread_id
|
|
||||||
for flow_id in instance.flows:
|
|
||||||
flow = instance.flows[flow_id]
|
|
||||||
if flow.thread_id != int(json_dict['thread_id']):
|
|
||||||
continue
|
|
||||||
if json_dict['daemon_event_name'].lower() == 'init':
|
|
||||||
flow.cleanup_reason = FlowManager.CLEANUP_REASON_DAEMON_INIT
|
|
||||||
else:
|
|
||||||
flow.cleanup_reason = FlowManager.CLEANUP_REASON_DAEMON_SHUTDOWN
|
|
||||||
flows[flow_id] = flow
|
|
||||||
for flow_id in flows:
|
|
||||||
del instance.flows[flow_id]
|
|
||||||
if len(instance.flows) == 0:
|
|
||||||
del self.instances[instance.alias][instance.source]
|
|
||||||
|
|
||||||
elif 'flow_event_name' in json_dict and \
|
|
||||||
(json_dict['flow_event_name'].lower() == 'end' or \
|
|
||||||
json_dict['flow_event_name'].lower() == 'idle' or \
|
|
||||||
json_dict['flow_event_name'].lower() == 'guessed' or \
|
|
||||||
json_dict['flow_event_name'].lower() == 'not-detected' or \
|
|
||||||
json_dict['flow_event_name'].lower() == 'detected'):
|
|
||||||
flow_id = json_dict['flow_id']
|
|
||||||
if json_dict['flow_event_name'].lower() == 'end':
|
|
||||||
instance.flows[flow_id].cleanup_reason = FlowManager.CLEANUP_REASON_FLOW_END
|
|
||||||
elif json_dict['flow_event_name'].lower() == 'idle':
|
|
||||||
instance.flows[flow_id].cleanup_reason = FlowManager.CLEANUP_REASON_FLOW_IDLE
|
|
||||||
# TODO: Flow Guessing/Detection can happen right before an idle event.
|
|
||||||
# We need to prevent that it results in a CLEANUP_REASON_FLOW_TIMEOUT.
|
|
||||||
# This may cause inconsistency and needs to be handled in another way.
|
|
||||||
if json_dict['flow_event_name'].lower() != 'guessed' and \
|
|
||||||
json_dict['flow_event_name'].lower() != 'not-detected' and \
|
|
||||||
json_dict['flow_event_name'].lower() != 'detected':
|
|
||||||
flows[flow_id] = instance.flows.pop(flow_id)
|
|
||||||
|
|
||||||
elif 'flow_last_seen' in json_dict:
|
|
||||||
if int(json_dict['flow_last_seen']) + int(json_dict['flow_idle_time']) < \
|
|
||||||
instance.getMostRecentFlowTimeFromJSON(json_dict):
|
|
||||||
flow_id = json_dict['flow_id']
|
|
||||||
instance.flows[flow_id].cleanup_reason = FlowManager.CLEANUP_REASON_FLOW_TIMEOUT
|
|
||||||
flows[flow_id] = instance.flows.pop(flow_id)
|
|
||||||
|
|
||||||
return flows
|
|
||||||
|
|
||||||
def doShutdown(self):
|
|
||||||
flows = dict()
|
|
||||||
|
|
||||||
for alias in self.instances:
|
|
||||||
for source in self.instances[alias]:
|
|
||||||
for flow_id in self.instances[alias][source].flows:
|
|
||||||
flow = self.instances[alias][source].flows[flow_id]
|
|
||||||
flow.cleanup_reason = FlowManager.CLEANUP_REASON_APP_SHUTDOWN
|
|
||||||
flows[flow_id] = flow
|
|
||||||
|
|
||||||
del self.instances
|
|
||||||
|
|
||||||
return flows
|
|
||||||
|
|
||||||
def verifyFlows(self):
|
|
||||||
invalid_flows = list()
|
|
||||||
|
|
||||||
for alias in self.instances:
|
|
||||||
for source in self.instances[alias]:
|
|
||||||
for flow_id in self.instances[alias][source].flows:
|
|
||||||
thread_id = self.instances[alias][source].flows[flow_id].thread_id
|
|
||||||
if self.instances[alias][source].flows[flow_id].flow_last_seen + \
|
|
||||||
self.instances[alias][source].flows[flow_id].flow_idle_time < \
|
|
||||||
self.instances[alias][source].getMostRecentFlowTime(thread_id):
|
|
||||||
invalid_flows += [flow_id]
|
|
||||||
|
|
||||||
return invalid_flows
|
|
||||||
|
|
||||||
class nDPIsrvdException(Exception):
|
|
||||||
UNSUPPORTED_ADDRESS_TYPE = 1
|
|
||||||
BUFFER_CAPACITY_REACHED = 2
|
|
||||||
SOCKET_CONNECTION_BROKEN = 3
|
|
||||||
INVALID_LINE_RECEIVED = 4
|
|
||||||
CALLBACK_RETURNED_FALSE = 5
|
|
||||||
SOCKET_TIMEOUT = 6
|
|
||||||
JSON_DECODE_ERROR = 7
|
|
||||||
|
|
||||||
def __init__(self, etype):
|
|
||||||
self.etype = etype
|
|
||||||
def __str__(self):
|
|
||||||
return 'nDPIsrvdException type {}'.format(self.etype)
|
|
||||||
|
|
||||||
class UnsupportedAddressType(nDPIsrvdException):
|
|
||||||
def __init__(self, addr):
|
|
||||||
super().__init__(nDPIsrvdException.UNSUPPORTED_ADDRESS_TYPE)
|
|
||||||
self.addr = addr
|
|
||||||
def __str__(self):
|
|
||||||
return '{}'.format(str(self.addr))
|
|
||||||
|
|
||||||
class BufferCapacityReached(nDPIsrvdException):
|
|
||||||
def __init__(self, current_length, max_length):
|
|
||||||
super().__init__(nDPIsrvdException.BUFFER_CAPACITY_REACHED)
|
|
||||||
self.current_length = current_length
|
|
||||||
self.max_length = max_length
|
|
||||||
def __str__(self):
|
|
||||||
return '{} of {} bytes'.format(self.current_length, self.max_length)
|
|
||||||
|
|
||||||
class SocketConnectionBroken(nDPIsrvdException):
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__(nDPIsrvdException.SOCKET_CONNECTION_BROKEN)
|
|
||||||
def __str__(self):
|
|
||||||
return 'Disconnected.'
|
|
||||||
|
|
||||||
class InvalidLineReceived(nDPIsrvdException):
|
|
||||||
def __init__(self, packet_buffer):
|
|
||||||
super().__init__(nDPIsrvdException.INVALID_LINE_RECEIVED)
|
|
||||||
self.packet_buffer = packet_buffer
|
|
||||||
def __str__(self):
|
|
||||||
return 'Received JSON line is invalid.'
|
|
||||||
|
|
||||||
class CallbackReturnedFalse(nDPIsrvdException):
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__(nDPIsrvdException.CALLBACK_RETURNED_FALSE)
|
|
||||||
def __str__(self):
|
|
||||||
return 'Callback returned False, abort.'
|
|
||||||
|
|
||||||
class SocketTimeout(nDPIsrvdException):
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__(nDPIsrvdException.SOCKET_TIMEOUT)
|
|
||||||
def __str__(self):
|
|
||||||
return 'Socket timeout.'
|
|
||||||
|
|
||||||
class JsonDecodeError(nDPIsrvdException):
|
|
||||||
def __init__(self, json_exception, failed_line):
|
|
||||||
super().__init__(nDPIsrvdException.JSON_DECODE_ERROR)
|
|
||||||
self.json_exception = json_exception
|
|
||||||
self.failed_line = failed_line
|
|
||||||
def __str__(self):
|
|
||||||
return '{}: {}'.format(self.json_exception, self.failed_line)
|
|
||||||
|
|
||||||
class JsonFilter():
|
|
||||||
def __init__(self, filter_string):
|
|
||||||
self.filter_string = filter_string
|
|
||||||
self.filter = compile(filter_string, '<string>', 'eval')
|
|
||||||
def evaluate(self, json_dict):
|
|
||||||
if type(json_dict) is not dict:
|
|
||||||
raise nDPIsrvdException('Could not evaluate JSON Filter: expected dictionary, got {}'.format(type(json_dict)))
|
|
||||||
return eval(self.filter, {'json_dict': json_dict})
|
|
||||||
|
|
||||||
class nDPIsrvdSocket:
|
|
||||||
def __init__(self):
|
|
||||||
self.sock_family = None
|
|
||||||
self.flow_mgr = FlowManager()
|
|
||||||
self.received_bytes = 0
|
|
||||||
self.json_filter = list()
|
|
||||||
|
|
||||||
def addFilter(self, filter_str):
|
|
||||||
self.json_filter.append(JsonFilter(filter_str))
|
|
||||||
|
|
||||||
def evalFilters(self, json_dict):
|
|
||||||
for jf in self.json_filter:
|
|
||||||
try:
|
|
||||||
json_filter_retval = jf.evaluate(json_dict)
|
|
||||||
except Exception as err:
|
|
||||||
print()
|
|
||||||
sys.stderr.write('Error while evaluating expression "{}"\n'.format(jf.filter_string))
|
|
||||||
raise err
|
|
||||||
|
|
||||||
if not isinstance(json_filter_retval, bool):
|
|
||||||
print()
|
|
||||||
sys.stderr.write('Error while evaluating expression "{}"\n'.format(jf.filter_string))
|
|
||||||
raise nDPIsrvdException('JSON Filter returned an invalid type: expected bool, got {}'.format(type(json_filter_retval)))
|
|
||||||
|
|
||||||
if json_filter_retval is False:
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def connect(self, addr):
|
|
||||||
if type(addr) is tuple:
|
|
||||||
self.sock_family = socket.AF_INET
|
|
||||||
elif type(addr) is str:
|
|
||||||
self.sock_family = socket.AF_UNIX
|
|
||||||
else:
|
|
||||||
raise UnsupportedAddressType(addr)
|
|
||||||
|
|
||||||
self.sock = socket.socket(self.sock_family, socket.SOCK_STREAM)
|
|
||||||
self.sock.connect(addr)
|
|
||||||
self.buffer = bytes()
|
|
||||||
self.msglen = 0
|
|
||||||
self.digitlen = 0
|
|
||||||
self.lines = []
|
|
||||||
self.failed_lines = []
|
|
||||||
self.filtered_lines = 0
|
|
||||||
|
|
||||||
def timeout(self, timeout):
|
|
||||||
self.sock.settimeout(timeout)
|
|
||||||
|
|
||||||
def receive(self):
|
|
||||||
if len(self.buffer) == NETWORK_BUFFER_MAX_SIZE:
|
|
||||||
raise BufferCapacityReached(len(self.buffer), NETWORK_BUFFER_MAX_SIZE)
|
|
||||||
|
|
||||||
connection_finished = False
|
|
||||||
try:
|
|
||||||
recvd = self.sock.recv(NETWORK_BUFFER_MAX_SIZE - len(self.buffer))
|
|
||||||
except ConnectionResetError:
|
|
||||||
connection_finished = True
|
|
||||||
recvd = bytes()
|
|
||||||
except TimeoutError:
|
|
||||||
raise SocketTimeout()
|
|
||||||
except socket.timeout:
|
|
||||||
raise SocketTimeout()
|
|
||||||
|
|
||||||
if len(recvd) == 0:
|
|
||||||
connection_finished = True
|
|
||||||
|
|
||||||
self.buffer += recvd
|
|
||||||
|
|
||||||
new_data_avail = False
|
|
||||||
while self.msglen + self.digitlen <= len(self.buffer):
|
|
||||||
|
|
||||||
if self.msglen == 0:
|
|
||||||
starts_with_digits = re.match(r'(^\d+){', self.buffer[:NETWORK_BUFFER_MIN_SIZE].decode(errors='strict'))
|
|
||||||
if starts_with_digits is None:
|
|
||||||
if len(self.buffer) < NETWORK_BUFFER_MIN_SIZE:
|
|
||||||
break
|
|
||||||
raise InvalidLineReceived(self.buffer)
|
|
||||||
self.msglen = int(starts_with_digits.group(1))
|
|
||||||
self.digitlen = len(starts_with_digits.group(1))
|
|
||||||
|
|
||||||
if len(self.buffer) >= self.msglen + self.digitlen:
|
|
||||||
recvd = self.buffer[self.digitlen:self.msglen + self.digitlen]
|
|
||||||
self.buffer = self.buffer[self.msglen + self.digitlen:]
|
|
||||||
self.lines += [(recvd,self.msglen,self.digitlen)]
|
|
||||||
new_data_avail = True
|
|
||||||
|
|
||||||
self.received_bytes += self.msglen + self.digitlen
|
|
||||||
self.msglen = 0
|
|
||||||
self.digitlen = 0
|
|
||||||
|
|
||||||
if connection_finished is True:
|
|
||||||
raise SocketConnectionBroken()
|
|
||||||
|
|
||||||
return new_data_avail
|
|
||||||
|
|
||||||
def parse(self, callback_json, callback_flow_cleanup, global_user_data):
|
|
||||||
retval = True
|
|
||||||
|
|
||||||
for received_line in self.lines:
|
|
||||||
try:
|
|
||||||
json_dict = json.loads(received_line[0].decode('ascii', errors='replace'), strict=True)
|
|
||||||
except json.decoder.JSONDecodeError as e:
|
|
||||||
json_dict = dict()
|
|
||||||
self.failed_lines += [received_line]
|
|
||||||
self.lines = self.lines[1:]
|
|
||||||
raise JsonDecodeError(e, received_line)
|
|
||||||
|
|
||||||
instance = self.flow_mgr.getInstance(json_dict)
|
|
||||||
if instance is None:
|
|
||||||
self.failed_lines += [received_line]
|
|
||||||
retval = False
|
|
||||||
continue
|
|
||||||
|
|
||||||
current_flow = self.flow_mgr.getFlow(instance, json_dict)
|
|
||||||
filter_eval = self.evalFilters(json_dict)
|
|
||||||
if filter_eval is True:
|
|
||||||
try:
|
|
||||||
if callback_json(json_dict, instance, current_flow, global_user_data) is not True:
|
|
||||||
self.failed_lines += [received_line]
|
|
||||||
retval = False
|
|
||||||
except Exception as e:
|
|
||||||
self.failed_lines += [received_line]
|
|
||||||
self.lines = self.lines[1:]
|
|
||||||
raise(e)
|
|
||||||
else:
|
|
||||||
self.filtered_lines += 1
|
|
||||||
|
|
||||||
for _, flow in self.flow_mgr.getFlowsToCleanup(instance, json_dict).items():
|
|
||||||
if callback_flow_cleanup is None:
|
|
||||||
pass
|
|
||||||
elif filter_eval is True and callback_flow_cleanup(instance, flow, global_user_data) is not True:
|
|
||||||
self.failed_lines += [received_line]
|
|
||||||
self.lines = self.lines[1:]
|
|
||||||
retval = False
|
|
||||||
|
|
||||||
self.lines = self.lines[1:]
|
|
||||||
|
|
||||||
return retval
|
|
||||||
|
|
||||||
def loop(self, callback_json, callback_flow_cleanup, global_user_data):
|
|
||||||
throw_ex = None
|
|
||||||
|
|
||||||
while True:
|
|
||||||
bytes_recv = 0
|
|
||||||
try:
|
|
||||||
bytes_recv = self.receive()
|
|
||||||
except Exception as err:
|
|
||||||
throw_ex = err
|
|
||||||
|
|
||||||
if self.parse(callback_json, callback_flow_cleanup, global_user_data) is False:
|
|
||||||
raise CallbackReturnedFalse()
|
|
||||||
|
|
||||||
if throw_ex is not None:
|
|
||||||
raise throw_ex
|
|
||||||
|
|
||||||
def shutdown(self):
|
|
||||||
return self.flow_mgr.doShutdown().items()
|
|
||||||
|
|
||||||
def verify(self):
|
|
||||||
if len(self.failed_lines) > 0:
|
|
||||||
raise nDPIsrvdException('Failed lines > 0: {}'.format(len(self.failed_lines)))
|
|
||||||
return self.flow_mgr.verifyFlows()
|
|
||||||
|
|
||||||
def defaultArgumentParser(desc='nDPIsrvd Python Interface', enable_json_filter=False,
|
|
||||||
help_formatter=argparse.ArgumentDefaultsHelpFormatter):
|
|
||||||
parser = argparse.ArgumentParser(description=desc, formatter_class=help_formatter)
|
|
||||||
parser.add_argument('--host', type=str, help='nDPIsrvd host IP')
|
|
||||||
parser.add_argument('--port', type=int, default=DEFAULT_PORT, help='nDPIsrvd TCP port')
|
|
||||||
parser.add_argument('--unix', type=str, help='nDPIsrvd unix socket path')
|
|
||||||
if enable_json_filter is True:
|
|
||||||
parser.add_argument('--filter', type=str, action='append',
|
|
||||||
help='Set a filter string which if evaluates to True will invoke the JSON callback.\n'
|
|
||||||
'Example: json_dict[\'flow_event_name\'] == \'detected\' will only process \'detected\' events.')
|
|
||||||
return parser
|
|
||||||
|
|
||||||
def toSeconds(usec):
|
|
||||||
return usec / (1000 * 1000)
|
|
||||||
|
|
||||||
def validateAddress(args):
|
|
||||||
tcp_addr_set = False
|
|
||||||
address = None
|
|
||||||
|
|
||||||
if args.host is None:
|
|
||||||
address_tcpip = (DEFAULT_HOST, args.port)
|
|
||||||
else:
|
|
||||||
address_tcpip = (args.host, args.port)
|
|
||||||
tcp_addr_set = True
|
|
||||||
|
|
||||||
if args.unix is None:
|
|
||||||
address_unix = DEFAULT_UNIX
|
|
||||||
else:
|
|
||||||
address_unix = args.unix
|
|
||||||
|
|
||||||
possible_sock_mode = 0
|
|
||||||
try:
|
|
||||||
possible_sock_mode = os.stat(address_unix).st_mode
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
if tcp_addr_set == False and stat.S_ISSOCK(possible_sock_mode):
|
|
||||||
address = address_unix
|
|
||||||
else:
|
|
||||||
address = address_tcpip
|
|
||||||
|
|
||||||
return address
|
|
||||||
|
|
||||||
def prepareJsonFilter(args, nsock):
|
|
||||||
# HowTo use JSON Filters:
|
|
||||||
# Add `--filter [FILTER_STRING]` to the Python scripts that support JSON filtering.
|
|
||||||
# Examples:
|
|
||||||
# ./examples/py-json-stdout/json-stdout.py --filter '"ndpi" in json_dict and "proto" in json_dict["ndpi"]'
|
|
||||||
# The command above will print only JSONs that have the subobjects json_dict["ndpi"] and json_dict["ndpi"]["proto"] available.
|
|
||||||
# ./examples/py-flow-info/flow-info.py --filter 'json_dict["source"] == "eth0"' --filter '"flow_event_name" in json_dict and json_dict["flow_event_name"] == "analyse"'
|
|
||||||
# Multiple JSON filter will be ANDed together.
|
|
||||||
# Note: You may *only* use the global "json_dict" in your expressions.
|
|
||||||
try:
|
|
||||||
json_filter = args.filter
|
|
||||||
if json_filter is not None:
|
|
||||||
for jf in json_filter:
|
|
||||||
nsock.addFilter(jf)
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
global schema
|
|
||||||
schema = {'packet_event_schema' : None, 'error_event_schema' : None, 'daemon_event_schema' : None, 'flow_event_schema' : None}
|
|
||||||
|
|
||||||
def initSchemaValidator(schema_dirs=[]):
|
|
||||||
if len(schema_dirs) == 0:
|
|
||||||
schema_dirs += [os.path.dirname(sys.argv[0]) + '/../../schema']
|
|
||||||
schema_dirs += [os.path.dirname(sys.argv[0]) + '/../share/nDPId']
|
|
||||||
schema_dirs += [sys.base_prefix + '/share/nDPId']
|
|
||||||
|
|
||||||
for key in schema:
|
|
||||||
for schema_dir in schema_dirs:
|
|
||||||
try:
|
|
||||||
with open(schema_dir + '/' + str(key) + '.json', 'r') as schema_file:
|
|
||||||
schema[key] = json.load(schema_file)
|
|
||||||
except FileNotFoundError:
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
|
|
||||||
def validateAgainstSchema(json_dict):
|
|
||||||
import jsonschema
|
|
||||||
|
|
||||||
if 'packet_event_id' in json_dict:
|
|
||||||
try:
|
|
||||||
jsonschema.Draft7Validator(schema=schema['packet_event_schema']).validate(instance=json_dict)
|
|
||||||
except AttributeError:
|
|
||||||
jsonschema.validate(instance=json_dict, schema=schema['packet_event_schema'])
|
|
||||||
return True
|
|
||||||
if 'error_event_id' in json_dict:
|
|
||||||
try:
|
|
||||||
jsonschema.Draft7Validator(schema=schema['error_event_schema']).validate(instance=json_dict)
|
|
||||||
except AttributeError:
|
|
||||||
jsonschema.validate(instance=json_dict, schema=schema['error_event_schema'])
|
|
||||||
return True
|
|
||||||
if 'daemon_event_id' in json_dict:
|
|
||||||
try:
|
|
||||||
jsonschema.Draft7Validator(schema=schema['daemon_event_schema']).validate(instance=json_dict)
|
|
||||||
except AttributeError:
|
|
||||||
jsonschema.validate(instance=json_dict, schema=schema['daemon_event_schema'])
|
|
||||||
return True
|
|
||||||
if 'flow_event_id' in json_dict:
|
|
||||||
try:
|
|
||||||
jsonschema.Draft7Validator(schema=schema['flow_event_schema']).validate(instance=json_dict)
|
|
||||||
except AttributeError:
|
|
||||||
jsonschema.validate(instance=json_dict, schema=schema['flow_event_schema'])
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
6
dependencies/update_jsmn.sh
vendored
6
dependencies/update_jsmn.sh
vendored
@@ -1,6 +0,0 @@
|
|||||||
#!/usr/bin/env sh
|
|
||||||
|
|
||||||
MYDIR="$(dirname ${0})"
|
|
||||||
cd "${MYDIR}/.."
|
|
||||||
|
|
||||||
git subtree pull --squash --prefix=dependencies/jsmn https://github.com/zserge/jsmn.git master
|
|
||||||
6
dependencies/update_uthash.sh
vendored
6
dependencies/update_uthash.sh
vendored
@@ -1,6 +0,0 @@
|
|||||||
#!/usr/bin/env sh
|
|
||||||
|
|
||||||
MYDIR="$(dirname ${0})"
|
|
||||||
cd "${MYDIR}/.."
|
|
||||||
|
|
||||||
git subtree pull --squash --prefix=dependencies/uthash https://github.com/troydhanson/uthash.git master
|
|
||||||
41
dependencies/uthash/.github/workflows/build.yml
vendored
41
dependencies/uthash/.github/workflows/build.yml
vendored
@@ -1,41 +0,0 @@
|
|||||||
name: build # This name shows up in badge.svg
|
|
||||||
|
|
||||||
on:
|
|
||||||
push: # any branch
|
|
||||||
pull_request:
|
|
||||||
branches: [ "master" ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-gcc:
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-latest, macos-latest]
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- run: make -C tests EXTRA_CFLAGS="-W -Wall -Wextra -Wswitch-default"
|
|
||||||
- run: make -C tests clean ; make -C tests pedantic
|
|
||||||
- run: make -C tests clean ; make -C tests pedantic EXTRA_CFLAGS=-DNO_DECLTYPE
|
|
||||||
- run: make -C tests clean ; make -C tests cplusplus
|
|
||||||
- run: make -C tests clean ; make -C tests cplusplus EXTRA_CFLAGS=-DNO_DECLTYPE
|
|
||||||
build-clang:
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-latest, macos-latest]
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
env:
|
|
||||||
CC: clang
|
|
||||||
CXX: clang++
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- run: make -C tests EXTRA_CFLAGS="-W -Wall -Wextra -Wswitch-default"
|
|
||||||
- run: make -C tests clean ; make -C tests pedantic
|
|
||||||
- run: make -C tests clean ; make -C tests pedantic EXTRA_CFLAGS=-DNO_DECLTYPE
|
|
||||||
- run: make -C tests clean ; make -C tests cplusplus
|
|
||||||
- run: make -C tests clean ; make -C tests cplusplus EXTRA_CFLAGS=-DNO_DECLTYPE
|
|
||||||
build-asciidoc:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- run: sudo apt-get update && sudo apt-get install asciidoc -y
|
|
||||||
- run: make -C doc
|
|
||||||
3
dependencies/uthash/.gitignore
vendored
3
dependencies/uthash/.gitignore
vendored
@@ -1,3 +0,0 @@
|
|||||||
*~
|
|
||||||
*.out
|
|
||||||
keystat.*
|
|
||||||
14
dependencies/uthash/.travis.yml
vendored
14
dependencies/uthash/.travis.yml
vendored
@@ -1,14 +0,0 @@
|
|||||||
language: cpp
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- os: linux
|
|
||||||
compiler: gcc
|
|
||||||
- os: linux
|
|
||||||
compiler: clang
|
|
||||||
- os: osx
|
|
||||||
script:
|
|
||||||
- make -C tests EXTRA_CFLAGS="-W -Wall -Wextra -Wswitch-default"
|
|
||||||
- make -C tests clean ; make -C tests pedantic
|
|
||||||
- make -C tests clean ; make -C tests pedantic EXTRA_CFLAGS=-DNO_DECLTYPE
|
|
||||||
- make -C tests clean ; make -C tests cplusplus
|
|
||||||
- make -C tests clean ; make -C tests cplusplus EXTRA_CFLAGS=-DNO_DECLTYPE
|
|
||||||
21
dependencies/uthash/LICENSE
vendored
21
dependencies/uthash/LICENSE
vendored
@@ -1,21 +0,0 @@
|
|||||||
Copyright (c) 2005-2022, Troy D. Hanson https://troydhanson.github.io/uthash/
|
|
||||||
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.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
9
dependencies/uthash/README.md
vendored
9
dependencies/uthash/README.md
vendored
@@ -1,9 +0,0 @@
|
|||||||
|
|
||||||
[](https://travis-ci.org/troydhanson/uthash)
|
|
||||||
[](https://github.com/troydhanson/uthash/actions/workflows/build.yml)
|
|
||||||
|
|
||||||
Documentation for uthash is available at:
|
|
||||||
|
|
||||||
https://troydhanson.github.io/uthash/
|
|
||||||
|
|
||||||
|
|
||||||
7
dependencies/uthash/doc/.gitignore
vendored
7
dependencies/uthash/doc/.gitignore
vendored
@@ -1,7 +0,0 @@
|
|||||||
ChangeLog.html
|
|
||||||
userguide.html
|
|
||||||
utarray.html
|
|
||||||
utlist.html
|
|
||||||
utringbuffer.html
|
|
||||||
utstack.html
|
|
||||||
utstring.html
|
|
||||||
282
dependencies/uthash/doc/ChangeLog.txt
vendored
282
dependencies/uthash/doc/ChangeLog.txt
vendored
@@ -1,282 +0,0 @@
|
|||||||
uthash ChangeLog
|
|
||||||
================
|
|
||||||
|
|
||||||
Click to return to the link:index.html[uthash home page].
|
|
||||||
|
|
||||||
NOTE: This ChangeLog may be incomplete and/or incorrect. See the git commit log.
|
|
||||||
|
|
||||||
Version 2.3.0 (2021-02-25)
|
|
||||||
--------------------------
|
|
||||||
* remove HASH_FCN; the HASH_FUNCTION and HASH_KEYCMP macros now behave similarly
|
|
||||||
* remove uthash_memcmp (deprecated in v2.1.0) in favor of HASH_KEYCMP
|
|
||||||
* silence -Wswitch-default warnings (thanks, Olaf Bergmann!)
|
|
||||||
|
|
||||||
Version 2.2.0 (2020-12-17)
|
|
||||||
--------------------------
|
|
||||||
* add HASH_NO_STDINT for platforms without C99 <stdint.h>
|
|
||||||
* silence many -Wcast-qual warnings (thanks, Olaf Bergmann!)
|
|
||||||
* skip hash computation when finding in an empty hash (thanks, Huansong Fu!)
|
|
||||||
* rename oom to utarray_oom, in utarray.h (thanks, Hong Xu!)
|
|
||||||
* rename oom to utstring_oom, in utstring.h (thanks, Hong Xu!)
|
|
||||||
* remove MurmurHash/HASH_MUR
|
|
||||||
|
|
||||||
Version 2.1.0 (2018-12-20)
|
|
||||||
--------------------------
|
|
||||||
* silence some Clang static analysis warnings
|
|
||||||
* add LL_INSERT_INORDER and LL_LOWER_BOUND etc (thanks, Jeffrey Lovitz and Mattias Eriksson!)
|
|
||||||
* add uthash_bzero for platforms without <string.h>
|
|
||||||
* fix a missing HASH_BLOOM_ADD in HASH_SELECT (thanks, Pawel Veselov!)
|
|
||||||
* permit malloc failure to be recoverable via HASH_NONFATAL_OOM (thanks, Pawel Veselov!)
|
|
||||||
* avoid repeated calls to uthash_strlen in HASH_FIND_STR
|
|
||||||
* rename uthash_memcmp to HASH_KEYCMP, leaving the old name for compatibility
|
|
||||||
* add utstack.h
|
|
||||||
* remove libut
|
|
||||||
|
|
||||||
Version 2.0.2 (2017-03-02)
|
|
||||||
--------------------------
|
|
||||||
* fix segfault in HASH_ADD_INORDER etc (thanks, Yana Kireyonok!)
|
|
||||||
* remove spurious cast to unsigned in utstring_len (thanks, Michal Sestrienka!)
|
|
||||||
* add uthash_memcmp and uthash_strlen for platforms without <stdlib.h> (thanks, Pawel Veselov!)
|
|
||||||
* fix a C++ incompatibility in utringbuffer
|
|
||||||
|
|
||||||
Version 2.0.1 (2016-07-05)
|
|
||||||
--------------------------
|
|
||||||
* in-order insertion macros HASH_ADD_INORDER etc (thanks, Thilo Schulz!)
|
|
||||||
* by-hashvalue insertion macros HASH_ADD_BYHASHVALUE etc
|
|
||||||
* during key comparison, check hashvalue before doing a full memcmp
|
|
||||||
* add utringbuffer.h
|
|
||||||
|
|
||||||
Version 1.9.9.1 (2014-11-18)
|
|
||||||
----------------------------
|
|
||||||
* inclusion of experimental libut bundle with utvector in opt/
|
|
||||||
* use shift in Bernstein hash instead of multiply (thanks, Jimmy Zhuo!)
|
|
||||||
* switch ssize_t types in utarray/utstring to size_t (thanks, Hong Xu!)
|
|
||||||
* fix utstring pointer math on >4GB strings (thanks, Thomas Bottesch!)
|
|
||||||
* change FNV hash to FNV-1a varation (thanks, dwest1975!)
|
|
||||||
* fix bug in HASH_REPLACE_STR (thanks, Ilya Kaliman!)
|
|
||||||
|
|
||||||
Version 1.9.9 (2014-02-25)
|
|
||||||
--------------------------
|
|
||||||
* made HASH_ADD_STR compatible with char* or char[] (thanks, Samuel Thibault!)
|
|
||||||
* fixed header inclusion of sys/types.h for ssize_t (thanks, Fernando Campos!)
|
|
||||||
* added LL_COUNT/DL_COUNT/CDL_COUNT (thansk, Paul Praet!)
|
|
||||||
* added LRU cache example in `tests/lru_cache` (thanks, Oliver Lorenz!)
|
|
||||||
* fix LL_DELETE2 for VS2008 (thanks, Greg Davydouski!)
|
|
||||||
* fix missing argument in `HASH_REPLACE_STR` (thanks, Alex!)
|
|
||||||
* bump version number in source files to match docs (thanks, John Crow!)
|
|
||||||
* add `HASH_OVERHEAD` macro to get overhead size for hash table
|
|
||||||
|
|
||||||
Version 1.9.8 (2013-03-10)
|
|
||||||
--------------------------
|
|
||||||
* `HASH_REPLACE` now in uthash (thanks, Nick Vatamaniuc!)
|
|
||||||
* fixed clang warnings (thanks wynnw!)
|
|
||||||
* fixed `utarray_insert` when inserting past array end (thanks Rob Willett!)
|
|
||||||
* you can now find http://troydhanson.github.io/uthash/[uthash on GitHub]
|
|
||||||
* there's a https://groups.google.com/d/forum/uthash[uthash Google Group]
|
|
||||||
* uthash has been downloaded 29,000+ times since 2006 on SourceForge
|
|
||||||
|
|
||||||
Version 1.9.7 (2012-10-09)
|
|
||||||
--------------------------
|
|
||||||
* utstring now supports substring search using `utstring_find` (thanks, Joe Wei!)
|
|
||||||
* utlist now supports element 'prepend' and 'replace' (thanks, Zoltán Lajos Kis!)
|
|
||||||
* utlist element prev/next fields can now have any names (thanks, Pawel S. Veselov!)
|
|
||||||
* uthash cast quiets a clang warning (thanks, Roman Divacky and Baptiste Daroussin!)
|
|
||||||
* uthash userguide example shows how to check key uniqueness (thanks, Richard Cook!)
|
|
||||||
* uthash HASH_MUR compiles under MSVC++ 10 in C mode (thanks, Arun Kirthi Cherian!)
|
|
||||||
* `utstring_printf` now supports format checking (thanks, Donald Carr!)
|
|
||||||
|
|
||||||
Version 1.9.6 (2012-04-28)
|
|
||||||
--------------------------
|
|
||||||
* add utarray_prev (thanks, Ben Hiett!)
|
|
||||||
* add parens/casts for greater compatibility (thanks, Atis, Debasis Ganguly, and Steve McClellan!)
|
|
||||||
* added ifndef to uthash_malloc and related hooks (thanks, Holger Machens!)
|
|
||||||
* edit examples so they do not leak memory (thanks, 任晶磊!)
|
|
||||||
|
|
||||||
Version 1.9.5 (2011-11-16)
|
|
||||||
--------------------------
|
|
||||||
* added `utarray_renew`
|
|
||||||
* fixed memory leak in `uthash_clear` when using Bloom filter (thanks, Jan Hättig!)
|
|
||||||
* utarray now copies the UT_icd on array creation rather than storing a pointer
|
|
||||||
* add parentheses to `HASH_ADD` to fix preprocessing of certain arguments (thanks, Aaron Rosen!)
|
|
||||||
* more parenthesizations for greater macro argument flexibility
|
|
||||||
|
|
||||||
Version 1.9.4 (2011-06-05)
|
|
||||||
--------------------------
|
|
||||||
* uthash now supports MurmurHash v3
|
|
||||||
* utlist now includes concatenation macros (`LL_CONCAT` and `DL_CONCAT`)
|
|
||||||
* utarray now supports binary search (`utarray_find`)
|
|
||||||
* utstring now supports a new-or-clear-existing macro (`utstring_renew`)
|
|
||||||
* documented technique for a multi-level hash table
|
|
||||||
* clarified scope requirements for `UT_icd` in the utarray documentation
|
|
||||||
* fixed termination when `utstring_clear` is followed by `utstring_body`
|
|
||||||
* fixed utarray_inserta macro when used with complex arguments
|
|
||||||
* on Visual Studio define missing type `uint8_t`
|
|
||||||
* Debian/Ubuntu include uthash in the package `uthash-dev`.
|
|
||||||
* uthash has been downloaded 16,211 times.
|
|
||||||
|
|
||||||
Thanks to Yu Feng, Richard Cook, Dino Ciuffetti, Chris Groer, and Arun Cherian
|
|
||||||
for feedback and fixes in this release!
|
|
||||||
|
|
||||||
Version 1.9.3 (2010-10-31)
|
|
||||||
--------------------------
|
|
||||||
* fix an `ifdef` for compatibility with Intel compiler (thanks, degski!)
|
|
||||||
* fix `HASH_ITER` macro to satisfy C++ casting rules (thanks, Erik Bai!)
|
|
||||||
|
|
||||||
Version 1.9.2 (2010-10-04)
|
|
||||||
--------------------------
|
|
||||||
* new `HASH_ITER` macro for more convenient deletion-safe iteration
|
|
||||||
* `hashscan` can now run on FreeBSD 8.1 and later (thanks, Markus Gebert!)
|
|
||||||
* More parens to evaluate complex macro arguments properly (thanks, ngg!)
|
|
||||||
* Add sz parameter to the `uthash_free` hook for platforms that do their own memory management. Hopefully this minor API change doesn't cause too much breakage for people. (thanks, Niall Douglas!)
|
|
||||||
* uthash has been downloaded 12,294 times
|
|
||||||
|
|
||||||
Version 1.9.1 (2010-05-15)
|
|
||||||
--------------------------
|
|
||||||
* Fix a redefinition warning when using `uthash.h` and `utstring.h` together
|
|
||||||
* Fix a bug in `utstring_init`
|
|
||||||
* Added `HASH_FIND_PTR` and `HASH_ADD_PTR` (thanks, Niall Douglas!)
|
|
||||||
|
|
||||||
Version 1.9 (2010-03-31)
|
|
||||||
--------------------------
|
|
||||||
* uthash now supports Visual Studio 2008 and 2010 in C or C++ code!
|
|
||||||
* new headers link:utarray.html[utarray.h] and link:utstring.html[utstring.h]
|
|
||||||
are now included. These implement dynamic arrays and strings using macros
|
|
||||||
* link:utlist.html[utlist.h] now has deletion-safe iterators and search macros
|
|
||||||
* the test suite now runs under Visual Studio (thanks again degski!)
|
|
||||||
* special thanks for suggesting utarray and utlist features to Charalampos P.!
|
|
||||||
* uthash has been downloaded 9,616 times
|
|
||||||
|
|
||||||
Version 1.8 (2009-09-08)
|
|
||||||
--------------------------
|
|
||||||
* Added the `hashscan` utility that can report on the size and quality of
|
|
||||||
hash tables in a running process (Linux-only)
|
|
||||||
* Added Bloom filter support. This has the potential to speed up certain
|
|
||||||
types of programs that look up non-existant keys in sufficient numbers.
|
|
||||||
* Restored the MurmurHash, which can once again be used, if an additional
|
|
||||||
symbol is defined. This is a "safety" by which the user declares they
|
|
||||||
understand that `-fno-strict-aliasing` flag must be used if they are
|
|
||||||
using MurmurHash under gcc with optimization.
|
|
||||||
* Unified the bucket/table malloc hooks; now there is only one malloc hook
|
|
||||||
* Re-organized the manual into a main section and advanced topics section
|
|
||||||
* Fixed a bug in `utlist.h` where sorting a singly-linked list threw a
|
|
||||||
compile-time error.
|
|
||||||
* Fixed a bug in `utlist.h` where a doubly-linked list that is sorted
|
|
||||||
did not maintain the special `head->prev` pointer back to the list tail.
|
|
||||||
|
|
||||||
Version 1.7 (2009-06-11)
|
|
||||||
--------------------------
|
|
||||||
* The MurmurHash has been removed, and Jenkin's hash is once again the default.
|
|
||||||
While MurmurHash performed well, it's unsafe with regard to the strict
|
|
||||||
aliasing rule. This results in incorrect code when compiled with optimization.
|
|
||||||
It's not possible to enable `-fno-strict-aliasing` from within a header file.
|
|
||||||
* The linked list macros in `utlist.h` now comply with the strict-aliasing
|
|
||||||
rule so they generate correct code under high optimization levels (O2 or O3).
|
|
||||||
The use of the `__typeof__` extension, which was originally a GNU extension,
|
|
||||||
may reduce portability to other compilers that do not support this extension.
|
|
||||||
This extension is used in the singly-linked list macros and the sort macros.
|
|
||||||
|
|
||||||
Version 1.6 (2009-05-08)
|
|
||||||
--------------------------
|
|
||||||
Special thanks to Alfred Heisner for contributing several enhancements:
|
|
||||||
|
|
||||||
* Support for two new hash functions:
|
|
||||||
- the Paul Hsieh hash function (`HASH_SFH`)
|
|
||||||
- Austin Appleby's MurmurHash function (`HASH_MUR`)
|
|
||||||
* Because of its excellent performance, MurmurHash is now the default hash function.
|
|
||||||
* `keystats` now has much better elapsed time accuracy under Cygwin and MinGW
|
|
||||||
* fixed casting in `HASH_FNV`, `HASH_SAX` and `HASH_OAT` for non-char keys
|
|
||||||
|
|
||||||
This release also includes:
|
|
||||||
|
|
||||||
* a new `HASH_CLEAR` operation clears a hash table in one step.
|
|
||||||
* a new `HASH_SELECT` operation inserts those elements from one hash that
|
|
||||||
satisfy a given condition into another hash. The selected items have
|
|
||||||
dual presence in both hash tables. For example a game could select the
|
|
||||||
visible polygons from a hash of all polygons.
|
|
||||||
* fixed a compile-time error which occurred if the final argument to
|
|
||||||
`HASH_ADD_KEYPTR` was a pointer to an array member like `&a[i]`
|
|
||||||
* added another test script `tests/all_funcs` which executes the test suite
|
|
||||||
using every supported hash function
|
|
||||||
|
|
||||||
And lastly,
|
|
||||||
|
|
||||||
* a new, separate header called link:utlist.html[utlist.h] is included which
|
|
||||||
provides 'linked list macros' for C structures, similar in style to the
|
|
||||||
uthash macros
|
|
||||||
|
|
||||||
Version 1.5 (2009-02-19)
|
|
||||||
--------------------------
|
|
||||||
* now thread-safe for concurrent readers
|
|
||||||
* use scratch variables on stack rather than in table (thanks, Petter Arvidsson!).
|
|
||||||
This change made HASH_FIND about 13% faster and enabled reader concurrency.
|
|
||||||
* made link:license.html[BSD license] terms even more permissive
|
|
||||||
* added link:userguide.pdf[PDF version] of User Guide
|
|
||||||
* added http://troydhanson.wordpress.com/feed/[update news] image:rss.png[(RSS)]
|
|
||||||
|
|
||||||
Version 1.4 (2008-09-23)
|
|
||||||
--------------------------
|
|
||||||
* Add `HASH_COUNT` for counting items in the hash
|
|
||||||
* Compatibility with C\+\+. Satisfy additional casting requirements.
|
|
||||||
Also in the `tests/` directory, running `make cplusplus` now compiles
|
|
||||||
all the test programs with the C++ compiler.
|
|
||||||
* Eliminate `elmt` pointer from the UT_hash_handle. Calculate elmt
|
|
||||||
from hash handle address by subtracting `hho` (hash handle offset).
|
|
||||||
* Contributed by L.S.Chin:
|
|
||||||
Cast `void*` to char* before pointer arithmetic to suppress compiler
|
|
||||||
warnings. We assume compilers abide to C standards which impose
|
|
||||||
requirement that `sizeof(void*) == sizeof(char*)`.
|
|
||||||
* Return meaningful exit status from do_tests per Tiago Cunha,
|
|
||||||
so that package manager-based install can verify tests are successful
|
|
||||||
|
|
||||||
|
|
||||||
Version 1.3 (2008-07-27)
|
|
||||||
------------------------
|
|
||||||
* use integer-only math-- no floating point! Support FPU-less CPU's.
|
|
||||||
* eliminate `hash_q` metric, which measured the fraction of items with
|
|
||||||
non-ideal chain positions. We only need to know if this fraction
|
|
||||||
is below 0.5. This is now determined using fast bitwise tests.
|
|
||||||
* when an item is added to the hash, calculate the key's hash value
|
|
||||||
upfront and store it, instead of recomputing it as needed. This hashv
|
|
||||||
is stored in the hash handle. Potentially major speed benefit for
|
|
||||||
bucket expansion algorithm. Deleting is marginally improved too.
|
|
||||||
* fixed a minor bug in the calculation of the max ideal chain length;
|
|
||||||
line 446 in v1.2 erroneously calculated a/b*2 instead of a/(b*2).
|
|
||||||
The effect of this bug was that bucket expansion could occur more
|
|
||||||
readily because the per-bucket 'max chain length multiplier factor'
|
|
||||||
(which delays bucket expansion when certain buckets are overused)
|
|
||||||
was set to a lower, expansion-favoring value than intended.
|
|
||||||
* improved source commenting and improved variable names in structures
|
|
||||||
* remove `HASH_JSW`. Lengthy random number array made code less readable
|
|
||||||
* add `HASH_SRT(hh,hash,cmp)` as a generalized `HASH_SORT(hash,cmp)`.
|
|
||||||
It was an omission in uthash 1.2 that there was no sort macro for
|
|
||||||
hash handles with names other than hh.
|
|
||||||
* Corrected `HASH_FSCK` so it works with any name for the hash handle.
|
|
||||||
* behave properly in pathological `HASH_DEL(a,a)` case where the same
|
|
||||||
variable references the head and the deletee (advancing the head
|
|
||||||
then loses the correct reference to the deletee); fix by using
|
|
||||||
scratch area in the hash table to store deletee hash handle.
|
|
||||||
* made tests runnable on MinGW
|
|
||||||
* 3000+ downloads since uthash-1.0
|
|
||||||
|
|
||||||
|
|
||||||
Version 1.2 (2006-11-22)
|
|
||||||
------------------------
|
|
||||||
* new `HASH_SORT` macro
|
|
||||||
* Cygwin support
|
|
||||||
* User Guide now features a clickable Table of Contents.
|
|
||||||
(The technique for generating the TOC on the browser was contributed
|
|
||||||
back to the AsciiDoc project and incorporated into AsciiDoc v8.1.0).
|
|
||||||
|
|
||||||
|
|
||||||
Version 1.1 (2006-06-28)
|
|
||||||
------------------------
|
|
||||||
* uthash-1.1 released
|
|
||||||
* supports several built-in user-selectable hash functions
|
|
||||||
* new keystats utility quantifies performance of hash functions
|
|
||||||
|
|
||||||
|
|
||||||
Version 1.0 (2006-06-02)
|
|
||||||
------------------------
|
|
||||||
* Initial release
|
|
||||||
|
|
||||||
// vim: set syntax=asciidoc:
|
|
||||||
18
dependencies/uthash/doc/Makefile
vendored
18
dependencies/uthash/doc/Makefile
vendored
@@ -1,18 +0,0 @@
|
|||||||
HTML=$(patsubst %.txt,%.html,$(wildcard *.txt))
|
|
||||||
|
|
||||||
all: $(HTML)
|
|
||||||
|
|
||||||
# when each target of a multi-target rule has its own prereq
|
|
||||||
# we use a static pattern rule.
|
|
||||||
$(HTML): %.html: %.txt
|
|
||||||
asciidoc -a toc2 $<
|
|
||||||
|
|
||||||
TMP=/tmp/uthash-gh-pages
|
|
||||||
stage:
|
|
||||||
mkdir -p ${TMP}
|
|
||||||
rm -if ${TMP}/*
|
|
||||||
cp *.html *.css *.png ${TMP}
|
|
||||||
|
|
||||||
.PHONY: clean
|
|
||||||
clean:
|
|
||||||
$(RM) $(HTML)
|
|
||||||
BIN
dependencies/uthash/doc/banner.png
vendored
BIN
dependencies/uthash/doc/banner.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB |
451
dependencies/uthash/doc/banner.svg
vendored
451
dependencies/uthash/doc/banner.svg
vendored
@@ -1,451 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://web.resource.org/cc/"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="728px"
|
|
||||||
height="90px"
|
|
||||||
id="svg1307"
|
|
||||||
sodipodi:version="0.32"
|
|
||||||
inkscape:version="0.45.1"
|
|
||||||
sodipodi:docbase="/Users/thanson/code/uthash/trunk/doc/html/img"
|
|
||||||
sodipodi:docname="banner.svg"
|
|
||||||
inkscape:export-filename="/home/thanson/code/uthash/doc/html/img/banner.png"
|
|
||||||
inkscape:export-xdpi="90"
|
|
||||||
inkscape:export-ydpi="90"
|
|
||||||
inkscape:output_extension="org.inkscape.output.svg.inkscape">
|
|
||||||
<defs
|
|
||||||
id="defs1309">
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient12743">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#99e1fa;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop12745" />
|
|
||||||
<stop
|
|
||||||
id="stop12753"
|
|
||||||
offset="0"
|
|
||||||
style="stop-color:#99e1fa;stop-opacity:0.49803922;" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#99e1fa;stop-opacity:0;"
|
|
||||||
offset="1"
|
|
||||||
id="stop12747" />
|
|
||||||
</linearGradient>
|
|
||||||
<marker
|
|
||||||
inkscape:stockid="Arrow1Mend"
|
|
||||||
orient="auto"
|
|
||||||
refY="0.0"
|
|
||||||
refX="0.0"
|
|
||||||
id="Arrow1Mend"
|
|
||||||
style="overflow:visible;">
|
|
||||||
<path
|
|
||||||
id="path7755"
|
|
||||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
|
||||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
|
|
||||||
transform="scale(0.4) rotate(180)" />
|
|
||||||
</marker>
|
|
||||||
<marker
|
|
||||||
inkscape:stockid="Arrow1Sstart"
|
|
||||||
orient="auto"
|
|
||||||
refY="0.0"
|
|
||||||
refX="0.0"
|
|
||||||
id="Arrow1Sstart"
|
|
||||||
style="overflow:visible">
|
|
||||||
<path
|
|
||||||
id="path7752"
|
|
||||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
|
||||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
|
|
||||||
transform="scale(0.2)" />
|
|
||||||
</marker>
|
|
||||||
<marker
|
|
||||||
inkscape:stockid="Arrow1Send"
|
|
||||||
orient="auto"
|
|
||||||
refY="0.0"
|
|
||||||
refX="0.0"
|
|
||||||
id="Arrow1Send"
|
|
||||||
style="overflow:visible;">
|
|
||||||
<path
|
|
||||||
id="path7749"
|
|
||||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
|
||||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
|
|
||||||
transform="scale(0.2) rotate(180)" />
|
|
||||||
</marker>
|
|
||||||
<marker
|
|
||||||
inkscape:stockid="StopM"
|
|
||||||
orient="auto"
|
|
||||||
refY="0.0"
|
|
||||||
refX="0.0"
|
|
||||||
id="StopM"
|
|
||||||
style="overflow:visible">
|
|
||||||
<path
|
|
||||||
id="path7651"
|
|
||||||
d="M 0.0,5.65 L 0.0,-5.65"
|
|
||||||
style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt"
|
|
||||||
transform="scale(0.4)" />
|
|
||||||
</marker>
|
|
||||||
<marker
|
|
||||||
inkscape:stockid="Arrow2Mend"
|
|
||||||
orient="auto"
|
|
||||||
refY="0.0"
|
|
||||||
refX="0.0"
|
|
||||||
id="Arrow2Mend"
|
|
||||||
style="overflow:visible;">
|
|
||||||
<path
|
|
||||||
id="path7737"
|
|
||||||
style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
|
|
||||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
|
||||||
transform="scale(0.6) rotate(180) translate(-5,0)" />
|
|
||||||
</marker>
|
|
||||||
<marker
|
|
||||||
inkscape:stockid="TriangleInM"
|
|
||||||
orient="auto"
|
|
||||||
refY="0.0"
|
|
||||||
refX="0.0"
|
|
||||||
id="TriangleInM"
|
|
||||||
style="overflow:visible">
|
|
||||||
<path
|
|
||||||
id="path7669"
|
|
||||||
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
|
|
||||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
|
|
||||||
transform="scale(-0.4)" />
|
|
||||||
</marker>
|
|
||||||
<marker
|
|
||||||
inkscape:stockid="StopL"
|
|
||||||
orient="auto"
|
|
||||||
refY="0.0"
|
|
||||||
refX="0.0"
|
|
||||||
id="StopL"
|
|
||||||
style="overflow:visible">
|
|
||||||
<path
|
|
||||||
id="path7654"
|
|
||||||
d="M 0.0,5.65 L 0.0,-5.65"
|
|
||||||
style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt"
|
|
||||||
transform="scale(0.8)" />
|
|
||||||
</marker>
|
|
||||||
<marker
|
|
||||||
inkscape:stockid="TriangleOutM"
|
|
||||||
orient="auto"
|
|
||||||
refY="0.0"
|
|
||||||
refX="0.0"
|
|
||||||
id="TriangleOutM"
|
|
||||||
style="overflow:visible">
|
|
||||||
<path
|
|
||||||
id="path7660"
|
|
||||||
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
|
|
||||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
|
|
||||||
transform="scale(0.4)" />
|
|
||||||
</marker>
|
|
||||||
<marker
|
|
||||||
inkscape:stockid="DiamondS"
|
|
||||||
orient="auto"
|
|
||||||
refY="0.0"
|
|
||||||
refX="0.0"
|
|
||||||
id="DiamondS"
|
|
||||||
style="overflow:visible">
|
|
||||||
<path
|
|
||||||
id="path7675"
|
|
||||||
d="M -2.1579186e-005,-7.0710768 L -7.0710894,-8.9383918e-006 L -2.1579186e-005,7.0710589 L 7.0710462,-8.9383918e-006 L -2.1579186e-005,-7.0710768 z "
|
|
||||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
|
|
||||||
transform="scale(0.2)" />
|
|
||||||
</marker>
|
|
||||||
<marker
|
|
||||||
inkscape:stockid="Tail"
|
|
||||||
orient="auto"
|
|
||||||
refY="0.0"
|
|
||||||
refX="0.0"
|
|
||||||
id="Tail"
|
|
||||||
style="overflow:visible">
|
|
||||||
<g
|
|
||||||
id="g7716"
|
|
||||||
transform="scale(-1.2)">
|
|
||||||
<path
|
|
||||||
id="path7718"
|
|
||||||
d="M -3.8048674,-3.9585227 L 0.54352094,-0.00068114835"
|
|
||||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.8;marker-start:none;marker-end:none;stroke-linecap:round" />
|
|
||||||
<path
|
|
||||||
id="path7720"
|
|
||||||
d="M -1.2866832,-3.9585227 L 3.0617053,-0.00068114835"
|
|
||||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.8;marker-start:none;marker-end:none;stroke-linecap:round" />
|
|
||||||
<path
|
|
||||||
id="path7722"
|
|
||||||
d="M 1.3053582,-3.9585227 L 5.6537466,-0.00068114835"
|
|
||||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.8;marker-start:none;marker-end:none;stroke-linecap:round" />
|
|
||||||
<path
|
|
||||||
id="path7724"
|
|
||||||
d="M -3.8048674,4.1775838 L 0.54352094,0.21974226"
|
|
||||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.8;marker-start:none;marker-end:none;stroke-linecap:round" />
|
|
||||||
<path
|
|
||||||
id="path7726"
|
|
||||||
d="M -1.2866832,4.1775838 L 3.0617053,0.21974226"
|
|
||||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.8;marker-start:none;marker-end:none;stroke-linecap:round" />
|
|
||||||
<path
|
|
||||||
id="path7728"
|
|
||||||
d="M 1.3053582,4.1775838 L 5.6537466,0.21974226"
|
|
||||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.8;marker-start:none;marker-end:none;stroke-linecap:round" />
|
|
||||||
</g>
|
|
||||||
</marker>
|
|
||||||
<marker
|
|
||||||
inkscape:stockid="Arrow1Lstart"
|
|
||||||
orient="auto"
|
|
||||||
refY="0.0"
|
|
||||||
refX="0.0"
|
|
||||||
id="Arrow1Lstart"
|
|
||||||
style="overflow:visible">
|
|
||||||
<path
|
|
||||||
id="path7764"
|
|
||||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
|
||||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
|
|
||||||
transform="scale(0.8)" />
|
|
||||||
</marker>
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
id="linearGradient3964">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#00eb00;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop3966" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#00eb00;stop-opacity:0;"
|
|
||||||
offset="1"
|
|
||||||
id="stop3968" />
|
|
||||||
</linearGradient>
|
|
||||||
<radialGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient3964"
|
|
||||||
id="radialGradient3996"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="matrix(1,0,0,0.237347,4.901628e-13,36.5688)"
|
|
||||||
cx="176.99219"
|
|
||||||
cy="47.949429"
|
|
||||||
fx="176.99219"
|
|
||||||
fy="47.949429"
|
|
||||||
r="78.257812" />
|
|
||||||
<radialGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient12743"
|
|
||||||
id="radialGradient12751"
|
|
||||||
cx="165.91866"
|
|
||||||
cy="45.584854"
|
|
||||||
fx="165.91866"
|
|
||||||
fy="45.584854"
|
|
||||||
r="56.51194"
|
|
||||||
gradientTransform="matrix(1,0,0,0.603517,0,18.07364)"
|
|
||||||
gradientUnits="userSpaceOnUse" />
|
|
||||||
</defs>
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="0.9793956"
|
|
||||||
inkscape:cx="372.32157"
|
|
||||||
inkscape:cy="45"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
inkscape:current-layer="g2335"
|
|
||||||
inkscape:window-width="791"
|
|
||||||
inkscape:window-height="581"
|
|
||||||
inkscape:window-x="4"
|
|
||||||
inkscape:window-y="48" />
|
|
||||||
<metadata
|
|
||||||
id="metadata1312">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
id="layer1"
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer">
|
|
||||||
<rect
|
|
||||||
style="opacity:1;fill:#393be9;fill-opacity:1;stroke:#f18a00;stroke-width:5.65522385;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
|
||||||
id="rect3981"
|
|
||||||
width="435.17825"
|
|
||||||
height="78.666664"
|
|
||||||
x="5.1747785"
|
|
||||||
y="6"
|
|
||||||
rx="29.141403"
|
|
||||||
ry="20"
|
|
||||||
inkscape:export-filename="/home/thanson/code/uthash/doc/html/img/logo.png"
|
|
||||||
inkscape:export-xdpi="90"
|
|
||||||
inkscape:export-ydpi="90" />
|
|
||||||
<flowRoot
|
|
||||||
transform="matrix(1.673678,0,0,1.673678,-141.8484,-37.12273)"
|
|
||||||
style="font-size:47.99999774;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr;text-anchor:start;fill:#faf599;fill-opacity:1;stroke:#f3bf33;stroke-opacity:1;font-family:Bitstream Vera Sans"
|
|
||||||
id="flowRoot3988"
|
|
||||||
xml:space="preserve"
|
|
||||||
inkscape:export-filename="/home/thanson/code/uthash/doc/html/img/logo.png"
|
|
||||||
inkscape:export-xdpi="90"
|
|
||||||
inkscape:export-ydpi="90"><flowRegion
|
|
||||||
style="fill:url(#radialGradient3996);fill-opacity:1"
|
|
||||||
id="flowRegion3990"><rect
|
|
||||||
style="font-size:47.99999774;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr;text-anchor:start;fill:#faf599;fill-opacity:1;stroke:#f3bf33;stroke-opacity:1;font-family:Bitstream Vera Sans"
|
|
||||||
y="18"
|
|
||||||
x="94.666664"
|
|
||||||
height="61.333332"
|
|
||||||
width="321.33334"
|
|
||||||
id="rect3992" /></flowRegion><flowPara
|
|
||||||
id="flowPara7831">ut hash</flowPara></flowRoot> <rect
|
|
||||||
style="opacity:1;fill:url(#radialGradient12751);fill-opacity:1.0;stroke:none;stroke-width:2.82532263;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
|
||||||
id="rect10995"
|
|
||||||
width="113.02388"
|
|
||||||
height="68.211792"
|
|
||||||
x="109.40672"
|
|
||||||
y="11.478957"
|
|
||||||
inkscape:export-filename="/home/thanson/code/uthash/doc/html/img/logo.png"
|
|
||||||
inkscape:export-xdpi="90"
|
|
||||||
inkscape:export-ydpi="90" />
|
|
||||||
<g
|
|
||||||
id="g7808"
|
|
||||||
transform="matrix(0.807859,0,0,0.807859,-140.848,9.677403)"
|
|
||||||
inkscape:export-filename="/home/thanson/code/uthash/doc/html/img/logo.png"
|
|
||||||
inkscape:export-xdpi="90"
|
|
||||||
inkscape:export-ydpi="90">
|
|
||||||
<rect
|
|
||||||
y="37.730064"
|
|
||||||
x="382.39673"
|
|
||||||
height="18.405188"
|
|
||||||
width="23.206543"
|
|
||||||
id="rect4882"
|
|
||||||
style="opacity:1;fill:#9be5ea;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
|
||||||
<rect
|
|
||||||
style="opacity:1;fill:#d48c21;fill-opacity:0.97777776;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
|
||||||
id="rect4886"
|
|
||||||
width="23.206543"
|
|
||||||
height="18.405188"
|
|
||||||
x="416.39673"
|
|
||||||
y="37.730064" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-type="polyline"
|
|
||||||
id="path4890"
|
|
||||||
d="M 372.60327,46.932658 L 381.39673,46.932658"
|
|
||||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-type="polyline"
|
|
||||||
id="path4892"
|
|
||||||
d="M 406.60327,46.932658 L 415.39673,46.932658"
|
|
||||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
|
||||||
<rect
|
|
||||||
y="9.7300644"
|
|
||||||
x="348.39673"
|
|
||||||
height="18.405188"
|
|
||||||
width="23.206543"
|
|
||||||
id="rect4896"
|
|
||||||
style="opacity:1;fill:#79c71a;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
|
||||||
<rect
|
|
||||||
style="opacity:1;fill:#f5e1a2;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
|
||||||
id="rect4898"
|
|
||||||
width="23.206543"
|
|
||||||
height="18.405188"
|
|
||||||
x="382.39673"
|
|
||||||
y="9.7300644" />
|
|
||||||
<path
|
|
||||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="M 372.60327,18.932658 L 381.39673,18.932658"
|
|
||||||
id="path4902"
|
|
||||||
inkscape:connector-type="polyline" />
|
|
||||||
<rect
|
|
||||||
y="14.207111"
|
|
||||||
x="318.45328"
|
|
||||||
height="10.1194"
|
|
||||||
width="10.1194"
|
|
||||||
id="rect4906"
|
|
||||||
style="opacity:1;fill:#1336e6;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-type="polyline"
|
|
||||||
id="path5789"
|
|
||||||
d="M 328.57268,19.220474 L 347.39673,19.048081"
|
|
||||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
|
||||||
<path
|
|
||||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="M 328.57268,19.220474 L 347.39673,19.048081"
|
|
||||||
id="path5795"
|
|
||||||
inkscape:connector-type="polyline" />
|
|
||||||
<rect
|
|
||||||
y="37.789951"
|
|
||||||
x="348.20978"
|
|
||||||
height="18.405188"
|
|
||||||
width="23.206543"
|
|
||||||
id="rect5803"
|
|
||||||
style="opacity:1;fill:#e5e5e5;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
|
||||||
<rect
|
|
||||||
y="42.267002"
|
|
||||||
x="318.26633"
|
|
||||||
height="10.1194"
|
|
||||||
width="10.1194"
|
|
||||||
id="rect5805"
|
|
||||||
style="opacity:1;fill:#1336e6;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
|
||||||
<path
|
|
||||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="M 328.38572,47.280365 L 347.20977,47.107972"
|
|
||||||
id="path5807"
|
|
||||||
inkscape:connector-type="polyline" />
|
|
||||||
<rect
|
|
||||||
style="opacity:1;fill:#ddf9ed;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
|
||||||
id="rect5809"
|
|
||||||
width="23.206543"
|
|
||||||
height="18.405188"
|
|
||||||
x="348.20978"
|
|
||||||
y="63.720913" />
|
|
||||||
<rect
|
|
||||||
style="opacity:1;fill:#1336e6;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
|
||||||
id="rect5811"
|
|
||||||
width="10.1194"
|
|
||||||
height="10.1194"
|
|
||||||
x="318.26633"
|
|
||||||
y="68.197968" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-type="polyline"
|
|
||||||
id="path5813"
|
|
||||||
d="M 328.38572,73.211328 L 347.20977,73.038935"
|
|
||||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-type="polyline"
|
|
||||||
id="path5833"
|
|
||||||
d="M 323.47927,24.326511 L 323.35974,42.267002"
|
|
||||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#2f29df;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-type="polyline"
|
|
||||||
id="path5835"
|
|
||||||
d="M 323.32603,52.386402 L 323.32603,68.197968"
|
|
||||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#2f29df;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
|
||||||
<path
|
|
||||||
id="path6716"
|
|
||||||
d="M 429.08836,47.11641 L 394.37307,18.527349 L 394.37307,49.158485 L 359.65778,18.527349 L 359.65778,50.179523 L 359.65778,75.70547"
|
|
||||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#f3bf33;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#StopM);marker-end:url(#Arrow1Send);stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
id="g2335"
|
|
||||||
transform="translate(0,-10)"
|
|
||||||
inkscape:export-filename="/home/thanson/code/uthash/doc/html/img/logo_tag.png"
|
|
||||||
inkscape:export-xdpi="90"
|
|
||||||
inkscape:export-ydpi="90">
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-size:18.43119621px;font-style:normal;font-weight:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
|
|
||||||
x="565.8512"
|
|
||||||
y="50.633156"
|
|
||||||
id="text2331"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan2333"
|
|
||||||
x="565.85119"
|
|
||||||
y="50.633156">a hash table</tspan><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
x="565.8512"
|
|
||||||
y="73.672151"
|
|
||||||
id="tspan2361">for C structures</tspan></text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 18 KiB |
@@ -1 +0,0 @@
|
|||||||
google-site-verification: google315d692c9c632ed0.html
|
|
||||||
122
dependencies/uthash/doc/index.html
vendored
122
dependencies/uthash/doc/index.html
vendored
@@ -1,122 +0,0 @@
|
|||||||
<!DOCTYPE html
|
|
||||||
PUBLIC "-//W3C//DTD XTHML 1.0 Strict//EN"
|
|
||||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
|
||||||
<head>
|
|
||||||
<link rel="stylesheet" type="text/css" href="styles.css" />
|
|
||||||
<title>uthash: a hash table for C structures</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div id="banner">
|
|
||||||
<img src="banner.png" alt="uthash: a hash table for C structures" />
|
|
||||||
</div> <!-- banner -->
|
|
||||||
|
|
||||||
<div id="topnav">
|
|
||||||
<a href="https://github.com/troydhanson/uthash">GitHub page</a> >
|
|
||||||
uthash home <!-- https://troydhanson.github.io/uthash/ -->
|
|
||||||
|
|
||||||
<a href="https://twitter.com/share" class="twitter-share-button" data-via="troydhanson">Tweet</a>
|
|
||||||
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
<div id="mid">
|
|
||||||
|
|
||||||
<div id="nav">
|
|
||||||
|
|
||||||
<h2>documentation</h2>
|
|
||||||
<div><a href="userguide.html">uthash</a></div>
|
|
||||||
<div><a href="utlist.html">utlist</a></div>
|
|
||||||
<div><a href="utarray.html">utarray</a></div>
|
|
||||||
<div><a href="utringbuffer.html">utringbuffer</a></div>
|
|
||||||
<div><a href="utstack.html">utstack</a></div>
|
|
||||||
<div><a href="utstring.html">utstring</a></div>
|
|
||||||
|
|
||||||
<h2>download</h2>
|
|
||||||
<h3>GNU/Linux, Windows</h3>
|
|
||||||
<div><a href=https://github.com/troydhanson/uthash/archive/master.zip>uthash-master.zip</a></div>
|
|
||||||
<div><a href=https://github.com/troydhanson/uthash>git clone</a></div>
|
|
||||||
|
|
||||||
<h2>license</h2>
|
|
||||||
<div><a href="license.html">BSD revised</a></div>
|
|
||||||
|
|
||||||
|
|
||||||
<h2>developer</h2>
|
|
||||||
<div><a href="https://troydhanson.github.io/">Troy D. Hanson</a></div>
|
|
||||||
|
|
||||||
<h2>maintainer</h2>
|
|
||||||
<div><a href="https://github.com/Quuxplusone">Arthur O'Dwyer</a></div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="main">
|
|
||||||
Any C structure can be stored in a hash table using uthash. Just add a
|
|
||||||
<em>UT_hash_handle</em> to the structure and choose one or more fields
|
|
||||||
in your structure to act as the key. Then use these macros to store,
|
|
||||||
retrieve or delete items from the hash table.
|
|
||||||
|
|
||||||
<div class="listing">
|
|
||||||
Example 1. Adding an item to a hash.
|
|
||||||
<div class="code">
|
|
||||||
<pre>
|
|
||||||
#include "uthash.h"
|
|
||||||
|
|
||||||
struct my_struct {
|
|
||||||
int id; /* we'll use this field as the key */
|
|
||||||
char name[10];
|
|
||||||
UT_hash_handle hh; /* makes this structure hashable */
|
|
||||||
};
|
|
||||||
|
|
||||||
struct my_struct *users = NULL;
|
|
||||||
|
|
||||||
void add_user(struct my_struct *s) {
|
|
||||||
HASH_ADD_INT(users, id, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
</pre>
|
|
||||||
</div> <!-- code -->
|
|
||||||
</div> <!-- listing -->
|
|
||||||
|
|
||||||
<div class="listing">
|
|
||||||
Example 2. Looking up an item in a hash.
|
|
||||||
<div class="code">
|
|
||||||
<pre>
|
|
||||||
struct my_struct *find_user(int user_id) {
|
|
||||||
struct my_struct *s;
|
|
||||||
|
|
||||||
HASH_FIND_INT(users, &user_id, s);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
</pre>
|
|
||||||
</div> <!-- code -->
|
|
||||||
</div> <!-- listing -->
|
|
||||||
|
|
||||||
<div class="listing">
|
|
||||||
Example 3. Deleting an item from a hash.
|
|
||||||
<div class="code">
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
void delete_user(struct my_struct *user) {
|
|
||||||
HASH_DEL(users, user);
|
|
||||||
}
|
|
||||||
|
|
||||||
</pre>
|
|
||||||
</div> <!-- code -->
|
|
||||||
</div> <!-- listing -->
|
|
||||||
|
|
||||||
For more information and examples, please see the <a href="userguide.html">User Guide.</a>
|
|
||||||
|
|
||||||
</div> <!-- main -->
|
|
||||||
</div> <!-- mid -->
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
<div id="footer">
|
|
||||||
</div> <!-- footer -->
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
|
|
||||||
55
dependencies/uthash/doc/license.html
vendored
55
dependencies/uthash/doc/license.html
vendored
@@ -1,55 +0,0 @@
|
|||||||
<!DOCTYPE html
|
|
||||||
PUBLIC "-//W3C//DTD XTHML 1.0 Strict//EN"
|
|
||||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
|
||||||
<head>
|
|
||||||
<link rel="stylesheet" type="text/css" href="styles.css" />
|
|
||||||
<title>uthash: a hash table for C structures</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div id="banner">
|
|
||||||
<img src="banner.png" alt="uthash: a hash table for C structures" />
|
|
||||||
</div> <!-- banner -->
|
|
||||||
|
|
||||||
<div id="topnav">
|
|
||||||
<a href="https://troydhanson.github.io/uthash/">uthash home</a> >
|
|
||||||
BSD license
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
<div id="mid">
|
|
||||||
<div id="main">
|
|
||||||
<pre>
|
|
||||||
Copyright (c) 2005-2022, Troy D. Hanson https://troydhanson.github.io/uthash/
|
|
||||||
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.
|
|
||||||
|
|
||||||
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.
|
|
||||||
</pre>
|
|
||||||
</div> <!-- mid -->
|
|
||||||
</div> <!-- main -->
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
<div id="footer">
|
|
||||||
</div> <!-- footer -->
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
|
|
||||||
BIN
dependencies/uthash/doc/rss.png
vendored
BIN
dependencies/uthash/doc/rss.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 689 B |
140
dependencies/uthash/doc/styles.css
vendored
140
dependencies/uthash/doc/styles.css
vendored
@@ -1,140 +0,0 @@
|
|||||||
#banner {
|
|
||||||
/* font-size: x-large; */
|
|
||||||
/* background: #ff00ff; */
|
|
||||||
/* height: 100px; */
|
|
||||||
}
|
|
||||||
|
|
||||||
#topnav {
|
|
||||||
/* background-image: url(img/grad_topnav.png); */
|
|
||||||
/* background-repeat: repeat-y; */
|
|
||||||
/* background-color: #af00af; */
|
|
||||||
/* height: 25px; */
|
|
||||||
margin-top: 5px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
padding: 3px;
|
|
||||||
font-size: 9pt;
|
|
||||||
font-family: sans-serif;
|
|
||||||
/* border-style: solid; */
|
|
||||||
/* border-width: 1px; */
|
|
||||||
}
|
|
||||||
|
|
||||||
#topnav a {
|
|
||||||
padding: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1,p { margin: 0; } /* non-0 margin on firefox */
|
|
||||||
|
|
||||||
#mid {
|
|
||||||
/* background-image: url(img/grad_blue.png); */
|
|
||||||
background-repeat: repeat-y;
|
|
||||||
/* background-color: #ffddaa; */
|
|
||||||
padding-top: 20px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#mid img {
|
|
||||||
padding-left: 10px;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
a img {
|
|
||||||
border: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
.twitter-share-button {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.twitter-follow-button {
|
|
||||||
padding: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#nav {
|
|
||||||
background-color: #fff8f1;
|
|
||||||
margin-left: 10px;
|
|
||||||
margin-top: 20px;
|
|
||||||
margin-right: 20px;
|
|
||||||
float: left;
|
|
||||||
padding: 10px;
|
|
||||||
border-style: solid;
|
|
||||||
border-width: 2px;
|
|
||||||
font-family: sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#nav h2 {
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 10pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
#nav h3 {
|
|
||||||
/* font-weight: bold; */
|
|
||||||
padding-left: 5px;
|
|
||||||
/* font-style: oblique; */
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 7pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
#nav div {
|
|
||||||
font-size: 9pt;
|
|
||||||
padding-left: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#main {
|
|
||||||
background: #ffffff;
|
|
||||||
margin-top: 20px;
|
|
||||||
margin-left: 170px;
|
|
||||||
padding-left: 20px;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#main h1 {
|
|
||||||
font-family: sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.listing {
|
|
||||||
margin: 20px;
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code {
|
|
||||||
padding: 10px;
|
|
||||||
margin: 10px;
|
|
||||||
font-size: 8pt;
|
|
||||||
font-weight: normal;
|
|
||||||
background: #f3f3f3;
|
|
||||||
width: 100%;
|
|
||||||
border-style: solid;
|
|
||||||
border-width: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#footer {
|
|
||||||
/* background: #00ffff; */
|
|
||||||
margin-top: 5px;
|
|
||||||
font-size: small;
|
|
||||||
font-family: sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
height: 0.04em;
|
|
||||||
background: black;
|
|
||||||
margin: 0 10% 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#footer {
|
|
||||||
width: 90%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#footer img {
|
|
||||||
margin-right: 5px;
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
#footer #support {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
width: 80%;
|
|
||||||
}
|
|
||||||
1911
dependencies/uthash/doc/userguide.txt
vendored
1911
dependencies/uthash/doc/userguide.txt
vendored
File diff suppressed because it is too large
Load Diff
383
dependencies/uthash/doc/utarray.txt
vendored
383
dependencies/uthash/doc/utarray.txt
vendored
@@ -1,383 +0,0 @@
|
|||||||
utarray: dynamic array macros for C
|
|
||||||
===================================
|
|
||||||
Troy D. Hanson <tdh@tkhanson.net>
|
|
||||||
v2.3.0, February 2021
|
|
||||||
|
|
||||||
Here's a link back to the https://github.com/troydhanson/uthash[GitHub project page].
|
|
||||||
|
|
||||||
Introduction
|
|
||||||
------------
|
|
||||||
A set of general-purpose dynamic array macros for C structures are included with
|
|
||||||
uthash in `utarray.h`. To use these macros in your own C program, just
|
|
||||||
copy `utarray.h` into your source directory and use it in your programs.
|
|
||||||
|
|
||||||
#include "utarray.h"
|
|
||||||
|
|
||||||
The dynamic array supports basic operations such as push, pop, and erase on the
|
|
||||||
array elements. These array elements can be any simple datatype or structure.
|
|
||||||
The array <<operations,operations>> are based loosely on the C++ STL vector methods.
|
|
||||||
|
|
||||||
Internally the dynamic array contains a contiguous memory region into which
|
|
||||||
the elements are copied. This buffer is grown as needed using `realloc` to
|
|
||||||
accommodate all the data that is pushed into it.
|
|
||||||
|
|
||||||
Download
|
|
||||||
~~~~~~~~
|
|
||||||
To download the `utarray.h` header file,
|
|
||||||
follow the links on https://github.com/troydhanson/uthash to clone uthash or get a zip file,
|
|
||||||
then look in the src/ sub-directory.
|
|
||||||
|
|
||||||
BSD licensed
|
|
||||||
~~~~~~~~~~~~
|
|
||||||
This software is made available under the
|
|
||||||
link:license.html[revised BSD license].
|
|
||||||
It is free and open source.
|
|
||||||
|
|
||||||
Platforms
|
|
||||||
~~~~~~~~~
|
|
||||||
The 'utarray' macros have been tested on:
|
|
||||||
|
|
||||||
* Linux,
|
|
||||||
* Mac OS X,
|
|
||||||
* Windows, using Visual Studio 2008 and Visual Studio 2010
|
|
||||||
|
|
||||||
Usage
|
|
||||||
-----
|
|
||||||
|
|
||||||
Declaration
|
|
||||||
~~~~~~~~~~~
|
|
||||||
|
|
||||||
The array itself has the data type `UT_array`, regardless of the type of
|
|
||||||
elements to be stored in it. It is declared like,
|
|
||||||
|
|
||||||
UT_array *nums;
|
|
||||||
|
|
||||||
New and free
|
|
||||||
~~~~~~~~~~~~
|
|
||||||
The next step is to create the array using `utarray_new`. Later when you're
|
|
||||||
done with the array, `utarray_free` will free it and all its elements.
|
|
||||||
|
|
||||||
Push, pop, etc
|
|
||||||
~~~~~~~~~~~~~~
|
|
||||||
The central features of the utarray involve putting elements into it, taking
|
|
||||||
them out, and iterating over them. There are several <<operations,operations>>
|
|
||||||
to pick from that deal with either single elements or ranges of elements at a
|
|
||||||
time. In the examples below we will use only the push operation to insert
|
|
||||||
elements.
|
|
||||||
|
|
||||||
Elements
|
|
||||||
--------
|
|
||||||
|
|
||||||
Support for dynamic arrays of integers or strings is especially easy. These are
|
|
||||||
best shown by example:
|
|
||||||
|
|
||||||
Integers
|
|
||||||
~~~~~~~~
|
|
||||||
This example makes a utarray of integers, pushes 0-9 into it, then prints it.
|
|
||||||
Lastly it frees it.
|
|
||||||
|
|
||||||
.Integer elements
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "utarray.h"
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
UT_array *nums;
|
|
||||||
int i, *p;
|
|
||||||
|
|
||||||
utarray_new(nums,&ut_int_icd);
|
|
||||||
for(i=0; i < 10; i++) utarray_push_back(nums,&i);
|
|
||||||
|
|
||||||
for(p=(int*)utarray_front(nums);
|
|
||||||
p!=NULL;
|
|
||||||
p=(int*)utarray_next(nums,p)) {
|
|
||||||
printf("%d\n",*p);
|
|
||||||
}
|
|
||||||
|
|
||||||
utarray_free(nums);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
The second argument to `utarray_push_back` is always a 'pointer' to the type
|
|
||||||
(so a literal cannot be used). So for integers, it is an `int*`.
|
|
||||||
|
|
||||||
Strings
|
|
||||||
~~~~~~~
|
|
||||||
In this example we make a utarray of strings, push two strings into it, print
|
|
||||||
it and free it.
|
|
||||||
|
|
||||||
.String elements
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "utarray.h"
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
UT_array *strs;
|
|
||||||
char *s, **p;
|
|
||||||
|
|
||||||
utarray_new(strs,&ut_str_icd);
|
|
||||||
|
|
||||||
s = "hello"; utarray_push_back(strs, &s);
|
|
||||||
s = "world"; utarray_push_back(strs, &s);
|
|
||||||
p = NULL;
|
|
||||||
while ( (p=(char**)utarray_next(strs,p))) {
|
|
||||||
printf("%s\n",*p);
|
|
||||||
}
|
|
||||||
|
|
||||||
utarray_free(strs);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
In this example, since the element is a `char*`, we pass a pointer to it
|
|
||||||
(`char**`) as the second argument to `utarray_push_back`. Note that "push" makes
|
|
||||||
a copy of the source string and pushes that copy into the array.
|
|
||||||
|
|
||||||
About UT_icd
|
|
||||||
~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Arrays can be made of any type of element, not just integers and strings. The
|
|
||||||
elements can be basic types or structures. Unless you're dealing with integers
|
|
||||||
and strings (which use pre-defined `ut_int_icd` and `ut_str_icd`), you'll need
|
|
||||||
to define a `UT_icd` helper structure. This structure contains everything that
|
|
||||||
utarray needs to initialize, copy or destruct elements.
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
size_t sz;
|
|
||||||
init_f *init;
|
|
||||||
ctor_f *copy;
|
|
||||||
dtor_f *dtor;
|
|
||||||
} UT_icd;
|
|
||||||
|
|
||||||
The three function pointers `init`, `copy`, and `dtor` have these prototypes:
|
|
||||||
|
|
||||||
typedef void (ctor_f)(void *dst, const void *src);
|
|
||||||
typedef void (dtor_f)(void *elt);
|
|
||||||
typedef void (init_f)(void *elt);
|
|
||||||
|
|
||||||
The `sz` is just the size of the element being stored in the array.
|
|
||||||
|
|
||||||
The `init` function will be invoked whenever utarray needs to initialize an
|
|
||||||
empty element. This only happens as a byproduct of `utarray_resize` or
|
|
||||||
`utarray_extend_back`. If `init` is `NULL`, it defaults to zero filling the
|
|
||||||
new element using memset.
|
|
||||||
|
|
||||||
The `copy` function is used whenever an element is copied into the array.
|
|
||||||
It is invoked during `utarray_push_back`, `utarray_insert`, `utarray_inserta`,
|
|
||||||
or `utarray_concat`. If `copy` is `NULL`, it defaults to a bitwise copy using
|
|
||||||
memcpy.
|
|
||||||
|
|
||||||
The `dtor` function is used to clean up an element that is being removed from
|
|
||||||
the array. It may be invoked due to `utarray_resize`, `utarray_pop_back`,
|
|
||||||
`utarray_erase`, `utarray_clear`, `utarray_done` or `utarray_free`. If the
|
|
||||||
elements need no cleanup upon destruction, `dtor` may be `NULL`.
|
|
||||||
|
|
||||||
Scalar types
|
|
||||||
~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The next example uses `UT_icd` with all its defaults to make a utarray of
|
|
||||||
`long` elements. This example pushes two longs, prints them, and frees the
|
|
||||||
array.
|
|
||||||
|
|
||||||
.long elements
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "utarray.h"
|
|
||||||
|
|
||||||
UT_icd long_icd = {sizeof(long), NULL, NULL, NULL };
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
UT_array *nums;
|
|
||||||
long l, *p;
|
|
||||||
utarray_new(nums, &long_icd);
|
|
||||||
|
|
||||||
l=1; utarray_push_back(nums, &l);
|
|
||||||
l=2; utarray_push_back(nums, &l);
|
|
||||||
|
|
||||||
p=NULL;
|
|
||||||
while( (p=(long*)utarray_next(nums,p))) printf("%ld\n", *p);
|
|
||||||
|
|
||||||
utarray_free(nums);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
Structures
|
|
||||||
~~~~~~~~~~
|
|
||||||
|
|
||||||
Structures can be used as utarray elements. If the structure requires no
|
|
||||||
special effort to initialize, copy or destruct, we can use `UT_icd` with all
|
|
||||||
its defaults. This example shows a structure that consists of two integers. Here
|
|
||||||
we push two values, print them and free the array.
|
|
||||||
|
|
||||||
.Structure (simple)
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "utarray.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int a;
|
|
||||||
int b;
|
|
||||||
} intpair_t;
|
|
||||||
|
|
||||||
UT_icd intpair_icd = {sizeof(intpair_t), NULL, NULL, NULL};
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
|
|
||||||
UT_array *pairs;
|
|
||||||
intpair_t ip, *p;
|
|
||||||
utarray_new(pairs,&intpair_icd);
|
|
||||||
|
|
||||||
ip.a=1; ip.b=2; utarray_push_back(pairs, &ip);
|
|
||||||
ip.a=10; ip.b=20; utarray_push_back(pairs, &ip);
|
|
||||||
|
|
||||||
for(p=(intpair_t*)utarray_front(pairs);
|
|
||||||
p!=NULL;
|
|
||||||
p=(intpair_t*)utarray_next(pairs,p)) {
|
|
||||||
printf("%d %d\n", p->a, p->b);
|
|
||||||
}
|
|
||||||
|
|
||||||
utarray_free(pairs);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
The real utility of `UT_icd` is apparent when the elements of the utarray are
|
|
||||||
structures that require special work to initialize, copy or destruct.
|
|
||||||
|
|
||||||
For example, when a structure contains pointers to related memory areas that
|
|
||||||
need to be copied when the structure is copied (and freed when the structure is
|
|
||||||
freed), we can use custom `init`, `copy`, and `dtor` members in the `UT_icd`.
|
|
||||||
|
|
||||||
Here we take an example of a structure that contains an integer and a string.
|
|
||||||
When this element is copied (such as when an element is pushed into the array),
|
|
||||||
we want to "deep copy" the `s` pointer (so the original element and the new
|
|
||||||
element point to their own copies of `s`). When an element is destructed, we
|
|
||||||
want to "deep free" its copy of `s`. Lastly, this example is written to work
|
|
||||||
even if `s` has the value `NULL`.
|
|
||||||
|
|
||||||
.Structure (complex)
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "utarray.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int a;
|
|
||||||
char *s;
|
|
||||||
} intchar_t;
|
|
||||||
|
|
||||||
void intchar_copy(void *_dst, const void *_src) {
|
|
||||||
intchar_t *dst = (intchar_t*)_dst, *src = (intchar_t*)_src;
|
|
||||||
dst->a = src->a;
|
|
||||||
dst->s = src->s ? strdup(src->s) : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void intchar_dtor(void *_elt) {
|
|
||||||
intchar_t *elt = (intchar_t*)_elt;
|
|
||||||
if (elt->s) free(elt->s);
|
|
||||||
}
|
|
||||||
|
|
||||||
UT_icd intchar_icd = {sizeof(intchar_t), NULL, intchar_copy, intchar_dtor};
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
UT_array *intchars;
|
|
||||||
intchar_t ic, *p;
|
|
||||||
utarray_new(intchars, &intchar_icd);
|
|
||||||
|
|
||||||
ic.a=1; ic.s="hello"; utarray_push_back(intchars, &ic);
|
|
||||||
ic.a=2; ic.s="world"; utarray_push_back(intchars, &ic);
|
|
||||||
|
|
||||||
p=NULL;
|
|
||||||
while( (p=(intchar_t*)utarray_next(intchars,p))) {
|
|
||||||
printf("%d %s\n", p->a, (p->s ? p->s : "null"));
|
|
||||||
}
|
|
||||||
|
|
||||||
utarray_free(intchars);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
[[operations]]
|
|
||||||
Reference
|
|
||||||
---------
|
|
||||||
This table lists all the utarray operations. These are loosely based on the C++
|
|
||||||
vector class.
|
|
||||||
|
|
||||||
Operations
|
|
||||||
~~~~~~~~~~
|
|
||||||
|
|
||||||
[width="100%",cols="50<m,40<",grid="none",options="none"]
|
|
||||||
|===============================================================================
|
|
||||||
| utarray_new(UT_array *a, UT_icd *icd)| allocate a new array
|
|
||||||
| utarray_free(UT_array *a) | free an allocated array
|
|
||||||
| utarray_init(UT_array *a,UT_icd *icd)| init an array (non-alloc)
|
|
||||||
| utarray_done(UT_array *a) | dispose of an array (non-allocd)
|
|
||||||
| utarray_reserve(UT_array *a,int n) | ensure space available for 'n' more elements
|
|
||||||
| utarray_push_back(UT_array *a,void *p) | push element p onto a
|
|
||||||
| utarray_pop_back(UT_array *a) | pop last element from a
|
|
||||||
| utarray_extend_back(UT_array *a) | push empty element onto a
|
|
||||||
| utarray_len(UT_array *a) | get length of a
|
|
||||||
| utarray_eltptr(UT_array *a,int j) | get pointer of element from index
|
|
||||||
| utarray_eltidx(UT_array *a,void *e) | get index of element from pointer
|
|
||||||
| utarray_insert(UT_array *a,void *p, int j) | insert element p to index j
|
|
||||||
| utarray_inserta(UT_array *a,UT_array *w, int j) | insert array w into array a at index j
|
|
||||||
| utarray_resize(UT_array *dst,int num) | extend or shrink array to num elements
|
|
||||||
| utarray_concat(UT_array *dst,UT_array *src) | copy src to end of dst array
|
|
||||||
| utarray_erase(UT_array *a,int pos,int len) | remove len elements from a[pos]..a[pos+len-1]
|
|
||||||
| utarray_clear(UT_array *a) | clear all elements from a, setting its length to zero
|
|
||||||
| utarray_sort(UT_array *a,cmpfcn *cmp) | sort elements of a using comparison function
|
|
||||||
| utarray_find(UT_array *a,void *v, cmpfcn *cmp) | find element v in utarray (must be sorted)
|
|
||||||
| utarray_front(UT_array *a) | get first element of a
|
|
||||||
| utarray_next(UT_array *a,void *e) | get element of a following e (front if e is NULL)
|
|
||||||
| utarray_prev(UT_array *a,void *e) | get element of a before e (back if e is NULL)
|
|
||||||
| utarray_back(UT_array *a) | get last element of a
|
|
||||||
|===============================================================================
|
|
||||||
|
|
||||||
Notes
|
|
||||||
~~~~~
|
|
||||||
|
|
||||||
1. `utarray_new` and `utarray_free` are used to allocate a new array and free it,
|
|
||||||
while `utarray_init` and `utarray_done` can be used if the UT_array is already
|
|
||||||
allocated and just needs to be initialized or have its internal resources
|
|
||||||
freed.
|
|
||||||
|
|
||||||
2. `utarray_reserve` takes the "delta" of elements to reserve, not the total
|
|
||||||
desired capacity of the array. This differs from the C++ STL "reserve" notion.
|
|
||||||
|
|
||||||
3. `utarray_sort` expects a comparison function having the usual `strcmp`-like
|
|
||||||
convention where it accepts two elements (a and b) and returns a negative
|
|
||||||
value if a precedes b, 0 if a and b sort equally, and positive if b precedes a.
|
|
||||||
This is an example of a comparison function:
|
|
||||||
|
|
||||||
int intsort(const void *a, const void *b) {
|
|
||||||
int _a = *(const int *)a;
|
|
||||||
int _b = *(const int *)b;
|
|
||||||
return (_a < _b) ? -1 : (_a > _b);
|
|
||||||
}
|
|
||||||
|
|
||||||
4. `utarray_find` uses a binary search to locate an element having a certain value
|
|
||||||
according to the given comparison function. The utarray must be first sorted
|
|
||||||
using the same comparison function. An example of using `utarray_find` with
|
|
||||||
a utarray of strings is included in `tests/test61.c`.
|
|
||||||
|
|
||||||
5. A 'pointer' to a particular element (obtained using `utarray_eltptr` or
|
|
||||||
`utarray_front`, `utarray_next`, `utarray_prev`, `utarray_back`) becomes invalid whenever
|
|
||||||
another element is inserted into the utarray. This is because the internal
|
|
||||||
memory management may need to `realloc` the element storage to a new address.
|
|
||||||
For this reason, it's usually better to refer to an element by its integer
|
|
||||||
'index' in code whose duration may include element insertion.
|
|
||||||
|
|
||||||
6. To override the default out-of-memory handling behavior (which calls `exit(-1)`),
|
|
||||||
override the `utarray_oom()` macro before including `utarray.h`.
|
|
||||||
For example,
|
|
||||||
|
|
||||||
#define utarray_oom() do { longjmp(error_handling_location); } while (0)
|
|
||||||
...
|
|
||||||
#include "utarray.h"
|
|
||||||
|
|
||||||
// vim: set nowrap syntax=asciidoc:
|
|
||||||
BIN
dependencies/uthash/doc/uthash-mini.png
vendored
BIN
dependencies/uthash/doc/uthash-mini.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 3.5 KiB |
288
dependencies/uthash/doc/uthash-mini.svg
vendored
288
dependencies/uthash/doc/uthash-mini.svg
vendored
@@ -1,288 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://web.resource.org/cc/"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="118.44112"
|
|
||||||
height="22.655222"
|
|
||||||
id="svg2018"
|
|
||||||
sodipodi:version="0.32"
|
|
||||||
inkscape:version="0.44"
|
|
||||||
version="1.0"
|
|
||||||
sodipodi:docbase="/home/thanson/code/uthash/trunk/doc/html/img"
|
|
||||||
sodipodi:docname="uthash-mini.svg">
|
|
||||||
<defs
|
|
||||||
id="defs3">
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
id="linearGradient3964">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#00eb00;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop3966" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#00eb00;stop-opacity:0;"
|
|
||||||
offset="1"
|
|
||||||
id="stop3968" />
|
|
||||||
</linearGradient>
|
|
||||||
<radialGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient3964"
|
|
||||||
id="radialGradient3996"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="matrix(1,0,0,0.237347,0,36.5688)"
|
|
||||||
cx="176.99219"
|
|
||||||
cy="47.949429"
|
|
||||||
fx="176.99219"
|
|
||||||
fy="47.949429"
|
|
||||||
r="78.257812" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient12743">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#99e1fa;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop12745" />
|
|
||||||
<stop
|
|
||||||
id="stop12753"
|
|
||||||
offset="0"
|
|
||||||
style="stop-color:#99e1fa;stop-opacity:0.49803922;" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#99e1fa;stop-opacity:0;"
|
|
||||||
offset="1"
|
|
||||||
id="stop12747" />
|
|
||||||
</linearGradient>
|
|
||||||
<radialGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient12743"
|
|
||||||
id="radialGradient12751"
|
|
||||||
cx="165.91866"
|
|
||||||
cy="45.584854"
|
|
||||||
fx="165.91866"
|
|
||||||
fy="45.584854"
|
|
||||||
r="56.51194"
|
|
||||||
gradientTransform="matrix(0.268675,0,0,0.16215,17.28599,40.67469)"
|
|
||||||
gradientUnits="userSpaceOnUse" />
|
|
||||||
<marker
|
|
||||||
inkscape:stockid="Arrow1Send"
|
|
||||||
orient="auto"
|
|
||||||
refY="0"
|
|
||||||
refX="0"
|
|
||||||
id="Arrow1Send"
|
|
||||||
style="overflow:visible">
|
|
||||||
<path
|
|
||||||
id="path7749"
|
|
||||||
d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z "
|
|
||||||
style="fill-rule:evenodd;stroke:black;stroke-width:1pt;marker-start:none"
|
|
||||||
transform="scale(-0.2,-0.2)" />
|
|
||||||
</marker>
|
|
||||||
<marker
|
|
||||||
inkscape:stockid="StopM"
|
|
||||||
orient="auto"
|
|
||||||
refY="0"
|
|
||||||
refX="0"
|
|
||||||
id="StopM"
|
|
||||||
style="overflow:visible">
|
|
||||||
<path
|
|
||||||
id="path7651"
|
|
||||||
d="M 0,5.65 L 0,-5.65"
|
|
||||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:1pt"
|
|
||||||
transform="scale(0.4,0.4)" />
|
|
||||||
</marker>
|
|
||||||
</defs>
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="1.2"
|
|
||||||
inkscape:cx="160"
|
|
||||||
inkscape:cy="90"
|
|
||||||
inkscape:document-units="mm"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
inkscape:window-width="916"
|
|
||||||
inkscape:window-height="626"
|
|
||||||
inkscape:window-x="5"
|
|
||||||
inkscape:window-y="73" />
|
|
||||||
<metadata
|
|
||||||
id="metadata2022">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1"
|
|
||||||
transform="translate(-17.9166,-36.67108)">
|
|
||||||
<rect
|
|
||||||
style="opacity:1;fill:#393be9;fill-opacity:1;stroke:#f18a00;stroke-width:1.51941979;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
|
||||||
id="rect3981"
|
|
||||||
width="116.92171"
|
|
||||||
height="21.135801"
|
|
||||||
x="18.67631"
|
|
||||||
y="37.430794"
|
|
||||||
rx="7.8295798"
|
|
||||||
ry="5.3735089"
|
|
||||||
inkscape:export-filename="/home/thanson/code/uthash/doc/html/img/logo.png"
|
|
||||||
inkscape:export-xdpi="90"
|
|
||||||
inkscape:export-ydpi="90" />
|
|
||||||
<flowRoot
|
|
||||||
transform="matrix(0.449676,0,0,0.449676,-20.8252,25.84477)"
|
|
||||||
style="font-size:47.99999619px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#faf599;fill-opacity:1;stroke:#f3bf33;stroke-opacity:1;font-family:Bitstream Vera Sans"
|
|
||||||
id="flowRoot3988"
|
|
||||||
xml:space="preserve"
|
|
||||||
inkscape:export-filename="/home/thanson/code/uthash/doc/html/img/logo.png"
|
|
||||||
inkscape:export-xdpi="90"
|
|
||||||
inkscape:export-ydpi="90"><flowRegion
|
|
||||||
style="fill:url(#radialGradient3996);fill-opacity:1"
|
|
||||||
id="flowRegion3990"><rect
|
|
||||||
style="font-size:47.99999619px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#faf599;fill-opacity:1;stroke:#f3bf33;stroke-opacity:1;font-family:Bitstream Vera Sans"
|
|
||||||
y="18"
|
|
||||||
x="94.666664"
|
|
||||||
height="61.333332"
|
|
||||||
width="321.33334"
|
|
||||||
id="rect3992" /></flowRegion><flowPara
|
|
||||||
id="flowPara7831">ut hash</flowPara></flowRoot> <rect
|
|
||||||
style="opacity:1;fill:url(#radialGradient12751);fill-opacity:1;stroke:none;stroke-width:2.82532263;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
|
||||||
id="rect10995"
|
|
||||||
width="30.366741"
|
|
||||||
height="18.326834"
|
|
||||||
x="46.68087"
|
|
||||||
y="38.902855"
|
|
||||||
inkscape:export-filename="/home/thanson/code/uthash/doc/html/img/logo.png"
|
|
||||||
inkscape:export-xdpi="90"
|
|
||||||
inkscape:export-ydpi="90" />
|
|
||||||
<g
|
|
||||||
id="g7808"
|
|
||||||
transform="matrix(0.217052,0,0,0.217052,-20.55641,38.41883)"
|
|
||||||
inkscape:export-filename="/home/thanson/code/uthash/doc/html/img/logo.png"
|
|
||||||
inkscape:export-xdpi="90"
|
|
||||||
inkscape:export-ydpi="90">
|
|
||||||
<rect
|
|
||||||
y="37.730064"
|
|
||||||
x="382.39673"
|
|
||||||
height="18.405188"
|
|
||||||
width="23.206543"
|
|
||||||
id="rect4882"
|
|
||||||
style="opacity:1;fill:#9be5ea;fill-opacity:1;stroke:black;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
|
||||||
<rect
|
|
||||||
style="opacity:1;fill:#d48c21;fill-opacity:0.97777776;stroke:black;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
|
||||||
id="rect4886"
|
|
||||||
width="23.206543"
|
|
||||||
height="18.405188"
|
|
||||||
x="416.39673"
|
|
||||||
y="37.730064" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-type="polyline"
|
|
||||||
id="path4890"
|
|
||||||
d="M 372.60327,46.932658 L 381.39673,46.932658"
|
|
||||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-type="polyline"
|
|
||||||
id="path4892"
|
|
||||||
d="M 406.60327,46.932658 L 415.39673,46.932658"
|
|
||||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
|
||||||
<rect
|
|
||||||
y="9.7300644"
|
|
||||||
x="348.39673"
|
|
||||||
height="18.405188"
|
|
||||||
width="23.206543"
|
|
||||||
id="rect4896"
|
|
||||||
style="opacity:1;fill:#79c71a;fill-opacity:1;stroke:black;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
|
||||||
<rect
|
|
||||||
style="opacity:1;fill:#f5e1a2;fill-opacity:1;stroke:black;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
|
||||||
id="rect4898"
|
|
||||||
width="23.206543"
|
|
||||||
height="18.405188"
|
|
||||||
x="382.39673"
|
|
||||||
y="9.7300644" />
|
|
||||||
<path
|
|
||||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="M 372.60327,18.932658 L 381.39673,18.932658"
|
|
||||||
id="path4902"
|
|
||||||
inkscape:connector-type="polyline" />
|
|
||||||
<rect
|
|
||||||
y="14.207111"
|
|
||||||
x="318.45328"
|
|
||||||
height="10.1194"
|
|
||||||
width="10.1194"
|
|
||||||
id="rect4906"
|
|
||||||
style="opacity:1;fill:#1336e6;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-type="polyline"
|
|
||||||
id="path5789"
|
|
||||||
d="M 328.57268,19.220474 L 347.39673,19.048081"
|
|
||||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
|
||||||
<path
|
|
||||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="M 328.57268,19.220474 L 347.39673,19.048081"
|
|
||||||
id="path5795"
|
|
||||||
inkscape:connector-type="polyline" />
|
|
||||||
<rect
|
|
||||||
y="37.789951"
|
|
||||||
x="348.20978"
|
|
||||||
height="18.405188"
|
|
||||||
width="23.206543"
|
|
||||||
id="rect5803"
|
|
||||||
style="opacity:1;fill:#e5e5e5;fill-opacity:1;stroke:black;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
|
||||||
<rect
|
|
||||||
y="42.267002"
|
|
||||||
x="318.26633"
|
|
||||||
height="10.1194"
|
|
||||||
width="10.1194"
|
|
||||||
id="rect5805"
|
|
||||||
style="opacity:1;fill:#1336e6;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
|
||||||
<path
|
|
||||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="M 328.38572,47.280365 L 347.20977,47.107972"
|
|
||||||
id="path5807"
|
|
||||||
inkscape:connector-type="polyline" />
|
|
||||||
<rect
|
|
||||||
style="opacity:1;fill:#ddf9ed;fill-opacity:1;stroke:black;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
|
||||||
id="rect5809"
|
|
||||||
width="23.206543"
|
|
||||||
height="18.405188"
|
|
||||||
x="348.20978"
|
|
||||||
y="63.720913" />
|
|
||||||
<rect
|
|
||||||
style="opacity:1;fill:#1336e6;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
|
||||||
id="rect5811"
|
|
||||||
width="10.1194"
|
|
||||||
height="10.1194"
|
|
||||||
x="318.26633"
|
|
||||||
y="68.197968" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-type="polyline"
|
|
||||||
id="path5813"
|
|
||||||
d="M 328.38572,73.211328 L 347.20977,73.038935"
|
|
||||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-type="polyline"
|
|
||||||
id="path5833"
|
|
||||||
d="M 323.47927,24.326511 L 323.35974,42.267002"
|
|
||||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#2f29df;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-type="polyline"
|
|
||||||
id="path5835"
|
|
||||||
d="M 323.32603,52.386402 L 323.32603,68.197968"
|
|
||||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#2f29df;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
|
||||||
<path
|
|
||||||
id="path6716"
|
|
||||||
d="M 429.08836,47.11641 L 394.37307,18.527349 L 394.37307,49.158485 L 359.65778,18.527349 L 359.65778,50.179523 L 359.65778,75.70547"
|
|
||||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#f3bf33;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#StopM);marker-end:url(#Arrow1Send);stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 12 KiB |
BIN
dependencies/uthash/doc/uthash.png
vendored
BIN
dependencies/uthash/doc/uthash.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 21 KiB |
293
dependencies/uthash/doc/utlist.txt
vendored
293
dependencies/uthash/doc/utlist.txt
vendored
@@ -1,293 +0,0 @@
|
|||||||
utlist: linked list macros for C structures
|
|
||||||
===========================================
|
|
||||||
Troy D. Hanson <tdh@tkhanson.net>
|
|
||||||
v2.3.0, February 2021
|
|
||||||
|
|
||||||
Here's a link back to the https://github.com/troydhanson/uthash[GitHub project page].
|
|
||||||
|
|
||||||
Introduction
|
|
||||||
------------
|
|
||||||
A set of general-purpose 'linked list' macros for C structures are included with
|
|
||||||
uthash in `utlist.h`. To use these macros in your own C program, just
|
|
||||||
copy `utlist.h` into your source directory and use it in your programs.
|
|
||||||
|
|
||||||
#include "utlist.h"
|
|
||||||
|
|
||||||
These macros support the basic linked list operations: adding and deleting
|
|
||||||
elements, sorting them and iterating over them.
|
|
||||||
|
|
||||||
Download
|
|
||||||
~~~~~~~~
|
|
||||||
To download the `utlist.h` header file,
|
|
||||||
follow the links on https://github.com/troydhanson/uthash to clone uthash or get a zip file,
|
|
||||||
then look in the src/ sub-directory.
|
|
||||||
|
|
||||||
BSD licensed
|
|
||||||
~~~~~~~~~~~~
|
|
||||||
This software is made available under the
|
|
||||||
link:license.html[revised BSD license].
|
|
||||||
It is free and open source.
|
|
||||||
|
|
||||||
Platforms
|
|
||||||
~~~~~~~~~
|
|
||||||
The 'utlist' macros have been tested on:
|
|
||||||
|
|
||||||
* Linux,
|
|
||||||
* Mac OS X, and
|
|
||||||
* Windows, using Visual Studio 2008, Visual Studio 2010, or Cygwin/MinGW.
|
|
||||||
|
|
||||||
Using utlist
|
|
||||||
------------
|
|
||||||
|
|
||||||
Types of lists
|
|
||||||
~~~~~~~~~~~~~~
|
|
||||||
Three types of linked lists are supported:
|
|
||||||
|
|
||||||
- *singly-linked* lists,
|
|
||||||
- *doubly-linked* lists, and
|
|
||||||
- *circular, doubly-linked* lists
|
|
||||||
|
|
||||||
Efficiency
|
|
||||||
^^^^^^^^^^
|
|
||||||
Prepending elements::
|
|
||||||
Constant-time on all list types.
|
|
||||||
Appending::
|
|
||||||
'O(n)' on singly-linked lists; constant-time on doubly-linked list.
|
|
||||||
(The utlist implementation of the doubly-linked list keeps a tail pointer in
|
|
||||||
`head->prev` so that append can be done in constant time).
|
|
||||||
Deleting elements::
|
|
||||||
'O(n)' on singly-linked lists; constant-time on doubly-linked list.
|
|
||||||
Sorting::
|
|
||||||
'O(n log(n))' for all list types.
|
|
||||||
Insertion in order (for sorted lists)::
|
|
||||||
'O(n)' for all list types.
|
|
||||||
Iteration, counting and searching::
|
|
||||||
'O(n)' for all list types.
|
|
||||||
|
|
||||||
List elements
|
|
||||||
~~~~~~~~~~~~~
|
|
||||||
You can use any structure with these macros, as long as the structure
|
|
||||||
contains a `next` pointer. If you want to make a doubly-linked list,
|
|
||||||
the element also needs to have a `prev` pointer.
|
|
||||||
|
|
||||||
typedef struct element {
|
|
||||||
char *name;
|
|
||||||
struct element *prev; /* needed for a doubly-linked list only */
|
|
||||||
struct element *next; /* needed for singly- or doubly-linked lists */
|
|
||||||
} element;
|
|
||||||
|
|
||||||
You can name your structure anything. In the example above it is called `element`.
|
|
||||||
Within a particular list, all elements must be of the same type.
|
|
||||||
|
|
||||||
Flexible prev/next naming
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
You can name your `prev` and `next` pointers something else. If you do, there is
|
|
||||||
a <<flex_names,family of macros>> that work identically but take these names as
|
|
||||||
extra arguments.
|
|
||||||
|
|
||||||
List head
|
|
||||||
~~~~~~~~~
|
|
||||||
The list head is simply a pointer to your element structure. You can name it
|
|
||||||
anything. *It must be initialized to `NULL`*.
|
|
||||||
|
|
||||||
element *head = NULL;
|
|
||||||
|
|
||||||
List operations
|
|
||||||
~~~~~~~~~~~~~~~
|
|
||||||
The lists support inserting or deleting elements, sorting the elements and
|
|
||||||
iterating over them.
|
|
||||||
|
|
||||||
[width="100%",cols="10<m,10<m,10<m",grid="cols",options="header"]
|
|
||||||
|===============================================================================
|
|
||||||
|Singly-linked | Doubly-linked | Circular, doubly-linked
|
|
||||||
|LL_PREPEND(head,add); | DL_PREPEND(head,add); | CDL_PREPEND(head,add);
|
|
||||||
|LL_PREPEND_ELEM(head,ref,add); | DL_PREPEND_ELEM(head,ref,add); | CDL_PREPEND_ELEM(head,ref,add);
|
|
||||||
|LL_APPEND_ELEM(head,ref,add); | DL_APPEND_ELEM(head,ref,add); | CDL_APPEND_ELEM(head,ref,add);
|
|
||||||
|LL_REPLACE_ELEM(head,del,add); | DL_REPLACE_ELEM(head,del,add); | CDL_REPLACE_ELEM(head,del,add);
|
|
||||||
|LL_APPEND(head,add); | DL_APPEND(head,add); | CDL_APPEND(head,add);
|
|
||||||
|LL_INSERT_INORDER(head,add,cmp); | DL_INSERT_INORDER(head,add,cmp); | CDL_INSERT_INORDER(head,add,cmp);
|
|
||||||
|LL_CONCAT(head1,head2); | DL_CONCAT(head1,head2); |
|
|
||||||
|LL_DELETE(head,del); | DL_DELETE(head,del); | CDL_DELETE(head,del);
|
|
||||||
|LL_SORT(head,cmp); | DL_SORT(head,cmp); | CDL_SORT(head,cmp);
|
|
||||||
|LL_FOREACH(head,elt) {...}| DL_FOREACH(head,elt) {...} | CDL_FOREACH(head,elt) {...}
|
|
||||||
|LL_FOREACH_SAFE(head,elt,tmp) {...}| DL_FOREACH_SAFE(head,elt,tmp) {...} | CDL_FOREACH_SAFE(head,elt,tmp1,tmp2) {...}
|
|
||||||
|LL_SEARCH_SCALAR(head,elt,mbr,val);| DL_SEARCH_SCALAR(head,elt,mbr,val); | CDL_SEARCH_SCALAR(head,elt,mbr,val);
|
|
||||||
|LL_SEARCH(head,elt,like,cmp);| DL_SEARCH(head,elt,like,cmp); | CDL_SEARCH(head,elt,like,cmp);
|
|
||||||
|LL_LOWER_BOUND(head,elt,like,cmp); | DL_LOWER_BOUND(head,elt,like,cmp); | CDL_LOWER_BOUND(head,elt,like,cmp);
|
|
||||||
|LL_COUNT(head,elt,count); | DL_COUNT(head,elt,count); | CDL_COUNT(head,elt,count);
|
|
||||||
|===============================================================================
|
|
||||||
|
|
||||||
'Prepend' means to insert an element in front of the existing list head (if any),
|
|
||||||
changing the list head to the new element. 'Append' means to add an element at the
|
|
||||||
end of the list, so it becomes the new tail element. 'Concatenate' takes two
|
|
||||||
properly constructed lists and appends the second list to the first. (Visual
|
|
||||||
Studio 2008 does not support `LL_CONCAT` and `DL_CONCAT`, but VS2010 is ok.)
|
|
||||||
To prepend before an arbitrary element instead of the list head, use the
|
|
||||||
`_PREPEND_ELEM` macro family.
|
|
||||||
To append after an arbitrary element element instead of the list head, use the
|
|
||||||
`_APPEND_ELEM` macro family.
|
|
||||||
To 'replace' an arbitrary list element with another element use the `_REPLACE_ELEM`
|
|
||||||
family of macros.
|
|
||||||
|
|
||||||
The 'sort' operation never moves the elements in memory; rather it only adjusts
|
|
||||||
the list order by altering the `prev` and `next` pointers in each element. Also
|
|
||||||
the sort operation can change the list head to point to a new element.
|
|
||||||
|
|
||||||
The 'foreach' operation is for easy iteration over the list from the head to the
|
|
||||||
tail. A usage example is shown below. You can of course just use the `prev` and
|
|
||||||
`next` pointers directly instead of using the 'foreach' macros.
|
|
||||||
The 'foreach_safe' operation should be used if you plan to delete any of the list
|
|
||||||
elements while iterating.
|
|
||||||
|
|
||||||
The 'search' operation is a shortcut for iteration in search of a particular
|
|
||||||
element. It is not any faster than manually iterating and testing each element.
|
|
||||||
There are two forms: the "scalar" version searches for an element using a
|
|
||||||
simple equality test on a given structure member, while the general version takes an
|
|
||||||
element to which all others in the list will be compared using a `cmp` function.
|
|
||||||
|
|
||||||
The 'lower_bound' operation finds the first element of the list which is no greater
|
|
||||||
than the provided `like` element, according to the provided `cmp` function.
|
|
||||||
The 'lower_bound' operation sets `elt` to a suitable value for passing to
|
|
||||||
`LL_APPEND_ELEM`; i.e., `elt=NULL` if the proper insertion point is at the front of
|
|
||||||
the list, and `elt=p` if the proper insertion point is between `p` and `p->next`.
|
|
||||||
|
|
||||||
The 'count' operation iterates over the list and increments a supplied counter.
|
|
||||||
|
|
||||||
The parameters shown in the table above are explained here:
|
|
||||||
|
|
||||||
head::
|
|
||||||
The list head (a pointer to your list element structure).
|
|
||||||
add::
|
|
||||||
A pointer to the list element structure you are adding to the list.
|
|
||||||
del::
|
|
||||||
A pointer to the list element structure you are replacing or
|
|
||||||
deleting from the list.
|
|
||||||
elt::
|
|
||||||
A pointer that will be assigned to each list element in succession (see
|
|
||||||
example) in the case of iteration macros; or, the output pointer from
|
|
||||||
the search macros.
|
|
||||||
ref::
|
|
||||||
Reference element for prepend and append operations that will be
|
|
||||||
prepended before or appended after.
|
|
||||||
If `ref` is a pointer with value NULL, the new element will be appended to the
|
|
||||||
list for _PREPEND_ELEM() operations and prepended for _APPEND_ELEM() operations.
|
|
||||||
`ref` must be the name of a pointer variable and cannot be literally NULL,
|
|
||||||
use _PREPEND() and _APPEND() macro family instead.
|
|
||||||
like::
|
|
||||||
An element pointer, having the same type as `elt`, for which the search macro
|
|
||||||
seeks a match (if found, the match is stored in `elt`). A match is determined
|
|
||||||
by the given `cmp` function.
|
|
||||||
cmp::
|
|
||||||
pointer to comparison function which accepts two arguments-- these are
|
|
||||||
pointers to two element structures to be compared. The comparison function
|
|
||||||
must return an `int` that is negative, zero, or positive, which specifies
|
|
||||||
whether the first item should sort before, equal to, or after the second item,
|
|
||||||
respectively. (In other words, the same convention that is used by `strcmp`).
|
|
||||||
Note that under Visual Studio 2008 you may need to declare the two arguments
|
|
||||||
as `void *` and then cast them back to their actual types.
|
|
||||||
tmp::
|
|
||||||
A pointer of the same type as `elt`. Used internally. Need not be initialized.
|
|
||||||
mbr::
|
|
||||||
In the scalar search macro, the name of a member within the `elt` structure which
|
|
||||||
will be tested (using `==`) for equality with the value `val`.
|
|
||||||
val::
|
|
||||||
In the scalar search macro, specifies the value of (of structure member
|
|
||||||
`field`) of the element being sought.
|
|
||||||
count::
|
|
||||||
integer which will be set to the length of the list
|
|
||||||
|
|
||||||
Example
|
|
||||||
~~~~~~~
|
|
||||||
This example program reads names from a text file (one name per line), and
|
|
||||||
appends each name to a doubly-linked list. Then it sorts and prints them.
|
|
||||||
|
|
||||||
.A doubly-linked list
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "utlist.h"
|
|
||||||
|
|
||||||
#define BUFLEN 20
|
|
||||||
|
|
||||||
typedef struct el {
|
|
||||||
char bname[BUFLEN];
|
|
||||||
struct el *next, *prev;
|
|
||||||
} el;
|
|
||||||
|
|
||||||
int namecmp(el *a, el *b) {
|
|
||||||
return strcmp(a->bname,b->bname);
|
|
||||||
}
|
|
||||||
|
|
||||||
el *head = NULL; /* important- initialize to NULL! */
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
el *name, *elt, *tmp, etmp;
|
|
||||||
|
|
||||||
char linebuf[BUFLEN];
|
|
||||||
int count;
|
|
||||||
FILE *file;
|
|
||||||
|
|
||||||
if ( (file = fopen( "test11.dat", "r" )) == NULL ) {
|
|
||||||
perror("can't open: ");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (fgets(linebuf,BUFLEN,file) != NULL) {
|
|
||||||
if ( (name = (el *)malloc(sizeof *name)) == NULL) exit(-1);
|
|
||||||
strcpy(name->bname, linebuf);
|
|
||||||
DL_APPEND(head, name);
|
|
||||||
}
|
|
||||||
DL_SORT(head, namecmp);
|
|
||||||
DL_FOREACH(head,elt) printf("%s", elt->bname);
|
|
||||||
DL_COUNT(head, elt, count);
|
|
||||||
printf("%d number of elements in list\n", count);
|
|
||||||
|
|
||||||
memcpy(&etmp.bname, "WES\n", 5);
|
|
||||||
DL_SEARCH(head,elt,&etmp,namecmp);
|
|
||||||
if (elt) printf("found %s\n", elt->bname);
|
|
||||||
|
|
||||||
/* now delete each element, use the safe iterator */
|
|
||||||
DL_FOREACH_SAFE(head,elt,tmp) {
|
|
||||||
DL_DELETE(head,elt);
|
|
||||||
free(elt);
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(file);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
[[flex_names]]
|
|
||||||
Other names for prev and next
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
If the `prev` and `next` fields are named something else, a separate group of
|
|
||||||
macros must be used. These work the same as the regular macros, but take the
|
|
||||||
field names as extra parameters.
|
|
||||||
|
|
||||||
These "flexible field name" macros are shown below. They all end with `2`. Each
|
|
||||||
operates the same as its counterpart without the `2`, but they take the name of
|
|
||||||
the `prev` and `next` fields (as applicable) as trailing arguments.
|
|
||||||
|
|
||||||
[width="100%",cols="10<m,10<m,10<m",grid="cols",options="header"]
|
|
||||||
|===============================================================================
|
|
||||||
|Singly-linked | Doubly-linked | Circular, doubly-linked
|
|
||||||
|LL_PREPEND2(head,add,next); | DL_PREPEND2(head,add,prev,next); | CDL_PREPEND2(head,add,prev,next);
|
|
||||||
|LL_PREPEND_ELEM2(head,ref,add,next); | DL_PREPEND_ELEM2(head,ref,add,prev,next); | CDL_PREPEND_ELEM2(head,ref,add,prev,next);
|
|
||||||
|LL_APPEND_ELEM2(head,ref,add,next); | DL_APPEND_ELEM2(head,ref,add,prev,next); | CDL_APPEND_ELEM2(head,ref,add,prev,next);
|
|
||||||
|LL_REPLACE_ELEM2(head,del,add,next); | DL_REPLACE_ELEM2(head,del,add,prev,next); | CDL_REPLACE_ELEM2(head,del,add,prev,next);
|
|
||||||
|LL_APPEND2(head,add,next); | DL_APPEND2(head,add,prev,next); | CDL_APPEND2(head,add,prev,next);
|
|
||||||
|LL_INSERT_INORDER2(head,add,cmp,next); | DL_INSERT_INORDER2(head,add,cmp,prev,next); | CDL_INSERT_INORDER2(head,add,cmp,prev,next);
|
|
||||||
|LL_CONCAT2(head1,head2,next); | DL_CONCAT2(head1,head2,prev,next); |
|
|
||||||
|LL_DELETE2(head,del,next); | DL_DELETE2(head,del,prev,next); | CDL_DELETE2(head,del,prev,next);
|
|
||||||
|LL_SORT2(head,cmp,next); | DL_SORT2(head,cmp,prev,next); | CDL_SORT2(head,cmp,prev,next);
|
|
||||||
|LL_FOREACH2(head,elt,next) {...} | DL_FOREACH2(head,elt,next) {...} | CDL_FOREACH2(head,elt,next) {...}
|
|
||||||
|LL_FOREACH_SAFE2(head,elt,tmp,next) {...} | DL_FOREACH_SAFE2(head,elt,tmp,next) {...} | CDL_FOREACH_SAFE2(head,elt,tmp1,tmp2,prev,next) {...}
|
|
||||||
|LL_SEARCH_SCALAR2(head,elt,mbr,val,next); | DL_SEARCH_SCALAR2(head,elt,mbr,val,next); | CDL_SEARCH_SCALAR2(head,elt,mbr,val,next);
|
|
||||||
|LL_SEARCH2(head,elt,like,cmp,next); | DL_SEARCH2(head,elt,like,cmp,next); | CDL_SEARCH2(head,elt,like,cmp,next);
|
|
||||||
|LL_LOWER_BOUND2(head,elt,like,cmp,next); | DL_LOWER_BOUND2(head,elt,like,cmp,next); | CDL_LOWER_BOUND2(head,elt,like,cmp,next);
|
|
||||||
|LL_COUNT2(head,elt,count,next); | DL_COUNT2(head,elt,count,next); | CDL_COUNT2(head,elt,count,next);
|
|
||||||
|===============================================================================
|
|
||||||
|
|
||||||
// vim: set tw=80 wm=2 syntax=asciidoc:
|
|
||||||
374
dependencies/uthash/doc/utringbuffer.txt
vendored
374
dependencies/uthash/doc/utringbuffer.txt
vendored
@@ -1,374 +0,0 @@
|
|||||||
utringbuffer: dynamic ring-buffer macros for C
|
|
||||||
==============================================
|
|
||||||
Arthur O'Dwyer <arthur.j.odwyer@gmail.com>
|
|
||||||
v2.3.0, February 2021
|
|
||||||
|
|
||||||
Here's a link back to the https://github.com/troydhanson/uthash[GitHub project page].
|
|
||||||
|
|
||||||
Introduction
|
|
||||||
------------
|
|
||||||
The functions in `utringbuffer.h` are based on the general-purpose array macros
|
|
||||||
provided in `utarray.h`, so before reading this page you should read
|
|
||||||
link:utarray.html[that page] first.
|
|
||||||
|
|
||||||
To use these macros in your own C program, copy both `utarray.h` and `utringbuffer.h`
|
|
||||||
into your source directory and use `utringbuffer.h` in your program.
|
|
||||||
|
|
||||||
#include "utringbuffer.h"
|
|
||||||
|
|
||||||
The provided <<operations,operations>> are based loosely on the C++ STL vector methods.
|
|
||||||
The ring-buffer data type supports construction (with a specified capacity),
|
|
||||||
destruction, iteration, and push, but not pop; once the ring-buffer reaches full
|
|
||||||
capacity, pushing a new element automatically pops and destroys the oldest element.
|
|
||||||
The elements contained in the ring-buffer can be any simple datatype or structure.
|
|
||||||
|
|
||||||
Internally the ring-buffer contains a pre-allocated memory region into which the
|
|
||||||
elements are copied, starting at position 0. When the ring-buffer reaches full
|
|
||||||
capacity, the next element to be pushed is pushed at position 0, overwriting the
|
|
||||||
oldest element, and the internal index representing the "start" of the ring-buffer
|
|
||||||
is incremented. A ring-buffer, once full, can never become un-full.
|
|
||||||
|
|
||||||
|
|
||||||
Download
|
|
||||||
~~~~~~~~
|
|
||||||
To download the `utringbuffer.h` header file,
|
|
||||||
follow the links on https://github.com/troydhanson/uthash to clone uthash or get a zip file,
|
|
||||||
then look in the src/ sub-directory.
|
|
||||||
|
|
||||||
BSD licensed
|
|
||||||
~~~~~~~~~~~~
|
|
||||||
This software is made available under the
|
|
||||||
link:license.html[revised BSD license].
|
|
||||||
It is free and open source.
|
|
||||||
|
|
||||||
Platforms
|
|
||||||
~~~~~~~~~
|
|
||||||
The 'utringbuffer' macros have been tested on:
|
|
||||||
|
|
||||||
* Linux,
|
|
||||||
* Mac OS X,
|
|
||||||
* Windows, using Visual Studio 2008 and Visual Studio 2010
|
|
||||||
|
|
||||||
Usage
|
|
||||||
-----
|
|
||||||
|
|
||||||
Declaration
|
|
||||||
~~~~~~~~~~~
|
|
||||||
|
|
||||||
The ring-buffer itself has the data type `UT_ringbuffer`, regardless of the type of
|
|
||||||
elements to be stored in it. It is declared like,
|
|
||||||
|
|
||||||
UT_ringbuffer *history;
|
|
||||||
|
|
||||||
New and free
|
|
||||||
~~~~~~~~~~~~
|
|
||||||
The next step is to create the ring-buffer using `utringbuffer_new`. Later when you're
|
|
||||||
done with the ring-buffer, `utringbuffer_free` will free it and all its elements.
|
|
||||||
|
|
||||||
Push, etc
|
|
||||||
~~~~~~~~~
|
|
||||||
The central features of the ring-buffer involve putting elements into it
|
|
||||||
and iterating over them. There are several <<operations,operations>>
|
|
||||||
that deal with either single elements or ranges of elements at a
|
|
||||||
time. In the examples below we will use only the push operation to insert
|
|
||||||
elements.
|
|
||||||
|
|
||||||
Elements
|
|
||||||
--------
|
|
||||||
|
|
||||||
Support for dynamic arrays of integers or strings is especially easy. These are
|
|
||||||
best shown by example:
|
|
||||||
|
|
||||||
Integers
|
|
||||||
~~~~~~~~
|
|
||||||
This example makes a ring-buffer of integers, pushes 0-9 into it, then prints it
|
|
||||||
two different ways. Lastly it frees it.
|
|
||||||
|
|
||||||
.Integer elements
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "utringbuffer.h"
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
UT_ringbuffer *history;
|
|
||||||
int i, *p;
|
|
||||||
|
|
||||||
utringbuffer_new(history, 7, &ut_int_icd);
|
|
||||||
for(i=0; i < 10; i++) utringbuffer_push_back(history, &i);
|
|
||||||
|
|
||||||
for (p = (int*)utringbuffer_front(history);
|
|
||||||
p != NULL;
|
|
||||||
p = (int*)utringbuffer_next(history, p)) {
|
|
||||||
printf("%d\n", *p); /* prints "3 4 5 6 7 8 9" */
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i=0; i < utringbuffer_len(history); i++) {
|
|
||||||
p = utringbuffer_eltptr(history, i);
|
|
||||||
printf("%d\n", *p); /* prints "3 4 5 6 7 8 9" */
|
|
||||||
}
|
|
||||||
|
|
||||||
utringbuffer_free(history);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
The second argument to `utringbuffer_push_back` is always a 'pointer' to the type
|
|
||||||
(so a literal cannot be used). So for integers, it is an `int*`.
|
|
||||||
|
|
||||||
Strings
|
|
||||||
~~~~~~~
|
|
||||||
In this example we make a ring-buffer of strings, push two strings into it, print
|
|
||||||
it and free it.
|
|
||||||
|
|
||||||
.String elements
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "utringbuffer.h"
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
UT_ringbuffer *strs;
|
|
||||||
char *s, **p;
|
|
||||||
|
|
||||||
utringbuffer_new(strs, 7, &ut_str_icd);
|
|
||||||
|
|
||||||
s = "hello"; utringbuffer_push_back(strs, &s);
|
|
||||||
s = "world"; utringbuffer_push_back(strs, &s);
|
|
||||||
p = NULL;
|
|
||||||
while ( (p=(char**)utringbuffer_next(strs,p))) {
|
|
||||||
printf("%s\n",*p);
|
|
||||||
}
|
|
||||||
|
|
||||||
utringbuffer_free(strs);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
In this example, since the element is a `char*`, we pass a pointer to it
|
|
||||||
(`char**`) as the second argument to `utringbuffer_push_back`. Note that "push" makes
|
|
||||||
a copy of the source string and pushes that copy into the array.
|
|
||||||
|
|
||||||
About UT_icd
|
|
||||||
~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Arrays can be made of any type of element, not just integers and strings. The
|
|
||||||
elements can be basic types or structures. Unless you're dealing with integers
|
|
||||||
and strings (which use pre-defined `ut_int_icd` and `ut_str_icd`), you'll need
|
|
||||||
to define a `UT_icd` helper structure. This structure contains everything that
|
|
||||||
utringbuffer (or utarray) needs to initialize, copy or destruct elements.
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
size_t sz;
|
|
||||||
init_f *init;
|
|
||||||
ctor_f *copy;
|
|
||||||
dtor_f *dtor;
|
|
||||||
} UT_icd;
|
|
||||||
|
|
||||||
The three function pointers `init`, `copy`, and `dtor` have these prototypes:
|
|
||||||
|
|
||||||
typedef void (ctor_f)(void *dst, const void *src);
|
|
||||||
typedef void (dtor_f)(void *elt);
|
|
||||||
typedef void (init_f)(void *elt);
|
|
||||||
|
|
||||||
The `sz` is just the size of the element being stored in the array.
|
|
||||||
|
|
||||||
The `init` function is used by utarray but is never used by utringbuffer;
|
|
||||||
you may safely set it to any value you want.
|
|
||||||
|
|
||||||
The `copy` function is used whenever an element is copied into the buffer.
|
|
||||||
It is invoked during `utringbuffer_push_back`.
|
|
||||||
If `copy` is `NULL`, it defaults to a bitwise copy using memcpy.
|
|
||||||
|
|
||||||
The `dtor` function is used to clean up an element that is being removed from
|
|
||||||
the buffer. It may be invoked due to `utringbuffer_push_back` (on the oldest
|
|
||||||
element in the buffer), `utringbuffer_clear`, `utringbuffer_done`, or
|
|
||||||
`utringbuffer_free`.
|
|
||||||
If the elements need no cleanup upon destruction, `dtor` may be `NULL`.
|
|
||||||
|
|
||||||
Scalar types
|
|
||||||
~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The next example uses `UT_icd` with all its defaults to make a ring-buffer of
|
|
||||||
`long` elements. This example pushes two longs into a buffer of capacity 1,
|
|
||||||
prints the contents of the buffer (which is to say, the most recent value
|
|
||||||
pushed), and then frees the buffer.
|
|
||||||
|
|
||||||
.long elements
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "utringbuffer.h"
|
|
||||||
|
|
||||||
UT_icd long_icd = {sizeof(long), NULL, NULL, NULL };
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
UT_ringbuffer *nums;
|
|
||||||
long l, *p;
|
|
||||||
utringbuffer_new(nums, 1, &long_icd);
|
|
||||||
|
|
||||||
l=1; utringbuffer_push_back(nums, &l);
|
|
||||||
l=2; utringbuffer_push_back(nums, &l);
|
|
||||||
|
|
||||||
p=NULL;
|
|
||||||
while((p = (long*)utringbuffer_next(nums,p))) printf("%ld\n", *p);
|
|
||||||
|
|
||||||
utringbuffer_free(nums);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
Structures
|
|
||||||
~~~~~~~~~~
|
|
||||||
|
|
||||||
Structures can be used as utringbuffer elements. If the structure requires no
|
|
||||||
special effort to initialize, copy or destruct, we can use `UT_icd` with all
|
|
||||||
its defaults. This example shows a structure that consists of two integers. Here
|
|
||||||
we push two values, print them and free the buffer.
|
|
||||||
|
|
||||||
.Structure (simple)
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "utringbuffer.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int a;
|
|
||||||
int b;
|
|
||||||
} intpair_t;
|
|
||||||
|
|
||||||
UT_icd intpair_icd = {sizeof(intpair_t), NULL, NULL, NULL};
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
|
|
||||||
UT_ringbuffer *pairs;
|
|
||||||
intpair_t ip, *p;
|
|
||||||
utringbuffer_new(pairs, 7, &intpair_icd);
|
|
||||||
|
|
||||||
ip.a=1; ip.b=2; utringbuffer_push_back(pairs, &ip);
|
|
||||||
ip.a=10; ip.b=20; utringbuffer_push_back(pairs, &ip);
|
|
||||||
|
|
||||||
for(p=(intpair_t*)utringbuffer_front(pairs);
|
|
||||||
p!=NULL;
|
|
||||||
p=(intpair_t*)utringbuffer_next(pairs,p)) {
|
|
||||||
printf("%d %d\n", p->a, p->b);
|
|
||||||
}
|
|
||||||
|
|
||||||
utringbuffer_free(pairs);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
The real utility of `UT_icd` is apparent when the elements stored in the
|
|
||||||
ring-buffer are structures that require special work to initialize, copy or
|
|
||||||
destruct.
|
|
||||||
|
|
||||||
For example, when a structure contains pointers to related memory areas that
|
|
||||||
need to be copied when the structure is copied (and freed when the structure is
|
|
||||||
freed), we can use custom `init`, `copy`, and `dtor` members in the `UT_icd`.
|
|
||||||
|
|
||||||
Here we take an example of a structure that contains an integer and a string.
|
|
||||||
When this element is copied (such as when an element is pushed),
|
|
||||||
we want to "deep copy" the `s` pointer (so the original element and the new
|
|
||||||
element point to their own copies of `s`). When an element is destructed, we
|
|
||||||
want to "deep free" its copy of `s`. Lastly, this example is written to work
|
|
||||||
even if `s` has the value `NULL`.
|
|
||||||
|
|
||||||
.Structure (complex)
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "utringbuffer.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int a;
|
|
||||||
char *s;
|
|
||||||
} intchar_t;
|
|
||||||
|
|
||||||
void intchar_copy(void *_dst, const void *_src) {
|
|
||||||
intchar_t *dst = (intchar_t*)_dst, *src = (intchar_t*)_src;
|
|
||||||
dst->a = src->a;
|
|
||||||
dst->s = src->s ? strdup(src->s) : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void intchar_dtor(void *_elt) {
|
|
||||||
intchar_t *elt = (intchar_t*)_elt;
|
|
||||||
free(elt->s);
|
|
||||||
}
|
|
||||||
|
|
||||||
UT_icd intchar_icd = {sizeof(intchar_t), NULL, intchar_copy, intchar_dtor};
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
UT_ringbuffer *intchars;
|
|
||||||
intchar_t ic, *p;
|
|
||||||
utringbuffer_new(intchars, 2, &intchar_icd);
|
|
||||||
|
|
||||||
ic.a=1; ic.s="hello"; utringbuffer_push_back(intchars, &ic);
|
|
||||||
ic.a=2; ic.s="world"; utringbuffer_push_back(intchars, &ic);
|
|
||||||
ic.a=3; ic.s="peace"; utringbuffer_push_back(intchars, &ic);
|
|
||||||
|
|
||||||
p=NULL;
|
|
||||||
while( (p=(intchar_t*)utringbuffer_next(intchars,p))) {
|
|
||||||
printf("%d %s\n", p->a, (p->s ? p->s : "null"));
|
|
||||||
/* prints "2 world 3 peace" */
|
|
||||||
}
|
|
||||||
|
|
||||||
utringbuffer_free(intchars);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
[[operations]]
|
|
||||||
Reference
|
|
||||||
---------
|
|
||||||
This table lists all the utringbuffer operations. These are loosely based on the C++
|
|
||||||
vector class.
|
|
||||||
|
|
||||||
Operations
|
|
||||||
~~~~~~~~~~
|
|
||||||
|
|
||||||
[width="100%",cols="50<m,40<",grid="none",options="none"]
|
|
||||||
|===============================================================================
|
|
||||||
| utringbuffer_new(UT_ringbuffer *a, int n, UT_icd *icd) | allocate a new ringbuffer
|
|
||||||
| utringbuffer_free(UT_ringbuffer *a) | free an allocated ringbuffer
|
|
||||||
| utringbuffer_init(UT_ringbuffer *a, int n, UT_icd *icd) | init a ringbuffer (non-alloc)
|
|
||||||
| utringbuffer_done(UT_ringbuffer *a) | dispose of a ringbuffer (non-alloc)
|
|
||||||
| utringbuffer_clear(UT_ringbuffer *a) | clear all elements from a, making it empty
|
|
||||||
| utringbuffer_push_back(UT_ringbuffer *a, element *p) | push element p onto a
|
|
||||||
| utringbuffer_len(UT_ringbuffer *a) | get length of a
|
|
||||||
| utringbuffer_empty(UT_ringbuffer *a) | get whether a is empty
|
|
||||||
| utringbuffer_full(UT_ringbuffer *a) | get whether a is full
|
|
||||||
| utringbuffer_eltptr(UT_ringbuffer *a, int j) | get pointer of element from index
|
|
||||||
| utringbuffer_eltidx(UT_ringbuffer *a, element *e) | get index of element from pointer
|
|
||||||
| utringbuffer_front(UT_ringbuffer *a) | get oldest element of a
|
|
||||||
| utringbuffer_next(UT_ringbuffer *a, element *e) | get element of a following e (front if e is NULL)
|
|
||||||
| utringbuffer_prev(UT_ringbuffer *a, element *e) | get element of a before e (back if e is NULL)
|
|
||||||
| utringbuffer_back(UT_ringbuffer *a) | get newest element of a
|
|
||||||
|===============================================================================
|
|
||||||
|
|
||||||
Notes
|
|
||||||
~~~~~
|
|
||||||
|
|
||||||
1. `utringbuffer_new` and `utringbuffer_free` are used to allocate a new ring-buffer
|
|
||||||
and to free it,
|
|
||||||
while `utringbuffer_init` and `utringbuffer_done` can be used if the UT_ringbuffer
|
|
||||||
is already allocated and just needs to be initialized or have its internal resources
|
|
||||||
freed.
|
|
||||||
2. Both `utringbuffer_new` and `utringbuffer_init` take a second parameter `n` indicating
|
|
||||||
the capacity of the ring-buffer, that is, the size at which the ring-buffer is considered
|
|
||||||
"full" and begins to overwrite old elements with newly pushed ones.
|
|
||||||
3. Once a ring-buffer has become full, it will never again become un-full except by
|
|
||||||
means of `utringbuffer_clear`. There is no way to "pop" a single old item from the
|
|
||||||
front of the ring-buffer. You can simulate this ability by maintaining a separate
|
|
||||||
integer count of the number of "logically popped elements", and starting your iteration
|
|
||||||
with `utringbuffer_eltptr(a, popped_count)` instead of with `utringbuffer_front(a)`.
|
|
||||||
4. Pointers to elements (obtained using `utringbuffer_eltptr`, `utringbuffer_front`,
|
|
||||||
`utringbuffer_next`, etc.) are not generally invalidated by `utringbuffer_push_back`,
|
|
||||||
because utringbuffer does not perform reallocation; however, a pointer to the oldest
|
|
||||||
element may suddenly turn into a pointer to the 'newest' element if
|
|
||||||
`utringbuffer_push_back` is called while the buffer is full.
|
|
||||||
5. The elements of a ring-buffer are stored in contiguous memory, but once the ring-buffer
|
|
||||||
has become full, it is no longer true that the elements are contiguously in order from
|
|
||||||
oldest to newest; i.e., `(element *)utringbuffer_front(a) + utringbuffer_len(a)-1` is
|
|
||||||
not generally equal to `(element *)utringbuffer_back(a)`.
|
|
||||||
|
|
||||||
// vim: set nowrap syntax=asciidoc:
|
|
||||||
158
dependencies/uthash/doc/utstack.txt
vendored
158
dependencies/uthash/doc/utstack.txt
vendored
@@ -1,158 +0,0 @@
|
|||||||
utstack: intrusive stack macros for C
|
|
||||||
=====================================
|
|
||||||
Arthur O'Dwyer <arthur.j.odwyer@gmail.com>
|
|
||||||
v2.3.0, February 2021
|
|
||||||
|
|
||||||
Here's a link back to the https://github.com/troydhanson/uthash[GitHub project page].
|
|
||||||
|
|
||||||
Introduction
|
|
||||||
------------
|
|
||||||
A set of very simple stack macros for C structures are included with
|
|
||||||
uthash in `utstack.h`. To use these macros in your own C program, just
|
|
||||||
copy `utstack.h` into your source directory and use it in your programs.
|
|
||||||
|
|
||||||
#include "utstack.h"
|
|
||||||
|
|
||||||
These macros support the basic operations of a stack, implemented as
|
|
||||||
an intrusive linked list. A stack supports the "push", "pop", and "count"
|
|
||||||
operations, as well as the trivial operation of getting the top element
|
|
||||||
of the stack.
|
|
||||||
|
|
||||||
Download
|
|
||||||
~~~~~~~~
|
|
||||||
To download the `utstack.h` header file,
|
|
||||||
follow the links on https://github.com/troydhanson/uthash to clone uthash or get a zip file,
|
|
||||||
then look in the src/ sub-directory.
|
|
||||||
|
|
||||||
BSD licensed
|
|
||||||
~~~~~~~~~~~~
|
|
||||||
This software is made available under the
|
|
||||||
link:license.html[revised BSD license].
|
|
||||||
It is free and open source.
|
|
||||||
|
|
||||||
Platforms
|
|
||||||
~~~~~~~~~
|
|
||||||
The 'utstack' macros have been tested on:
|
|
||||||
|
|
||||||
* Linux,
|
|
||||||
* Mac OS X,
|
|
||||||
* Windows, using Visual Studio 2008 and Visual Studio 2010
|
|
||||||
|
|
||||||
Usage
|
|
||||||
-----
|
|
||||||
|
|
||||||
Stack (list) head
|
|
||||||
~~~~~~~~~~~~~~~~~
|
|
||||||
The stack head is simply a pointer to your element structure. You can name it
|
|
||||||
anything. *It must be initialized to `NULL`*. It doubles as a pointer to the
|
|
||||||
top element of your stack.
|
|
||||||
|
|
||||||
element *stack = NULL;
|
|
||||||
|
|
||||||
Stack operations
|
|
||||||
~~~~~~~~~~~~~~~
|
|
||||||
The only operations on a stack are O(1) pushing, O(1) popping, and
|
|
||||||
O(n) counting the number of elements on the stack. None of the provided
|
|
||||||
macros permit directly accessing stack elements other than the top element.
|
|
||||||
|
|
||||||
To increase the readability of your code, you can use the macro
|
|
||||||
`STACK_EMPTY(head)` as a more readable alternative to `head == NULL`,
|
|
||||||
and `STACK_TOP(head)` as a more readable alternative to `head`.
|
|
||||||
|
|
||||||
[width="100%",cols="50<m,40<",grid="none",options="none"]
|
|
||||||
|===============================================================================
|
|
||||||
|STACK_PUSH(stack,add); | push `add` onto `stack`
|
|
||||||
|STACK_POP(stack,elt); | pop `stack` and save previous top as `elt`
|
|
||||||
|STACK_COUNT(stack,tmp,count); | store number of elements into `count`
|
|
||||||
|STACK_TOP(stack) | return `stack`
|
|
||||||
|STACK_EMPTY(stack) | return `stack == NULL`
|
|
||||||
|===============================================================================
|
|
||||||
|
|
||||||
The parameters shown in the table above are explained here:
|
|
||||||
|
|
||||||
stack::
|
|
||||||
The stack head (a pointer to your element structure).
|
|
||||||
add::
|
|
||||||
A pointer to the element structure you are adding to the stack.
|
|
||||||
elt::
|
|
||||||
A pointer that will be assigned the address of the popped element. Need not be initialized.
|
|
||||||
tmp::
|
|
||||||
A pointer of the same type as `elt`. Used internally. Need not be initialized.
|
|
||||||
count::
|
|
||||||
An integer that will be assigned the size of the stack. Need not be initialized.
|
|
||||||
|
|
||||||
|
|
||||||
Example
|
|
||||||
~~~~~~~
|
|
||||||
This example program reads names from a text file (one name per line), and
|
|
||||||
pushes each name on the stack; then pops and prints them in reverse order.
|
|
||||||
|
|
||||||
.A stack of names
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "utstack.h"
|
|
||||||
|
|
||||||
#define BUFLEN 20
|
|
||||||
|
|
||||||
typedef struct el {
|
|
||||||
char bname[BUFLEN];
|
|
||||||
struct el *next;
|
|
||||||
} el;
|
|
||||||
|
|
||||||
el *head = NULL; /* important- initialize to NULL! */
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
el *elt, *tmp;
|
|
||||||
|
|
||||||
char linebuf[sizeof el->bname];
|
|
||||||
int count;
|
|
||||||
FILE *file = fopen("test11.dat", "r");
|
|
||||||
if (file == NULL) {
|
|
||||||
perror("can't open: ");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (fgets(linebuf, sizeof linebuf, file) != NULL) {
|
|
||||||
el *name = malloc(sizeof *name);
|
|
||||||
if (name == NULL) exit(-1);
|
|
||||||
strcpy(name->bname, linebuf);
|
|
||||||
STACK_PUSH(head, name);
|
|
||||||
}
|
|
||||||
fclose(file);
|
|
||||||
|
|
||||||
STACK_COUNT(head, elt, count);
|
|
||||||
printf("%d elements were read into the stack\n", count);
|
|
||||||
|
|
||||||
/* now pop, print, and delete each element */
|
|
||||||
while (!STACK_EMPTY(head)) {
|
|
||||||
printf("%s\n", STACK_TOP(head)->bname);
|
|
||||||
STACK_POP(head, elt);
|
|
||||||
free(elt);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
[[flex_names]]
|
|
||||||
Other names for next
|
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
|
||||||
If the element structure's `next` field is named something else, a separate group
|
|
||||||
of macros must be used. These work the same as the regular macros, but take the
|
|
||||||
field name as an extra parameter.
|
|
||||||
|
|
||||||
These "flexible field name" macros are shown below. They all end with `2`. Each
|
|
||||||
operates the same as its counterpart without the `2`, but they take the name of
|
|
||||||
the `next` field as a trailing argument.
|
|
||||||
|
|
||||||
[width="100%",cols="50<m,40<",grid="none",options="none"]
|
|
||||||
|===============================================================================
|
|
||||||
|STACK_PUSH2(stack,add,next); | push `add` onto `stack`
|
|
||||||
|STACK_POP2(stack,elt,next); | pop `stack` and save previous top as `elt`
|
|
||||||
|STACK_COUNT2(stack,tmp,count,next); | store number of elements into `count`
|
|
||||||
|===============================================================================
|
|
||||||
|
|
||||||
|
|
||||||
// vim: set nowrap syntax=asciidoc:
|
|
||||||
239
dependencies/uthash/doc/utstring.txt
vendored
239
dependencies/uthash/doc/utstring.txt
vendored
@@ -1,239 +0,0 @@
|
|||||||
utstring: dynamic string macros for C
|
|
||||||
=====================================
|
|
||||||
Troy D. Hanson <tdh@tkhanson.net>
|
|
||||||
v2.3.0, February 2021
|
|
||||||
|
|
||||||
Here's a link back to the https://github.com/troydhanson/uthash[GitHub project page].
|
|
||||||
|
|
||||||
Introduction
|
|
||||||
------------
|
|
||||||
A set of basic dynamic string macros for C programs are included with
|
|
||||||
uthash in `utstring.h`. To use these in your own C program, just copy
|
|
||||||
`utstring.h` into your source directory and use it in your programs.
|
|
||||||
|
|
||||||
#include "utstring.h"
|
|
||||||
|
|
||||||
The dynamic string supports operations such as inserting data, concatenation,
|
|
||||||
getting the length and content, substring search, and clear. It's ok to put
|
|
||||||
binary data into a utstring too. The string <<operations,operations>> are
|
|
||||||
listed below.
|
|
||||||
|
|
||||||
Some utstring operations are implemented as functions rather than macros.
|
|
||||||
|
|
||||||
Download
|
|
||||||
~~~~~~~~
|
|
||||||
To download the `utstring.h` header file,
|
|
||||||
follow the links on https://github.com/troydhanson/uthash to clone uthash or get a zip file,
|
|
||||||
then look in the src/ sub-directory.
|
|
||||||
|
|
||||||
BSD licensed
|
|
||||||
~~~~~~~~~~~~
|
|
||||||
This software is made available under the
|
|
||||||
link:license.html[revised BSD license].
|
|
||||||
It is free and open source.
|
|
||||||
|
|
||||||
Platforms
|
|
||||||
~~~~~~~~~
|
|
||||||
The 'utstring' macros have been tested on:
|
|
||||||
|
|
||||||
* Linux,
|
|
||||||
* Windows, using Visual Studio 2008 and Visual Studio 2010
|
|
||||||
|
|
||||||
Usage
|
|
||||||
-----
|
|
||||||
|
|
||||||
Declaration
|
|
||||||
~~~~~~~~~~~
|
|
||||||
|
|
||||||
The dynamic string itself has the data type `UT_string`. It is declared like,
|
|
||||||
|
|
||||||
UT_string *str;
|
|
||||||
|
|
||||||
New and free
|
|
||||||
~~~~~~~~~~~~
|
|
||||||
The next step is to create the string using `utstring_new`. Later when you're
|
|
||||||
done with it, `utstring_free` will free it and all its content.
|
|
||||||
|
|
||||||
Manipulation
|
|
||||||
~~~~~~~~~~~~
|
|
||||||
The `utstring_printf` or `utstring_bincpy` operations insert (copy) data into
|
|
||||||
the string. To concatenate one utstring to another, use `utstring_concat`. To
|
|
||||||
clear the content of the string, use `utstring_clear`. The length of the string
|
|
||||||
is available from `utstring_len`, and its content from `utstring_body`. This
|
|
||||||
evaluates to a `char*`. The buffer it points to is always null-terminated.
|
|
||||||
So, it can be used directly with external functions that expect a string.
|
|
||||||
This automatic null terminator is not counted in the length of the string.
|
|
||||||
|
|
||||||
Samples
|
|
||||||
~~~~~~~
|
|
||||||
|
|
||||||
These examples show how to use utstring.
|
|
||||||
|
|
||||||
.Sample 1
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "utstring.h"
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
UT_string *s;
|
|
||||||
|
|
||||||
utstring_new(s);
|
|
||||||
utstring_printf(s, "hello world!" );
|
|
||||||
printf("%s\n", utstring_body(s));
|
|
||||||
|
|
||||||
utstring_free(s);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
The next example demonstrates that `utstring_printf` 'appends' to the string.
|
|
||||||
It also shows concatenation.
|
|
||||||
|
|
||||||
.Sample 2
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "utstring.h"
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
UT_string *s, *t;
|
|
||||||
|
|
||||||
utstring_new(s);
|
|
||||||
utstring_new(t);
|
|
||||||
|
|
||||||
utstring_printf(s, "hello " );
|
|
||||||
utstring_printf(s, "world " );
|
|
||||||
|
|
||||||
utstring_printf(t, "hi " );
|
|
||||||
utstring_printf(t, "there " );
|
|
||||||
|
|
||||||
utstring_concat(s, t);
|
|
||||||
printf("length: %u\n", utstring_len(s));
|
|
||||||
printf("%s\n", utstring_body(s));
|
|
||||||
|
|
||||||
utstring_free(s);
|
|
||||||
utstring_free(t);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
The next example shows how binary data can be inserted into the string. It also
|
|
||||||
clears the string and prints new data into it.
|
|
||||||
|
|
||||||
.Sample 3
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "utstring.h"
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
UT_string *s;
|
|
||||||
char binary[] = "\xff\xff";
|
|
||||||
|
|
||||||
utstring_new(s);
|
|
||||||
utstring_bincpy(s, binary, sizeof(binary));
|
|
||||||
printf("length is %u\n", utstring_len(s));
|
|
||||||
|
|
||||||
utstring_clear(s);
|
|
||||||
utstring_printf(s,"number %d", 10);
|
|
||||||
printf("%s\n", utstring_body(s));
|
|
||||||
|
|
||||||
utstring_free(s);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
[[operations]]
|
|
||||||
Reference
|
|
||||||
---------
|
|
||||||
These are the utstring operations.
|
|
||||||
|
|
||||||
Operations
|
|
||||||
~~~~~~~~~~
|
|
||||||
|
|
||||||
[width="100%",cols="50<m,40<",grid="none",options="none"]
|
|
||||||
|===============================================================================
|
|
||||||
| utstring_new(s) | allocate a new utstring
|
|
||||||
| utstring_renew(s) | allocate a new utstring (if s is `NULL`) otherwise clears it
|
|
||||||
| utstring_free(s) | free an allocated utstring
|
|
||||||
| utstring_init(s) | init a utstring (non-alloc)
|
|
||||||
| utstring_done(s) | dispose of a utstring (non-alloc)
|
|
||||||
| utstring_printf(s,fmt,...) | printf into a utstring (appends)
|
|
||||||
| utstring_bincpy(s,bin,len) | insert binary data of length len (appends)
|
|
||||||
| utstring_concat(dst,src) | concatenate src utstring to end of dst utstring
|
|
||||||
| utstring_clear(s) | clear the content of s (setting its length to 0)
|
|
||||||
| utstring_len(s) | obtain the length of s as an unsigned integer
|
|
||||||
| utstring_body(s) | get `char*` to body of s (buffer is always null-terminated)
|
|
||||||
| utstring_find(s,pos,str,len) | forward search from pos for a substring
|
|
||||||
| utstring_findR(s,pos,str,len) | reverse search from pos for a substring
|
|
||||||
|===============================================================================
|
|
||||||
|
|
||||||
New/free vs. init/done
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Use `utstring_new` and `utstring_free` to allocate a new string or free it. If
|
|
||||||
the UT_string is statically allocated, use `utstring_init` and `utstring_done`
|
|
||||||
to initialize or free its internal memory.
|
|
||||||
|
|
||||||
Substring search
|
|
||||||
~~~~~~~~~~~~~~~~
|
|
||||||
Use `utstring_find` and `utstring_findR` to search for a substring in a utstring.
|
|
||||||
It comes in forward and reverse varieties. The reverse search scans from the end of
|
|
||||||
the string backward. These take a position to start searching from, measured from 0
|
|
||||||
(the start of the utstring). A negative position is counted from the end of
|
|
||||||
the string, so, -1 is the last position. Note that in the reverse search, the
|
|
||||||
initial position anchors to the 'end' of the substring being searched for;
|
|
||||||
e.g., the 't' in 'cat'. The return value always refers to the offset where the
|
|
||||||
substring 'starts' in the utstring. When no substring match is found, -1 is
|
|
||||||
returned.
|
|
||||||
|
|
||||||
For example if a utstring called `s` contains:
|
|
||||||
|
|
||||||
ABC ABCDAB ABCDABCDABDE
|
|
||||||
|
|
||||||
Then these forward and reverse substring searches for `ABC` produce these results:
|
|
||||||
|
|
||||||
utstring_find( s, -9, "ABC", 3 ) = 15
|
|
||||||
utstring_find( s, 3, "ABC", 3 ) = 4
|
|
||||||
utstring_find( s, 16, "ABC", 3 ) = -1
|
|
||||||
utstring_findR( s, -9, "ABC", 3 ) = 11
|
|
||||||
utstring_findR( s, 12, "ABC", 3 ) = 4
|
|
||||||
utstring_findR( s, 2, "ABC", 3 ) = 0
|
|
||||||
|
|
||||||
"Multiple use" substring search
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
The preceding examples show "single use" versions of substring matching, where
|
|
||||||
the internal Knuth-Morris-Pratt (KMP) table is internally built and then freed
|
|
||||||
after the search. If your program needs to run many searches for a given
|
|
||||||
substring, it is more efficient to save the KMP table and reuse it.
|
|
||||||
|
|
||||||
To reuse the KMP table, build it manually and then pass it into the internal
|
|
||||||
search functions. The functions involved are:
|
|
||||||
|
|
||||||
_utstring_BuildTable (build the KMP table for a forward search)
|
|
||||||
_utstring_BuildTableR (build the KMP table for a reverse search)
|
|
||||||
_utstring_find (forward search using a prebuilt KMP table)
|
|
||||||
_utstring_findR (reverse search using a prebuilt KMP table)
|
|
||||||
|
|
||||||
This is an example of building a forward KMP table for the substring "ABC", and
|
|
||||||
then using it in a search:
|
|
||||||
|
|
||||||
long *KPM_TABLE, offset;
|
|
||||||
KPM_TABLE = (long *)malloc( sizeof(long) * (strlen("ABC")) + 1));
|
|
||||||
_utstring_BuildTable("ABC", 3, KPM_TABLE);
|
|
||||||
offset = _utstring_find(utstring_body(s), utstring_len(s), "ABC", 3, KPM_TABLE );
|
|
||||||
free(KPM_TABLE);
|
|
||||||
|
|
||||||
Note that the internal `_utstring_find` has the length of the UT_string as its
|
|
||||||
second argument, rather than the start position. You can emulate the position
|
|
||||||
parameter by adding to the string start address and subtracting from its length.
|
|
||||||
|
|
||||||
Notes
|
|
||||||
~~~~~
|
|
||||||
|
|
||||||
1. To override the default out-of-memory handling behavior (which calls `exit(-1)`),
|
|
||||||
override the `utstring_oom()` macro before including `utstring.h`.
|
|
||||||
For example,
|
|
||||||
|
|
||||||
#define utstring_oom() do { longjmp(error_handling_location); } while (0)
|
|
||||||
...
|
|
||||||
#include "utstring.h"
|
|
||||||
|
|
||||||
// vim: set nowrap syntax=asciidoc:
|
|
||||||
1
dependencies/uthash/include
vendored
1
dependencies/uthash/include
vendored
@@ -1 +0,0 @@
|
|||||||
src
|
|
||||||
28
dependencies/uthash/package.json
vendored
28
dependencies/uthash/package.json
vendored
@@ -1,28 +0,0 @@
|
|||||||
{
|
|
||||||
"description": "C macros for hash tables and more",
|
|
||||||
|
|
||||||
"keywords": [
|
|
||||||
"array",
|
|
||||||
"data",
|
|
||||||
"hash",
|
|
||||||
"list",
|
|
||||||
"macro",
|
|
||||||
"string",
|
|
||||||
"structure",
|
|
||||||
"uthash"
|
|
||||||
],
|
|
||||||
|
|
||||||
"name": "uthash",
|
|
||||||
"repo": "troydhanson/uthash",
|
|
||||||
|
|
||||||
"src": [
|
|
||||||
"src/utarray.h",
|
|
||||||
"src/uthash.h",
|
|
||||||
"src/utlist.h",
|
|
||||||
"src/utringbuffer.h",
|
|
||||||
"src/utstack.h",
|
|
||||||
"src/utstring.h"
|
|
||||||
],
|
|
||||||
|
|
||||||
"version": "2.3.0"
|
|
||||||
}
|
|
||||||
252
dependencies/uthash/src/utarray.h
vendored
252
dependencies/uthash/src/utarray.h
vendored
@@ -1,252 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2008-2022, Troy D. Hanson https://troydhanson.github.io/uthash/
|
|
||||||
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.
|
|
||||||
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* a dynamic array implementation using macros
|
|
||||||
*/
|
|
||||||
#ifndef UTARRAY_H
|
|
||||||
#define UTARRAY_H
|
|
||||||
|
|
||||||
#define UTARRAY_VERSION 2.3.0
|
|
||||||
|
|
||||||
#include <stddef.h> /* size_t */
|
|
||||||
#include <string.h> /* memset, etc */
|
|
||||||
#include <stdlib.h> /* exit */
|
|
||||||
|
|
||||||
#ifdef __GNUC__
|
|
||||||
#define UTARRAY_UNUSED __attribute__((__unused__))
|
|
||||||
#else
|
|
||||||
#define UTARRAY_UNUSED
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef utarray_oom
|
|
||||||
#define utarray_oom() exit(-1)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef void (ctor_f)(void *dst, const void *src);
|
|
||||||
typedef void (dtor_f)(void *elt);
|
|
||||||
typedef void (init_f)(void *elt);
|
|
||||||
typedef struct {
|
|
||||||
size_t sz;
|
|
||||||
init_f *init;
|
|
||||||
ctor_f *copy;
|
|
||||||
dtor_f *dtor;
|
|
||||||
} UT_icd;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
unsigned i,n;/* i: index of next available slot, n: num slots */
|
|
||||||
UT_icd icd; /* initializer, copy and destructor functions */
|
|
||||||
char *d; /* n slots of size icd->sz*/
|
|
||||||
} UT_array;
|
|
||||||
|
|
||||||
#define utarray_init(a,_icd) do { \
|
|
||||||
memset(a,0,sizeof(UT_array)); \
|
|
||||||
(a)->icd = *(_icd); \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utarray_done(a) do { \
|
|
||||||
if ((a)->n) { \
|
|
||||||
if ((a)->icd.dtor) { \
|
|
||||||
unsigned _ut_i; \
|
|
||||||
for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \
|
|
||||||
(a)->icd.dtor(utarray_eltptr(a,_ut_i)); \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
free((a)->d); \
|
|
||||||
} \
|
|
||||||
(a)->n=0; \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utarray_new(a,_icd) do { \
|
|
||||||
(a) = (UT_array*)malloc(sizeof(UT_array)); \
|
|
||||||
if ((a) == NULL) { \
|
|
||||||
utarray_oom(); \
|
|
||||||
} \
|
|
||||||
utarray_init(a,_icd); \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utarray_free(a) do { \
|
|
||||||
utarray_done(a); \
|
|
||||||
free(a); \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utarray_reserve(a,by) do { \
|
|
||||||
if (((a)->i+(by)) > (a)->n) { \
|
|
||||||
char *utarray_tmp; \
|
|
||||||
while (((a)->i+(by)) > (a)->n) { (a)->n = ((a)->n ? (2*(a)->n) : 8); } \
|
|
||||||
utarray_tmp=(char*)realloc((a)->d, (a)->n*(a)->icd.sz); \
|
|
||||||
if (utarray_tmp == NULL) { \
|
|
||||||
utarray_oom(); \
|
|
||||||
} \
|
|
||||||
(a)->d=utarray_tmp; \
|
|
||||||
} \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utarray_push_back(a,p) do { \
|
|
||||||
utarray_reserve(a,1); \
|
|
||||||
if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,(a)->i++), p); } \
|
|
||||||
else { memcpy(_utarray_eltptr(a,(a)->i++), p, (a)->icd.sz); }; \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utarray_pop_back(a) do { \
|
|
||||||
if ((a)->icd.dtor) { (a)->icd.dtor( _utarray_eltptr(a,--((a)->i))); } \
|
|
||||||
else { (a)->i--; } \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utarray_extend_back(a) do { \
|
|
||||||
utarray_reserve(a,1); \
|
|
||||||
if ((a)->icd.init) { (a)->icd.init(_utarray_eltptr(a,(a)->i)); } \
|
|
||||||
else { memset(_utarray_eltptr(a,(a)->i),0,(a)->icd.sz); } \
|
|
||||||
(a)->i++; \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utarray_len(a) ((a)->i)
|
|
||||||
|
|
||||||
#define utarray_eltptr(a,j) (((j) < (a)->i) ? _utarray_eltptr(a,j) : NULL)
|
|
||||||
#define _utarray_eltptr(a,j) ((void*)((a)->d + ((a)->icd.sz * (j))))
|
|
||||||
|
|
||||||
#define utarray_insert(a,p,j) do { \
|
|
||||||
if ((j) > (a)->i) utarray_resize(a,j); \
|
|
||||||
utarray_reserve(a,1); \
|
|
||||||
if ((j) < (a)->i) { \
|
|
||||||
memmove( _utarray_eltptr(a,(j)+1), _utarray_eltptr(a,j), \
|
|
||||||
((a)->i - (j))*((a)->icd.sz)); \
|
|
||||||
} \
|
|
||||||
if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,j), p); } \
|
|
||||||
else { memcpy(_utarray_eltptr(a,j), p, (a)->icd.sz); }; \
|
|
||||||
(a)->i++; \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utarray_inserta(a,w,j) do { \
|
|
||||||
if (utarray_len(w) == 0) break; \
|
|
||||||
if ((j) > (a)->i) utarray_resize(a,j); \
|
|
||||||
utarray_reserve(a,utarray_len(w)); \
|
|
||||||
if ((j) < (a)->i) { \
|
|
||||||
memmove(_utarray_eltptr(a,(j)+utarray_len(w)), \
|
|
||||||
_utarray_eltptr(a,j), \
|
|
||||||
((a)->i - (j))*((a)->icd.sz)); \
|
|
||||||
} \
|
|
||||||
if ((a)->icd.copy) { \
|
|
||||||
unsigned _ut_i; \
|
|
||||||
for(_ut_i=0;_ut_i<(w)->i;_ut_i++) { \
|
|
||||||
(a)->icd.copy(_utarray_eltptr(a, (j) + _ut_i), _utarray_eltptr(w, _ut_i)); \
|
|
||||||
} \
|
|
||||||
} else { \
|
|
||||||
memcpy(_utarray_eltptr(a,j), _utarray_eltptr(w,0), \
|
|
||||||
utarray_len(w)*((a)->icd.sz)); \
|
|
||||||
} \
|
|
||||||
(a)->i += utarray_len(w); \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utarray_resize(dst,num) do { \
|
|
||||||
unsigned _ut_i; \
|
|
||||||
if ((dst)->i > (unsigned)(num)) { \
|
|
||||||
if ((dst)->icd.dtor) { \
|
|
||||||
for (_ut_i = (num); _ut_i < (dst)->i; ++_ut_i) { \
|
|
||||||
(dst)->icd.dtor(_utarray_eltptr(dst, _ut_i)); \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
} else if ((dst)->i < (unsigned)(num)) { \
|
|
||||||
utarray_reserve(dst, (num) - (dst)->i); \
|
|
||||||
if ((dst)->icd.init) { \
|
|
||||||
for (_ut_i = (dst)->i; _ut_i < (unsigned)(num); ++_ut_i) { \
|
|
||||||
(dst)->icd.init(_utarray_eltptr(dst, _ut_i)); \
|
|
||||||
} \
|
|
||||||
} else { \
|
|
||||||
memset(_utarray_eltptr(dst, (dst)->i), 0, (dst)->icd.sz*((num) - (dst)->i)); \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
(dst)->i = (num); \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utarray_concat(dst,src) do { \
|
|
||||||
utarray_inserta(dst, src, utarray_len(dst)); \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utarray_erase(a,pos,len) do { \
|
|
||||||
if ((a)->icd.dtor) { \
|
|
||||||
unsigned _ut_i; \
|
|
||||||
for (_ut_i = 0; _ut_i < (len); _ut_i++) { \
|
|
||||||
(a)->icd.dtor(utarray_eltptr(a, (pos) + _ut_i)); \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
if ((a)->i > ((pos) + (len))) { \
|
|
||||||
memmove(_utarray_eltptr(a, pos), _utarray_eltptr(a, (pos) + (len)), \
|
|
||||||
((a)->i - ((pos) + (len))) * (a)->icd.sz); \
|
|
||||||
} \
|
|
||||||
(a)->i -= (len); \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utarray_renew(a,u) do { \
|
|
||||||
if (a) utarray_clear(a); \
|
|
||||||
else utarray_new(a, u); \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utarray_clear(a) do { \
|
|
||||||
if ((a)->i > 0) { \
|
|
||||||
if ((a)->icd.dtor) { \
|
|
||||||
unsigned _ut_i; \
|
|
||||||
for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \
|
|
||||||
(a)->icd.dtor(_utarray_eltptr(a, _ut_i)); \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
(a)->i = 0; \
|
|
||||||
} \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utarray_sort(a,cmp) do { \
|
|
||||||
qsort((a)->d, (a)->i, (a)->icd.sz, cmp); \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utarray_find(a,v,cmp) bsearch((v),(a)->d,(a)->i,(a)->icd.sz,cmp)
|
|
||||||
|
|
||||||
#define utarray_front(a) (((a)->i) ? (_utarray_eltptr(a,0)) : NULL)
|
|
||||||
#define utarray_next(a,e) (((e)==NULL) ? utarray_front(a) : (((a)->i != utarray_eltidx(a,e)+1) ? _utarray_eltptr(a,utarray_eltidx(a,e)+1) : NULL))
|
|
||||||
#define utarray_prev(a,e) (((e)==NULL) ? utarray_back(a) : ((utarray_eltidx(a,e) != 0) ? _utarray_eltptr(a,utarray_eltidx(a,e)-1) : NULL))
|
|
||||||
#define utarray_back(a) (((a)->i) ? (_utarray_eltptr(a,(a)->i-1)) : NULL)
|
|
||||||
#define utarray_eltidx(a,e) (((char*)(e) - (a)->d) / (a)->icd.sz)
|
|
||||||
|
|
||||||
/* last we pre-define a few icd for common utarrays of ints and strings */
|
|
||||||
static void utarray_str_cpy(void *dst, const void *src) {
|
|
||||||
char *const *srcc = (char *const *)src;
|
|
||||||
char **dstc = (char**)dst;
|
|
||||||
if (*srcc == NULL) {
|
|
||||||
*dstc = NULL;
|
|
||||||
} else {
|
|
||||||
*dstc = (char*)malloc(strlen(*srcc) + 1);
|
|
||||||
if (*dstc == NULL) {
|
|
||||||
utarray_oom();
|
|
||||||
} else {
|
|
||||||
strcpy(*dstc, *srcc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static void utarray_str_dtor(void *elt) {
|
|
||||||
char **eltc = (char**)elt;
|
|
||||||
if (*eltc != NULL) free(*eltc);
|
|
||||||
}
|
|
||||||
static const UT_icd ut_str_icd UTARRAY_UNUSED = {sizeof(char*),NULL,utarray_str_cpy,utarray_str_dtor};
|
|
||||||
static const UT_icd ut_int_icd UTARRAY_UNUSED = {sizeof(int),NULL,NULL,NULL};
|
|
||||||
static const UT_icd ut_ptr_icd UTARRAY_UNUSED = {sizeof(void*),NULL,NULL,NULL};
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* UTARRAY_H */
|
|
||||||
1140
dependencies/uthash/src/uthash.h
vendored
1140
dependencies/uthash/src/uthash.h
vendored
File diff suppressed because it is too large
Load Diff
1076
dependencies/uthash/src/utlist.h
vendored
1076
dependencies/uthash/src/utlist.h
vendored
File diff suppressed because it is too large
Load Diff
108
dependencies/uthash/src/utringbuffer.h
vendored
108
dependencies/uthash/src/utringbuffer.h
vendored
@@ -1,108 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2015-2022, Troy D. Hanson https://troydhanson.github.io/uthash/
|
|
||||||
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.
|
|
||||||
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* a ring-buffer implementation using macros
|
|
||||||
*/
|
|
||||||
#ifndef UTRINGBUFFER_H
|
|
||||||
#define UTRINGBUFFER_H
|
|
||||||
|
|
||||||
#define UTRINGBUFFER_VERSION 2.3.0
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "utarray.h" // for "UT_icd"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
unsigned i; /* index of next available slot; wraps at n */
|
|
||||||
unsigned n; /* capacity */
|
|
||||||
unsigned char f; /* full */
|
|
||||||
UT_icd icd; /* initializer, copy and destructor functions */
|
|
||||||
char *d; /* n slots of size icd->sz */
|
|
||||||
} UT_ringbuffer;
|
|
||||||
|
|
||||||
#define utringbuffer_init(a, _n, _icd) do { \
|
|
||||||
memset(a, 0, sizeof(UT_ringbuffer)); \
|
|
||||||
(a)->icd = *(_icd); \
|
|
||||||
(a)->n = (_n); \
|
|
||||||
if ((a)->n) { (a)->d = (char*)malloc((a)->n * (_icd)->sz); } \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utringbuffer_clear(a) do { \
|
|
||||||
if ((a)->icd.dtor) { \
|
|
||||||
if ((a)->f) { \
|
|
||||||
unsigned _ut_i; \
|
|
||||||
for (_ut_i = 0; _ut_i < (a)->n; ++_ut_i) { \
|
|
||||||
(a)->icd.dtor(utringbuffer_eltptr(a, _ut_i)); \
|
|
||||||
} \
|
|
||||||
} else { \
|
|
||||||
unsigned _ut_i; \
|
|
||||||
for (_ut_i = 0; _ut_i < (a)->i; ++_ut_i) { \
|
|
||||||
(a)->icd.dtor(utringbuffer_eltptr(a, _ut_i)); \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
(a)->i = 0; \
|
|
||||||
(a)->f = 0; \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utringbuffer_done(a) do { \
|
|
||||||
utringbuffer_clear(a); \
|
|
||||||
free((a)->d); (a)->d = NULL; \
|
|
||||||
(a)->n = 0; \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utringbuffer_new(a,n,_icd) do { \
|
|
||||||
a = (UT_ringbuffer*)malloc(sizeof(UT_ringbuffer)); \
|
|
||||||
utringbuffer_init(a, n, _icd); \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utringbuffer_free(a) do { \
|
|
||||||
utringbuffer_done(a); \
|
|
||||||
free(a); \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utringbuffer_push_back(a,p) do { \
|
|
||||||
if ((a)->icd.dtor && (a)->f) { (a)->icd.dtor(_utringbuffer_internalptr(a,(a)->i)); } \
|
|
||||||
if ((a)->icd.copy) { (a)->icd.copy( _utringbuffer_internalptr(a,(a)->i), p); } \
|
|
||||||
else { memcpy(_utringbuffer_internalptr(a,(a)->i), p, (a)->icd.sz); }; \
|
|
||||||
if (++(a)->i == (a)->n) { (a)->i = 0; (a)->f = 1; } \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utringbuffer_len(a) ((a)->f ? (a)->n : (a)->i)
|
|
||||||
#define utringbuffer_empty(a) ((a)->i == 0 && !(a)->f)
|
|
||||||
#define utringbuffer_full(a) ((a)->f != 0)
|
|
||||||
|
|
||||||
#define _utringbuffer_real_idx(a,j) ((a)->f ? ((j) + (a)->i) % (a)->n : (j))
|
|
||||||
#define _utringbuffer_internalptr(a,j) ((void*)((a)->d + ((a)->icd.sz * (j))))
|
|
||||||
#define utringbuffer_eltptr(a,j) ((0 <= (j) && (j) < utringbuffer_len(a)) ? _utringbuffer_internalptr(a,_utringbuffer_real_idx(a,j)) : NULL)
|
|
||||||
|
|
||||||
#define _utringbuffer_fake_idx(a,j) ((a)->f ? ((j) + (a)->n - (a)->i) % (a)->n : (j))
|
|
||||||
#define _utringbuffer_internalidx(a,e) (((char*)(e) >= (a)->d) ? (((char*)(e) - (a)->d)/(a)->icd.sz) : -1)
|
|
||||||
#define utringbuffer_eltidx(a,e) _utringbuffer_fake_idx(a, _utringbuffer_internalidx(a,e))
|
|
||||||
|
|
||||||
#define utringbuffer_front(a) utringbuffer_eltptr(a,0)
|
|
||||||
#define utringbuffer_next(a,e) ((e)==NULL ? utringbuffer_front(a) : utringbuffer_eltptr(a, utringbuffer_eltidx(a,e)+1))
|
|
||||||
#define utringbuffer_prev(a,e) ((e)==NULL ? utringbuffer_back(a) : utringbuffer_eltptr(a, utringbuffer_eltidx(a,e)-1))
|
|
||||||
#define utringbuffer_back(a) (utringbuffer_empty(a) ? NULL : utringbuffer_eltptr(a, utringbuffer_len(a) - 1))
|
|
||||||
|
|
||||||
#endif /* UTRINGBUFFER_H */
|
|
||||||
88
dependencies/uthash/src/utstack.h
vendored
88
dependencies/uthash/src/utstack.h
vendored
@@ -1,88 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2018-2022, Troy D. Hanson https://troydhanson.github.io/uthash/
|
|
||||||
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.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
||||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
||||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
|
||||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef UTSTACK_H
|
|
||||||
#define UTSTACK_H
|
|
||||||
|
|
||||||
#define UTSTACK_VERSION 2.3.0
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file contains macros to manipulate a singly-linked list as a stack.
|
|
||||||
*
|
|
||||||
* To use utstack, your structure must have a "next" pointer.
|
|
||||||
*
|
|
||||||
* ----------------.EXAMPLE -------------------------
|
|
||||||
* struct item {
|
|
||||||
* int id;
|
|
||||||
* struct item *next;
|
|
||||||
* };
|
|
||||||
*
|
|
||||||
* struct item *stack = NULL;
|
|
||||||
*
|
|
||||||
* int main() {
|
|
||||||
* int count;
|
|
||||||
* struct item *tmp;
|
|
||||||
* struct item *item = malloc(sizeof *item);
|
|
||||||
* item->id = 42;
|
|
||||||
* STACK_COUNT(stack, tmp, count); assert(count == 0);
|
|
||||||
* STACK_PUSH(stack, item);
|
|
||||||
* STACK_COUNT(stack, tmp, count); assert(count == 1);
|
|
||||||
* STACK_POP(stack, item);
|
|
||||||
* free(item);
|
|
||||||
* STACK_COUNT(stack, tmp, count); assert(count == 0);
|
|
||||||
* }
|
|
||||||
* --------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define STACK_TOP(head) (head)
|
|
||||||
|
|
||||||
#define STACK_EMPTY(head) (!(head))
|
|
||||||
|
|
||||||
#define STACK_PUSH(head,add) \
|
|
||||||
STACK_PUSH2(head,add,next)
|
|
||||||
|
|
||||||
#define STACK_PUSH2(head,add,next) \
|
|
||||||
do { \
|
|
||||||
(add)->next = (head); \
|
|
||||||
(head) = (add); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define STACK_POP(head,result) \
|
|
||||||
STACK_POP2(head,result,next)
|
|
||||||
|
|
||||||
#define STACK_POP2(head,result,next) \
|
|
||||||
do { \
|
|
||||||
(result) = (head); \
|
|
||||||
(head) = (head)->next; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define STACK_COUNT(head,el,counter) \
|
|
||||||
STACK_COUNT2(head,el,counter,next) \
|
|
||||||
|
|
||||||
#define STACK_COUNT2(head,el,counter,next) \
|
|
||||||
do { \
|
|
||||||
(counter) = 0; \
|
|
||||||
for ((el) = (head); el; (el) = (el)->next) { ++(counter); } \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#endif /* UTSTACK_H */
|
|
||||||
407
dependencies/uthash/src/utstring.h
vendored
407
dependencies/uthash/src/utstring.h
vendored
@@ -1,407 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2008-2022, Troy D. Hanson https://troydhanson.github.io/uthash/
|
|
||||||
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.
|
|
||||||
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* a dynamic string implementation using macros
|
|
||||||
*/
|
|
||||||
#ifndef UTSTRING_H
|
|
||||||
#define UTSTRING_H
|
|
||||||
|
|
||||||
#define UTSTRING_VERSION 2.3.0
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
|
|
||||||
#ifdef __GNUC__
|
|
||||||
#define UTSTRING_UNUSED __attribute__((__unused__))
|
|
||||||
#else
|
|
||||||
#define UTSTRING_UNUSED
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef oom
|
|
||||||
#error "The name of macro 'oom' has been changed to 'utstring_oom'. Please update your code."
|
|
||||||
#define utstring_oom() oom()
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef utstring_oom
|
|
||||||
#define utstring_oom() exit(-1)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char *d; /* pointer to allocated buffer */
|
|
||||||
size_t n; /* allocated capacity */
|
|
||||||
size_t i; /* index of first unused byte */
|
|
||||||
} UT_string;
|
|
||||||
|
|
||||||
#define utstring_reserve(s,amt) \
|
|
||||||
do { \
|
|
||||||
if (((s)->n - (s)->i) < (size_t)(amt)) { \
|
|
||||||
char *utstring_tmp = (char*)realloc( \
|
|
||||||
(s)->d, (s)->n + (amt)); \
|
|
||||||
if (!utstring_tmp) { \
|
|
||||||
utstring_oom(); \
|
|
||||||
} \
|
|
||||||
(s)->d = utstring_tmp; \
|
|
||||||
(s)->n += (amt); \
|
|
||||||
} \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utstring_init(s) \
|
|
||||||
do { \
|
|
||||||
(s)->n = 0; (s)->i = 0; (s)->d = NULL; \
|
|
||||||
utstring_reserve(s,100); \
|
|
||||||
(s)->d[0] = '\0'; \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utstring_done(s) \
|
|
||||||
do { \
|
|
||||||
if ((s)->d != NULL) free((s)->d); \
|
|
||||||
(s)->n = 0; \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utstring_free(s) \
|
|
||||||
do { \
|
|
||||||
utstring_done(s); \
|
|
||||||
free(s); \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utstring_new(s) \
|
|
||||||
do { \
|
|
||||||
(s) = (UT_string*)malloc(sizeof(UT_string)); \
|
|
||||||
if (!(s)) { \
|
|
||||||
utstring_oom(); \
|
|
||||||
} \
|
|
||||||
utstring_init(s); \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utstring_renew(s) \
|
|
||||||
do { \
|
|
||||||
if (s) { \
|
|
||||||
utstring_clear(s); \
|
|
||||||
} else { \
|
|
||||||
utstring_new(s); \
|
|
||||||
} \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utstring_clear(s) \
|
|
||||||
do { \
|
|
||||||
(s)->i = 0; \
|
|
||||||
(s)->d[0] = '\0'; \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utstring_bincpy(s,b,l) \
|
|
||||||
do { \
|
|
||||||
utstring_reserve((s),(l)+1); \
|
|
||||||
if (l) memcpy(&(s)->d[(s)->i], b, l); \
|
|
||||||
(s)->i += (l); \
|
|
||||||
(s)->d[(s)->i]='\0'; \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utstring_concat(dst,src) \
|
|
||||||
do { \
|
|
||||||
utstring_reserve((dst),((src)->i)+1); \
|
|
||||||
if ((src)->i) memcpy(&(dst)->d[(dst)->i], (src)->d, (src)->i); \
|
|
||||||
(dst)->i += (src)->i; \
|
|
||||||
(dst)->d[(dst)->i]='\0'; \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define utstring_len(s) ((s)->i)
|
|
||||||
|
|
||||||
#define utstring_body(s) ((s)->d)
|
|
||||||
|
|
||||||
UTSTRING_UNUSED static void utstring_printf_va(UT_string *s, const char *fmt, va_list ap) {
|
|
||||||
int n;
|
|
||||||
va_list cp;
|
|
||||||
for (;;) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
cp = ap;
|
|
||||||
#else
|
|
||||||
va_copy(cp, ap);
|
|
||||||
#endif
|
|
||||||
n = vsnprintf (&s->d[s->i], s->n-s->i, fmt, cp);
|
|
||||||
va_end(cp);
|
|
||||||
|
|
||||||
if ((n > -1) && ((size_t) n < (s->n-s->i))) {
|
|
||||||
s->i += n;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Else try again with more space. */
|
|
||||||
if (n > -1) utstring_reserve(s,n+1); /* exact */
|
|
||||||
else utstring_reserve(s,(s->n)*2); /* 2x */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#ifdef __GNUC__
|
|
||||||
/* support printf format checking (2=the format string, 3=start of varargs) */
|
|
||||||
static void utstring_printf(UT_string *s, const char *fmt, ...)
|
|
||||||
__attribute__ (( format( printf, 2, 3) ));
|
|
||||||
#endif
|
|
||||||
UTSTRING_UNUSED static void utstring_printf(UT_string *s, const char *fmt, ...) {
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap,fmt);
|
|
||||||
utstring_printf_va(s,fmt,ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
* begin substring search functions *
|
|
||||||
******************************************************************************/
|
|
||||||
/* Build KMP table from left to right. */
|
|
||||||
UTSTRING_UNUSED static void _utstring_BuildTable(
|
|
||||||
const char *P_Needle,
|
|
||||||
size_t P_NeedleLen,
|
|
||||||
long *P_KMP_Table)
|
|
||||||
{
|
|
||||||
long i, j;
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
j = i - 1;
|
|
||||||
P_KMP_Table[i] = j;
|
|
||||||
while (i < (long) P_NeedleLen)
|
|
||||||
{
|
|
||||||
while ( (j > -1) && (P_Needle[i] != P_Needle[j]) )
|
|
||||||
{
|
|
||||||
j = P_KMP_Table[j];
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
j++;
|
|
||||||
if (i < (long) P_NeedleLen)
|
|
||||||
{
|
|
||||||
if (P_Needle[i] == P_Needle[j])
|
|
||||||
{
|
|
||||||
P_KMP_Table[i] = P_KMP_Table[j];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
P_KMP_Table[i] = j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
P_KMP_Table[i] = j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Build KMP table from right to left. */
|
|
||||||
UTSTRING_UNUSED static void _utstring_BuildTableR(
|
|
||||||
const char *P_Needle,
|
|
||||||
size_t P_NeedleLen,
|
|
||||||
long *P_KMP_Table)
|
|
||||||
{
|
|
||||||
long i, j;
|
|
||||||
|
|
||||||
i = P_NeedleLen - 1;
|
|
||||||
j = i + 1;
|
|
||||||
P_KMP_Table[i + 1] = j;
|
|
||||||
while (i >= 0)
|
|
||||||
{
|
|
||||||
while ( (j < (long) P_NeedleLen) && (P_Needle[i] != P_Needle[j]) )
|
|
||||||
{
|
|
||||||
j = P_KMP_Table[j + 1];
|
|
||||||
}
|
|
||||||
i--;
|
|
||||||
j--;
|
|
||||||
if (i >= 0)
|
|
||||||
{
|
|
||||||
if (P_Needle[i] == P_Needle[j])
|
|
||||||
{
|
|
||||||
P_KMP_Table[i + 1] = P_KMP_Table[j + 1];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
P_KMP_Table[i + 1] = j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
P_KMP_Table[i + 1] = j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Search data from left to right. ( Multiple search mode. ) */
|
|
||||||
UTSTRING_UNUSED static long _utstring_find(
|
|
||||||
const char *P_Haystack,
|
|
||||||
size_t P_HaystackLen,
|
|
||||||
const char *P_Needle,
|
|
||||||
size_t P_NeedleLen,
|
|
||||||
long *P_KMP_Table)
|
|
||||||
{
|
|
||||||
long i, j;
|
|
||||||
long V_FindPosition = -1;
|
|
||||||
|
|
||||||
/* Search from left to right. */
|
|
||||||
i = j = 0;
|
|
||||||
while ( (j < (int)P_HaystackLen) && (((P_HaystackLen - j) + i) >= P_NeedleLen) )
|
|
||||||
{
|
|
||||||
while ( (i > -1) && (P_Needle[i] != P_Haystack[j]) )
|
|
||||||
{
|
|
||||||
i = P_KMP_Table[i];
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
j++;
|
|
||||||
if (i >= (int)P_NeedleLen)
|
|
||||||
{
|
|
||||||
/* Found. */
|
|
||||||
V_FindPosition = j - i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return V_FindPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Search data from right to left. ( Multiple search mode. ) */
|
|
||||||
UTSTRING_UNUSED static long _utstring_findR(
|
|
||||||
const char *P_Haystack,
|
|
||||||
size_t P_HaystackLen,
|
|
||||||
const char *P_Needle,
|
|
||||||
size_t P_NeedleLen,
|
|
||||||
long *P_KMP_Table)
|
|
||||||
{
|
|
||||||
long i, j;
|
|
||||||
long V_FindPosition = -1;
|
|
||||||
|
|
||||||
/* Search from right to left. */
|
|
||||||
j = (P_HaystackLen - 1);
|
|
||||||
i = (P_NeedleLen - 1);
|
|
||||||
while ( (j >= 0) && (j >= i) )
|
|
||||||
{
|
|
||||||
while ( (i < (int)P_NeedleLen) && (P_Needle[i] != P_Haystack[j]) )
|
|
||||||
{
|
|
||||||
i = P_KMP_Table[i + 1];
|
|
||||||
}
|
|
||||||
i--;
|
|
||||||
j--;
|
|
||||||
if (i < 0)
|
|
||||||
{
|
|
||||||
/* Found. */
|
|
||||||
V_FindPosition = j + 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return V_FindPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Search data from left to right. ( One time search mode. ) */
|
|
||||||
UTSTRING_UNUSED static long utstring_find(
|
|
||||||
UT_string *s,
|
|
||||||
long P_StartPosition, /* Start from 0. -1 means last position. */
|
|
||||||
const char *P_Needle,
|
|
||||||
size_t P_NeedleLen)
|
|
||||||
{
|
|
||||||
long V_StartPosition;
|
|
||||||
long V_HaystackLen;
|
|
||||||
long *V_KMP_Table;
|
|
||||||
long V_FindPosition = -1;
|
|
||||||
|
|
||||||
if (P_StartPosition < 0)
|
|
||||||
{
|
|
||||||
V_StartPosition = s->i + P_StartPosition;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
V_StartPosition = P_StartPosition;
|
|
||||||
}
|
|
||||||
V_HaystackLen = s->i - V_StartPosition;
|
|
||||||
if ( (V_HaystackLen >= (long) P_NeedleLen) && (P_NeedleLen > 0) )
|
|
||||||
{
|
|
||||||
V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1));
|
|
||||||
if (V_KMP_Table != NULL)
|
|
||||||
{
|
|
||||||
_utstring_BuildTable(P_Needle, P_NeedleLen, V_KMP_Table);
|
|
||||||
|
|
||||||
V_FindPosition = _utstring_find(s->d + V_StartPosition,
|
|
||||||
V_HaystackLen,
|
|
||||||
P_Needle,
|
|
||||||
P_NeedleLen,
|
|
||||||
V_KMP_Table);
|
|
||||||
if (V_FindPosition >= 0)
|
|
||||||
{
|
|
||||||
V_FindPosition += V_StartPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(V_KMP_Table);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return V_FindPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Search data from right to left. ( One time search mode. ) */
|
|
||||||
UTSTRING_UNUSED static long utstring_findR(
|
|
||||||
UT_string *s,
|
|
||||||
long P_StartPosition, /* Start from 0. -1 means last position. */
|
|
||||||
const char *P_Needle,
|
|
||||||
size_t P_NeedleLen)
|
|
||||||
{
|
|
||||||
long V_StartPosition;
|
|
||||||
long V_HaystackLen;
|
|
||||||
long *V_KMP_Table;
|
|
||||||
long V_FindPosition = -1;
|
|
||||||
|
|
||||||
if (P_StartPosition < 0)
|
|
||||||
{
|
|
||||||
V_StartPosition = s->i + P_StartPosition;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
V_StartPosition = P_StartPosition;
|
|
||||||
}
|
|
||||||
V_HaystackLen = V_StartPosition + 1;
|
|
||||||
if ( (V_HaystackLen >= (long) P_NeedleLen) && (P_NeedleLen > 0) )
|
|
||||||
{
|
|
||||||
V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1));
|
|
||||||
if (V_KMP_Table != NULL)
|
|
||||||
{
|
|
||||||
_utstring_BuildTableR(P_Needle, P_NeedleLen, V_KMP_Table);
|
|
||||||
|
|
||||||
V_FindPosition = _utstring_findR(s->d,
|
|
||||||
V_HaystackLen,
|
|
||||||
P_Needle,
|
|
||||||
P_NeedleLen,
|
|
||||||
V_KMP_Table);
|
|
||||||
|
|
||||||
free(V_KMP_Table);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return V_FindPosition;
|
|
||||||
}
|
|
||||||
/*******************************************************************************
|
|
||||||
* end substring search functions *
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
#endif /* UTSTRING_H */
|
|
||||||
120
dependencies/uthash/tests/Makefile
vendored
120
dependencies/uthash/tests/Makefile
vendored
@@ -1,120 +0,0 @@
|
|||||||
#CC=clang
|
|
||||||
HASHDIR = ../src
|
|
||||||
UTILS = emit_keys
|
|
||||||
PROGS = test1 test2 test3 test4 test5 test6 test7 test8 test9 \
|
|
||||||
test10 test11 test12 test13 test14 test15 test16 test17 \
|
|
||||||
test18 test19 test20 test21 test22 test23 test24 test25 \
|
|
||||||
test26 test27 test28 test29 test30 test31 test32 test33 \
|
|
||||||
test34 test35 test36 test37 test38 test39 test40 test41 \
|
|
||||||
test42 test43 test44 test45 test46 test47 test48 test49 \
|
|
||||||
test50 test51 test52 test53 test54 test55 test56 test57 \
|
|
||||||
test58 test59 test60 test61 test62 test63 test64 test65 \
|
|
||||||
test66 test67 test68 test69 test70 test71 test72 test73 \
|
|
||||||
test74 test75 test76 test77 test78 test79 test80 test81 \
|
|
||||||
test82 test83 test84 test85 test86 test87 test88 test89 \
|
|
||||||
test90 test91 test92 test93 test94 test95 test96 test97
|
|
||||||
CFLAGS += -I$(HASHDIR)
|
|
||||||
#CFLAGS += -DHASH_BLOOM=16
|
|
||||||
#CFLAGS += -O2
|
|
||||||
CFLAGS += -g
|
|
||||||
#CFLAGS += -Wstrict-aliasing=2
|
|
||||||
CFLAGS += -Wall
|
|
||||||
#CFLAGS += -Wextra
|
|
||||||
#CFLAGS += -std=c89
|
|
||||||
CFLAGS += ${EXTRA_CFLAGS}
|
|
||||||
|
|
||||||
ifeq ($(HASH_DEBUG),1)
|
|
||||||
CFLAGS += -DHASH_DEBUG=1
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(HASH_PEDANTIC),1)
|
|
||||||
CFLAGS += -pedantic
|
|
||||||
endif
|
|
||||||
|
|
||||||
TEST_TARGET=run_tests
|
|
||||||
TESTS=./do_tests
|
|
||||||
|
|
||||||
# detect Cygwin
|
|
||||||
ifneq ($(strip $(shell $(CC) -v 2>&1 |grep "cygwin")),)
|
|
||||||
TESTS=./do_tests.cygwin
|
|
||||||
endif
|
|
||||||
|
|
||||||
# detect MinGW
|
|
||||||
ifneq ($(strip $(shell $(CC) -v 2>&1 |grep "mingw")),)
|
|
||||||
TEST_TARGET=run_tests_mingw
|
|
||||||
TESTS=./do_tests.mingw
|
|
||||||
endif
|
|
||||||
|
|
||||||
#detect Linux (platform specific utilities)
|
|
||||||
ifneq ($(strip $(shell $(CC) -v 2>&1 |grep "linux")),)
|
|
||||||
PLAT_UTILS = hashscan sleep_test
|
|
||||||
endif
|
|
||||||
|
|
||||||
#detect FreeBSD (platform specific utilities)
|
|
||||||
ifeq ($(strip $(shell uname -s)), FreeBSD)
|
|
||||||
ifeq ($(shell if [ `sysctl -n kern.osreldate` -ge 0801000 ]; then echo "ok"; fi), ok)
|
|
||||||
PLAT_UTILS = hashscan sleep_test
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
all: $(PROGS) $(UTILS) $(PLAT_UTILS) keystat $(TEST_TARGET)
|
|
||||||
|
|
||||||
tests_only: $(PROGS) $(TEST_TARGET)
|
|
||||||
|
|
||||||
GITIGN = .gitignore
|
|
||||||
MKGITIGN = [ -f "$(GITIGN)" ] || echo "$(GITIGN)" > $(GITIGN); grep -q '^\$@$$' $(GITIGN) || echo "$@" >> $(GITIGN)
|
|
||||||
|
|
||||||
debug:
|
|
||||||
$(MAKE) all HASH_DEBUG=1
|
|
||||||
|
|
||||||
pedantic:
|
|
||||||
$(MAKE) all HASH_PEDANTIC=1
|
|
||||||
|
|
||||||
cplusplus:
|
|
||||||
CC="$(CXX) -x c++" $(MAKE) all
|
|
||||||
|
|
||||||
thorough:
|
|
||||||
$(MAKE) clean && $(MAKE) all EXTRA_CFLAGS='-pedantic'
|
|
||||||
$(MAKE) clean && $(MAKE) all EXTRA_CFLAGS='-pedantic -DHASH_BLOOM=16'
|
|
||||||
$(MAKE) clean && $(MAKE) tests_only EXTRA_CFLAGS='-pedantic -DHASH_BLOOM=16 -DHASH_DEBUG -DNO_DECLTYPE'
|
|
||||||
$(MAKE) clean && CC="$(CXX) -x c++" $(MAKE) all EXTRA_CFLAGS='-pedantic'
|
|
||||||
$(MAKE) clean && CC="$(CXX) -x c++" $(MAKE) all EXTRA_CFLAGS='-pedantic -DHASH_BLOOM=16'
|
|
||||||
$(MAKE) clean && CC="$(CXX) -x c++" $(MAKE) tests_only EXTRA_CFLAGS='-pedantic -DHASH_BLOOM=16 -DHASH_DEBUG -DNO_DECLTYPE'
|
|
||||||
|
|
||||||
example: example.c $(HASHDIR)/uthash.h
|
|
||||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $(@).c
|
|
||||||
|
|
||||||
$(PROGS) $(UTILS) : $(HASHDIR)/uthash.h
|
|
||||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $(@).c
|
|
||||||
@$(MKGITIGN)
|
|
||||||
|
|
||||||
hashscan : $(HASHDIR)/uthash.h
|
|
||||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $(@).c
|
|
||||||
@$(MKGITIGN)
|
|
||||||
|
|
||||||
sleep_test : $(HASHDIR)/uthash.h
|
|
||||||
$(CC) $(CPPFLAGS) $(CFLAGS) -DHASH_BLOOM=16 $(LDFLAGS) -o $@ $(@).c
|
|
||||||
@$(MKGITIGN)
|
|
||||||
|
|
||||||
keystat : $(HASHDIR)/uthash.h
|
|
||||||
$(CC) $(CPPFLAGS) $(CFLAGS) -DHASH_FUNCTION=HASH_BER $(LDFLAGS) -o keystat.BER keystat.c
|
|
||||||
$(CC) $(CPPFLAGS) $(CFLAGS) -DHASH_FUNCTION=HASH_FNV $(LDFLAGS) -o keystat.FNV keystat.c
|
|
||||||
$(CC) $(CPPFLAGS) $(CFLAGS) -DHASH_FUNCTION=HASH_JEN $(LDFLAGS) -o keystat.JEN keystat.c
|
|
||||||
$(CC) $(CPPFLAGS) $(CFLAGS) -DHASH_FUNCTION=HASH_OAT $(LDFLAGS) -o keystat.OAT keystat.c
|
|
||||||
$(CC) $(CPPFLAGS) $(CFLAGS) -DHASH_FUNCTION=HASH_SAX $(LDFLAGS) -o keystat.SAX keystat.c
|
|
||||||
$(CC) $(CPPFLAGS) $(CFLAGS) -DHASH_FUNCTION=HASH_SFH $(LDFLAGS) -o keystat.SFH keystat.c
|
|
||||||
|
|
||||||
run_tests: $(PROGS)
|
|
||||||
perl $(TESTS)
|
|
||||||
|
|
||||||
run_tests_mingw: $(PROGS)
|
|
||||||
/bin/bash do_tests.mingw
|
|
||||||
|
|
||||||
astyle:
|
|
||||||
astyle -n --style=kr --indent-switches --add-brackets *.c
|
|
||||||
|
|
||||||
.PHONY: clean astyle
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f $(UTILS) $(PLAT_UTILS) $(PROGS) test*.out keystat.??? example hashscan sleep_test *.exe $(GITIGN)
|
|
||||||
rm -rf *.dSYM
|
|
||||||
133
dependencies/uthash/tests/README
vendored
133
dependencies/uthash/tests/README
vendored
@@ -1,133 +0,0 @@
|
|||||||
Automated tests for uthash
|
|
||||||
==============================================================================
|
|
||||||
Run "make" in this directory to build the tests and run them.
|
|
||||||
|
|
||||||
test1: make 10-item hash, iterate and print each one
|
|
||||||
test2: make 10-item hash, lookup items with even keys, print
|
|
||||||
test3: make 10-item hash, delete items with even keys, print others
|
|
||||||
test4: 10 structs have dual hash handles, separate keys
|
|
||||||
test5: 10 structs have dual hash handles, lookup evens by alt key
|
|
||||||
test6: test alt malloc macros (and alt key-comparison macro)
|
|
||||||
test7: test alt malloc macros with 1000 structs so bucket expansion occurs
|
|
||||||
test8: test num_items counter in UT_hash_handle
|
|
||||||
test9: test "find" after bucket expansion
|
|
||||||
test10: dual-hash handle test, bucket expansion on one and not the other
|
|
||||||
test11: read dat file of names into hash, sort them and print
|
|
||||||
test12: create hash with string keys, add 10 items, lookup each item
|
|
||||||
test13: make 10-item hash, delete items with even keys, reverse print others
|
|
||||||
test14: read dat file of names into hash, read file again and lookup each one
|
|
||||||
test15: build string-keyed hash of 3 items, lookup one item (c.f. test40.c)
|
|
||||||
test16: hash on aggregate key, iterate, lookup, using generalized macros
|
|
||||||
test17: sort, add more items, sort again
|
|
||||||
test18: test pathological HASH_DEL(a,a) scenario (single head,deletee variable)
|
|
||||||
test19: sort two hash tables with shared elements using HASH_SRT
|
|
||||||
test20: test a 5-byte "binary" key
|
|
||||||
test21: test a structure key (userguide)
|
|
||||||
test22: test multi-field key using flexible array member (userguide utf32)
|
|
||||||
test23: test whether delete in iteration works
|
|
||||||
test24: make 10-item hash and confirm item count (HASH_COUNT)
|
|
||||||
test25: CDL / DL / LL tests
|
|
||||||
test26: test the linked list sort macros in utlist.h
|
|
||||||
test27: LL_APPEND, SORT
|
|
||||||
test28: CDL / DL / LL tests
|
|
||||||
test29: DL_APPEND, SORT
|
|
||||||
test30: CDL_PREPEND, SORT
|
|
||||||
test31: CDL_PREPEND, SORT
|
|
||||||
test32: DL_PREPEND
|
|
||||||
test33: LL_PREPEND
|
|
||||||
test34: CDL_PREPEND
|
|
||||||
test35: CDL_PREPEND
|
|
||||||
test36: HASH_SELECT
|
|
||||||
test37: HASH_CLEAR
|
|
||||||
test38: find-or-add test on integer keys in short loop
|
|
||||||
test39: HASH_ADD_KEYPTR then HASH_FIND using array element as key pointer
|
|
||||||
test40: HASH_ADD_KEYPTR on string keys; pointer equivalent to test15.c
|
|
||||||
test41: test LL_FOREACH_SAFE,DL_FOREACH_SAFE,CDL_FOREACH_SAFE
|
|
||||||
test42: test LL_SEARCH, LL_SEARCH_SCALAR, and DL and CDL counterparts
|
|
||||||
test43: test utarray with intpair objects
|
|
||||||
test44: test utarray with int objects
|
|
||||||
test45: test utarray with int objects
|
|
||||||
test46: test utarray with char* objects
|
|
||||||
test47: test utstring
|
|
||||||
test48: test utarray of int
|
|
||||||
test49: test utarray of str
|
|
||||||
test50: test utarray of long
|
|
||||||
test51: test utarray of intpair
|
|
||||||
test52: test utarray of intchar
|
|
||||||
test53: test utstring
|
|
||||||
test54: test utstring
|
|
||||||
test55: test utstring
|
|
||||||
test56: test uthash, utlist and utstring together for #define conflicts etc
|
|
||||||
test57: test uthash HASH_ADD_PTR and HASH_FIND_PTR
|
|
||||||
test58: test HASH_ITER macro
|
|
||||||
test59: sample of multi-level hash
|
|
||||||
test60: sample of multi-level hash that also does HASH_DEL and free
|
|
||||||
test61: test utarray_find
|
|
||||||
test62: test macros used in safe unaligned reads on non-Intel type platforms
|
|
||||||
test63: LL_CONCAT test
|
|
||||||
test64: DL_CONCAT test
|
|
||||||
test65: LRU cache example courtesy of jehiah.cz with modifications
|
|
||||||
test66: test example where output variable to HASH_FIND needs extra parens
|
|
||||||
test67: test utarray_prev
|
|
||||||
test68: test DL_REPLACE_ELEM (Zoltán Lajos Kis)
|
|
||||||
test69: test DL_PREPEND_ELEM (Zoltán Lajos Kis)
|
|
||||||
test70: test LL_REPLACE_ELEM (Zoltán Lajos Kis)
|
|
||||||
test71: test LL_PREPEND_ELEM (Zoltán Lajos Kis)
|
|
||||||
test72: test CDL_REPLACE_ELEM (Zoltán Lajos Kis)
|
|
||||||
test73: test CDL_PREPEND_ELEM (Zoltán Lajos Kis)
|
|
||||||
test74: test utstring with utstring_find (Joe Wei)
|
|
||||||
test75: test utstring with utstring_findR (Joe Wei)
|
|
||||||
test76: test utstring with _utstring_find (Joe Wei)
|
|
||||||
test77: test utstring with _utstring_findR (Joe Wei)
|
|
||||||
test78: test utlist "2" family with flexible Prev/Next naming eg. DL_DELETE2
|
|
||||||
test79: test HASH_REPLACE
|
|
||||||
test80: test utarray_insert past end of array
|
|
||||||
test81: test utarray_insert past end of array
|
|
||||||
test82: test utarray_inserta past end of array
|
|
||||||
test83: test HASH_REPLACE_STR with char[] key
|
|
||||||
test84: test HASH_REPLACE_STR with char* key
|
|
||||||
test85: test HASH_OVERHEAD on null and non null hash
|
|
||||||
test86: test *_APPEND_ELEM / *_PREPEND_ELEM (Thilo Schulz)
|
|
||||||
test87: test HASH_ADD_INORDER() macro (Thilo Schulz)
|
|
||||||
test88: test alt key-comparison and strlen macros
|
|
||||||
test89: test code from the tinydtls project
|
|
||||||
test90: regression-test HASH_ADD_KEYPTR_INORDER (IronBug)
|
|
||||||
test91: test LL_INSERT_INORDER etc.
|
|
||||||
test92: HASH_NONFATAL_OOM
|
|
||||||
test93: alt_fatal
|
|
||||||
test94: utlist with fields named other than 'next' and 'prev'
|
|
||||||
test95: utstack
|
|
||||||
test96: HASH_FUNCTION + HASH_KEYCMP
|
|
||||||
test97: deleting a const-qualified node from a hash
|
|
||||||
|
|
||||||
Other Make targets
|
|
||||||
================================================================================
|
|
||||||
pedantic: makes the tests with extra CFLAGS for pedantic compiling
|
|
||||||
cplusplus: compiles all the C tests using the C++ compiler to test compatibility
|
|
||||||
debug: makes the tests with debugging symbols and no optimization
|
|
||||||
example: builds the 'example' program from the user guide
|
|
||||||
================================================================================
|
|
||||||
|
|
||||||
Testing a specific hash function
|
|
||||||
--------------------------------
|
|
||||||
Set EXTRA_CFLAGS with this Makefile to use a specific hash function:
|
|
||||||
EXTRA_CFLAGS=-DHASH_FUNCTION=HASH_BER make
|
|
||||||
|
|
||||||
Other files
|
|
||||||
================================================================================
|
|
||||||
keystats: key statistics analyzer. See the uthash User Guide.
|
|
||||||
emit_keys: reads a data file of unique strings, emits as keys w/HASH_EMIT_KEYS=1
|
|
||||||
all_funcs: a script which executes the test suite with every hash function
|
|
||||||
win32tests:builds and runs the test suite under Microsoft Visual Studio
|
|
||||||
|
|
||||||
LINUX/FREEBSD
|
|
||||||
-------------
|
|
||||||
hashscan: tool to examine a running process and get info on its hash tables
|
|
||||||
test_sleep:used as a subject for inspection by hashscan
|
|
||||||
|
|
||||||
Manual performance testing
|
|
||||||
================================================================================
|
|
||||||
# test performance characteristics on keys that are English dictionary words
|
|
||||||
emit_keys /usr/share/dict/words > words.keys
|
|
||||||
./keystats words.keys
|
|
||||||
|
|
||||||
13
dependencies/uthash/tests/all_funcs
vendored
13
dependencies/uthash/tests/all_funcs
vendored
@@ -1,13 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
function proceed {
|
|
||||||
read -p "proceed ? [n] " response
|
|
||||||
if [ "$response" != "y" ]; then exit -1; fi
|
|
||||||
}
|
|
||||||
|
|
||||||
make clean tests_only EXTRA_CFLAGS='-DHASH_FUNCTION=HASH_BER'; proceed
|
|
||||||
make clean tests_only EXTRA_CFLAGS='-DHASH_FUNCTION=HASH_FNV'; proceed
|
|
||||||
make clean tests_only EXTRA_CFLAGS='-DHASH_FUNCTION=HASH_JEN'; proceed
|
|
||||||
make clean tests_only EXTRA_CFLAGS='-DHASH_FUNCTION=HASH_OAT'; proceed
|
|
||||||
make clean tests_only EXTRA_CFLAGS='-DHASH_FUNCTION=HASH_SAX'; proceed
|
|
||||||
make clean tests_only EXTRA_CFLAGS='-DHASH_FUNCTION=HASH_SFH'; proceed
|
|
||||||
82
dependencies/uthash/tests/bloom_perf.c
vendored
82
dependencies/uthash/tests/bloom_perf.c
vendored
@@ -1,82 +0,0 @@
|
|||||||
#include <stdlib.h> /* malloc */
|
|
||||||
#include <sys/time.h> /* gettimeofday */
|
|
||||||
#include <errno.h> /* perror */
|
|
||||||
#include <stdio.h> /* printf */
|
|
||||||
#include "uthash.h"
|
|
||||||
|
|
||||||
#define BUFLEN 20
|
|
||||||
#if 0
|
|
||||||
#undef uthash_expand_fyi
|
|
||||||
#define uthash_expand_fyi(tbl) printf("expanding to %d buckets\n", tbl->num_buckets)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct name_rec {
|
|
||||||
char boy_name[BUFLEN];
|
|
||||||
UT_hash_handle hh;
|
|
||||||
} name_rec;
|
|
||||||
|
|
||||||
int main(int argc,char *argv[])
|
|
||||||
{
|
|
||||||
name_rec *name, *names=NULL;
|
|
||||||
char linebuf[BUFLEN];
|
|
||||||
FILE *file;
|
|
||||||
int i=0,j,nloops=3,loopnum=0,miss;
|
|
||||||
struct timeval tv1,tv2;
|
|
||||||
long elapsed_usec;
|
|
||||||
if (argc > 1) {
|
|
||||||
nloops = atoi(argv[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( (file = fopen( "test14.dat", "r" )) == NULL ) {
|
|
||||||
perror("can't open: ");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (fgets(linebuf,BUFLEN,file) != NULL) {
|
|
||||||
i++;
|
|
||||||
if ( (name = (name_rec*)malloc(sizeof(name_rec))) == NULL) {
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
strcpy(name->boy_name, linebuf);
|
|
||||||
HASH_ADD_STR(names,boy_name,name);
|
|
||||||
}
|
|
||||||
|
|
||||||
again:
|
|
||||||
if (fseek(file,0,SEEK_SET) == -1) {
|
|
||||||
fprintf(stderr,"fseek failed: %s\n", strerror(errno));
|
|
||||||
}
|
|
||||||
j=0;
|
|
||||||
|
|
||||||
if (gettimeofday(&tv1,NULL) == -1) {
|
|
||||||
perror("gettimeofday: ");
|
|
||||||
}
|
|
||||||
while (fgets(linebuf,BUFLEN,file) != NULL) {
|
|
||||||
/* if we do 10 loops, the first has a 0% miss rate,
|
|
||||||
* the second has a 10% miss rate, etc */
|
|
||||||
miss = ((rand()*1.0/RAND_MAX) < (loopnum*1.0/nloops)) ? 1 : 0;
|
|
||||||
/* generate a miss if we want one */
|
|
||||||
if (miss) {
|
|
||||||
linebuf[0]++;
|
|
||||||
if (linebuf[1] != '\0') {
|
|
||||||
linebuf[1]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
HASH_FIND_STR(names,linebuf,name);
|
|
||||||
if (name) {
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (gettimeofday(&tv2,NULL) == -1) {
|
|
||||||
perror("gettimeofday: ");
|
|
||||||
}
|
|
||||||
elapsed_usec = ((tv2.tv_sec - tv1.tv_sec) * 1000000) + (tv2.tv_usec - tv1.tv_usec);
|
|
||||||
printf("lookup on %d of %d (%.2f%%) names succeeded (%.2f usec)\n", j, i,
|
|
||||||
j*100.0/i, (double)(elapsed_usec));
|
|
||||||
if (++loopnum < nloops) {
|
|
||||||
goto again;
|
|
||||||
}
|
|
||||||
fclose(file);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
17
dependencies/uthash/tests/bloom_perf.sh
vendored
17
dependencies/uthash/tests/bloom_perf.sh
vendored
@@ -1,17 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
BITS="16"
|
|
||||||
|
|
||||||
cc -I../src -O3 -Wall -m64 bloom_perf.c -o bloom_perf.none
|
|
||||||
for bits in $BITS
|
|
||||||
do
|
|
||||||
cc -I../src -DHASH_BLOOM=$bits -O3 -Wall -m64 bloom_perf.c -o bloom_perf.$bits
|
|
||||||
done
|
|
||||||
|
|
||||||
for bits in none $BITS
|
|
||||||
do
|
|
||||||
echo
|
|
||||||
echo "using $bits-bit filter:"
|
|
||||||
./bloom_perf.$bits 10
|
|
||||||
done
|
|
||||||
|
|
||||||
21
dependencies/uthash/tests/do_tests
vendored
21
dependencies/uthash/tests/do_tests
vendored
@@ -1,21 +0,0 @@
|
|||||||
#!/usr/bin/perl
|
|
||||||
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
|
|
||||||
my @tests;
|
|
||||||
for (glob "test*[0-9]") {
|
|
||||||
push @tests, $_ if -e "$_.ans";
|
|
||||||
}
|
|
||||||
|
|
||||||
my $num_failed=0;
|
|
||||||
|
|
||||||
for my $test (@tests) {
|
|
||||||
`./$test > $test.out`;
|
|
||||||
`diff $test.out $test.ans`;
|
|
||||||
print "$test failed\n" if $?;
|
|
||||||
$num_failed++ if $?;
|
|
||||||
}
|
|
||||||
|
|
||||||
print scalar @tests . " tests conducted, $num_failed failed.\n";
|
|
||||||
exit $num_failed;
|
|
||||||
22
dependencies/uthash/tests/do_tests.cygwin
vendored
22
dependencies/uthash/tests/do_tests.cygwin
vendored
@@ -1,22 +0,0 @@
|
|||||||
#!/usr/bin/perl
|
|
||||||
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
|
|
||||||
my @tests;
|
|
||||||
for (glob "test*[0-9].exe") {
|
|
||||||
push @tests, "$_" if -e substr($_, 0, - 4).".ans";
|
|
||||||
}
|
|
||||||
|
|
||||||
my $num_failed=0;
|
|
||||||
|
|
||||||
for my $test (@tests) {
|
|
||||||
`./$test > $test.out`;
|
|
||||||
my $ansfile = substr($test, 0, - 4).".ans";
|
|
||||||
`diff $test.out $ansfile`;
|
|
||||||
print "$test failed\n" if $?;
|
|
||||||
$num_failed++ if $?;
|
|
||||||
}
|
|
||||||
|
|
||||||
print scalar @tests . " tests conducted, $num_failed failed.\n";
|
|
||||||
exit $num_failed;
|
|
||||||
20
dependencies/uthash/tests/do_tests.mingw
vendored
20
dependencies/uthash/tests/do_tests.mingw
vendored
@@ -1,20 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
echo "MinGW test script starting"
|
|
||||||
|
|
||||||
for f in test*.exe
|
|
||||||
do
|
|
||||||
t=`echo $f | sed s/.exe//`
|
|
||||||
"./$f" > "$t.out"
|
|
||||||
diff -qb "$t.out" "$t.ans"
|
|
||||||
if [ $? -eq 1 ]
|
|
||||||
then
|
|
||||||
echo "$f failed"
|
|
||||||
else
|
|
||||||
true # can't have empty else
|
|
||||||
#echo "$f passed"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "All tests complete"
|
|
||||||
16
dependencies/uthash/tests/do_tests_win32.cmd
vendored
16
dependencies/uthash/tests/do_tests_win32.cmd
vendored
@@ -1,16 +0,0 @@
|
|||||||
:: this compiles and runs the test suite under Visual Studio 2008
|
|
||||||
::@echo off
|
|
||||||
call "C:\Program Files\Microsoft Visual Studio 9.0\VC\bin\vcvars32.bat" > vc.out
|
|
||||||
::call "C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\vcvars32.bat" > vc.out
|
|
||||||
set "COMPILE=cl.exe /I ..\src /EHsc /nologo"
|
|
||||||
echo compiling...
|
|
||||||
%COMPILE% tdiff.cpp > compile.out
|
|
||||||
::for %%f in (test*.c) do %COMPILE% /Tp %%f >> compile.out
|
|
||||||
for %%f in (test*.c) do %COMPILE% /Tc %%f >> compile.out
|
|
||||||
echo running tests...
|
|
||||||
for %%f in (test*.exe) do %%f > %%~nf.out
|
|
||||||
echo scanning for failures...
|
|
||||||
for %%f in (test*.out) do tdiff %%f %%~nf.ans
|
|
||||||
echo tests completed
|
|
||||||
::for %%f in (test*.out test*.obj test*.exe vc.out compile.out tdiff.obj tdiff.exe) do del %%f
|
|
||||||
pause
|
|
||||||
48
dependencies/uthash/tests/emit_keys.c
vendored
48
dependencies/uthash/tests/emit_keys.c
vendored
@@ -1,48 +0,0 @@
|
|||||||
#include <stdlib.h> /* malloc */
|
|
||||||
#include <errno.h> /* perror */
|
|
||||||
#include <stdio.h> /* printf */
|
|
||||||
#include <unistd.h> /* write */
|
|
||||||
|
|
||||||
/* this define must precede uthash.h */
|
|
||||||
#define HASH_EMIT_KEYS 1
|
|
||||||
#include "uthash.h"
|
|
||||||
|
|
||||||
#define BUFLEN 30
|
|
||||||
|
|
||||||
typedef struct name_rec {
|
|
||||||
char boy_name[BUFLEN];
|
|
||||||
UT_hash_handle hh;
|
|
||||||
} name_rec;
|
|
||||||
|
|
||||||
int main(int argc,char *argv[])
|
|
||||||
{
|
|
||||||
name_rec *name, *names=NULL;
|
|
||||||
char linebuf[BUFLEN];
|
|
||||||
FILE *file;
|
|
||||||
int i=0;
|
|
||||||
|
|
||||||
if (argc != 2) {
|
|
||||||
fprintf(stderr,"usage: %s file\n", argv[0]);
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( (file = fopen( argv[1], "r" )) == NULL ) {
|
|
||||||
perror("can't open: ");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (fgets(linebuf,BUFLEN,file) != NULL) {
|
|
||||||
name = (name_rec*)malloc(sizeof(name_rec));
|
|
||||||
if (name == NULL) {
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
strcpy(name->boy_name, linebuf);
|
|
||||||
HASH_ADD_STR(names,boy_name,name);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr,"%d keys emitted.\n", i);
|
|
||||||
fclose(file);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
147
dependencies/uthash/tests/example.c
vendored
147
dependencies/uthash/tests/example.c
vendored
@@ -1,147 +0,0 @@
|
|||||||
#include <stdio.h> /* printf */
|
|
||||||
#include <stdlib.h> /* atoi, malloc */
|
|
||||||
#include <string.h> /* strcpy */
|
|
||||||
#include "uthash.h"
|
|
||||||
|
|
||||||
struct my_struct {
|
|
||||||
int id; /* key */
|
|
||||||
char name[21];
|
|
||||||
UT_hash_handle hh; /* makes this structure hashable */
|
|
||||||
};
|
|
||||||
|
|
||||||
struct my_struct *users = NULL;
|
|
||||||
|
|
||||||
void add_user(int user_id, const char *name)
|
|
||||||
{
|
|
||||||
struct my_struct *s;
|
|
||||||
|
|
||||||
HASH_FIND_INT(users, &user_id, s); /* id already in the hash? */
|
|
||||||
if (s == NULL) {
|
|
||||||
s = (struct my_struct*)malloc(sizeof *s);
|
|
||||||
s->id = user_id;
|
|
||||||
HASH_ADD_INT(users, id, s); /* id is the key field */
|
|
||||||
}
|
|
||||||
strcpy(s->name, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct my_struct *find_user(int user_id)
|
|
||||||
{
|
|
||||||
struct my_struct *s;
|
|
||||||
|
|
||||||
HASH_FIND_INT(users, &user_id, s); /* s: output pointer */
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
void delete_user(struct my_struct *user)
|
|
||||||
{
|
|
||||||
HASH_DEL(users, user); /* user: pointer to deletee */
|
|
||||||
free(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
void delete_all()
|
|
||||||
{
|
|
||||||
struct my_struct *current_user;
|
|
||||||
struct my_struct *tmp;
|
|
||||||
|
|
||||||
HASH_ITER(hh, users, current_user, tmp) {
|
|
||||||
HASH_DEL(users, current_user); /* delete it (users advances to next) */
|
|
||||||
free(current_user); /* free it */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_users()
|
|
||||||
{
|
|
||||||
struct my_struct *s;
|
|
||||||
|
|
||||||
for (s = users; s != NULL; s = (struct my_struct*)(s->hh.next)) {
|
|
||||||
printf("user id %d: name %s\n", s->id, s->name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int by_name(const struct my_struct *a, const struct my_struct *b)
|
|
||||||
{
|
|
||||||
return strcmp(a->name, b->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
int by_id(const struct my_struct *a, const struct my_struct *b)
|
|
||||||
{
|
|
||||||
return (a->id - b->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *getl(const char *prompt)
|
|
||||||
{
|
|
||||||
static char buf[21];
|
|
||||||
char *p;
|
|
||||||
printf("%s? ", prompt); fflush(stdout);
|
|
||||||
p = fgets(buf, sizeof(buf), stdin);
|
|
||||||
if (p == NULL || (p = strchr(buf, '\n')) == NULL) {
|
|
||||||
puts("Invalid input!");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
*p = '\0';
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
int id = 1;
|
|
||||||
int running = 1;
|
|
||||||
struct my_struct *s;
|
|
||||||
int temp;
|
|
||||||
|
|
||||||
while (running) {
|
|
||||||
printf(" 1. add user\n");
|
|
||||||
printf(" 2. add or rename user by id\n");
|
|
||||||
printf(" 3. find user\n");
|
|
||||||
printf(" 4. delete user\n");
|
|
||||||
printf(" 5. delete all users\n");
|
|
||||||
printf(" 6. sort items by name\n");
|
|
||||||
printf(" 7. sort items by id\n");
|
|
||||||
printf(" 8. print users\n");
|
|
||||||
printf(" 9. count users\n");
|
|
||||||
printf("10. quit\n");
|
|
||||||
switch (atoi(getl("Command"))) {
|
|
||||||
case 1:
|
|
||||||
add_user(id++, getl("Name (20 char max)"));
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
temp = atoi(getl("ID"));
|
|
||||||
add_user(temp, getl("Name (20 char max)"));
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
s = find_user(atoi(getl("ID to find")));
|
|
||||||
printf("user: %s\n", s ? s->name : "unknown");
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
s = find_user(atoi(getl("ID to delete")));
|
|
||||||
if (s) {
|
|
||||||
delete_user(s);
|
|
||||||
} else {
|
|
||||||
printf("id unknown\n");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
delete_all();
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
HASH_SORT(users, by_name);
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
HASH_SORT(users, by_id);
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
print_users();
|
|
||||||
break;
|
|
||||||
case 9:
|
|
||||||
temp = HASH_COUNT(users);
|
|
||||||
printf("there are %d users\n", temp);
|
|
||||||
break;
|
|
||||||
case 10:
|
|
||||||
running = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete_all(); /* free any structures */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
678
dependencies/uthash/tests/hashscan.c
vendored
678
dependencies/uthash/tests/hashscan.c
vendored
@@ -1,678 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2005-2022, Troy D. Hanson https://troydhanson.github.io/uthash/
|
|
||||||
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.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
||||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
||||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
|
||||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <sys/types.h> /* on OSX, must come before ptrace.h */
|
|
||||||
#include <sys/ptrace.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#ifdef __FreeBSD__
|
|
||||||
#include <sys/param.h> /* MAXPATHLEN */
|
|
||||||
#include <vm/vm.h> /* VM_PROT_* flags */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(PT_ATTACH) && !defined(PTRACE_ATTACH)
|
|
||||||
#define PTRACE_ATTACH PT_ATTACH
|
|
||||||
#define PTRACE_DETACH PT_DETACH
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* need this defined so offsetof can give us bloom offsets in UT_hash_table */
|
|
||||||
#define HASH_BLOOM 16
|
|
||||||
#include "uthash.h"
|
|
||||||
|
|
||||||
#ifdef __FreeBSD__
|
|
||||||
typedef struct {
|
|
||||||
void *start;
|
|
||||||
void *end;
|
|
||||||
} vma_t;
|
|
||||||
#else
|
|
||||||
typedef struct {
|
|
||||||
off_t start;
|
|
||||||
off_t end;
|
|
||||||
char perms[4]; /* rwxp */
|
|
||||||
char device[5]; /* fd:01 or 00:00 */
|
|
||||||
} vma_t;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const uint32_t sig = HASH_SIGNATURE;
|
|
||||||
int verbose=0;
|
|
||||||
int getkeys=0;
|
|
||||||
|
|
||||||
#define vv(...) do {if (verbose>0) printf(__VA_ARGS__);} while(0)
|
|
||||||
#define vvv(...) do {if (verbose>1) printf(__VA_ARGS__);} while(0)
|
|
||||||
|
|
||||||
/* these id's are arbitrary, only meaningful within this file */
|
|
||||||
#define JEN 1
|
|
||||||
#define BER 2
|
|
||||||
#define SFH 3
|
|
||||||
#define SAX 4
|
|
||||||
#define FNV 5
|
|
||||||
#define OAT 6
|
|
||||||
#define NUM_HASH_FUNCS 7 /* includes id 0, the non-function */
|
|
||||||
const char *hash_fcns[] = {"???","JEN","BER","SFH","SAX","FNV","OAT"};
|
|
||||||
|
|
||||||
/* given a peer key/len/hashv, reverse engineer its hash function */
|
|
||||||
static int infer_hash_function(char *key, size_t keylen, uint32_t hashv)
|
|
||||||
{
|
|
||||||
uint32_t ohashv;
|
|
||||||
/* BER SAX FNV OAT JEN SFH */
|
|
||||||
HASH_JEN(key,keylen,ohashv);
|
|
||||||
if (ohashv == hashv) {
|
|
||||||
return JEN;
|
|
||||||
}
|
|
||||||
HASH_BER(key,keylen,ohashv);
|
|
||||||
if (ohashv == hashv) {
|
|
||||||
return BER;
|
|
||||||
}
|
|
||||||
HASH_SFH(key,keylen,ohashv);
|
|
||||||
if (ohashv == hashv) {
|
|
||||||
return SFH;
|
|
||||||
}
|
|
||||||
HASH_SAX(key,keylen,ohashv);
|
|
||||||
if (ohashv == hashv) {
|
|
||||||
return SAX;
|
|
||||||
}
|
|
||||||
HASH_FNV(key,keylen,ohashv);
|
|
||||||
if (ohashv == hashv) {
|
|
||||||
return FNV;
|
|
||||||
}
|
|
||||||
HASH_OAT(key,keylen,ohashv);
|
|
||||||
if (ohashv == hashv) {
|
|
||||||
return OAT;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read peer's memory from addr for len bytes, store into our dst */
|
|
||||||
#ifdef __FreeBSD__
|
|
||||||
static int read_mem(void *dst, pid_t pid, void *start, size_t len)
|
|
||||||
{
|
|
||||||
struct ptrace_io_desc io_desc;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
io_desc.piod_op = PIOD_READ_D;
|
|
||||||
io_desc.piod_offs = start;
|
|
||||||
io_desc.piod_addr = dst;
|
|
||||||
io_desc.piod_len = len;
|
|
||||||
|
|
||||||
ret = ptrace(PT_IO, pid, (void *) &io_desc, 0);
|
|
||||||
|
|
||||||
if (ret) {
|
|
||||||
vv("read_mem: ptrace failed: %s\n", strerror(errno));
|
|
||||||
return -1;
|
|
||||||
} else if (io_desc.piod_len != len) {
|
|
||||||
vv("read_mem: short read!\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
static int read_mem(void *dst, int fd, off_t start, size_t len)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
size_t bytes_read=0;
|
|
||||||
if (lseek(fd, start, SEEK_SET) == (off_t)-1) {
|
|
||||||
fprintf(stderr, "lseek failed: %s\n", strerror(errno));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
while ( len && ((rc=read(fd, (char*)dst+bytes_read, len)) > 0)) {
|
|
||||||
len -= rc;
|
|
||||||
bytes_read += rc;
|
|
||||||
}
|
|
||||||
if (rc==-1) {
|
|
||||||
vv("read_mem failed (%s)\n",strerror(errno));
|
|
||||||
}
|
|
||||||
if ((len != 0 && rc >= 0)) {
|
|
||||||
vv("INTERNAL ERROR\n");
|
|
||||||
}
|
|
||||||
return (rc == -1) ? -1 : 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* later compensate for possible presence of bloom filter */
|
|
||||||
static char *tbl_from_sig_addr(char *sig)
|
|
||||||
{
|
|
||||||
return (sig - offsetof(UT_hash_table,signature));
|
|
||||||
}
|
|
||||||
|
|
||||||
#define HS_BIT_TEST(v,i) (v[i/8] & (1U << (i%8)))
|
|
||||||
static void found(int fd, char* peer_sig, pid_t pid)
|
|
||||||
{
|
|
||||||
UT_hash_table *tbl=NULL;
|
|
||||||
UT_hash_bucket *bkts=NULL;
|
|
||||||
UT_hash_handle hh;
|
|
||||||
size_t i, bloom_len, bloom_bitlen, bloom_on_bits=0,bloom_off_bits=0;
|
|
||||||
char *peer_tbl, *peer_bloom_sig, *peer_bloom_nbits, *peer_bloombv_ptr,
|
|
||||||
*peer_bloombv, *peer_bkts, *peer_hh, *key=NULL;
|
|
||||||
const char *peer_key;
|
|
||||||
const char *hash_fcn = NULL;
|
|
||||||
unsigned char *bloombv=NULL;
|
|
||||||
static int fileno=0;
|
|
||||||
char keyfile[50];
|
|
||||||
unsigned char bloom_nbits=0;
|
|
||||||
int keyfd=-1, mode=S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH,
|
|
||||||
hash_fcn_hits[NUM_HASH_FUNCS], hash_fcn_winner;
|
|
||||||
unsigned max_chain=0;
|
|
||||||
uint32_t bloomsig;
|
|
||||||
int has_bloom_filter_fields = 0;
|
|
||||||
|
|
||||||
for(i=0; i < NUM_HASH_FUNCS; i++) {
|
|
||||||
hash_fcn_hits[i]=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getkeys) {
|
|
||||||
snprintf(keyfile, sizeof(keyfile), "/tmp/%u-%u.key", (unsigned)pid,fileno++);
|
|
||||||
if ( (keyfd = open(keyfile, O_WRONLY|O_CREAT|O_TRUNC, mode)) == -1) {
|
|
||||||
fprintf(stderr, "can't open %s: %s\n", keyfile, strerror(errno));
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vv("found signature at peer %p\n", (void*)peer_sig);
|
|
||||||
peer_tbl = tbl_from_sig_addr(peer_sig);
|
|
||||||
vvv("reading table at peer %p\n", (void*)peer_tbl);
|
|
||||||
|
|
||||||
if ( (tbl = (UT_hash_table*)malloc(sizeof(UT_hash_table))) == NULL) {
|
|
||||||
fprintf(stderr, "out of memory\n");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
#ifdef __FreeBSD__
|
|
||||||
if (read_mem(tbl, pid, (void *)peer_tbl, sizeof(UT_hash_table)) != 0) {
|
|
||||||
#else
|
|
||||||
if (read_mem(tbl, fd, (off_t)peer_tbl, sizeof(UT_hash_table)) != 0) {
|
|
||||||
#endif
|
|
||||||
fprintf(stderr, "failed to read peer memory\n");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* got the table. how about the buckets */
|
|
||||||
peer_bkts = (char*)tbl->buckets;
|
|
||||||
vvv("reading %u buckets at peer %p\n", tbl->num_buckets, (void*)peer_bkts);
|
|
||||||
bkts = (UT_hash_bucket*)malloc(sizeof(UT_hash_bucket)*tbl->num_buckets);
|
|
||||||
if (bkts == NULL) {
|
|
||||||
fprintf(stderr, "out of memory\n");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
#ifdef __FreeBSD__
|
|
||||||
if (read_mem(bkts, pid, (void *)peer_bkts, sizeof(UT_hash_bucket)*tbl->num_buckets) != 0) {
|
|
||||||
#else
|
|
||||||
if (read_mem(bkts, fd, (off_t)peer_bkts, sizeof(UT_hash_bucket)*tbl->num_buckets) != 0) {
|
|
||||||
#endif
|
|
||||||
fprintf(stderr, "failed to read peer memory\n");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
vvv("scanning %u peer buckets\n", tbl->num_buckets);
|
|
||||||
for(i=0; i < tbl->num_buckets; i++) {
|
|
||||||
vvv("bucket %u has %u items\n", (unsigned)i, (unsigned)(bkts[i].count));
|
|
||||||
if (bkts[i].count > max_chain) {
|
|
||||||
max_chain = bkts[i].count;
|
|
||||||
}
|
|
||||||
if (bkts[i].expand_mult) {
|
|
||||||
vvv(" bucket %u has expand_mult %u\n", (unsigned)i, (unsigned)(bkts[i].expand_mult));
|
|
||||||
}
|
|
||||||
|
|
||||||
vvv("scanning bucket %u chain:\n", (unsigned)i);
|
|
||||||
peer_hh = (char*)bkts[i].hh_head;
|
|
||||||
while(peer_hh) {
|
|
||||||
#ifdef __FreeBSD__
|
|
||||||
if (read_mem(&hh, pid, (void *)peer_hh, sizeof(hh)) != 0) {
|
|
||||||
#else
|
|
||||||
if (read_mem(&hh, fd, (off_t)peer_hh, sizeof(hh)) != 0) {
|
|
||||||
#endif
|
|
||||||
fprintf(stderr, "failed to read peer memory\n");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if ((char*)hh.tbl != peer_tbl) {
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
peer_hh = (char*)hh.hh_next;
|
|
||||||
peer_key = (const char*)(hh.key);
|
|
||||||
/* malloc space to read the key, and read it */
|
|
||||||
if ( (key = (char*)malloc(sizeof(hh.keylen))) == NULL) {
|
|
||||||
fprintf(stderr, "out of memory\n");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
#ifdef __FreeBSD__
|
|
||||||
if (read_mem(key, pid, (void*)peer_key, hh.keylen) != 0) {
|
|
||||||
#else
|
|
||||||
if (read_mem(key, fd, (off_t)peer_key, hh.keylen) != 0) {
|
|
||||||
#endif
|
|
||||||
fprintf(stderr, "failed to read peer memory\n");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
hash_fcn_hits[infer_hash_function(key,hh.keylen,hh.hashv)]++;
|
|
||||||
/* write the key if requested */
|
|
||||||
if (getkeys) {
|
|
||||||
write(keyfd, &hh.keylen, sizeof(unsigned));
|
|
||||||
write(keyfd, key, hh.keylen);
|
|
||||||
}
|
|
||||||
free(key);
|
|
||||||
key=NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* does it have a bloom filter? */
|
|
||||||
peer_bloom_sig = peer_tbl + offsetof(UT_hash_table, bloom_sig);
|
|
||||||
peer_bloombv_ptr = peer_tbl + offsetof(UT_hash_table, bloom_bv);
|
|
||||||
peer_bloom_nbits = peer_tbl + offsetof(UT_hash_table, bloom_nbits);
|
|
||||||
vvv("looking for bloom signature at peer %p\n", (void*)peer_bloom_sig);
|
|
||||||
#ifdef __FreeBSD__
|
|
||||||
if ((read_mem(&bloomsig, pid, (void *)peer_bloom_sig, sizeof(uint32_t)) == 0) &&
|
|
||||||
(bloomsig == HASH_BLOOM_SIGNATURE)) {
|
|
||||||
#else
|
|
||||||
if ((read_mem(&bloomsig, fd, (off_t)peer_bloom_sig, sizeof(uint32_t)) == 0) &&
|
|
||||||
(bloomsig == HASH_BLOOM_SIGNATURE)) {
|
|
||||||
#endif
|
|
||||||
vvv("bloom signature (%x) found\n",bloomsig);
|
|
||||||
/* bloom found. get at bv, nbits */
|
|
||||||
#ifdef __FreeBSD__
|
|
||||||
if (read_mem(&bloom_nbits, pid, (void *)peer_bloom_nbits, sizeof(char)) == 0) {
|
|
||||||
#else
|
|
||||||
if (read_mem(&bloom_nbits, fd, (off_t)peer_bloom_nbits, sizeof(char)) == 0) {
|
|
||||||
#endif
|
|
||||||
/* scan bloom filter, calculate saturation */
|
|
||||||
bloom_bitlen = (1ULL << bloom_nbits);
|
|
||||||
bloom_len = (bloom_bitlen / 8) + ((bloom_bitlen % 8) ? 1 : 0);
|
|
||||||
vvv("bloom bitlen is %u, bloom_bytelen is %u\n", (unsigned)bloom_bitlen, (unsigned)bloom_len);
|
|
||||||
if ( (bloombv = (unsigned char*)malloc(bloom_len)) == NULL) {
|
|
||||||
fprintf(stderr, "out of memory\n");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
/* read the address of the bitvector in the peer, then read the bv itself */
|
|
||||||
#ifdef __FreeBSD__
|
|
||||||
if ((read_mem(&peer_bloombv, pid, (void *)peer_bloombv_ptr, sizeof(void*)) == 0) &&
|
|
||||||
(read_mem(bloombv, pid, (void *)peer_bloombv, bloom_len) == 0)) {
|
|
||||||
#else
|
|
||||||
if ((read_mem(&peer_bloombv, fd, (off_t)peer_bloombv_ptr, sizeof(void*)) == 0) &&
|
|
||||||
(read_mem(bloombv, fd, (off_t)peer_bloombv, bloom_len) == 0)) {
|
|
||||||
#endif
|
|
||||||
/* calculate saturation */
|
|
||||||
vvv("read peer bloom bitvector from %p (%u bytes)\n", (void*)peer_bloombv, (unsigned)bloom_len);
|
|
||||||
for(i=0; i < bloom_bitlen; i++) {
|
|
||||||
if (HS_BIT_TEST(bloombv,(unsigned)i)) {
|
|
||||||
/* vvv("bit %u set\n",(unsigned)i); */
|
|
||||||
bloom_on_bits++;
|
|
||||||
} else {
|
|
||||||
bloom_off_bits++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
has_bloom_filter_fields = 1;
|
|
||||||
vvv("there were %u on_bits among %u total bits\n", (unsigned)bloom_on_bits, (unsigned)bloom_bitlen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* choose apparent hash function */
|
|
||||||
hash_fcn_winner=0;
|
|
||||||
for(i=0; i<NUM_HASH_FUNCS; i++) {
|
|
||||||
if (hash_fcn_hits[i] > hash_fcn_hits[hash_fcn_winner]) {
|
|
||||||
hash_fcn_winner=i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hash_fcn = hash_fcns[hash_fcn_winner];
|
|
||||||
|
|
||||||
/*
|
|
||||||
Address ideal items buckets mc fl bloom sat fcn keys saved to
|
|
||||||
------------------ ----- -------- -------- -- -- ----- ----- --- -------------
|
|
||||||
0x10aa4090 98% 10000000 32000000 10 ok BER /tmp/9110-0.key
|
|
||||||
0x10abcdef 100% 10000000 32000000 9 NX 27 12% BER /tmp/9110-1.key
|
|
||||||
*/
|
|
||||||
printf("Address ideal items buckets mc fl bloom sat fcn keys saved to\n");
|
|
||||||
printf("------------------ ----- -------- -------- -- -- ----- ----- --- -------------\n");
|
|
||||||
if (has_bloom_filter_fields) {
|
|
||||||
printf("%-18p %4.0f%% %8u %8u %2u %2s %5u %4.0f%c %3s %s\n",
|
|
||||||
(void*)peer_tbl,
|
|
||||||
(tbl->num_items - tbl->nonideal_items) * 100.0 / tbl->num_items,
|
|
||||||
tbl->num_items,
|
|
||||||
tbl->num_buckets,
|
|
||||||
max_chain,
|
|
||||||
tbl->noexpand ? "NX" : "ok",
|
|
||||||
bloom_nbits,
|
|
||||||
bloom_on_bits * 100.0 / bloom_bitlen, '%',
|
|
||||||
hash_fcn,
|
|
||||||
(getkeys ? keyfile : ""));
|
|
||||||
} else {
|
|
||||||
printf("%-18p %4.0f%% %8u %8u %2u %2s %5s %4s%c %3s %s\n",
|
|
||||||
(void*)peer_tbl,
|
|
||||||
(tbl->num_items - tbl->nonideal_items) * 100.0 / tbl->num_items,
|
|
||||||
tbl->num_items,
|
|
||||||
tbl->num_buckets,
|
|
||||||
max_chain,
|
|
||||||
tbl->noexpand ? "NX" : "ok",
|
|
||||||
"",
|
|
||||||
"", ' ',
|
|
||||||
hash_fcn,
|
|
||||||
(getkeys ? keyfile : ""));
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
printf("read peer tbl:\n");
|
|
||||||
printf("num_buckets: %u\n", tbl->num_buckets);
|
|
||||||
printf("num_items: %u\n", tbl->num_items);
|
|
||||||
printf("nonideal_items: %u (%.2f%%)\n", tbl->nonideal_items,
|
|
||||||
tbl->nonideal_items*100.0/tbl->num_items);
|
|
||||||
printf("expand: %s\n", tbl->noexpand ? "inhibited": "normal");
|
|
||||||
if (getkeys) {
|
|
||||||
printf("keys written to %s\n", keyfile);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
done:
|
|
||||||
if (bkts) {
|
|
||||||
free(bkts);
|
|
||||||
}
|
|
||||||
if (tbl) {
|
|
||||||
free(tbl);
|
|
||||||
}
|
|
||||||
if (key) {
|
|
||||||
free(key);
|
|
||||||
}
|
|
||||||
if (keyfd != -1) {
|
|
||||||
close(keyfd);
|
|
||||||
}
|
|
||||||
if (bloombv) {
|
|
||||||
free(bloombv);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __FreeBSD__
|
|
||||||
static void sigscan(pid_t pid, void *start, void *end, uint32_t sig)
|
|
||||||
{
|
|
||||||
struct ptrace_io_desc io_desc;
|
|
||||||
int page_size = getpagesize();
|
|
||||||
char *buf;
|
|
||||||
char *pos;
|
|
||||||
|
|
||||||
/* make sure page_size is a multiple of the signature size, code below assumes this */
|
|
||||||
assert(page_size % sizeof(sig) == 0);
|
|
||||||
|
|
||||||
buf = malloc(page_size);
|
|
||||||
|
|
||||||
if (buf == NULL) {
|
|
||||||
fprintf(stderr, "malloc failed in sigscan()\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
io_desc.piod_op = PIOD_READ_D;
|
|
||||||
io_desc.piod_offs = start;
|
|
||||||
io_desc.piod_addr = buf;
|
|
||||||
io_desc.piod_len = page_size;
|
|
||||||
|
|
||||||
/* read in one page after another and search sig */
|
|
||||||
while(!ptrace(PT_IO, pid, (void *) &io_desc, 0)) {
|
|
||||||
if (io_desc.piod_len != page_size) {
|
|
||||||
fprintf(stderr, "PT_IO returned less than page size in sigscan()\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* iterate over the the page using the signature size and look for the sig */
|
|
||||||
for (pos = buf; pos < (buf + page_size); pos += sizeof(sig)) {
|
|
||||||
if (*(uint32_t *) pos == sig) {
|
|
||||||
found(pid, (char *) io_desc.piod_offs + (pos - buf), pid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 'end' is inclusive (the address of the last valid byte), so if the current offset
|
|
||||||
* plus a page is beyond 'end', we're already done. since all vm map entries consist
|
|
||||||
* of entire pages and 'end' is inclusive, current offset plus one page should point
|
|
||||||
* exactly one byte beyond 'end'. this is assert()ed below to be on the safe side.
|
|
||||||
*/
|
|
||||||
if (io_desc.piod_offs + page_size > end) {
|
|
||||||
assert(io_desc.piod_offs + page_size == (end + 1));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* advance to the next page */
|
|
||||||
io_desc.piod_offs += page_size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
static void sigscan(int fd, off_t start, off_t end, uint32_t sig, pid_t pid)
|
|
||||||
{
|
|
||||||
int rlen;
|
|
||||||
uint32_t u;
|
|
||||||
off_t at=0;
|
|
||||||
|
|
||||||
if (lseek(fd, start, SEEK_SET) == (off_t)-1) {
|
|
||||||
fprintf(stderr, "lseek failed: %s\n", strerror(errno));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
while ( (rlen = read(fd,&u,sizeof(u))) == sizeof(u)) {
|
|
||||||
if (!memcmp(&u,&sig,sizeof(u))) {
|
|
||||||
found(fd, (char*)(start+at),pid);
|
|
||||||
}
|
|
||||||
at += sizeof(u);
|
|
||||||
if ((off_t)(at + sizeof(u)) > end-start) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rlen == -1) {
|
|
||||||
//fprintf(stderr,"read failed: %s\n", strerror(errno));
|
|
||||||
//exit(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __FreeBSD__
|
|
||||||
static int scan(pid_t pid)
|
|
||||||
{
|
|
||||||
vma_t *vmas=NULL, vma;
|
|
||||||
unsigned i, num_vmas = 0;
|
|
||||||
int ret;
|
|
||||||
struct ptrace_vm_entry vm_entry;
|
|
||||||
char path[MAXPATHLEN];
|
|
||||||
|
|
||||||
vv("attaching to peer\n");
|
|
||||||
if (ptrace(PT_ATTACH,pid,NULL,0) == -1) {
|
|
||||||
fprintf(stderr,"failed to attach to %u: %s\n", (unsigned)pid, strerror(errno));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
vv("waiting for peer to suspend temporarily\n");
|
|
||||||
if (waitpid(pid,NULL,0) != pid) {
|
|
||||||
fprintf(stderr,"failed to wait for pid %u: %s\n",(unsigned)pid, strerror(errno));
|
|
||||||
goto die;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read memory map using ptrace */
|
|
||||||
vv("listing peer virtual memory areas\n");
|
|
||||||
vm_entry.pve_entry = 0;
|
|
||||||
vm_entry.pve_path = path; /* not used but required to make vm_entry.pve_pathlen work */
|
|
||||||
while(1) {
|
|
||||||
/* set pve_pathlen every turn, it gets overwritten by ptrace */
|
|
||||||
vm_entry.pve_pathlen = MAXPATHLEN;
|
|
||||||
errno = 0;
|
|
||||||
|
|
||||||
ret = ptrace(PT_VM_ENTRY, pid, (void *) &vm_entry, 0);
|
|
||||||
|
|
||||||
if (ret) {
|
|
||||||
if (errno == ENOENT) {
|
|
||||||
/* we've reached the last entry */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
fprintf(stderr, "fetching vm map entry failed: %s (%i)\n", strerror(errno), errno);
|
|
||||||
goto die;
|
|
||||||
}
|
|
||||||
|
|
||||||
vvv("vmmap entry: start: %p, end: %p", (void *) vm_entry.pve_start, (void *) vm_entry.pve_end);
|
|
||||||
|
|
||||||
/* skip unreadable or vnode-backed entries */
|
|
||||||
if (!(vm_entry.pve_prot & VM_PROT_READ) || vm_entry.pve_pathlen > 0) {
|
|
||||||
vvv(" -> skipped (not readable or vnode-backed)\n");
|
|
||||||
vm_entry.pve_path[0] = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* useful entry, add to list */
|
|
||||||
vvv(" -> will be scanned\n");
|
|
||||||
vma.start = (void *)vm_entry.pve_start;
|
|
||||||
vma.end = (void *)vm_entry.pve_end;
|
|
||||||
vmas = (vma_t *) realloc(vmas, (num_vmas + 1) * sizeof(vma_t));
|
|
||||||
if (vmas == NULL) {
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
vmas[num_vmas++] = vma;
|
|
||||||
}
|
|
||||||
|
|
||||||
vv("peer has %u virtual memory areas\n", num_vmas);
|
|
||||||
|
|
||||||
/* look for the hash signature */
|
|
||||||
vv("scanning peer memory for hash table signatures\n");
|
|
||||||
for(i=0; i<num_vmas; i++) {
|
|
||||||
vma = vmas[i];
|
|
||||||
sigscan(pid, vma.start, vma.end, sig);
|
|
||||||
}
|
|
||||||
|
|
||||||
die:
|
|
||||||
vv("detaching and resuming peer\n");
|
|
||||||
if (ptrace(PT_DETACH, pid, NULL, 0) == -1) {
|
|
||||||
fprintf(stderr,"failed to detach from %u: %s\n", (unsigned)pid, strerror(errno));
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
# else
|
|
||||||
static int scan(pid_t pid)
|
|
||||||
{
|
|
||||||
FILE *mapf;
|
|
||||||
char mapfile[30], memfile[30], line[100];
|
|
||||||
vma_t *vmas=NULL, vma;
|
|
||||||
unsigned i, num_vmas = 0;
|
|
||||||
int memfd;
|
|
||||||
void *pstart, *pend, *unused;
|
|
||||||
|
|
||||||
/* attach to the target process and wait for it to suspend */
|
|
||||||
vv("attaching to peer\n");
|
|
||||||
if (ptrace(PTRACE_ATTACH, pid, NULL, 0) == -1) {
|
|
||||||
fprintf(stderr,"failed to attach to %u: %s\n", (unsigned)pid, strerror(errno));
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
vv("waiting for peer to suspend temporarily\n");
|
|
||||||
if (waitpid(pid,NULL,0) != pid) {
|
|
||||||
fprintf(stderr,"failed to wait for pid %u: %s\n",(unsigned)pid, strerror(errno));
|
|
||||||
goto die;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get ready to open its memory map. this gives us its valid memory areas */
|
|
||||||
snprintf(mapfile,sizeof(mapfile),"/proc/%u/maps",(unsigned)pid);
|
|
||||||
snprintf(memfile,sizeof(memfile),"/proc/%u/mem", (unsigned)pid);
|
|
||||||
vv("opening peer memory map [%s]\n", mapfile);
|
|
||||||
if ( (mapf = fopen(mapfile,"r")) == NULL) {
|
|
||||||
fprintf(stderr,"failed to open %s: %s\n", mapfile, strerror(errno));
|
|
||||||
goto die;
|
|
||||||
}
|
|
||||||
vv("listing peer virtual memory areas\n");
|
|
||||||
while(fgets(line,sizeof(line),mapf)) {
|
|
||||||
if (sscanf(line, "%p-%p %4c %p %5c", &pstart, &pend, vma.perms,
|
|
||||||
&unused, vma.device) == 5) {
|
|
||||||
vma.start = (off_t)pstart;
|
|
||||||
vma.end = (off_t)pend;
|
|
||||||
if (vma.perms[0] != 'r') {
|
|
||||||
continue; /* only readable vma's */
|
|
||||||
}
|
|
||||||
if (memcmp(vma.device,"fd",2)==0) {
|
|
||||||
continue; /* skip mapped files */
|
|
||||||
}
|
|
||||||
vmas = (vma_t*)realloc(vmas, (num_vmas+1) * sizeof(vma_t));
|
|
||||||
if (vmas == NULL) {
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
vmas[num_vmas++] = vma;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vv("peer has %u virtual memory areas\n",num_vmas);
|
|
||||||
fclose(mapf);
|
|
||||||
|
|
||||||
/* ok, open up its memory and start looking around in there */
|
|
||||||
vv("opening peer memory\n");
|
|
||||||
if ( (memfd=open(memfile,O_RDONLY)) == -1) {
|
|
||||||
fprintf(stderr,"failed to open %s: %s\n", memfile, strerror(errno));
|
|
||||||
goto die;
|
|
||||||
}
|
|
||||||
/* look for the hash signature */
|
|
||||||
vv("scanning peer memory for hash table signatures\n");
|
|
||||||
for(i=0; i<num_vmas; i++) {
|
|
||||||
vma = vmas[i];
|
|
||||||
pstart = (void*)vma.start;
|
|
||||||
pend = (void*)vma.end;
|
|
||||||
/*fprintf(stderr,"scanning %p-%p %.4s %.5s\n", pstart, pend,
|
|
||||||
vma.perms, vma.device);*/
|
|
||||||
sigscan(memfd, vma.start, vma.end, sig, pid);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* done. close memory and detach. this resumes the target process */
|
|
||||||
close(memfd);
|
|
||||||
|
|
||||||
die:
|
|
||||||
vv("detaching and resuming peer\n");
|
|
||||||
if (ptrace(PTRACE_DETACH, pid, NULL, 0) == -1) {
|
|
||||||
fprintf(stderr,"failed to detach from %u: %s\n", (unsigned)pid, strerror(errno));
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
static int usage(const char *prog)
|
|
||||||
{
|
|
||||||
fprintf(stderr,"usage: %s [-v] [-k] <pid>\n", prog);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
int opt;
|
|
||||||
|
|
||||||
while ( (opt = getopt(argc, argv, "kv")) != -1) {
|
|
||||||
switch (opt) {
|
|
||||||
case 'v':
|
|
||||||
verbose++;
|
|
||||||
break;
|
|
||||||
case 'k':
|
|
||||||
getkeys++;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return usage(argv[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (optind < argc) {
|
|
||||||
pid_t pid = atoi(argv[optind++]);
|
|
||||||
return scan(pid);
|
|
||||||
} else {
|
|
||||||
return usage(argv[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
255
dependencies/uthash/tests/keystat.c
vendored
255
dependencies/uthash/tests/keystat.c
vendored
@@ -1,255 +0,0 @@
|
|||||||
#include <sys/types.h> /* for 'open' */
|
|
||||||
#include <sys/stat.h> /* for 'open' */
|
|
||||||
#include <fcntl.h> /* for 'open' */
|
|
||||||
#include <stdlib.h> /* for 'malloc' */
|
|
||||||
#include <stdio.h> /* for 'printf' */
|
|
||||||
#include <unistd.h> /* for 'read' */
|
|
||||||
#include <errno.h> /* for 'sterror' */
|
|
||||||
#include <sys/time.h> /* for 'gettimeofday' */
|
|
||||||
#include "uthash.h"
|
|
||||||
|
|
||||||
#undef uthash_noexpand_fyi
|
|
||||||
#define uthash_noexpand_fyi(t) die()
|
|
||||||
#define UNALIGNED_KEYS 0
|
|
||||||
|
|
||||||
static void die()
|
|
||||||
{
|
|
||||||
fprintf(stderr,"expansion inhibited\n");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Windows doesn't have gettimeofday. While Cygwin and some
|
|
||||||
* versions of MinGW supply one, it is very coarse. This substitute
|
|
||||||
* gives much more accurate elapsed times under Windows. */
|
|
||||||
#if (( defined __CYGWIN__ ) || ( defined __MINGW32__ ))
|
|
||||||
#include <windows.h>
|
|
||||||
static void win_gettimeofday(struct timeval* p, void* tz /* IGNORED */)
|
|
||||||
{
|
|
||||||
LARGE_INTEGER q;
|
|
||||||
static long long freq;
|
|
||||||
static long long cyg_timer;
|
|
||||||
QueryPerformanceFrequency(&q);
|
|
||||||
freq = q.QuadPart;
|
|
||||||
QueryPerformanceCounter(&q);
|
|
||||||
cyg_timer = q.QuadPart;
|
|
||||||
p->tv_sec = (long)(cyg_timer / freq);
|
|
||||||
p->tv_usec = (long)(((cyg_timer % freq) * 1000000) / freq);
|
|
||||||
}
|
|
||||||
#define gettimeofday win_gettimeofday
|
|
||||||
#define MODE (O_RDONLY|O_BINARY)
|
|
||||||
#else
|
|
||||||
#define MODE (O_RDONLY)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef timersub
|
|
||||||
#define timersub(a, b, result) \
|
|
||||||
do { \
|
|
||||||
(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
|
|
||||||
(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
|
|
||||||
if ((result)->tv_usec < 0) { \
|
|
||||||
--(result)->tv_sec; \
|
|
||||||
(result)->tv_usec += 1000000; \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct stat_key {
|
|
||||||
char *key;
|
|
||||||
unsigned len;
|
|
||||||
UT_hash_handle hh, hh2;
|
|
||||||
} stat_key;
|
|
||||||
|
|
||||||
#define CHAIN_0 0
|
|
||||||
#define CHAIN_5 1
|
|
||||||
#define CHAIN_10 2
|
|
||||||
#define CHAIN_20 3
|
|
||||||
#define CHAIN_100 4
|
|
||||||
#define CHAIN_MAX 5
|
|
||||||
static void hash_chain_len_histogram(const UT_hash_table *tbl)
|
|
||||||
{
|
|
||||||
unsigned i, bkt_hist[CHAIN_MAX+1];
|
|
||||||
double pct = 100.0/(double)tbl->num_buckets;
|
|
||||||
memset(bkt_hist,0,sizeof(bkt_hist));
|
|
||||||
for(i=0; i < tbl->num_buckets; i++) {
|
|
||||||
unsigned count = tbl->buckets[i].count;
|
|
||||||
if (count == 0U) {
|
|
||||||
bkt_hist[CHAIN_0]++;
|
|
||||||
} else if (count < 5U) {
|
|
||||||
bkt_hist[CHAIN_5]++;
|
|
||||||
} else if (count < 10U) {
|
|
||||||
bkt_hist[CHAIN_10]++;
|
|
||||||
} else if (count < 20U) {
|
|
||||||
bkt_hist[CHAIN_20]++;
|
|
||||||
} else if (count < 100U) {
|
|
||||||
bkt_hist[CHAIN_100]++;
|
|
||||||
} else {
|
|
||||||
bkt_hist[CHAIN_MAX]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fprintf(stderr, "Buckets with 0 items: %.1f%%\n", (double)bkt_hist[CHAIN_0 ]*pct);
|
|
||||||
fprintf(stderr, "Buckets with < 5 items: %.1f%%\n", (double)bkt_hist[CHAIN_5 ]*pct);
|
|
||||||
fprintf(stderr, "Buckets with < 10 items: %.1f%%\n", (double)bkt_hist[CHAIN_10]*pct);
|
|
||||||
fprintf(stderr, "Buckets with < 20 items: %.1f%%\n", (double)bkt_hist[CHAIN_20]*pct);
|
|
||||||
fprintf(stderr, "Buckets with < 100 items: %.1f%%\n", (double)bkt_hist[CHAIN_100]*pct);
|
|
||||||
fprintf(stderr, "Buckets with > 100 items: %.1f%%\n", (double)bkt_hist[CHAIN_MAX]*pct);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
int dups=0, rc, fd, done=0, err=0, want, i, padding=0, v=1, percent=100;
|
|
||||||
unsigned keylen, max_keylen=0, verbose=0;
|
|
||||||
const char *filename = "/dev/stdin";
|
|
||||||
char *dst;
|
|
||||||
stat_key *keyt, *keytmp, *keys=NULL, *keys2=NULL;
|
|
||||||
struct timeval start_tm, end_tm, elapsed_tm, elapsed_tm2, elapsed_tm3;
|
|
||||||
|
|
||||||
if ((argc >= 3) && (strcmp(argv[1],"-p") == 0)) {
|
|
||||||
percent = atoi(argv[2]);
|
|
||||||
v = 3;
|
|
||||||
}
|
|
||||||
if ((v < argc) && (strcmp(argv[v],"-v") == 0)) {
|
|
||||||
verbose=1;
|
|
||||||
v++;
|
|
||||||
}
|
|
||||||
if (v < argc) {
|
|
||||||
filename=argv[v];
|
|
||||||
}
|
|
||||||
fd=open(filename,MODE);
|
|
||||||
|
|
||||||
if ( fd == -1 ) {
|
|
||||||
fprintf(stderr,"open failed %s: %s\n", filename, strerror(errno));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(i=0; done==0; i++) {
|
|
||||||
|
|
||||||
want = sizeof(int);
|
|
||||||
dst = (char*)&keylen;
|
|
||||||
readmore1:
|
|
||||||
rc = read(fd,dst,want);
|
|
||||||
if (rc != want) {
|
|
||||||
if (rc == 0) {
|
|
||||||
done=1;
|
|
||||||
} else if (rc == -1) {
|
|
||||||
fprintf(stderr,"read failed: %s\n", strerror(errno));
|
|
||||||
err=1;
|
|
||||||
} else if (rc > 0) {
|
|
||||||
want -= rc;
|
|
||||||
dst += rc;
|
|
||||||
goto readmore1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (done || err) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (keylen > max_keylen) {
|
|
||||||
max_keylen=keylen;
|
|
||||||
}
|
|
||||||
|
|
||||||
keyt = (stat_key*)malloc(sizeof(stat_key));
|
|
||||||
if (keyt == NULL) {
|
|
||||||
fprintf(stderr,"out of memory\n");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read key */
|
|
||||||
#ifdef UNALIGNED_KEYS
|
|
||||||
padding = i%8;
|
|
||||||
#endif
|
|
||||||
keyt->key = (char*)malloc(padding+keylen);
|
|
||||||
if (keyt->key == NULL) {
|
|
||||||
fprintf(stderr,"out of memory\n");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
keyt->key += padding; /* forcibly alter the alignment of key */
|
|
||||||
keyt->len = keylen;
|
|
||||||
|
|
||||||
want = keylen;
|
|
||||||
dst = keyt->key;
|
|
||||||
readmore2:
|
|
||||||
rc = read(fd,dst,want);
|
|
||||||
if (rc != want) {
|
|
||||||
if (rc == -1) {
|
|
||||||
fprintf(stderr,"read failed: %s\n", strerror(errno));
|
|
||||||
err=1;
|
|
||||||
} else if (rc == 0) {
|
|
||||||
fprintf(stderr,"incomplete file\n");
|
|
||||||
err=1;
|
|
||||||
} else if (rc >= 0) {
|
|
||||||
want -= rc;
|
|
||||||
dst += rc;
|
|
||||||
goto readmore2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (err != 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* if percent was set to something less than 100%, skip some keys*/
|
|
||||||
if (((rand()*1.0) / RAND_MAX) > ((percent*1.0)/100)) {
|
|
||||||
free(keyt->key-padding);
|
|
||||||
free(keyt);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* eliminate dups */
|
|
||||||
HASH_FIND(hh,keys,keyt->key,keylen,keytmp);
|
|
||||||
if (keytmp != NULL) {
|
|
||||||
dups++;
|
|
||||||
free(keyt->key - padding);
|
|
||||||
free(keyt);
|
|
||||||
} else {
|
|
||||||
HASH_ADD_KEYPTR(hh,keys,keyt->key,keylen,keyt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (verbose != 0) {
|
|
||||||
unsigned key_count = HASH_COUNT(keys);
|
|
||||||
fprintf(stderr,"max key length: %u\n", max_keylen);
|
|
||||||
fprintf(stderr,"number unique keys: %u\n", key_count);
|
|
||||||
fprintf(stderr,"keystats memory: %u\n",
|
|
||||||
(unsigned)((sizeof(stat_key)+max_keylen)*key_count));
|
|
||||||
hash_chain_len_histogram(keys->hh.tbl);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* add all keys to a new hash, so we can measure add time w/o malloc */
|
|
||||||
gettimeofday(&start_tm,NULL);
|
|
||||||
for(keyt = keys; keyt != NULL; keyt=(stat_key*)keyt->hh.next) {
|
|
||||||
HASH_ADD_KEYPTR(hh2,keys2,keyt->key,keyt->len,keyt);
|
|
||||||
}
|
|
||||||
gettimeofday(&end_tm,NULL);
|
|
||||||
timersub(&end_tm, &start_tm, &elapsed_tm);
|
|
||||||
|
|
||||||
/* now look up all keys in the new hash, again measuring elapsed time */
|
|
||||||
gettimeofday(&start_tm,NULL);
|
|
||||||
for(keyt = keys; keyt != NULL; keyt=(stat_key*)keyt->hh.next) {
|
|
||||||
HASH_FIND(hh2,keys2,keyt->key,keyt->len,keytmp);
|
|
||||||
if (keytmp == NULL) {
|
|
||||||
fprintf(stderr,"internal error, key not found\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gettimeofday(&end_tm,NULL);
|
|
||||||
timersub(&end_tm, &start_tm, &elapsed_tm2);
|
|
||||||
|
|
||||||
/* now delete all items in the new hash, measuring elapsed time */
|
|
||||||
gettimeofday(&start_tm,NULL);
|
|
||||||
while (keys2 != NULL) {
|
|
||||||
keytmp = keys2;
|
|
||||||
HASH_DELETE(hh2,keys2,keytmp);
|
|
||||||
}
|
|
||||||
gettimeofday(&end_tm,NULL);
|
|
||||||
timersub(&end_tm, &start_tm, &elapsed_tm3);
|
|
||||||
|
|
||||||
if (err == 0) {
|
|
||||||
printf("%.3f,%u,%u,%d,%s,%ld,%ld,%ld\n",
|
|
||||||
1-(1.0*keys->hh.tbl->nonideal_items/keys->hh.tbl->num_items),
|
|
||||||
keys->hh.tbl->num_items,
|
|
||||||
keys->hh.tbl->num_buckets,
|
|
||||||
dups,
|
|
||||||
(keys->hh.tbl->noexpand != 0U) ? "nx" : "ok",
|
|
||||||
(elapsed_tm.tv_sec * 1000000) + elapsed_tm.tv_usec,
|
|
||||||
(elapsed_tm2.tv_sec * 1000000) + elapsed_tm2.tv_usec,
|
|
||||||
(elapsed_tm3.tv_sec * 1000000) + elapsed_tm3.tv_usec );
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
42
dependencies/uthash/tests/keystats
vendored
42
dependencies/uthash/tests/keystats
vendored
@@ -1,42 +0,0 @@
|
|||||||
#!/usr/bin/perl
|
|
||||||
|
|
||||||
use strict;
|
|
||||||
|
|
||||||
use FindBin;
|
|
||||||
|
|
||||||
sub usage {
|
|
||||||
print "usage: keystats [-v] keyfile\n";
|
|
||||||
print "usage: keystats [-p <pct> [-v]] keyfile\n";
|
|
||||||
exit -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
usage if ((@ARGV == 0) or ($ARGV[0] eq '-h'));
|
|
||||||
|
|
||||||
my @exes = glob "'$FindBin::Bin/keystat.???'";
|
|
||||||
|
|
||||||
my %stats;
|
|
||||||
for my $exe (@exes) {
|
|
||||||
$exe =~ s/\ /\\ /g;
|
|
||||||
$stats{$exe} = `$exe @ARGV`;
|
|
||||||
delete $stats{$exe} if ($? != 0); # omit hash functions that fail to produce stats (nx)
|
|
||||||
}
|
|
||||||
|
|
||||||
print( "fcn ideal% #items #buckets dup% fl add_usec find_usec del-all usec\n");
|
|
||||||
printf("--- ------ ---------- ---------- ----- -- ---------- ---------- ------------\n");
|
|
||||||
for my $exe (sort statsort keys %stats) {
|
|
||||||
my ($ideal,$items,$bkts,$dups,$ok,$add,$find,$del) = split /,/, $stats{$exe};
|
|
||||||
|
|
||||||
# convert 0-1 values to percentages
|
|
||||||
$dups = $items ? (100.0 * $dups / $items) : 0.0;
|
|
||||||
$ideal = 100.0 * $ideal;
|
|
||||||
|
|
||||||
printf("%3s %5.1f%% %10d %10d %4.0f%% %2s %10d %10d %12d\n", substr($exe,-3,3),
|
|
||||||
$ideal,$items,$bkts,$dups,$ok,$add,$find,$del);
|
|
||||||
}
|
|
||||||
|
|
||||||
# sort on hash_q (desc) then by find_usec (asc)
|
|
||||||
sub statsort {
|
|
||||||
my @a_stats = split /,/, $stats{$a};
|
|
||||||
my @b_stats = split /,/, $stats{$b};
|
|
||||||
return ($b_stats[0] <=> $a_stats[0]) || ($a_stats[-1] <=> $b_stats[-1]);
|
|
||||||
}
|
|
||||||
21
dependencies/uthash/tests/lru_cache/Makefile
vendored
21
dependencies/uthash/tests/lru_cache/Makefile
vendored
@@ -1,21 +0,0 @@
|
|||||||
CC=gcc
|
|
||||||
|
|
||||||
CFLAGS+=-W -Werror -Wall -Wextra -std=c99 \
|
|
||||||
-D_FORTIFY_SOURCE=2 -fstack-protector -g \
|
|
||||||
-Wformat=2 -pedantic -pedantic-errors \
|
|
||||||
-D_GNU_SOURCE=1 -D_BSD_SOURCE=1 \
|
|
||||||
-I../../src
|
|
||||||
|
|
||||||
LDFLAGS+=-pthread
|
|
||||||
|
|
||||||
cache: main.o cache.o
|
|
||||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) main.o cache.o -o cache
|
|
||||||
|
|
||||||
main.o: main.c
|
|
||||||
$(CC) $(CPPFLAGS) $(CFLAGS) -c main.c -o main.o
|
|
||||||
|
|
||||||
cache.o: cache.c
|
|
||||||
$(CC) $(CPPFLAGS) $(CFLAGS) -c cache.c -o cache.o
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f cache *.o
|
|
||||||
221
dependencies/uthash/tests/lru_cache/cache.c
vendored
221
dependencies/uthash/tests/lru_cache/cache.c
vendored
@@ -1,221 +0,0 @@
|
|||||||
/*
|
|
||||||
* =====================================================================================
|
|
||||||
*
|
|
||||||
* Filename: cache.c
|
|
||||||
*
|
|
||||||
* Description: A simple cache
|
|
||||||
*
|
|
||||||
* Version: 1.0
|
|
||||||
* Created: 04/11/2013 02:31:02 PM
|
|
||||||
* Revision: none
|
|
||||||
* Compiler: gcc
|
|
||||||
*
|
|
||||||
* Author: Oliver Lorenz (ol), olli@olorenz.org
|
|
||||||
* Company: https://olorenz.org
|
|
||||||
* License: This is licensed under the same terms as uthash itself
|
|
||||||
*
|
|
||||||
* =====================================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "cache.h"
|
|
||||||
#include "uthash.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A cache entry
|
|
||||||
*/
|
|
||||||
struct foo_cache_entry {
|
|
||||||
char *key; /**<The key */
|
|
||||||
void *data; /**<Payload */
|
|
||||||
UT_hash_handle hh; /**<Hash Handle for uthash */
|
|
||||||
};
|
|
||||||
#define KEY_MAX_LENGTH 32
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A cache object
|
|
||||||
*/
|
|
||||||
struct foo_cache {
|
|
||||||
size_t max_entries; /**<Amount of entries this cache object can hold */
|
|
||||||
pthread_rwlock_t cache_lock; /**<A lock for concurrent access */
|
|
||||||
struct foo_cache_entry *entries; /**<Head pointer for uthash */
|
|
||||||
void (*free_cb) (void *element);/**<Callback function to free cache entries */
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Creates a new cache object
|
|
||||||
|
|
||||||
@param dst
|
|
||||||
Where the newly allocated cache object will be stored in
|
|
||||||
|
|
||||||
@param capacity
|
|
||||||
The maximum number of elements this cache object can hold
|
|
||||||
|
|
||||||
@return EINVAL if dst is NULL, ENOMEM if malloc fails, 0 otherwise
|
|
||||||
*/
|
|
||||||
int foo_cache_create(struct foo_cache **dst, const size_t capacity,
|
|
||||||
void (*free_cb) (void *element))
|
|
||||||
{
|
|
||||||
struct foo_cache *new = NULL;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
if (!dst)
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
if ((new = malloc(sizeof(*new))) == NULL)
|
|
||||||
return ENOMEM;
|
|
||||||
|
|
||||||
if ((rv = pthread_rwlock_init(&(new->cache_lock), NULL)) != 0)
|
|
||||||
goto err_out;
|
|
||||||
|
|
||||||
new->max_entries = capacity;
|
|
||||||
new->entries = NULL;
|
|
||||||
new->free_cb = free_cb;
|
|
||||||
*dst = new;
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err_out:
|
|
||||||
if (new)
|
|
||||||
free(new);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Frees an allocated cache object
|
|
||||||
|
|
||||||
@param cache
|
|
||||||
The cache object to free
|
|
||||||
|
|
||||||
@param keep_data
|
|
||||||
Whether to free contained data or just delete references to it
|
|
||||||
|
|
||||||
@return EINVAL if cache is NULL, 0 otherwise
|
|
||||||
*/
|
|
||||||
int foo_cache_delete(struct foo_cache *cache, int keep_data)
|
|
||||||
{
|
|
||||||
struct foo_cache_entry *entry, *tmp;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
if (!cache)
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
rv = pthread_rwlock_wrlock(&(cache->cache_lock));
|
|
||||||
if (rv)
|
|
||||||
return rv;
|
|
||||||
|
|
||||||
if (keep_data) {
|
|
||||||
HASH_CLEAR(hh, cache->entries);
|
|
||||||
} else {
|
|
||||||
HASH_ITER(hh, cache->entries, entry, tmp) {
|
|
||||||
HASH_DEL(cache->entries, entry);
|
|
||||||
if (cache->free_cb)
|
|
||||||
cache->free_cb(entry->data);
|
|
||||||
free(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(void)pthread_rwlock_unlock(&(cache->cache_lock));
|
|
||||||
(void)pthread_rwlock_destroy(&(cache->cache_lock));
|
|
||||||
free(cache);
|
|
||||||
cache = NULL;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Checks if a given key is in the cache
|
|
||||||
|
|
||||||
@param cache
|
|
||||||
The cache object
|
|
||||||
|
|
||||||
@param key
|
|
||||||
The key to look-up
|
|
||||||
|
|
||||||
@param result
|
|
||||||
Where to store the result if key is found.
|
|
||||||
|
|
||||||
A warning: Even though result is just a pointer,
|
|
||||||
you have to call this function with a **ptr,
|
|
||||||
otherwise this will blow up in your face.
|
|
||||||
|
|
||||||
@return EINVAL if cache is NULL, 0 otherwise
|
|
||||||
*/
|
|
||||||
int foo_cache_lookup(struct foo_cache *cache, char *key, void *result)
|
|
||||||
{
|
|
||||||
int rv;
|
|
||||||
struct foo_cache_entry *tmp = NULL;
|
|
||||||
char **dirty_hack = result;
|
|
||||||
|
|
||||||
if (!cache || !key || !result)
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
rv = pthread_rwlock_wrlock(&(cache->cache_lock));
|
|
||||||
if (rv)
|
|
||||||
return rv;
|
|
||||||
|
|
||||||
HASH_FIND_STR(cache->entries, key, tmp);
|
|
||||||
if (tmp) {
|
|
||||||
size_t key_len = strnlen(tmp->key, KEY_MAX_LENGTH);
|
|
||||||
HASH_DELETE(hh, cache->entries, tmp);
|
|
||||||
HASH_ADD_KEYPTR(hh, cache->entries, tmp->key, key_len, tmp);
|
|
||||||
*dirty_hack = tmp->data;
|
|
||||||
} else {
|
|
||||||
*dirty_hack = result = NULL;
|
|
||||||
}
|
|
||||||
rv = pthread_rwlock_unlock(&(cache->cache_lock));
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Inserts a given <key, value> pair into the cache
|
|
||||||
|
|
||||||
@param cache
|
|
||||||
The cache object
|
|
||||||
|
|
||||||
@param key
|
|
||||||
The key that identifies <value>
|
|
||||||
|
|
||||||
@param data
|
|
||||||
Data associated with <key>
|
|
||||||
|
|
||||||
@return EINVAL if cache is NULL, ENOMEM if malloc fails, 0 otherwise
|
|
||||||
*/
|
|
||||||
int foo_cache_insert(struct foo_cache *cache, char *key, void *data)
|
|
||||||
{
|
|
||||||
struct foo_cache_entry *entry = NULL;
|
|
||||||
struct foo_cache_entry *tmp_entry = NULL;
|
|
||||||
size_t key_len = 0;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
if (!cache || !data)
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
if ((entry = malloc(sizeof(*entry))) == NULL)
|
|
||||||
return ENOMEM;
|
|
||||||
|
|
||||||
if ((rv = pthread_rwlock_wrlock(&(cache->cache_lock))) != 0)
|
|
||||||
goto err_out;
|
|
||||||
|
|
||||||
entry->key = key;
|
|
||||||
entry->data = data;
|
|
||||||
key_len = strnlen(entry->key, KEY_MAX_LENGTH);
|
|
||||||
HASH_ADD_KEYPTR(hh, cache->entries, entry->key, key_len, entry);
|
|
||||||
|
|
||||||
if (HASH_COUNT(cache->entries) >= cache->max_entries) {
|
|
||||||
HASH_ITER(hh, cache->entries, entry, tmp_entry) {
|
|
||||||
HASH_DELETE(hh, cache->entries, entry);
|
|
||||||
if (cache->free_cb)
|
|
||||||
cache->free_cb(entry->data);
|
|
||||||
else
|
|
||||||
free(entry->data);
|
|
||||||
/* free(key->key) if data has been copied */
|
|
||||||
free(entry);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = pthread_rwlock_unlock(&(cache->cache_lock));
|
|
||||||
return rv;
|
|
||||||
|
|
||||||
err_out:
|
|
||||||
if (entry)
|
|
||||||
free(entry);
|
|
||||||
(void)pthread_rwlock_unlock(&(cache->cache_lock));
|
|
||||||
return rv;
|
|
||||||
|
|
||||||
}
|
|
||||||
31
dependencies/uthash/tests/lru_cache/cache.h
vendored
31
dependencies/uthash/tests/lru_cache/cache.h
vendored
@@ -1,31 +0,0 @@
|
|||||||
/*
|
|
||||||
* =====================================================================================
|
|
||||||
*
|
|
||||||
* Filename: cache.h
|
|
||||||
*
|
|
||||||
* Description: A simple cache
|
|
||||||
*
|
|
||||||
* Version: 1.0
|
|
||||||
* Created: 04/11/2013 02:30:46 PM
|
|
||||||
* Revision: none
|
|
||||||
* Compiler: gcc
|
|
||||||
*
|
|
||||||
* Author: Oliver Lorenz (ol), olli@olorenz.org
|
|
||||||
* Company: https://olorenz.org
|
|
||||||
* License: This is licensed under the same terms as uthash itself
|
|
||||||
*
|
|
||||||
* =====================================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _CACHE_
|
|
||||||
#define _CACHE_
|
|
||||||
|
|
||||||
struct foo_cache;
|
|
||||||
|
|
||||||
extern int foo_cache_create(struct foo_cache **dst, const size_t capacity,
|
|
||||||
void (*free_cb) (void *element));
|
|
||||||
extern int foo_cache_delete(struct foo_cache *cache, int keep_data);
|
|
||||||
extern int foo_cache_lookup(struct foo_cache *cache, char *key, void *result);
|
|
||||||
extern int foo_cache_insert(struct foo_cache *cache, char *key, void *data);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
191
dependencies/uthash/tests/lru_cache/main.c
vendored
191
dependencies/uthash/tests/lru_cache/main.c
vendored
@@ -1,191 +0,0 @@
|
|||||||
#include <errno.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include "cache.h"
|
|
||||||
|
|
||||||
#define MAX_RANDOM_ENTRIES 32
|
|
||||||
|
|
||||||
struct key_record {
|
|
||||||
char *key;
|
|
||||||
char *value;
|
|
||||||
};
|
|
||||||
|
|
||||||
int generate_random_entry(struct key_record **entry);
|
|
||||||
int generate_random_string(char **dst, const size_t len);
|
|
||||||
void free_random_entry(void *entry);
|
|
||||||
|
|
||||||
void *producer(void *arg)
|
|
||||||
{
|
|
||||||
struct foo_cache *cache = arg;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < MAX_RANDOM_ENTRIES; i++) {
|
|
||||||
struct key_record *entry = NULL;
|
|
||||||
if (generate_random_entry(&entry)) {
|
|
||||||
fprintf(stderr, "generate_random_entry() failed\n");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
#if defined(DEBUG)
|
|
||||||
printf("Random Entry:\n");
|
|
||||||
printf(" key: %s\n", entry->key);
|
|
||||||
printf(" Key: %s\n", entry->value);
|
|
||||||
#else
|
|
||||||
printf("inserted %s (%d)\n", entry->key,
|
|
||||||
(int)strlen(entry->key));
|
|
||||||
#endif
|
|
||||||
if (foo_cache_insert(cache, entry->key, entry)) {
|
|
||||||
fprintf(stderr, "foo_cache_insert() failed\n");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_exit(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *consumer(void *arg)
|
|
||||||
{
|
|
||||||
struct foo_cache *cache = arg;
|
|
||||||
struct key_record *result = NULL;
|
|
||||||
char *buffer = malloc(64);
|
|
||||||
char key[33];
|
|
||||||
int stop = 0;
|
|
||||||
|
|
||||||
if (!buffer)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/* give producer time to populate the cache */
|
|
||||||
sleep(2);
|
|
||||||
printf("\n\n");
|
|
||||||
|
|
||||||
do {
|
|
||||||
memset(key, 0, 64);
|
|
||||||
result = NULL;
|
|
||||||
|
|
||||||
printf("Enter key for lookup: ");
|
|
||||||
fgets(buffer, sizeof(key), stdin);
|
|
||||||
sscanf(buffer, "%s\n", key);
|
|
||||||
/* read '\n' from stdin */
|
|
||||||
getchar();
|
|
||||||
|
|
||||||
if (strncmp(key, "exit", 4) == 0) {
|
|
||||||
stop = 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Got key %s (%d)\n", key, (int)strlen(key));
|
|
||||||
|
|
||||||
if (foo_cache_lookup(cache, key, &result)) {
|
|
||||||
fprintf(stderr, "Could not retrieve key %s\n", key);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result) {
|
|
||||||
printf("MISS\n");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("HIT\n");
|
|
||||||
printf("key: %s\n", result->key);
|
|
||||||
printf("key : %s\n", result->value);
|
|
||||||
} while (!stop);
|
|
||||||
|
|
||||||
out:
|
|
||||||
if (buffer)
|
|
||||||
free(buffer);
|
|
||||||
pthread_exit(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
int rv;
|
|
||||||
struct foo_cache *cache = NULL;
|
|
||||||
pthread_t workers[2];
|
|
||||||
|
|
||||||
rv = foo_cache_create(&cache, MAX_RANDOM_ENTRIES / 2,
|
|
||||||
free_random_entry);
|
|
||||||
if (rv) {
|
|
||||||
fprintf(stderr, "Could not create cache\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
(void)pthread_create(&workers[0], NULL, producer, (void *)cache);
|
|
||||||
(void)pthread_create(&workers[1], NULL, consumer, (void *)cache);
|
|
||||||
|
|
||||||
pthread_join(workers[0], NULL);
|
|
||||||
pthread_join(workers[1], NULL);
|
|
||||||
|
|
||||||
(void)foo_cache_delete(cache, 0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int generate_random_entry(struct key_record **entry)
|
|
||||||
{
|
|
||||||
struct key_record *new = NULL;
|
|
||||||
char *key = NULL;
|
|
||||||
char *value = NULL;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
if (!entry)
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
rv = generate_random_string(&key, 33);
|
|
||||||
if (rv)
|
|
||||||
return rv;
|
|
||||||
|
|
||||||
rv = generate_random_string(&value, 129);
|
|
||||||
if (rv)
|
|
||||||
return rv;
|
|
||||||
|
|
||||||
if ((new = malloc(sizeof(*new))) == NULL) {
|
|
||||||
free(key);
|
|
||||||
free(value);
|
|
||||||
return ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
new->key = key;
|
|
||||||
new->value = value;
|
|
||||||
|
|
||||||
*entry = new;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int generate_random_string(char **dst, const size_t len)
|
|
||||||
{
|
|
||||||
static const char alphanum[] =
|
|
||||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
||||||
size_t i;
|
|
||||||
char *s;
|
|
||||||
|
|
||||||
if (!dst || len == 0)
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
if ((s = malloc(len)) == NULL)
|
|
||||||
return ENOMEM;
|
|
||||||
|
|
||||||
for (i = 0; i < len - 1; i++) {
|
|
||||||
s[i] = alphanum[rand() % (sizeof(alphanum) - 1)];
|
|
||||||
}
|
|
||||||
|
|
||||||
s[len - 1] = '\0';
|
|
||||||
*dst = s;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void free_random_entry(void *entry)
|
|
||||||
{
|
|
||||||
#if defined(DEBUG)
|
|
||||||
fprintf(stderr, "In %s: entry @ %p\n", __func__, entry);
|
|
||||||
#endif
|
|
||||||
struct key_record *record = entry;
|
|
||||||
if (!record)
|
|
||||||
return;
|
|
||||||
if (record->key)
|
|
||||||
free(record->key);
|
|
||||||
if (record->value)
|
|
||||||
free(record->value);
|
|
||||||
free(record);
|
|
||||||
record = NULL;
|
|
||||||
}
|
|
||||||
28
dependencies/uthash/tests/simkeys.pl
vendored
28
dependencies/uthash/tests/simkeys.pl
vendored
@@ -1,28 +0,0 @@
|
|||||||
#!/usr/bin/perl
|
|
||||||
|
|
||||||
# This program generates a simkey10.dat (100, 1000, etc) each
|
|
||||||
# containing 100 random keys of length 10 (100, 1000, etc).
|
|
||||||
# These files can then be fed into keystats to observe that
|
|
||||||
# the time to add or find the keys is directly proportional to
|
|
||||||
# keylength n [in other words, O(n)].
|
|
||||||
#
|
|
||||||
# The conclusion is that really long keys (e.g. 100k) are not
|
|
||||||
# efficient. TDH 23Jan07
|
|
||||||
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
|
|
||||||
|
|
||||||
#for my $len (10,100,1000,10000,100000,1000000) {
|
|
||||||
for my $len (100) {
|
|
||||||
open OUTFILE, ">simkeys$len.dat" or die "can't open: $!\n";
|
|
||||||
# we'll do 100 keys of $len
|
|
||||||
print "keylen $len\n";
|
|
||||||
for my $i (0..99) {
|
|
||||||
my $key = pack "I", $len;
|
|
||||||
$key .= pack "C", (int(rand(256))) for (1..$len);
|
|
||||||
print OUTFILE $key;
|
|
||||||
}
|
|
||||||
close OUTFILE;
|
|
||||||
}
|
|
||||||
|
|
||||||
32
dependencies/uthash/tests/sleep_test.c
vendored
32
dependencies/uthash/tests/sleep_test.c
vendored
@@ -1,32 +0,0 @@
|
|||||||
#include "uthash.h"
|
|
||||||
#include <stdlib.h> /* malloc */
|
|
||||||
#include <stdio.h> /* printf */
|
|
||||||
#include <unistd.h> /* getpid */
|
|
||||||
|
|
||||||
typedef struct example_user_t {
|
|
||||||
int id;
|
|
||||||
int cookie;
|
|
||||||
UT_hash_handle hh;
|
|
||||||
} example_user_t;
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
example_user_t *user, *users=NULL;
|
|
||||||
|
|
||||||
/* create elements */
|
|
||||||
for(i=0; i<10000; i++) {
|
|
||||||
if ( (user = (example_user_t*)malloc(sizeof(example_user_t))) == NULL) {
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
user->id = i;
|
|
||||||
user->cookie = i*i;
|
|
||||||
HASH_ADD_INT(users,id,user);
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("pid: %u\n", (unsigned)getpid());
|
|
||||||
/* printf("sig: %p\n", &users->hh.tbl->signature); */
|
|
||||||
/* printf("bbv: %p\n", &users->hh.tbl->bloom_bv); */
|
|
||||||
sleep(60*10);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
34
dependencies/uthash/tests/tdiff.cpp
vendored
34
dependencies/uthash/tests/tdiff.cpp
vendored
@@ -1,34 +0,0 @@
|
|||||||
// Windows does not have unix diff so this is a simple replacement
|
|
||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
|
||||||
using namespace std;
|
|
||||||
int main(int argc, char *argv[] ) {
|
|
||||||
int rc=-1;
|
|
||||||
if (argc != 3) {
|
|
||||||
cout << "usage: " << argv[0] << " file1 file2\n";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
char *file1 = argv[1];
|
|
||||||
char *file2 = argv[2];
|
|
||||||
ifstream is1(file1, ios::in);
|
|
||||||
ifstream is2(file2, ios::in);
|
|
||||||
if (is1.fail()) {cerr << "failed to open " << file1 << "\n"; goto done;}
|
|
||||||
if (is2.fail()) {cerr << "failed to open " << file2 << "\n"; goto done;}
|
|
||||||
char d1[256], d2[256];
|
|
||||||
do {
|
|
||||||
is1.read(d1,sizeof(d1));
|
|
||||||
is2.read(d2,sizeof(d2));
|
|
||||||
if ((is1.gcount() != is2.gcount()) || memcmp(d1,d2,is1.gcount())) {
|
|
||||||
cout << file1 << " and " << file2 << " differ\n";
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
} while (!is1.eof() && !is2.eof());
|
|
||||||
|
|
||||||
rc=0;
|
|
||||||
|
|
||||||
done:
|
|
||||||
is1.close();
|
|
||||||
is2.close();
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
10
dependencies/uthash/tests/test1.ans
vendored
10
dependencies/uthash/tests/test1.ans
vendored
@@ -1,10 +0,0 @@
|
|||||||
user 0, cookie 0
|
|
||||||
user 1, cookie 1
|
|
||||||
user 2, cookie 4
|
|
||||||
user 3, cookie 9
|
|
||||||
user 4, cookie 16
|
|
||||||
user 5, cookie 25
|
|
||||||
user 6, cookie 36
|
|
||||||
user 7, cookie 49
|
|
||||||
user 8, cookie 64
|
|
||||||
user 9, cookie 81
|
|
||||||
31
dependencies/uthash/tests/test1.c
vendored
31
dependencies/uthash/tests/test1.c
vendored
@@ -1,31 +0,0 @@
|
|||||||
#include "uthash.h"
|
|
||||||
#include <stdlib.h> /* malloc */
|
|
||||||
#include <stdio.h> /* printf */
|
|
||||||
|
|
||||||
typedef struct example_user_t {
|
|
||||||
int id;
|
|
||||||
int cookie;
|
|
||||||
UT_hash_handle hh;
|
|
||||||
} example_user_t;
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
example_user_t *user, *users=NULL;
|
|
||||||
|
|
||||||
/* create elements */
|
|
||||||
for(i=0; i<10; i++) {
|
|
||||||
user = (example_user_t*)malloc(sizeof(example_user_t));
|
|
||||||
if (user == NULL) {
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
user->id = i;
|
|
||||||
user->cookie = i*i;
|
|
||||||
HASH_ADD_INT(users,id,user);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(user=users; user != NULL; user=(example_user_t*)(user->hh.next)) {
|
|
||||||
printf("user %d, cookie %d\n", user->id, user->cookie);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
4
dependencies/uthash/tests/test10.ans
vendored
4
dependencies/uthash/tests/test10.ans
vendored
@@ -1,4 +0,0 @@
|
|||||||
9 found in hh
|
|
||||||
9 found in alth
|
|
||||||
10 not found in hh
|
|
||||||
10 found in alth
|
|
||||||
54
dependencies/uthash/tests/test10.c
vendored
54
dependencies/uthash/tests/test10.c
vendored
@@ -1,54 +0,0 @@
|
|||||||
#include "uthash.h"
|
|
||||||
#include <stdlib.h> /* malloc */
|
|
||||||
#include <stdio.h> /* printf */
|
|
||||||
|
|
||||||
typedef struct example_user_t {
|
|
||||||
int id;
|
|
||||||
int cookie;
|
|
||||||
UT_hash_handle hh;
|
|
||||||
UT_hash_handle alth;
|
|
||||||
} example_user_t;
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
example_user_t *user, *tmp, *users=NULL, *altusers=NULL;
|
|
||||||
|
|
||||||
/* create elements */
|
|
||||||
for(i=0; i<1000; i++) {
|
|
||||||
user = (example_user_t*)malloc(sizeof(example_user_t));
|
|
||||||
if (user == NULL) {
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
user->id = i;
|
|
||||||
user->cookie = i*i;
|
|
||||||
if (i<10) {
|
|
||||||
HASH_ADD_INT(users,id,user);
|
|
||||||
}
|
|
||||||
HASH_ADD(alth,altusers,id,sizeof(int),user);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
printf("hh items: %d, alth items: %d\n",
|
|
||||||
users->hh.tbl->num_items, users->alth.tbl->num_items);
|
|
||||||
printf("hh buckets: %d, alth buckets: %d\n",
|
|
||||||
users->hh.tbl->num_buckets, users->alth.tbl->num_buckets);
|
|
||||||
*/
|
|
||||||
|
|
||||||
i=9;
|
|
||||||
HASH_FIND_INT(users,&i,tmp);
|
|
||||||
printf("%d %s in hh\n", i, (tmp != NULL) ? "found" : "not found");
|
|
||||||
HASH_FIND(alth,altusers,&i,sizeof(int),tmp);
|
|
||||||
printf("%d %s in alth\n", i, (tmp != NULL) ? "found" : "not found");
|
|
||||||
|
|
||||||
i=10;
|
|
||||||
HASH_FIND_INT(users,&i,tmp);
|
|
||||||
printf("%d %s in hh\n", i, (tmp != NULL) ? "found" : "not found");
|
|
||||||
HASH_FIND(alth,altusers,&i,sizeof(int),tmp);
|
|
||||||
printf("%d %s in alth\n", i, (tmp != NULL) ? "found" : "not found");
|
|
||||||
|
|
||||||
HASH_CLEAR(hh, users);
|
|
||||||
HASH_CLEAR(alth, altusers);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
51
dependencies/uthash/tests/test11.ans
vendored
51
dependencies/uthash/tests/test11.ans
vendored
@@ -1,51 +0,0 @@
|
|||||||
ADRIAN
|
|
||||||
ARNOLDO
|
|
||||||
CARROLL
|
|
||||||
CARY
|
|
||||||
CHONG
|
|
||||||
CLIFTON
|
|
||||||
CODY
|
|
||||||
COLTON
|
|
||||||
CORNELL
|
|
||||||
DAMON
|
|
||||||
DANNIE
|
|
||||||
DARIO
|
|
||||||
DONN
|
|
||||||
DOUG
|
|
||||||
DOUGLAS
|
|
||||||
FREDERICK
|
|
||||||
FRITZ
|
|
||||||
GERALD
|
|
||||||
GUS
|
|
||||||
HARVEY
|
|
||||||
IRVING
|
|
||||||
ISAIAH
|
|
||||||
JARVIS
|
|
||||||
JOHN
|
|
||||||
KENTON
|
|
||||||
LAURENCE
|
|
||||||
LESTER
|
|
||||||
LINCOLN
|
|
||||||
LOWELL
|
|
||||||
NELSON
|
|
||||||
NEVILLE
|
|
||||||
NIGEL
|
|
||||||
NORMAND
|
|
||||||
ODIS
|
|
||||||
OMAR
|
|
||||||
ORLANDO
|
|
||||||
RAYMUNDO
|
|
||||||
REX
|
|
||||||
ROLANDO
|
|
||||||
RON
|
|
||||||
SHANE
|
|
||||||
TONEY
|
|
||||||
TRINIDAD
|
|
||||||
WALTER
|
|
||||||
WARNER
|
|
||||||
WARREN
|
|
||||||
WES
|
|
||||||
WILLARD
|
|
||||||
WILLIAM
|
|
||||||
WINFRED
|
|
||||||
XAVIER
|
|
||||||
57
dependencies/uthash/tests/test11.c
vendored
57
dependencies/uthash/tests/test11.c
vendored
@@ -1,57 +0,0 @@
|
|||||||
#include "uthash.h"
|
|
||||||
#include <stdlib.h> /* malloc */
|
|
||||||
#include <errno.h> /* perror */
|
|
||||||
#include <stdio.h> /* printf */
|
|
||||||
|
|
||||||
#define BUFLEN 20
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
/* Print a message if the hash's no-expand flag is set. */
|
|
||||||
#undef uthash_noexpand_fyi
|
|
||||||
#undef uthash_expand_fyi
|
|
||||||
#define uthash_noexpand_fyi(tbl) printf("noexpand set\n");
|
|
||||||
#define uthash_expand_fyi(tbl) printf("hash expanded\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct name_rec {
|
|
||||||
char boy_name[BUFLEN];
|
|
||||||
UT_hash_handle hh;
|
|
||||||
} name_rec;
|
|
||||||
|
|
||||||
static int namecmp(void *_a, void *_b)
|
|
||||||
{
|
|
||||||
name_rec *a = (name_rec*)_a;
|
|
||||||
name_rec *b = (name_rec*)_b;
|
|
||||||
return strcmp(a->boy_name,b->boy_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
name_rec *name, *names=NULL;
|
|
||||||
char linebuf[BUFLEN];
|
|
||||||
FILE *file;
|
|
||||||
|
|
||||||
file = fopen( "test11.dat", "r" );
|
|
||||||
if (file == NULL) {
|
|
||||||
perror("can't open: ");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (fgets(linebuf,BUFLEN,file) != NULL) {
|
|
||||||
name = (name_rec*)malloc(sizeof(name_rec));
|
|
||||||
if (name == NULL) {
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
strcpy(name->boy_name, linebuf);
|
|
||||||
HASH_ADD_STR(names,boy_name,name);
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(file);
|
|
||||||
HASH_SORT(names,namecmp);
|
|
||||||
for(name=names; name!=NULL; name=(name_rec*)(name->hh.next)) {
|
|
||||||
printf("%s",name->boy_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user