359 Commits

Author SHA1 Message Date
Toni Uhlig
785603c276 Clone nDPI as submodule via CMake (if BUILD_NDPI=ON).
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-05-11 14:18:31 +02:00
Toni Uhlig
5b0a751159 README.md updated
* removed all those outdated Makefile.old references
 * added additional CMake build instructions

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-05-10 15:38:01 +02:00
Toni Uhlig
93bff603d0 py-flow-info: Support for --ipwhois, --new and --detection.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-04-30 22:39:11 +02:00
Toni Uhlig
4e3dda70a3 Unified and improved dependency build/mgmt via CMake and travis-ci.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-04-22 17:37:55 +02:00
Toni Uhlig
174bd89d8e Added JA3 blacklist downloader/checker from abuse.ch
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-04-17 16:45:03 +02:00
Toni Uhlig
a119a72d13 Added python example to check JA3 hashes against known hashes via JA3er.com
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-04-16 13:30:24 +02:00
Toni Uhlig
a0fa598cee travis-ci build CMake project
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-04-15 11:21:37 +02:00
Toni Uhlig
0c034ffba1 added some CMake install targets
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-04-15 00:07:40 +02:00
Toni Uhlig
713cd13322 added TODO
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-04-14 22:17:39 +02:00
Toni Uhlig
f713ec702b Added nDPId semantic validation test.
* fixed inconsistent processing of remaining flows during nDPId shutdown phase
 * fixed multiple `detected' flow events
   (instead only `detection-update' flow events can occur after a `detected' flow event)
 * fixed nDPIsrvd.py invalid message buffer handling
 * improved run_tests.sh so only valid pcap capture files are getting processed
   (and some more cosmetics + logging)

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-04-14 22:04:42 +02:00
Toni Uhlig
514c427917 Fixed nDPIsrvd.h flow end bug (flow end callback could never be called and caused some memory troubles).
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-04-11 12:34:38 +02:00
Toni Uhlig
131cf5385b Added IPv6 support for -I / -E.
* added another Python search path and try-catch ModuleNotFoundError again
 * run_tests.sh checks for OpenBSD netcat (required for -q)

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-04-11 00:16:52 +02:00
Toni Uhlig
ba586e1ecf nDPId-test: mimic full nDPId lifecycle
* generate DAEMON_EVENT_INIT as well as DAEMON_EVENT_SHUTDOWN
 * process remaining flows before shutdown (and generate events)

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-04-09 14:43:28 +02:00
Toni Uhlig
4e583cd4de Added JSON schema validation to run_tests.sh
* Python3 scripts are now compatible with versions <3.6
 * improved and prettified run_tests.sh

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-04-09 14:12:48 +02:00
Toni Uhlig
0a7ad7a76a nDPId-test: added JSON distribution + JSON parsing (Multithreaded design re-using most of nDPId/nDPIsrvd core)
* improved Makefile.old install targets
 * splitted nDPIsrvd_parse into nDPIsrvd_parse_line and nDPIsrvd_parse_all for the sake of readability
 * minor Python script improvments (check for nDPIsrvd.py on multiple locations, may be superseeded by setuptools in the future)
 * some paths needs to be absolute (chdir() during daemonize) and therefor additional checks introduced
 * test run script checks and fails if certain files are are missing (PCAP file <=> result output file)
 * removed not very useful "internal format error" JSON serialization if a BUG for same exists
 * fixed invalid l4 type statistics counters for nDPIsrvd-collectd

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-04-09 00:18:35 +02:00
Toni Uhlig
e576162a43 Reverted internal ndpi structs to dynamic memory allocation hoping that it will help to reduce the average memory consumption.
How? After the detection finished, internal ndpi structs can be free'd as they are not needed anymore.

 * Set the amount of max. packets to process via subopt.

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-03-26 14:46:35 +01:00
Toni Uhlig
38c6904bff run_tests.sh checks/print-stderr-on-fail
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-03-24 15:04:59 +01:00
Toni Uhlig
ef4a22ffac Updated README.md
* added examples/README.md and schema/README.md

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-03-24 14:44:27 +01:00
Toni Uhlig
1ab6b9e042 Updated test outputs and pinned travis-ci's nDPI git repo sync to a specific commit hash.
* fixed Makefile.old COpts hell

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-03-24 13:16:55 +01:00
Toni Uhlig
77b4b88b14 Added diff'able nDPId-test JSON dumps and travis-ci integration.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-03-24 11:33:04 +01:00
Toni Uhlig
e835d36f63 Added nDPId-test as all-in-one JSON dumper.
* fixed invalid flow event schema type
 * added run_tests.sh to generate/diff JSON dumps
 * renamed lot's of vars/fns in nDPId.c/nDPIsrvd.c, so nDPId-test.c can include "*.c"
 * improved CMake dependency checks

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-03-24 11:28:56 +01:00
Toni Uhlig
bdc8c5df2a Reduced code duplication. Preps for nDPId-test.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-03-23 14:25:56 +01:00
Toni Uhlig
c68c1750ba Switched to CMake build system.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-03-17 17:41:19 +01:00
Toni Uhlig
1c3ef69faa nDPIsrvd collectd-exec overhaul.
* Install targets updated.
 * Removed nDPIsrvd.h token validation function (done automatically by token_get).

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-03-15 14:39:43 +01:00
Toni Uhlig
9a06b97473 Fixed collectd-exec issues.
* Added collectd example config and types.db

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-03-12 17:30:04 +01:00
Toni Uhlig
772b67b767 Added basic collectd-exec example.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-03-10 18:32:09 +01:00
Toni Uhlig
412d8feba0 Added missing enum strings.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-03-09 17:46:16 +01:00
Toni Uhlig
1073c9626b nDPIsrvd refactoring
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-03-05 12:24:24 +01:00
Toni Uhlig
4699263d65 Fixed possible NULL pointer deref in dependencies/nDPIsrvd.h and examples/c-captured/c-captured.c
* `make install` C examples
 * examples/py-flow-info/flow-info.py supports filtering
 * added breed to examples/py-flow-info/flow-info.py

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-02-28 11:27:22 +01:00
Toni Uhlig
06ff353099 Added JA3 / SSL SHA1 fingerprint blacklists.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-02-27 17:47:49 +01:00
Toni Uhlig
e0310d7e1d Finalized examples/c-captured to dump packet bytes to PCAP for further analysis.
* Fixed memory holes in nDPId structs.

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-02-24 18:43:26 +01:00
Toni Uhlig
0b5b177c14 Extended nDPIsrvd.h with address parsing.
* nDPId supports looading of custom nDPI protocol/category files
 * extended JSON schemas according to nDPI / nDPId JSON serializing
 * removed memory holes in nDPId
 * extended examples/c-captured

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-02-23 14:46:47 +01:00
Toni Uhlig
0a6d44dc60 C-Api uses similiar flow key hash calculation as Python-Api.
* Make use of flow id / alias / source which is required for future use cases where multiple nDPId instances (same / different machines) feed one nDPIsrvd.

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-02-19 18:39:14 +01:00
Toni Uhlig
9ccd52134b Reverted f8692c6be1
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-02-17 14:53:04 +01:00
Toni Uhlig
a1805eb891 Added JSON schema files and a Python schema validator.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-02-17 14:00:10 +01:00
Toni Uhlig
893f437051 Aligned nDPIsrvd.py to nDPIsrvd.h for consistency.
* Simplified Python interface as well.
 * c-captured and flow-undetected-to-pcap.py produce similiar results
 * Removed overloaded nDPIsrvd.py event structures.
 * flow-info.py prints (with a color-hash) additional information e.g. alias/source and midstream

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-02-16 20:43:00 +01:00
Toni Uhlig
7218990e58 Removed superfluous nDPI (header) dependency for examples/c-captured. Use header definitions from libc instead.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-02-06 16:01:54 +01:00
Toni Uhlig
69b4e662c7 nDPIsrvd C API overhaul and massive simplification.
* nDPIsrvd.h does flow mgmt out of the box
 * dissect received JSON strings via callback
 * added new JSON key/values for packet-flows (usecTimestamp/L3/L4 info)

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-02-06 15:41:30 +01:00
Toni Uhlig
257cce1dcc Fixed braindead failure - Increase of NETWORK_BUFFER_MAX_SIZE > 9999 means to also increase NETWORK_BUFFER_LENGTH_DIGITS to 5.
* Fixed ARM32 xcompile warnings; Other GCC versions, other uint64_t's..
 * Replaced ridiculous nDPIsrvd_JSON_BYTES with NETWORK_BUFFER_LENGTH_DIGITS.

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-01-27 18:05:13 +01:00
Toni Uhlig
9564b0ce2c Increased JSON buffer size to 12288 (libnDPI serializes more and more information).
* Making Compare&Fetch mandatory.
 * Added some more Compare&Fetch to prevent TSAN complaining about data races.
   Fixed possible but more ore less harmless data races during shutdown process.
 * Shrink SIGNAL handler to a minimum. SYSV Signal handling and MT-safety is awkward.

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-01-27 17:19:29 +01:00
Toni Uhlig
102b61175c All colors are beautiful for py-flow-info.
* instance alias is now mandatory (will be required for future use-cases)

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-01-26 19:07:16 +01:00
Toni Uhlig
25c23e3d23 Finished (mostly) C guessed/undetected flow dumper.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-01-26 18:31:24 +01:00
Toni Uhlig
a94ae79cc4 Switched to a slightly modified base64 encoder basicially from wikibooks.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-01-20 16:15:42 +01:00
Toni Uhlig
3a1afb9434 Added simple python json out example used for comparing JSON output.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-01-20 16:10:16 +01:00
Toni Uhlig
f5d5c076a3 Added internal/external packet processing modes. #2
* fixed incorrect handling of skipped flows

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-01-10 19:12:05 +01:00
Toni Uhlig
b82a535a45 Increased network buffer size for python scripts (and raise exception if capacity reached).
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-01-08 10:54:44 +01:00
Toni Uhlig
f637802c0a Added internal/external packet processing modes.
* only IPv4 supported for now
 * refactored nDPId's internal IP address storage
 * use fresh ndpi_free_flow_data() to free nDPI's dynamic allocated data

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-01-06 19:42:00 +01:00
Toni Uhlig
613e60ca2a Declared nDPI id and flow structs in nDPId flow info struct.
Two reasons:
 * reduce heap memory allocations
 * nDPId flow info struct may be inflated in the future (more bytes to compress)

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-12-30 13:28:32 +01:00
Toni Uhlig
e0be911633 Set an optional bpf filter string for pcap based packet capture.
* serialize and send datalink layer type

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-12-29 17:52:37 +01:00
Toni Uhlig
9f3d7b479c nDPIsrvd: Distributor clients which are too slow can cause buffer bloat.
Switching back to blocking mode works as a quick fix but is not sufficient.
          See comments.

 * nDPId prints more accurate error messages if command line argument validation failed

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-12-19 20:51:21 +01:00
Toni Uhlig
b37cafe910 Improved testing capabilities via Makefile.
* increased nDPId_MAX_READER_THREADS to 32

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-12-15 10:32:16 +01:00
Toni Uhlig
cda7124fa5 nDPId option validation is now less restrictive
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-12-14 23:27:20 +01:00
Toni Uhlig
e864787848 Generic nDPIsrvd.h event handling integration and flow tracking with uthash.
* aims to be re-usable for many different apps and use-cases

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-12-06 17:01:06 +01:00
Toni Uhlig
00e5132a80 Merge commit 'c8bf38e5fb717d40635a2a89b22ed71b0de4266b' as 'dependencies/uthash' 2020-12-01 13:33:34 +01:00
Toni Uhlig
c8bf38e5fb Squashed 'dependencies/uthash/' content from commit 8e67ced
git-subtree-dir: dependencies/uthash
git-subtree-split: 8e67ced1d1c5bd8141c542a22630e6de78aa6b90
2020-12-01 13:33:34 +01:00
Toni Uhlig
32b192df3b Removed all LUA leftover as preparation for UTHASH.
* Added UNIX socket support for nDPIsrvd.h (and c-captured)

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-12-01 13:31:07 +01:00
Toni Uhlig
6f734f9eff Set rpath when compiling with use of pkg-config.
* New Makefile target: mocksrvd, fake nDPIsrvd instamce

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-12-01 10:28:44 +01:00
Toni Uhlig
f8692c6be1 Let travis use pcap files from nDPI to produce some JSON output. (disabled, needs further testing..)
* Added pcap diff script
 * Added \n to JSON string end (useful for debugging and readability)
 * Use first host/server name character for hash calculation as well
 * Removed error'ing EPOLLHUP handling in nDPIsrvd (connection closing will be detected via read())

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-12-01 10:22:47 +01:00
Toni Uhlig
a9b87e5739 Moved datalink layer processing into a separate function.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-11-18 18:16:00 +01:00
Toni Uhlig
ba179cc724 Increased serialization buffer size as required for recent nDPI builds.
* added .gitignore to prevent SCM check-ins of unwanted files
 * fixed invalid include path for python examples

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-11-18 18:12:38 +01:00
Toni Uhlig
dd5ff3b8ed Refactored Makefile pkg-config part to be able to work with multiple pkg-config based projects.
* added lua as new dependency for examples/c-captured
 * improved `make help' print

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-11-18 13:26:12 +01:00
Toni Uhlig
54dd72676d Provide functionality do deal with tokens from JSMN.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-11-17 19:29:10 +01:00
Toni Uhlig
a03e0c8ba8 Fixed inconsistent callback function call if a token parsed.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-11-13 19:55:54 +01:00
Toni Uhlig
aac0e77a2c Add 'dependencies/jsmn/' from commit '053d3cd29200edb1bfd181d917d140c16c1f8834'
git-subtree-dir: dependencies/jsmn
git-subtree-mainline: f79991d70c
git-subtree-split: 053d3cd292
2020-11-13 14:45:57 +01:00
Toni Uhlig
f79991d70c Re-Cloned JSMN as subtree.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-11-13 14:44:39 +01:00
Toni Uhlig
7853c79cd6 Renamed misleading "contrib" subfolder to "dependencies".
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-11-13 14:26:51 +01:00
Toni Uhlig
7362da8c5f Finished unfinished nDPIsrvd.h helper functionality.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-11-13 14:09:19 +01:00
Toni Uhlig
8c81c7c8db Fixed `make help' printout issues.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-11-11 17:32:02 +01:00
Toni Uhlig
41729212e5 More detailed `nanosleep() before close()' description.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-11-11 16:17:14 +01:00
Toni Uhlig
fa25caf92d Improved default capture device handling if none set via `-i'.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-11-08 17:41:12 +01:00
Toni Uhlig
ca28fd9a03 Make sure that DAEMON_EVENT_SHUTDOWN gets transmitted before close() (hacky, but works).
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-11-08 15:14:08 +01:00
Toni Uhlig
54f6ab2192 forced --prefix for travis-ci
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-11-04 11:46:55 +01:00
Toni Uhlig
97563faaa3 Added json-c to package dependencies required for libndpi unit tests.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-10-28 13:56:43 +01:00
Toni Uhlig
279868f4c8 build go example only if GOCC set
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-10-17 12:50:24 +02:00
Toni Uhlig
8cbac2ac88 cosmetics, exception verbosity
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-10-17 12:42:26 +02:00
Toni Uhlig
ec9dc251d2 py-flow-info: Print BasicEvents.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-10-12 20:02:20 +02:00
Toni Uhlig
3cfb4d9c73 nDPId/nDPIsrvd: change_user_group does now chown/chmod collector/distributor socket paths
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-10-07 14:38:10 +02:00
Toni Uhlig
4dd4537524 Travi CI
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-10-06 18:51:02 +02:00
Toni Uhlig
f3fd51a74f nDPId: Fixed BUG if flow was guessed but "Unknown" detected protocol serialized.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-10-01 19:16:21 +02:00
Toni Uhlig
9ea078b05e Improved python event validation with focus on readability.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-09-30 20:35:28 +02:00
Toni Uhlig
2f7a052f43 Centralized some python code parts to make future modifications easier.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-09-29 18:37:24 +02:00
Toni Uhlig
1a7342e798 nDPIsrvd: Added very important warning and use UDS now by default.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-09-29 18:05:31 +02:00
Toni Uhlig
9e4e41e5f6 Updated README.md ASCII architecture.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-09-29 17:43:36 +02:00
Toni Uhlig
f60e7a097d nDPIsrvd: Improved UDS related code parts.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-09-29 17:33:41 +02:00
Toni Uhlig
a2bf721722 Added additional datalink header check for DLT_NULL/IPv6.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-09-28 21:13:23 +02:00
Toni Uhlig
ab10561dcd Added alias support for an nDPId instance.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-09-27 00:44:52 +02:00
Toni Uhlig
458b77c3eb Added daemon events.
* Added missing IPv4 / IPv6 datalink layer (if no layer2 protocol used).

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-09-27 00:15:37 +02:00
Toni Uhlig
04bbbcd055 Improved event parsing for Python scripts.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-09-26 15:15:15 +02:00
Toni Uhlig
8c61f88362 Added event validation functions and fixed Python scripts.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-09-26 11:19:21 +02:00
Toni Uhlig
b6b62ac883 Use --squash for git subtree pull.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-09-26 00:51:59 +02:00
Toni Uhlig
84712686a7 Centralized EventName validation and moved code parts.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-09-26 00:49:34 +02:00
Toni Uhlig
adce2272dc Removed DISABLE_JSMN define for c-json-stdout (built-in per default).
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-09-26 00:49:34 +02:00
Toni Uhlig
d3bb1ccf33 Added shell script to update/pull JSMN.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-09-26 00:49:34 +02:00
Toni Uhlig
9abd1d8cf9 Merge commit 'e8af059ab3deb2a49e75c20ddcaf14ee19bcc223' as 'contrib/jsmn' 2020-09-26 00:49:15 +02:00
Toni Uhlig
e8af059ab3 Squashed 'contrib/jsmn/' content from commit 053d3cd
git-subtree-dir: contrib/jsmn
git-subtree-split: 053d3cd292
2020-09-26 00:49:15 +02:00
Toni Uhlig
a3ed1af871 Removed examples/c-json-stdout/jsmn as it's new location will be contrib/jsmn, soon.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-09-26 00:39:51 +02:00
Toni Uhlig
5b9965ce63 Added host_server_name length to hash to send a detection update if length changed (hacky).
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-09-26 00:35:37 +02:00
Toni Uhlig
6c0ac8b045 Added new flow event: FLOW_EVENT_DETECTION_UPDATE
* This event will be triggered when nDPI detection has some new information for us (hopefully).
 * Detection change is based on hashing with 32-bit murmur3 certain members of the ndpi flow struct.

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-09-25 20:43:23 +02:00
Toni Uhlig
370ca7c00d Process extra packets with nDPI, still not perfect but results in a more accurate detection.
* set default user used for setuid()
 * added 2 TODOs

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-09-24 23:41:12 +02:00
Toni Uhlig
ec1ffb6723 nDPId: Change user/group after init.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-09-21 19:34:17 +02:00
Toni Uhlig
5e5c1a4d20 nDPIsrvd: Use of an anonymous enum for collector/distributor socket types fits best.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-09-07 21:05:47 +02:00
Toni Uhlig
ab374d193f nDPIsrvd: Change user/group, allow listening on UNIX socket for incoming distributor connections
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-09-05 17:52:34 +02:00
Toni Uhlig
ec101fbc0f go-dashboard: Print unmarshalled JSON string in a textbox.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-09-03 20:34:57 +02:00
Toni Uhlig
001f84af3b go-dashboard: Added event structs and JSON unmarshal semantic.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-09-02 18:57:58 +02:00
Toni Uhlig
0a03293d16 go-dashboard: go mod/vendor support + termdash text user interface
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-09-01 23:46:07 +02:00
Toni Uhlig
70febd225b nDPId: Print compiled-in libgcrypt version (if libndpi was compiled with libgcrypt support).
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-31 16:13:04 +02:00
Toni Uhlig
9c3b5170fc Added golang JSON deserializer example.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-31 08:52:17 +02:00
Toni Uhlig
905d84506e Increased network buffer size from 8448 to 9216 with the hope that it might be finally enough.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-27 15:40:43 +02:00
Toni Uhlig
93fa7fcabf nDPId: Improved command line option parsing, app usage and subopts for (carefully) tuning some daemon options.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-26 17:24:12 +02:00
Toni Uhlig
1653137edb Makefile: Allow pkg-config based builds and also allow setting libndpi build options manually.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-24 18:18:54 +02:00
Toni Uhlig
efcdb81350 risky-flow-to-pcap.py: dump the first few packets of a "risky" flow to a PCAP file
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-23 13:39:04 +02:00
Toni Uhlig
bae579e926 nDPId: subopt parsing skeleton
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-19 20:16:11 +02:00
Toni Uhlig
5830beb70f nDPIsrvd.py: improved PCAP writing for guessed/undetected flows (ignore empty UDP/TCP packets)
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-19 19:54:49 +02:00
Toni Uhlig
4c9e099d0f added pkt_type and pkt_ipoffset to json serialization
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-19 19:52:38 +02:00
Toni Uhlig
1db474507b nDPIsrvd: add command line option for distributor listen host/port
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-18 17:14:05 +02:00
Toni Uhlig
e6c2bc6373 merged code to reduce code duplicates
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-18 16:48:01 +02:00
Toni Uhlig
230e96803e nDPIsrvd: log ip:port for distributor connection fails
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-17 16:10:43 +02:00
Toni Uhlig
41d0c414cc added nDPIsrvd communication/utils python module
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-16 00:44:15 +02:00
Toni Uhlig
9990865362 minor improvments regarding flow guessing on flow end/idle and other not worth to mention
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-16 00:22:18 +02:00
Toni Uhlig
3080fff9a3 yet another README.md update
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-15 13:08:57 +02:00
Toni Uhlig
84448d5e4e error handling enhancements
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-15 13:05:49 +02:00
Toni Uhlig
9ab656dbe2 README.md update
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-15 13:02:54 +02:00
Toni Uhlig
a619a850c7 nDPIsrvd: fixed another two bugs; one related to EPOLLIN event for fd with shutdown reading end, one if write() did not write all bytes
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-15 10:57:48 +02:00
Toni Uhlig
e8a115b39f adjust some config values
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-15 10:48:38 +02:00
Toni Uhlig
be4366b0e1 several fixes and improvments
- set errno to 0 if it is checked right after a libc call
  - ignore SIGPIPE as we want to avoid signal handling where possible
  - fixed another issue in nDPIsrvd/c-json-stdout which caused buffering errors

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-14 17:40:02 +02:00
Toni Uhlig
a5f8783bda minor improvments
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-14 16:10:21 +02:00
Toni Uhlig
37e46a506a c-json-stdout: fixed broken buffering
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-14 15:24:40 +02:00
Toni Uhlig
4b9b695946 nDPIsrvd: fixed broken buffering
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-14 14:39:10 +02:00
Toni Uhlig
a377008644 nDPId: removed unused code, process remaining flows on shutdown (useful for replaying pcap files)
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-14 14:37:50 +02:00
Toni Uhlig
0ce3965d1f flow-undetected-to-pcap.py: apply 'guessed' or 'undetected' to the filepath
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-14 14:17:53 +02:00
Toni Uhlig
d8a3693cd0 Makefile: ENABLE_DEBUG disables function inlining
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-14 14:16:47 +02:00
Toni Uhlig
52945ff903 set detection_completed = 1 if guessed/not-detected event thrown
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-13 10:55:08 +02:00
Toni Uhlig
68fb4030ee flow-info.py, flow-undetected-to-pcap.py: throw socket error runtime exception if disconnect received
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-13 10:55:04 +02:00
Toni Uhlig
c164c5f921 flow-undetected-to-pcap.py: do not write pcaps for midstream flows, write pcaps after detection completed or flow EoF but only once
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-13 09:51:39 +02:00
Toni Uhlig
3f783f9f01 improved TCP-FIN/TCP-RST and TCP-keepalive/-idle timeout handling
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-13 09:49:14 +02:00
Toni Uhlig
dcd206abfd replaced deprecated pcap_lookupdev with pcap_findalldevs 2020-08-12 12:57:24 +02:00
Toni Uhlig
5e0a27d213 improved nDPIsrvd buffering if write returned EAGAIN
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-12 12:24:39 +02:00
Toni Uhlig
8ccdadd3c7 do not disconnect a distributor client if write returned EAGAIN (still not perfect)
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-09 23:07:06 +02:00
Toni Uhlig
dc61464135 moved PACKET_EVENT_PAYLOAD_FLOW after FLOW_EVENT_NEW (and before FLOW_EVENT_END/IDLE)
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-09 22:58:35 +02:00
Toni Uhlig
67407a8a39 flow-info.py: fixed buffering issue
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-08 19:41:44 +02:00
Toni Uhlig
530ec3ccf7 event consistency: call ndpi_dpi2json only for GUESSED, DETECTED, NOT-DETECTED flow events
- force generation of GUESSED, NOT-DETECTED events on flow idle/end if detection finished

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-08 13:51:44 +02:00
Toni Uhlig
9ae4aa7ce9 flow-info.py overhaul, terminal color support, ndpi protocol/category/flow-risk output
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-07 15:21:24 +02:00
Toni Uhlig
79e4fc9bfe nDPId: do not serialize flow riskm twice
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-07 14:12:21 +02:00
Toni Uhlig
b3337c053d added some random info's
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-06 18:26:32 +02:00
Toni Uhlig
0c2b2a9750 added python flow info script, improved IPv4/IPv6 string conversion
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-06 16:48:45 +02:00
Toni Uhlig
5a7b0499ed nDPIsrvd sighandler and cmd opt parser
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-06 00:50:02 +02:00
Toni Uhlig
6031b07eb4 added utils module to share some code parts with other apps
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-05 16:03:28 +02:00
Toni Uhlig
88aa768184 nDPId daemonize / pidfile support + improved syslog logging
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-05 12:05:32 +02:00
Toni Uhlig
ea636f4ab6 improved Makefile help target
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-05 11:42:59 +02:00
Toni Uhlig
d828ef1bf0 json buffering and parsing
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-04 22:38:08 +02:00
Toni Uhlig
8a6021268e introduced NETWORK_BUFFER_MAX_SIZE to replace BUFSIZ as this might change depending on the arch/libc used
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-04 17:29:31 +02:00
Toni Uhlig
823b95828c fixed buffering issue, improved logging verbosity
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-04 16:22:00 +02:00
Toni Uhlig
115438bf44 print snprintf retval and buffer size if buffer preparation failed
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-04 16:18:53 +02:00
Toni Uhlig
0004753eb1 added basic "consumer" example
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-04 16:15:21 +02:00
Toni Uhlig
3fd32fb337 clang-format
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-04 10:28:56 +02:00
Toni Uhlig
bbeb147cde nDPIsrvd: collect, buffer and distribute JSON strings
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-04 10:27:18 +02:00
Toni Uhlig
913c8d5a18 moved more default config options into config.h
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-04 01:55:21 +02:00
Toni Uhlig
88b85cecb8 merged some pre-processor macros which are used in multiple executables
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-04 01:35:32 +02:00
Toni Uhlig
e9467b7fb6 Merge commit '791b8f611e5388015d30f64a50ab4fa5293a4a1c' as 'examples/c-json-stdout/jsmn' 2020-08-04 01:27:54 +02:00
Toni Uhlig
791b8f611e Squashed 'examples/c-json-stdout/jsmn/' content from commit 053d3cd
git-subtree-dir: examples/c-json-stdout/jsmn
git-subtree-split: 053d3cd292
2020-08-04 01:27:54 +02:00
Toni Uhlig
f99ddb5bce prefix every generated json string with the length of itself in bytes
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-03 21:42:58 +02:00
Toni Uhlig
89dd7a2178 removed python nDPId debug script
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-03 21:11:26 +02:00
Toni Uhlig
61807ffd09 forwarding data from collector(client,source,UNIX-sock) to distributor(client,sink,TCP-sock)
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-03 17:12:30 +02:00
Toni Uhlig
92925a8355 remote connection tracking/ event-handling for collector(UNIX) and distributor(TCP) connections
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-08-03 16:27:14 +02:00
Toni Uhlig
536a1c03a5 nDPIsrvd: getting events from epoll and accepting new connections
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-19 21:02:35 +02:00
Toni Uhlig
89191dff42 nDPIsrvd: UNIX/INET socket creation
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-19 16:53:00 +02:00
Toni Uhlig
f757b9d313 renamed collector to debug and removed obsolete, unused code 2020-07-17 20:21:15 +02:00
Toni Uhlig
ef7c11a750 added %zu format string, so CC won't complain if size types used
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-16 20:30:36 +02:00
Toni Uhlig
a5d46a67e3 moved CFLAGS to PROJECT_CFLAGS so OpenWrt won't overwrite it
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-16 20:28:46 +02:00
Toni Uhlig
de2d5749b7 add_flow / del_flow / add_pkt / cleanup
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-15 19:49:16 +02:00
Toni Uhlig
6068a99a56 serialize flow risk, send information about how much raw pkts are sent to the json endpoint, send also a json thread init complete event
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-15 18:37:33 +02:00
Toni Uhlig
5364603fba added packet based events
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-14 21:27:41 +02:00
Toni Uhlig
e7406606c1 shutdown socket reading end as we just want to send json strings and never read something
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-13 16:09:08 +02:00
Toni Uhlig
48dc26140a replaced fprintf with syslog calls
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-11 16:17:12 +02:00
Toni Uhlig
1085608ad4 removed obsolete DISABLE_JSONIZER mode
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-11 15:33:17 +02:00
Toni Uhlig
c6952a5995 removed obsolete EXTRA_VERBOSE mode
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-11 15:24:21 +02:00
Toni Uhlig
e4e2a9ccab -fsanitize=enum
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-11 15:23:22 +02:00
Toni Uhlig
cfcaa4eb81 use a string mapping table for event id to name conversion
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-11 15:22:40 +02:00
Toni Uhlig
d8122ca762 more write after read race conditions fixed
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-10 22:58:51 +02:00
Toni Uhlig
429aceec8f do not omit frame pointer in debug mode
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-10 22:48:04 +02:00
Toni Uhlig
ea8cba7f58 enable thread snitizer
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-10 22:45:34 +02:00
Toni Uhlig
e1debd9198 fixed some race conditions
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-10 22:45:12 +02:00
Toni Uhlig
d1739d97a8 support json key w/o value filtering
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-10 22:14:23 +02:00
Toni Uhlig
c2aa13c010 compare&fetch for global flow_id counter, set missing variable on new flow, added struct padding for alignment
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-10 22:07:26 +02:00
Toni Uhlig
cb7b9df945 support for filter for specific json key/values, useful for debugging
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-10 22:01:28 +02:00
Toni Uhlig
f490056fa7 care more about DISABLE_JSONIZER
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-10 20:59:44 +02:00
Toni Uhlig
a9aa4f1236 clang-format and set BreakBeforeBraces to Allmanz
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-10 20:48:23 +02:00
Toni Uhlig
7867c3979d json formatting/ stringification overhaul
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-10 20:40:16 +02:00
Toni Uhlig
640e69f4a8 renamed EchoServer to JsonCollector
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-09 22:56:23 +02:00
Toni Uhlig
d99bd825b2 send json string to sink, added basic json event serialization fn call
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-09 22:40:46 +02:00
Toni Uhlig
9644a27b3f Revert "not using python script as collector"
This reverts commit f700457d9b.

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-08 06:49:24 +02:00
Toni Uhlig
c0dd561ba0 renamed: jsonize_basic_event_error -> jsonize_format_error
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-08 00:01:06 +02:00
Toni Uhlig
1fe7832b7a jsonize_basic_event // jsonize_basic_event_error
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-07 23:59:14 +02:00
Toni Uhlig
f700457d9b not using python script as collector
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-07 23:58:45 +02:00
Toni Uhlig
97aababa97 send JSON string on a per thread basis to a remote sink, means that no expensive locking required
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-05 15:17:40 +02:00
Toni Uhlig
e922f9459e UNIX socket connect, primitive cmdline parsing
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-04 18:57:02 +02:00
Toni Uhlig
bb8884f853 clang-format, use syslog
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-03 23:41:07 +02:00
Toni Uhlig
f8dae488b4 store / calculate / jsonize per flow metrics e.g. min/max/avg l4 data len
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-03 19:40:49 +02:00
Toni Uhlig
50d2cd17fe print json string for FLOW_END / FLOW_IDLE as well
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-01 20:13:40 +02:00
Toni Uhlig
ab0ebc8a5b only printf() flow events #ifdef DISABLE_JSONIZER, print only json string otherwise
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-07-01 18:19:45 +02:00
Toni Uhlig
92403c2098 add configuration variable VERBOSE / EXTRA VERBOSE, print build configuration in Makefile
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-06-30 23:47:55 +02:00
Toni Uhlig
474e1cb666 print event for idle flows as well, simplified jsonize_flow/flow2json
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-06-30 23:25:59 +02:00
Toni Uhlig
4035f4900d init/close syslog logging
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-06-30 22:04:29 +02:00
Toni Uhlig
4ca38c90cf do not guess protocol if detection successful and completed
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-06-30 21:23:10 +02:00
Toni Uhlig
aa77fd75f9 json-serialize new flow
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-06-30 21:12:18 +02:00
Toni Uhlig
a47e0b0af3 generalized flow2json (IPv4 only for now)
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-06-30 06:28:53 +02:00
Toni Uhlig
61d872020c serialize and print new flow
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2020-06-28 23:39:54 +02:00
P4t
053d3cd292 Merge pull request #175 from pks-t/pks/struct-type
jsmn: declare struct names to allow forward decls
2020-04-02 15:08:12 +02:00
Toni Uhlig
a91022a07d fix gcc/clang warning and unnecessary implicit type conversion to different size/signedness (#187)
* fixed gcc/clang warnings regarding implicit numeric to enum type conversion

* fixed unnecessary implicit type casts regarding size and signedness

Co-authored-by: Toni Uhlig <Toni.Uhlig@rohde-schwarz.com>
2020-03-13 21:46:40 +01:00
Alexey Radkov
7b6858a585 Fixed a typo (value -> number) (#186) 2020-02-19 15:32:48 +01:00
Patrick Steinhardt
0837288b7c jsmn: declare struct names to allow forward decls
Both `jsmntok_t` and `jsmn_parser` are declared as anonymous structures
that are typedeffed to their actual name. This forces all downstream
users of jsmn to always use the typedef name, instead of using e.g.
`struct jsmn_parser`. While this might be considered a matter of taste,
using typedefs only has the technical downside of disallowing forward
declarations. E.g. if a dependent whishes to declare a pointer to
`jsmntok_t` without actually pulling in the "jsmn.h" header, then he is
not able to do so because there is no way in C to provide a forward
declaration for typedefs to anonymous structs.

Fix this by providing names for both `jsmntok_t` and `jsmn_parser`
structures.
2019-11-08 16:58:36 +01:00
P4t
85695f3d59 Merge pull request #162 from ghane/Case-Warning
Add default case for a switch statement to avoid complaints from the compiler.
2019-07-14 19:01:49 +02:00
Sanjeev Gupta
cdcfaafa49 Quieten a warning from the compiler
gcc (and others) like a default case for switch statement,
even if it is empty.
2019-07-13 22:01:56 +08:00
Serge Zaitsev
fdcef3ebf8 Modernize (#149)
* add .clang-format for automated code formatting
* automatic code formatting
* move config.mk below all target to allow custom non-default build targets
* add license to the top of the file
* use correct header file guards syntax
* convert to single-header, header-only library
* update makefile to use jsmn as a header-only library
* update readme
* add changed from PR #143
* fix clang warnings
* add changes from PR #142
* add consts as per PR #134
2019-04-20 08:05:39 +02:00
Serge Zaitsev
18e9fe42cb Merge pull request #139 from BenBE/patch-1
Minor typo in jsmn.c
2018-10-02 10:46:08 +02:00
BenBE
614a36c18c Typo in comment
Fixes #84
2018-10-01 17:47:01 +02:00
Serge Zaitsev
732d283ee9 Merge pull request #126 from abalkin/patch-1
Fixed two typos in a comment.
2018-02-12 09:40:59 +02:00
Alexander Belopolsky
f38f267b62 Fixed two typos in a comment. 2018-02-10 19:22:23 -05:00
Serge Zaitsev
6784c826d9 Merge pull request #123 from drbitboy/btc/typos
btc/typos - JSON_ERROR_... should be JSMN_ERROR_... in README.md
2018-01-25 22:49:10 +02:00
Brian Carcich
f276e23a74 btc/typos - JSON_ERROR_... should be JSMN_ERROR_... in README.md 2018-01-25 15:38:16 -05:00
Serge A. Zaitsev
35086597a7 added travis badge 2017-05-01 11:44:07 +03:00
Serge A. Zaitsev
fe296583c0 added travis.yml 2017-05-01 11:42:48 +03:00
Serge Zaitsev
c772a0e48b Merge pull request #99 from prubel/strict-unmatched-brackets-test-fix
strict checking fails a test, add {}s to fix it
2017-04-30 22:43:06 +03:00
Paul Rubel
6572217a0e strict checking fails a test, add {}s to fix it
The current test looks for success, but since there are
no surrounding {}s the test fails. Add them in and add
a test like the one that we replace but only in the
non-strict code path.
2016-12-13 15:23:23 -05:00
Serge Zaitsev
1682c32e9a Merge pull request #94 from pt300/master
Fix for no error with unmatched closing bracket with PARENT_LINKS
2016-10-12 17:09:07 +08:00
Serge Zaitsev
0f574ea35b Merge pull request #95 from crondaemon/doc-fix
Fix issue in documentation.
2016-10-06 17:38:02 +08:00
Dario Lombardo
f40811c4de Fix issue in documentation. 2016-10-06 10:30:20 +02:00
pt300
c3131d05a6 Changed unmatched bracket tests 2016-10-02 10:37:29 +02:00
Pat
adae9457cf Merge pull request #1 from zlolik/master
some tests for unmatched brackets added
2016-10-02 09:48:27 +02:00
zlolik
a01d301373 some tests for unmatched brackets added 2016-10-02 07:51:52 +03:00
pt300
4ce4404057 Seems to actually fix zserge/jsmn#81 2016-10-01 18:19:20 +02:00
pt300
ad72aac67a Partialy fixes zserge/jsmn#81
Still will report invalid amount if we fetch it with something like "{"key 1": 1234}}"
2016-10-01 18:07:35 +02:00
Serge Zaitsev
6021415cc7 Merge pull request #87 from RyDroid/make
Very minor changes to Makefile
2016-08-10 10:40:39 +08:00
Serge Zaitsev
452c926709 Merge pull request #88 from RyDroid/minor-c
Very minor changes to C source code
2016-08-10 10:40:26 +08:00
Nicola Spanti (RyDroid)
b80578ce08 Very minor changes to Makefile 2016-08-08 16:05:12 +02:00
Nicola Spanti (RyDroid)
e42bcbbada Very minor changes to C source code 2016-08-08 15:54:56 +02:00
Serge Zaitsev
d1c85c569d Merge pull request #79 from ferambot/master
Minor fixes
2016-06-13 01:12:23 -05:00
Feram
37672b0289 Fix typos 2016-06-13 06:10:46 +00:00
Serge Zaitsev
bbc6755fce Merge pull request #76 from simonsj/master
tests: fix test_object JSMN_PRIMITIVE bug
2016-04-02 03:10:43 -05:00
Jon Simons
e3f2629a56 tests: fix test_object JSMN_PRIMITIVE bug
Specify the argument after JSMN_PRIMITIVE as "0" instead of 0 so
that weird things don't happen on OSX due to using it later with
`va_arg(..., char *)` in `vtokeq`.
2016-04-01 20:06:13 -07:00
Serge Zaitsev
b77d84ba48 Merge pull request #75 from condemned77/master
Typo fix.
2016-03-28 06:10:47 -05:00
condemned77
76c9448ca8 Typo fix.
Minor typo fixed.
2016-03-28 12:28:36 +02:00
Serge Zaitsev
572ace5a43 Update README.md 2016-01-20 17:28:15 +08:00
Serge Zaitsev
09843be912 Merge pull request #66 from ivankravets/patch-1
@PlatformIO Library Registry manifest file
2016-01-19 23:00:50 +08:00
Ivan Kravets
86d595729c @PlatformIO Library Registry manifest file
* This library in Web Registry: http://platformio.org/#!/lib/show/568/jsmn
* Specification: [library.json](http://docs.platformio.org/en/latest/librarymanager/config.html)
* Library Manager:  http://docs.platformio.org/en/latest/librarymanager/index.html
2016-01-19 16:34:12 +02:00
Serge Zaitsev
78b1dca334 Merge pull request #65 from Smattr/02ab7ac3-79ee-4541-a80a-230e2ceae1dd
Fix trivial comment typo.
2016-01-13 19:20:44 +08:00
Matthew Fernandez
b7845b4ea4 Fix trivial comment typo. 2016-01-13 21:45:00 +11:00
Serge Zaitsev
4a54ae6987 Merge pull request #62 from baskerville/master
Fix deheader warnings
2015-12-14 23:04:37 +08:00
Bastien Dejean
e4d526a403 Fix deheader warnings
Prior to this commit the output of deheader was:
	remove <stdlib.h> from ./jsmn.c
	in ./example/jsondump.c, realloc() portability requires <unistd.h>.
	in ./example/simple.c, strtol() portability requires <stdlib.h>.
	saw 4 files, 16 includes, 1 removable
2015-12-14 15:05:46 +01:00
Serge Zaitsev
fdceddf685 Merge pull request #61 from elelay/master
fix jsmntype_t definition in README
2015-12-08 08:34:46 +08:00
Eric Le Lay
6bfa20872c fix jsmntype_t definition in README 2015-12-07 20:14:07 +01:00
Serge Zaitsev
8da639a334 Merge pull request #60 from baskerville/master
Fix typo
2015-11-19 17:04:51 +08:00
Bastien Dejean
f06a10c1b7 Fix typo 2015-11-19 09:55:46 +01:00
Serge A. Zaitsev
076abddc19 fixed privitive value typo in tests 2015-10-31 18:43:04 +02:00
Serge A. Zaitsev
7fb89f1eb2 initialized ok variable in tests, parse should still return 1 if expected result is a failure 2015-10-31 18:37:25 +02:00
Serge A. Zaitsev
8a5ee3d41b fixed a typo in the test name 2015-10-31 18:33:43 +02:00
Serge Zaitsev
2db03781c5 Merge pull request #55 from goriy/example_realloc_fix
small fix of jsondump example for cases of realloc failures
2015-10-25 22:42:48 +02:00
goriy
2d185aa465 tiny realloc function wrapper which handles cases of realloc failures
Memory reallocation with old data pointer handling is done by means
of realloc_it() function.

Tiny inline function realloc_it() is a wrapper function for standart
realloc() which frees old memory pointer and prints errno to stderr
if realloc fails.
2015-10-25 23:28:19 +03:00
goriy
e709651a20 small fix of jsondump example for cases of realloc failures
When realloc() function fails it returns NULL pointer.
But old data pointer remains valid in such a case.

It's a mistake to use old data pointer to store new
pointer returned by realloc. In case of realloc failure,
pointer is overwritten with NULL value, but old used memory
remains unreferenced and could not be even freed anymore.
Such mistakes could lead to memory leaks.
2015-10-24 23:32:51 +03:00
Serge A. Zaitsev
0c2d60b8e7 added and marked as fixme tests for false positives in objects 2015-10-17 15:25:44 +02:00
Serge A. Zaitsev
f7e6dcb7a2 added another test of invalid array 2015-10-17 15:24:44 +02:00
Serge A. Zaitsev
e5fb875b59 added another test of invalid array 2015-10-17 15:19:01 +02:00
Serge A. Zaitsev
aedc121ce8 added check for null pointer before fixing tokens after parsing is done 2015-10-17 15:14:13 +02:00
Serge A. Zaitsev
824d9a769c applied fix from wireshark 2015-10-17 15:09:35 +02:00
Serge A. Zaitsev
d0664f9a2e changed jsmnerr_t type to int 2015-10-17 15:06:39 +02:00
Serge A. Zaitsev
5e37daeec3 added issue 27 test case 2015-10-17 15:01:01 +02:00
Serge A. Zaitsev
edd751896d fixed return value on incremental parting 2015-10-17 14:58:47 +02:00
Serge A. Zaitsev
9b4e33199f moved tests into a subfolder, added table-driven tests 2015-10-17 14:58:31 +02:00
Serge A. Zaitsev
226f318224 json token type enum doesn't start with zero value anymore, fixes #24 2015-10-17 10:50:22 +02:00
Serge A. Zaitsev
d87c22d2cd removed mixed declarations and code, fixes #45 2015-10-17 10:42:30 +02:00
Serge A. Zaitsev
6809c029aa changed test function args to void, fixes #46 2015-10-17 10:39:27 +02:00
Serge A. Zaitsev
6cb9c3721d test exit status depends on number of failed tests 2015-10-17 10:29:54 +02:00
Serge Zaitsev
bd9bd55c3a Changed links to github 2015-09-24 18:22:14 +02:00
Serge A. Zaitsev
26193d39c2 updated README 2014-12-19 01:40:26 +02:00
Serge A. Zaitsev
cf38b7d171 added js string boundaries checks for string parser, fixes issue #31; added tests to cover it; fixed makefile to use custom cflags/ldflags 2014-11-17 16:21:36 +02:00
Serge A. Zaitsev
91d7389ec8 added some tests for key/values nodes and added some stricter validations 2014-11-17 15:36:18 +02:00
Serge A. Zaitsev
946a2b1e02 moved i declaration to the top of the block, issue #38 fixed 2014-11-17 15:06:12 +02:00
Serge A. Zaitsev
a0e2b876ca including stddef.h in the jsmn.h, fixed issue #28 2014-11-17 14:28:27 +02:00
Serge A. Zaitsev
838061aa96 removed trailing spaces 2014-11-17 14:14:45 +02:00
Serge A. Zaitsev
8a22e0a149 implemented key/value hierarchy; added 2 examples; fixed some warnings in tests 2014-11-17 14:13:58 +02:00
Serge Zaitsev
e0847b52b5 Merged in setekhid/jsmn/setekhid/jsmnh2822-warning-commas-at-the-end-of-e-1408807731663 (pull request #11)
jsmn.h:28:22: warning: commas at the end of enumerator lists are a C99-specific feature [-Wc99-extensions]
2014-10-09 19:49:10 +03:00
Wizard Tai
cf39341a39 jsmn.h:28:22: warning: commas at the end of enumerator lists are a C99-specific feature [-Wc99-extensions]
WITH:

-std=c89 -Wall -pedantic -Wno-declaration-after-statement
2014-08-23 15:28:58 +00:00
Serge A. Zaitsev
76cd398859 removed JSMN_SUCCESS value from the readme 2014-02-21 11:04:16 +02:00
Serge Zaitsev
f2864e69b9 Merged in MacGritsch/jsmn-patch (pull request #7)
Added "extern C" to use it with CPP.
2014-02-21 11:03:29 +02:00
Gabriel Gritsch
db379ec124 Added "extern C" to use it with CPP. 2014-02-21 09:53:45 +01:00
Gabriel Gritsch
a89501b2fb Integrate changes made by Serge Zaitsev. 2014-02-21 09:52:23 +01:00
Serge A. Zaitsev
5faee057cb edited readme about what's inside the repo. closes issue #19 2014-02-21 00:24:29 +02:00
Serge A. Zaitsev
f0ae25f873 added way to specify json string length, added test for non-strict mode 2014-02-21 00:11:52 +02:00
Serge A. Zaitsev
809c7c6db1 added way to estimate number of tokens before parsing 2014-02-20 22:27:07 +02:00
Gabriel Gritsch
733b8e958e Changes for SIZE_MAX 2014-02-16 10:54:19 +01:00
Gabriel Gritsch
c91adceded - Added possibility to specify the lenght of the input buffer (if not null-terminated).
- Added "extern C" to use it with CPP.
2014-02-16 10:45:20 +01:00
Gabriel Gritsch
659842c65c - Added possibility to specify the lenght of the input buffer (if not null-terminated).
- Added "extern C" to use it with CPP.
2014-02-16 10:42:27 +01:00
Gabriel Gritsch
385b42e740 - Added possibility to specify the lenght of the input buffer (if not null-terminated).
- Added "extern C" to use it with CPP.
2014-02-12 17:53:45 +01:00
Serge A. Zaitsev
86350f2bb1 a proper fix for signed comparison 2014-02-09 17:09:15 +02:00
Serge A. Zaitsev
da95978033 added cast size_t to int to avoid warnings, removed string.h header since it was merged by accident 2014-02-09 17:04:39 +02:00
Serge A. Zaitsev
d0ca2df480 fixed jsmntype_t definition 2014-01-27 17:11:54 +02:00
Serge A. Zaitsev
c29d151bfa added link to the web page 2014-01-27 16:04:43 +02:00
Serge Zaitsev
2b0e4464c7 Merged in frnknstn/jsmn/markdown (pull request #5)
rename README so markdown renders in source control
2014-01-27 16:09:33 +02:00
Serge Zaitsev
323395efac Merged in frnknstn/jsmn/clean (pull request #4)
delete jsmn_test.exe when doing make clean
2014-01-27 16:08:19 +02:00
Serge A. Zaitsev
40392b73e3 fixed indentation and comments style after merge 2014-01-27 15:58:43 +02:00
Peter Finlayson
e397f0dfb6 rename README so markdown renders in source control
The readme file is in markdown, but most source control systems and repos (including bitbucket) expect a markdown readme to be called README.md
2013-12-23 12:39:00 +02:00
Peter Finlayson
ee10da0f1e delete jsmn_test.exe when doing make clean
When building the jsmn test suite on Windows via mingw, the output executable is called "jsmn_test.exe" instead of just "jsmn_test".
2013-12-23 12:29:33 +02:00
del6597
07af5df94b Removes the incorrect counting of tokens 2013-07-19 00:23:58 -04:00
del6597
f190d18a52 Adds checking to unicode characters that are \uXXXX where X is a hexidecimal digit
Adds new tests for unicode character coverage
2013-07-19 00:15:49 -04:00
del6597
6f4e2f7a56 Added a method for jsmn_estimate_tokens(const char *json); 2013-07-17 00:05:47 -04:00
Serge A. Zaitsev
958c758f2e fixed: invalid object/arrays were passed successfully in tree mode; added tests for this case 2012-10-22 21:54:46 +03:00
Serge A. Zaitsev
3d6fb06ee9 fixed typo 2012-10-01 12:43:37 +03:00
Serge A. Zaitsev
cf172e71d7 fixed tests: missing jsmn_init() before parsing; added tests for empty arrays/objects 2012-10-01 11:15:48 +03:00
Serge A. Zaitsev
e0ceee893c fixed strict mode: space symbols are allowed after primitives; added some new tests 2012-09-26 20:28:12 +03:00
Serge A. Zaitsev
37964b40ee added optional parent token pointer support. Increases parsing speed 2012-09-26 20:19:21 +03:00
Serge A. Zaitsev
cfbb5b5091 removed unused variable 2012-09-23 20:30:56 +03:00
Serge A. Zaitsev
974133db85 tokens array items are now being initialized during allocation, removed redundant code that significantly slowed down the parser 2012-09-23 20:29:48 +03:00
Serge A. Zaitsev
e542dea54e changed return value to jsmnerr_t, fixed warning about signed/unsigned values 2012-09-03 19:54:40 +03:00
Serge A. Zaitsev
af04595fe2 fix: error is returned if brackets are unmatched 2012-02-02 14:05:47 +02:00
Serge A. Zaitsev
f3b41ae30c fixed: superior node size is now an index, not a pointer (safe to realloc) 2012-02-02 13:26:15 +02:00
Serge A. Zaitsev
a983a76061 added reading with small number of tokens test, fixed NOMEM issue #2 2012-02-02 13:15:22 +02:00
Serge A. Zaitsev
53454e5429 added test for partial array reading 2012-02-02 11:40:36 +02:00
Serge A. Zaitsev
41171ecd51 fixed typo, added JSMN_STRICT mode 2012-02-01 17:51:42 +02:00
Serge A. Zaitsev
e395ad562e added one more test case for partial string parser 2012-02-01 17:18:32 +02:00
Serge A. Zaitsev
30f932772e tested partial parser for strings, added primitive string parser test, fixed token allocation 2012-02-01 17:15:54 +02:00
Serge A. Zaitsev
929e233756 added tests for primitive types, primitive types now can be stored outside the objects without braces 2012-02-01 16:03:36 +02:00
Serge A. Zaitsev
443be365f6 removed JSON string and token array from parser internals, because they must not be saved between parse() calls 2012-02-01 14:02:12 +02:00
Serge A. Zaitsev
bed0a7a3e6 changed API: parse now is more flexible, but init jsut resets the parser; added new test macro to compate strings, fixed Makefile 2012-02-01 13:56:06 +02:00
Serge A. Zaitsev
4b5c5ed66a switched to C unit-tests instead of bash 2012-02-01 12:38:51 +02:00
Serge A. Zaitsev
2928f7ec0e renamed: get_token to alloc_token 2012-02-01 12:38:05 +02:00
Serge A. Zaitsev
0c22b772ed using system AR program, build options are moved to config.mk 2012-01-30 14:03:36 +02:00
Serge A. Zaitsev
d6209011a5 added download links in README 2011-09-15 18:43:48 +03:00
Serge A. Zaitsev
d6f48a6a3d fixed: issue #1, thanks to m_einman for his patch 2011-03-28 13:32:44 +03:00
Serge A. Zaitsev
42be9208f7 README updated 2010-12-28 12:06:52 +02:00
Serge A. Zaitsev
4e869f7e9e Complex types (objects and arrays) now have also size - number of child elements 2010-12-28 11:16:41 +02:00
Serge A. Zaitsev
c4d9412483 Some comments added in json.c 2010-12-27 17:05:22 +02:00
Serge A. Zaitsev
c955364a95 All objects renamed using jsmn prefix to keep consistency and not be mixed with other json libraries. Demo is now called just demo. 2010-12-27 17:00:24 +02:00
Serge Zaitsev
4e29ee705f Fix: check if no tokens lefs, return error in that case 2010-12-26 13:48:13 +02:00
Serge Zaitsev
9924734575 Typo in tests fixed. Argument variable quoted 2010-12-26 13:02:40 +02:00
Serge A. Zaitsev
3d94cd0a18 Removed useless argument from printf 2010-11-29 13:53:13 +02:00
Serge A. Zaitsev
2cf5193d64 README changed. Now it is a template for the official web page 2010-11-29 13:52:11 +02:00
Serge A. Zaitsev
978ea959f4 Added test for a large number of tokens 2010-11-24 11:15:21 +02:00
Serge Zaitsev
508bf43fa0 Test framework implemented 2010-11-24 00:54:21 +02:00
Serge Zaitsev
f88240ac2e Simplified demo output format 2010-11-24 00:26:15 +02:00
Serge A. Zaitsev
23b8487783 Demo: option added to specify buffer size when reading 2010-11-22 14:52:46 +02:00
Serge A. Zaitsev
8e31e4d214 jsmn: strings start from the first char after quotes 2010-11-22 14:52:26 +02:00
Serge A. Zaitsev
b91dee9102 Demo: declarations moved to the top of functions. 2010-11-17 17:33:29 +02:00
Serge A. Zaitsev
7a027a4dd2 Token manipulation functions changes. size_t replaced with unsigned int. 2010-11-17 15:16:12 +02:00
Serge A. Zaitsev
3922360800 Demo: options added. Number of tokens can be specified now. 2010-11-17 12:19:38 +02:00
Serge A. Zaitsev
991ca5dd94 Demo: can now read from stdin 2010-11-17 12:05:29 +02:00
Serge A. Zaitsev
d0d52f6847 Design: primitive type implemented as a replacement to boolean/number/null. String tokens point to the unquoted string 2010-11-17 12:03:26 +02:00
Serge A. Zaitsev
60509e2850 Design: rewritten using parser structure 2010-11-16 15:41:49 +02:00
Serge A. Zaitsev
a2755a7595 Design: some error codes added 2010-11-16 13:50:13 +02:00
Serge A. Zaitsev
daf93a0ebd Feature: make difference between numbers and other primitives 2010-11-16 11:56:08 +02:00
Serge A. Zaitsev
470c77fa63 Design: added assert macro and return macro. 2010-11-16 11:53:43 +02:00
Serge A. Zaitsev
30370e37f8 Design: Added asserts in parser. Increased number of tokens in demo. 2010-11-16 11:45:10 +02:00
Serge A. Zaitsev
b99663079c Fix: errpos has got a correct type now 2010-11-15 17:59:51 +02:00
Serge A. Zaitsev
b0e73ec44d Design: objects and arrays are different types now 2010-11-15 17:58:50 +02:00
Serge A. Zaitsev
a70dab5cf9 Design: unrolled reference tables. Nested functions moved outside. Got smaller code. 2010-11-15 17:39:25 +02:00
Serge A. Zaitsev
aa97d8b599 Demo: changed to read from '.js' file 2010-11-15 15:46:31 +02:00
Serge A. Zaitsev
a20a5aff1c Fix: error position is an offset, not a pointer 2010-11-15 15:02:35 +02:00
Serge A. Zaitsev
0568be6e0b Demo separated from the jsmn code. Makefile changed. Some comments added 2010-11-15 15:00:42 +02:00
Serge A. Zaitsev
6b6b3ba5c1 README and LICENSE added. MIT license choosen. 2010-11-15 13:27:14 +02:00
Serge A. Zaitsev
f22c2d30b7 Initial commit. Demo program is included in the jsmn.c code. Ugly names and no comments. Please, don't read this changeset 2010-11-15 13:11:08 +02:00
604 changed files with 125058 additions and 1106 deletions

View File

@@ -51,7 +51,7 @@ Cpp11BracedListStyle: true
IndentWidth: 4
TabWidth: 4
UseTab: Never
BreakBeforeBraces: Linux
BreakBeforeBraces: Allman
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpacesInAngles : false

6
.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
# python related
*.pyc
__pycache__
# go related
*.sum

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "libnDPI"]
path = libnDPI
url = https://github.com/ntop/nDPI

13
.travis.yml Normal file
View File

@@ -0,0 +1,13 @@
language: c
before_install:
- sudo apt-get -qq update
- sudo apt-get install -y build-essential make binutils gcc autoconf automake libtool pkg-config git libpcap-dev libgcrypt-dev libgpg-error-dev libjson-c-dev netcat-openbsd python3 python3-jsonschema
script:
- git submodule update --init
# static linked build
- mkdir build-cmake-submodule && cd build-cmake-submodule && cmake .. -DBUILD_EXAMPLES=ON -DBUILD_NDPI=ON -DENABLE_SANITIZER=ON && make && cd ..
# pkg-config dynamic linked build
- PKG_CONFIG_PATH="$(realpath ./build-cmake-submodule/libnDPI/lib/pkgconfig)" cmake . -DBUILD_EXAMPLES=ON -DENABLE_SANITIZER=ON -DENABLE_MEMORY_PROFILING=ON && make
- ./nDPId-test || test $? -eq 1
- ./nDPId -h || test $? -eq 1
- ./test/run_tests.sh

166
CMakeLists.txt Normal file
View File

@@ -0,0 +1,166 @@
cmake_minimum_required(VERSION 3.12.4)
project(nDPId C)
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
find_package(PkgConfig REQUIRED)
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(BUILD_EXAMPLES "Build C examples." ON)
option(BUILD_NDPI "Clone and build nDPI from github." OFF)
set(STATIC_LIBNDPI_INSTALLDIR "" CACHE STRING "Path to a installation directory of libnDPI e.g. /opt/libnDPI/usr")
add_executable(nDPId nDPId.c utils.c)
add_executable(nDPIsrvd nDPIsrvd.c utils.c)
add_executable(nDPId-test nDPId-test.c utils.c)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -DJSMN_STATIC=1 -DJSMN_STRICT=1")
set(BUILD_NDPI_CONFIGURE_OPTS "")
if(ENABLE_MEMORY_PROFILING)
set(MEMORY_PROFILING_CFLAGS "-DENABLE_MEMORY_PROFILING=1"
"-Duthash_malloc=nDPIsrvd_uthash_malloc"
"-Duthash_free=nDPIsrvd_uthash_free")
else()
set(MEMORY_PROFILING_CFLAGS "")
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -g3 -fno-omit-frame-pointer -fno-inline")
endif()
if(ENABLE_SANITIZER AND ENABLE_SANITIZER_THREAD)
message(STATUS_FATAL "ENABLE_SANITIZER and ENABLE_SANITIZER_THREAD can not be used together!")
endif()
if(ENABLE_SANITIZER)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fsanitize=undefined -fsanitize=enum -fsanitize=leak")
set(BUILD_NDPI_CONFIGURE_OPTS "${BUILD_NDPI_CONFIGURE_OPTS} --with-sanitizer")
endif()
if(ENABLE_SANITIZER_THREAD)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined -fsanitize=enum -fsanitize=thread")
endif()
if(BUILD_NDPI)
include(ExternalProject)
ExternalProject_Add(
libnDPI
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libnDPI
CONFIGURE_COMMAND git submodule update --init &&
${CMAKE_CURRENT_SOURCE_DIR}/libnDPI/autogen.sh
--prefix=${CMAKE_CURRENT_BINARY_DIR}/libnDPI
${BUILD_NDPI_CONFIGURE_OPTS}
BUILD_COMMAND make
BUILD_IN_SOURCE 1)
set(STATIC_LIBNDPI_INSTALLDIR "${CMAKE_BINARY_DIR}/libnDPI")
add_dependencies(nDPId libnDPI)
add_dependencies(nDPId-test libnDPI)
endif()
if(NOT STATIC_LIBNDPI_INSTALLDIR STREQUAL "" OR BUILD_NDPI)
option(NDPI_WITH_GCRYPT "Link static libndpi library against libgcrypt." ON)
option(NDPI_WITH_PCRE "Link static libndpi library against libpcre." OFF)
option(NDPI_WITH_MAXMINDDB "Link static libndpi library against libmaxminddb." OFF)
if(NDPI_WITH_GCRYPT)
find_package(GCRYPT "1.4.2" REQUIRED)
endif()
if(NDPI_WITH_PCRE)
pkg_check_modules(PCRE REQUIRED libpcre>=8.39)
endif()
if(NDPI_WITH_MAXMINDDB)
pkg_check_modules(MAXMINDDB REQUIRED libmaxminddb)
endif()
set(STATIC_LIBNDPI_INC "${STATIC_LIBNDPI_INSTALLDIR}/include/ndpi")
set(STATIC_LIBNDPI_LIB "${STATIC_LIBNDPI_INSTALLDIR}/lib/libndpi.a")
else()
pkg_check_modules(NDPI REQUIRED libndpi>=3.5.0)
set(STATIC_LIBNDPI_INC "")
set(STATIC_LIBNDPI_LIB "")
endif()
find_package(PCAP "1.8.1" REQUIRED)
target_compile_options(nDPId PRIVATE ${MEMORY_PROFILING_CFLAGS} "-pthread")
target_include_directories(nDPId PRIVATE "${STATIC_LIBNDPI_INC}" "${NDPI_INCLUDEDIR}" "${NDPI_INCLUDEDIR}/ndpi")
target_link_libraries(nDPId "${STATIC_LIBNDPI_LIB}" "${pkgcfg_lib_NDPI_ndpi}"
"${pkgcfg_lib_PCRE_pcre}" "${pkgcfg_lib_MAXMINDDB_maxminddb}"
"${GCRYPT_LIBRARY}" "${PCAP_LIBRARY}"
"-pthread")
target_compile_options(nDPId PRIVATE ${MEMORY_PROFILING_CFLAGS})
target_include_directories(nDPIsrvd PRIVATE
"${CMAKE_SOURCE_DIR}"
"${CMAKE_SOURCE_DIR}/dependencies"
"${CMAKE_SOURCE_DIR}/dependencies/jsmn"
"${CMAKE_SOURCE_DIR}/dependencies/uthash/src")
target_include_directories(nDPId-test PRIVATE
"${CMAKE_SOURCE_DIR}"
"${CMAKE_SOURCE_DIR}/dependencies"
"${CMAKE_SOURCE_DIR}/dependencies/jsmn"
"${CMAKE_SOURCE_DIR}/dependencies/uthash/src")
target_compile_options(nDPId-test PRIVATE ${MEMORY_PROFILING_CFLAGS} "-Wno-unused-function" "-pthread")
target_include_directories(nDPId-test PRIVATE "${STATIC_LIBNDPI_INC}" "${NDPI_INCLUDEDIR}" "${NDPI_INCLUDEDIR}/ndpi")
target_compile_definitions(nDPId-test PRIVATE "-D_GNU_SOURCE=1" "-DNO_MAIN=1" "-Dsyslog=mock_syslog_stderr")
target_link_libraries(nDPId-test "${STATIC_LIBNDPI_LIB}" "${pkgcfg_lib_NDPI_ndpi}"
"${pkgcfg_lib_PCRE_pcre}" "${pkgcfg_lib_MAXMINDDB_maxminddb}"
"${GCRYPT_LIBRARY}" "${PCAP_LIBRARY}"
"-pthread")
if(BUILD_EXAMPLES)
add_executable(nDPIsrvd-collectd examples/c-collectd/c-collectd.c)
target_compile_options(nDPIsrvd-collectd PRIVATE ${MEMORY_PROFILING_CFLAGS})
target_include_directories(nDPIsrvd-collectd PRIVATE
"${CMAKE_SOURCE_DIR}"
"${CMAKE_SOURCE_DIR}/dependencies"
"${CMAKE_SOURCE_DIR}/dependencies/jsmn"
"${CMAKE_SOURCE_DIR}/dependencies/uthash/src")
add_executable(nDPIsrvd-captured examples/c-captured/c-captured.c utils.c)
target_compile_options(nDPIsrvd-captured PRIVATE ${MEMORY_PROFILING_CFLAGS})
target_include_directories(nDPIsrvd-captured PRIVATE
"${CMAKE_SOURCE_DIR}"
"${CMAKE_SOURCE_DIR}/dependencies"
"${CMAKE_SOURCE_DIR}/dependencies/jsmn"
"${CMAKE_SOURCE_DIR}/dependencies/uthash/src")
target_link_libraries(nDPIsrvd-captured "${PCAP_LIBRARY}")
add_executable(nDPIsrvd-json-dump examples/c-json-stdout/c-json-stdout.c)
target_include_directories(nDPIsrvd-json-dump PRIVATE
"${CMAKE_SOURCE_DIR}"
"${CMAKE_SOURCE_DIR}/dependencies/jsmn")
install(TARGETS nDPIsrvd-collectd nDPIsrvd-captured nDPIsrvd-json-dump DESTINATION bin)
endif()
install(TARGETS nDPId DESTINATION sbin)
install(TARGETS nDPIsrvd nDPId-test DESTINATION bin)
install(FILES dependencies/nDPIsrvd.py DESTINATION share/nDPId)
install(FILES examples/py-flow-info/flow-info.py DESTINATION bin RENAME nDPIsrvd-flow-info.py)
message(STATUS "--------------------------")
message(STATUS "CMAKE_BUILD_TYPE.........: ${CMAKE_BUILD_TYPE}")
message(STATUS "CMAKE_C_FLAGS............: ${CMAKE_C_FLAGS}")
if(ENABLE_MEMORY_PROFILING)
message(STATUS "MEMORY_PROFILING_CFLAGS..: ${MEMORY_PROFILING_CFLAGS}")
endif()
message(STATUS "ENABLE_SANITIZER.........: ${ENABLE_SANITIZER}")
message(STATUS "ENABLE_SANITIZER_THREAD..: ${ENABLE_SANITIZER_THREAD}")
message(STATUS "ENABLE_MEMORY_PROFILING..: ${ENABLE_MEMORY_PROFILING}")
if(NOT BUILD_NDPI)
message(STATUS "STATIC_LIBNDPI_INSTALLDIR: ${STATIC_LIBNDPI_INSTALLDIR}")
if(NOT STATIC_LIBNDPI_INSTALLDIR STREQUAL "")
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()
endif()
message(STATUS "BUILD_NDPI...............: ${BUILD_NDPI}")
message(STATUS "--------------------------")

View File

@@ -1,34 +0,0 @@
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

147
README.md Normal file
View File

@@ -0,0 +1,147 @@
# abstract
nDPId is a set of daemons and tools to capture, process and classify network flows.
It's only dependencies (besides a half-way modern c library and POSIX threads) are libnDPI (>= 3.6.0 or current github dev branch) and libpcap.
The core daemon nDPId uses pthread but does use mutexes for performance reasons.
Instead synchronization is achieved by a packet distribution mechanism.
To balance all workload to all threads (more or less) equally a hash value is calculated using the 5-tuple.
This value serves as unique identifier for the processing thread. Multithreaded packet processing has to be flow-stable.
nDPId uses libnDPI's JSON serialization to produce meaningful JSON output which it then sends to the nDPIsrvd for distribution.
High level applications can connect to nDPIsrvd to get the latest flow/packet events from nDPId.
Unfortunately nDPIsrvd does currently not support any encryption/authentication for TCP connections.
# architecture
This project uses some kind of microservice architecture.
```text
_______________________ __________________________
| "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 >| `- connect to UNIX socket | | |
| (eth1) `- Thread 2 >| `- sends serialized data | |< example/... |
| `- Thread N >| | |________________________|
|_____________________| |
`- connect to UNIX/TCP socket
`- receives serialized data
```
It doesn't use a producer/consumer design pattern, so the wording is not precise.
# JSON TCP protocol
All JSON strings sent need to be in the following format:
```text
[5-digit-number][JSON string]
```
## Example:
```text
00015{"key":"value"}
```
where `00015` describes the length of a **complete** JSON string.
TODO: Describe data format via JSON schema.
# build (CMake)
```shell
mkdir build
cd build
cmake ..
```
or
```shell
mkdir build
cd build
ccmake ..
```
or to build with a staticially linked libnDPI:
```shell
mkdir build
cd build
cmake .. -DSTATIC_LIBNDPI_INSTALLDIR=[path/to/your/libnDPI/installdir]
```
If you're using the latter one, make sure that you've configured libnDPI with `./configure --prefix=[path/to/your/libnDPI/installdir]`
and do not forget to set the all necessary CMake variables to link against shared libraries used by your nDPI build.
e.g.:
```shell
mkdir build
cd build
cmake .. -DSTATIC_LIBNDPI_INSTALLDIR=[path/to/your/libnDPI/installdir] -DNDPI_WITH_GCRYPT=ON -DNDPI_WITH_PCRE=OFF -DNDPI_WITH_MAXMINDDB=OFF
```
Or if this is all too much for you, let CMake do it for you:
```shell
mkdir build
cd build
cmake .. -DBUILD_NDPI=ON
```
# run
Generate a nDPId compatible JSON dump:
```shell
./nDPId-test [path-to-a-PCAP-file]
```
Daemons:
```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
```shell
./nDPIsrvd-json-dump
```
or anything below `./examples`.
# test
You may want to run some integration tests using pcap files from nDPI:
`./test/run_tests.sh /path/to/libnDPI/root/directory`
e.g.:
`./test/run_tests.sh ${HOME}/git/nDPI`
For out-of-source builds, you'll need to specify a path to nDPId-test as well with:
`/test/run_tests.sh /path/to/libnDPI/root/directory /path/to/nDPId-test-executable`
For in-source builds and if CMake was configured with BUILD_NDPI=ON you can just type:
`/test/run_tests.sh`

7
TODO.md Normal file
View File

@@ -0,0 +1,7 @@
# TODOs
1. unify `struct io_buffer` from nDPIsrvd.c and `struct nDPIsrvd_buffer` from nDPIsrvd.h
2. improve nDPIsrvd buffer bloat handling (Do not fall back to blocking mode!)
3. improve UDP/TCP timeout handling by reading netfilter conntrack timeouts from /proc
4. detect interface / timeout changes and apply them to nDPId
5. implement AEAD crypto via libsodium (at least for TCP communication)

69
cmake/FindGCRYPT.cmake Normal file
View File

@@ -0,0 +1,69 @@
#
# - 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)

273
cmake/FindPCAP.cmake Normal file
View File

@@ -0,0 +1,273 @@
#
# - 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()

35
config.h Normal file
View File

@@ -0,0 +1,35 @@
#ifndef CONFIG_H
#define CONFIG_H 1
/* macros shared across multiple executables */
#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 12288u /* 8192 + 4096 */
#define NETWORK_BUFFER_LENGTH_DIGITS 5u
#define NETWORK_BUFFER_LENGTH_DIGITS_STR "5"
/* nDPId default config options */
#define nDPId_PIDFILE "/tmp/ndpid.pid"
#define nDPId_MAX_FLOWS_PER_THREAD 4096u
#define nDPId_MAX_IDLE_FLOWS_PER_THREAD 512u
#define nDPId_TICK_RESOLUTION 1000u
#define nDPId_MAX_READER_THREADS 32u
#define nDPId_IDLE_SCAN_PERIOD 10000u /* 10 sec */
#define nDPId_IDLE_TIME 600000u /* 600 sec */
#define nDPId_TCP_POST_END_FLOW_TIME 60000u /* 60 sec */
#define nDPId_THREAD_DISTRIBUTION_SEED 0x03dd018b
#define nDPId_PACKETS_PER_FLOW_TO_SEND 15u
#define nDPId_PACKETS_PER_FLOW_TO_PROCESS 255u
#define nDPId_FLOW_STRUCT_SEED 0x5defc104
/* nDPIsrvd default config options */
#define nDPIsrvd_PIDFILE "/tmp/ndpisrvd.pid"
#endif

90
dependencies/jsmn/.clang-format vendored Normal file
View File

@@ -0,0 +1,90 @@
---
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 Normal file
View File

@@ -0,0 +1,4 @@
language: c
sudo: false
script:
- make test

20
dependencies/jsmn/LICENSE vendored Normal file
View File

@@ -0,0 +1,20 @@
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 Normal file
View File

@@ -0,0 +1,36 @@
# 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 Normal file
View File

@@ -0,0 +1,182 @@
JSMN
====
[![Build Status](https://travis-ci.org/zserge/jsmn.svg?branch=master)](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);
```
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,
JSMN_ARRAY = 2,
JSMN_STRING = 3,
JSMN_PRIMITIVE = 4
} 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 Normal file
View File

@@ -0,0 +1,134 @@
#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 Normal file
View File

@@ -0,0 +1,77 @@
#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 Normal file
View File

@@ -0,0 +1,471 @@
/*
* 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,
JSMN_ARRAY = 2,
JSMN_STRING = 3,
JSMN_PRIMITIVE = 4
} 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;
parser->pos++;
/* Skip starting quote */
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 Normal file
View File

@@ -0,0 +1,16 @@
{
"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 Normal file
View File

@@ -0,0 +1,31 @@
#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 Normal file
View File

@@ -0,0 +1,359 @@
#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 Normal file
View File

@@ -0,0 +1,96 @@
#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__ */

841
dependencies/nDPIsrvd.h vendored Normal file
View File

@@ -0,0 +1,841 @@
#ifndef NDPISRVD_H
#define NDPISRVD_H 1
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include "config.h"
#include "jsmn.h"
#include "utarray.h"
#include "uthash.h"
#define nDPIsrvd_MAX_JSON_TOKENS 128
#define nDPIsrvd_FLOW_KEY_TOKENS 3
#define nDPIsrvd_FLOW_KEY_STRLEN 24
#define nDPIsrvd_JSON_KEY_STRLEN 32
#define nDPIsrvd_STRLEN_SZ(s) (sizeof(s) / sizeof(s[0]) - sizeof(s[0]))
#define TOKEN_GET_SZ(sock, key) token_get(sock, (char const *)key, nDPIsrvd_STRLEN_SZ(key))
#define TOKEN_GET_VALUE_SZ(sock, key, value_length) \
token_get_value(sock, (char const *)key, nDPIsrvd_STRLEN_SZ(key), value_length)
#define TOKEN_VALUE_EQUALS_SZ(token, string_to_check) \
token_value_equals(token, string_to_check, nDPIsrvd_STRLEN_SZ(string_to_check))
#define TOKEN_VALUE_TO_ULL(token, value) token_value_to_ull(token, value)
#define FIRST_ENUM_VALUE 1
#define LAST_ENUM_VALUE CONVERSION_LAST_ENUM_VALUE
enum nDPIsrvd_connect_return
{
CONNECT_OK = FIRST_ENUM_VALUE,
CONNECT_ERROR_SOCKET,
CONNECT_ERROR,
CONNECT_LAST_ENUM_VALUE
};
enum nDPIsrvd_read_return
{
READ_OK = CONNECT_LAST_ENUM_VALUE,
READ_PEER_DISCONNECT,
READ_ERROR,
READ_LAST_ENUM_VALUE
};
enum nDPIsrvd_parse_return
{
PARSE_OK = READ_LAST_ENUM_VALUE, /* can only be returned by nDPIsrvd_parse_line, not nDPIsrvd_parse_all */
PARSE_NEED_MORE_DATA, /* returned by nDPIsrvd_parse_line and nDPIsrvd_parse_all */
PARSE_INVALID_OPENING_CHAR,
PARSE_SIZE_EXCEEDS_CONVERSION_LIMIT,
PARSE_SIZE_MISSING,
PARSE_STRING_TOO_BIG,
PARSE_INVALID_CLOSING_CHAR,
PARSE_JSMN_ERROR,
PARSE_JSON_CALLBACK_ERROR,
PARSE_JSON_MGMT_ERROR,
PARSE_FLOW_MGMT_ERROR,
PARSE_LAST_ENUM_VALUE
};
enum nDPIsrvd_callback_return
{
CALLBACK_OK = PARSE_LAST_ENUM_VALUE,
CALLBACK_ERROR,
CALLBACK_LAST_ENUM_VALUE
};
enum nDPIsrvd_conversion_return
{
CONVERSION_OK = CALLBACK_LAST_ENUM_VALUE,
CONVERISON_KEY_NOT_FOUND,
CONVERSION_NOT_A_NUMBER,
CONVERSION_RANGE_EXCEEDED,
CONVERSION_LAST_ENUM_VALUE
};
typedef unsigned long long int nDPIsrvd_ull;
typedef nDPIsrvd_ull * nDPIsrvd_ull_ptr;
struct nDPIsrvd_flow_key
{
char key[nDPIsrvd_FLOW_KEY_STRLEN];
};
struct nDPIsrvd_flow
{
struct nDPIsrvd_flow_key flow_key;
nDPIsrvd_ull id_as_ull;
UT_hash_handle hh;
uint8_t flow_user_data[0];
};
struct nDPIsrvd_json_token
{
char key[nDPIsrvd_JSON_KEY_STRLEN];
int key_length;
UT_hash_handle hh;
char const * value;
int value_length;
};
struct nDPIsrvd_socket;
#ifdef ENABLE_MEMORY_PROFILING
static inline void * nDPIsrvd_uthash_malloc(size_t const size);
static inline void nDPIsrvd_uthash_free(void * const freeable, size_t const size);
#endif
typedef enum nDPIsrvd_callback_return (*json_callback)(struct nDPIsrvd_socket * const sock,
struct nDPIsrvd_flow * const flow);
typedef void (*flow_end_callback)(struct nDPIsrvd_socket * const sock, struct nDPIsrvd_flow * const flow);
struct nDPIsrvd_address
{
socklen_t size;
union {
struct sockaddr_in in;
struct sockaddr_in6 in6;
struct sockaddr_un un;
struct sockaddr raw;
};
};
struct nDPIsrvd_buffer
{
char raw[NETWORK_BUFFER_MAX_SIZE];
size_t used;
char * json_string;
size_t json_string_start;
nDPIsrvd_ull json_string_length;
};
struct nDPIsrvd_jsmn
{
jsmn_parser parser;
jsmntok_t tokens[nDPIsrvd_MAX_JSON_TOKENS];
int tokens_found;
};
struct nDPIsrvd_socket
{
int fd;
struct nDPIsrvd_address address;
size_t flow_user_data_size;
struct nDPIsrvd_flow * flow_table;
json_callback json_callback;
flow_end_callback flow_end_callback;
struct nDPIsrvd_buffer buffer;
struct nDPIsrvd_jsmn jsmn;
/* easy and fast JSON key/value access via hash table and a static array */
struct
{
UT_array * tokens;
struct nDPIsrvd_json_token * token_table;
} json;
size_t global_user_data_size;
uint8_t global_user_data[0];
};
static inline void nDPIsrvd_free(struct nDPIsrvd_socket ** const sock);
/* Slightly modified code: https://en.wikibooks.org/wiki/Algorithm_Implementation/Miscellaneous/Base64 */
#define WHITESPACE 64
#define EQUALS 65
#define INVALID 66
static inline int nDPIsrvd_base64decode(char * in, size_t inLen, unsigned char * out, size_t * outLen)
{
char * end = in + inLen;
char iter = 0;
uint32_t buf = 0;
size_t len = 0;
/* treat ASCII char 92 '\\' as whitespace because libnDPI escapes all strings by prepending '/' with a '\\' */
static const unsigned char d[] = {66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 64, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 62, 66, 66, 66, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 66, 66,
66, 65, 66, 66, 66, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 66, 64, 66, 66, 66, 66, 26, 27, 28,
29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66};
while (in < end)
{
unsigned char c = d[*(unsigned char *)in++];
switch (c)
{
case WHITESPACE:
continue; /* skip whitespace */
case INVALID:
return 1; /* invalid input, return error */
case EQUALS: /* pad character, end of data */
in = end;
continue;
default:
buf = buf << 6 | c;
iter++; // increment the number of iteration
/* If the buffer is full, split it into bytes */
if (iter == 4)
{
if ((len += 3) > *outLen)
return 1; /* buffer overflow */
*(out++) = (buf >> 16) & 255;
*(out++) = (buf >> 8) & 255;
*(out++) = buf & 255;
buf = 0;
iter = 0;
}
}
}
if (iter == 3)
{
if ((len += 2) > *outLen)
return 1; /* buffer overflow */
*(out++) = (buf >> 10) & 255;
*(out++) = (buf >> 2) & 255;
}
else if (iter == 2)
{
if (++len > *outLen)
return 1; /* buffer overflow */
*(out++) = (buf >> 4) & 255;
}
*outLen = len; /* modify to reflect the actual output size */
return 0;
}
static inline char const * nDPIsrvd_enum_to_string(int enum_value)
{
static char const * const enum_str[LAST_ENUM_VALUE + 1] = {"CONNECT_OK",
"CONNECT_ERROR_SOCKET",
"CONNECT_ERROR",
"READ_OK",
"READ_PEER_DISCONNECT",
"READ_ERROR",
"PARSE_OK",
"PARSE_NEED_MORE_DATA",
"PARSE_INVALID_OPENING_CHAR",
"PARSE_SIZE_EXCEEDS_CONVERSION_LIMIT",
"PARSE_SIZE_MISSING",
"PARSE_STRING_TOO_BIG",
"PARSE_INVALID_CLOSING_CHAR",
"PARSE_JSMN_ERROR",
"PARSE_JSON_CALLBACK_ERROR",
"PARSE_JSON_MGMT_ERROR",
"PARSE_FLOW_MGMT_ERROR",
"CALLBACK_OK",
"CALLBACK_ERROR",
"CONVERSION_OK",
"CONVERISON_KEY_NOT_FOUND",
"CONVERSION_NOT_A_NUMBER",
"CONVERSION_RANGE_EXCEEDED",
[LAST_ENUM_VALUE] = "LAST_ENUM_VALUE"};
if (enum_value < FIRST_ENUM_VALUE || enum_value >= LAST_ENUM_VALUE)
{
return NULL;
}
return enum_str[enum_value - FIRST_ENUM_VALUE];
}
static inline struct nDPIsrvd_socket * nDPIsrvd_init(size_t global_user_data_size,
size_t flow_user_data_size,
json_callback json_cb,
flow_end_callback flow_end_cb)
{
static const UT_icd packet_data_icd = {sizeof(struct nDPIsrvd_json_token), NULL, NULL, NULL};
struct nDPIsrvd_socket * sock = (struct nDPIsrvd_socket *)malloc(sizeof(*sock) + global_user_data_size);
if (json_cb == NULL)
{
goto error;
}
if (sock != NULL)
{
memset(sock, 0, sizeof(*sock));
sock->fd = -1;
sock->address.raw.sa_family = -1;
sock->flow_user_data_size = flow_user_data_size;
sock->json_callback = json_cb;
sock->flow_end_callback = flow_end_cb;
utarray_new(sock->json.tokens, &packet_data_icd);
if (sock->json.tokens == NULL)
{
goto error;
}
utarray_reserve(sock->json.tokens, nDPIsrvd_MAX_JSON_TOKENS);
sock->global_user_data_size = global_user_data_size;
}
return sock;
error:
nDPIsrvd_free(&sock);
return NULL;
}
static inline void nDPIsrvd_free(struct nDPIsrvd_socket ** const sock)
{
struct nDPIsrvd_flow * current_flow;
struct nDPIsrvd_flow * ftmp;
struct nDPIsrvd_json_token * current_json_token;
struct nDPIsrvd_json_token * jtmp;
if (sock == NULL || *sock == NULL)
{
return;
}
if ((*sock)->json.token_table != NULL)
{
HASH_ITER(hh, (*sock)->json.token_table, current_json_token, jtmp)
{
HASH_DEL((*sock)->json.token_table, current_json_token);
}
(*sock)->json.token_table = NULL;
}
if ((*sock)->json.tokens != NULL)
{
utarray_free((*sock)->json.tokens);
}
if ((*sock)->flow_table != NULL)
{
HASH_ITER(hh, (*sock)->flow_table, current_flow, ftmp)
{
if ((*sock)->flow_end_callback != NULL)
{
(*sock)->flow_end_callback(*sock, current_flow);
}
HASH_DEL((*sock)->flow_table, current_flow);
free(current_flow);
}
(*sock)->flow_table = NULL;
}
free(*sock);
*sock = NULL;
}
static inline int nDPIsrvd_setup_address(struct nDPIsrvd_address * const address, char const * const destination)
{
size_t len = strlen(destination);
char * first_colon = strchr(destination, ':');
char * last_colon = strrchr(destination, ':');
memset(address, 0, sizeof(*address));
if (last_colon == NULL)
{
address->raw.sa_family = AF_UNIX;
address->size = sizeof(address->un);
if (snprintf(address->un.sun_path, sizeof(address->un.sun_path), "%s", destination) <= 0)
{
return 1;
}
}
else
{
char addr_buf[INET6_ADDRSTRLEN];
char const * address_start = destination;
char const * address_end = last_colon;
void * sock_addr;
if (first_colon == last_colon)
{
address->raw.sa_family = AF_INET;
address->size = sizeof(address->in);
address->in.sin_port = htons(atoi(last_colon + 1));
sock_addr = &address->in.sin_addr;
if (len < 7)
{
return 1;
}
}
else
{
address->raw.sa_family = AF_INET6;
address->size = sizeof(address->in6);
address->in6.sin6_port = htons(atoi(last_colon + 1));
sock_addr = &address->in6.sin6_addr;
if (len < 2)
{
return 1;
}
if (destination[0] == '[')
{
if (*(last_colon - 1) != ']')
{
return 1;
}
address_start++;
address_end--;
}
}
if (snprintf(addr_buf, sizeof(addr_buf), "%.*s", (int)(address_end - address_start), address_start) <= 0)
{
return 1;
}
if (inet_pton(address->raw.sa_family, addr_buf, sock_addr) != 1)
{
return 1;
}
}
return 0;
}
static inline enum nDPIsrvd_connect_return nDPIsrvd_connect(struct nDPIsrvd_socket * const sock)
{
sock->fd = socket(sock->address.raw.sa_family, SOCK_STREAM, 0);
if (sock->fd < 0)
{
return CONNECT_ERROR_SOCKET;
}
if (connect(sock->fd, &sock->address.raw, sock->address.size) != 0)
{
return CONNECT_ERROR;
}
return CONNECT_OK;
}
static inline enum nDPIsrvd_read_return nDPIsrvd_read(struct nDPIsrvd_socket * const sock)
{
ssize_t bytes_read =
read(sock->fd, sock->buffer.raw + sock->buffer.used, sizeof(sock->buffer.raw) - sock->buffer.used);
if (bytes_read == 0)
{
return READ_PEER_DISCONNECT;
}
if (bytes_read < 0)
{
return READ_ERROR;
}
sock->buffer.used += bytes_read;
return READ_OK;
}
static inline int jsmn_token_is_key(int current_token_index)
{
return current_token_index % 2;
}
static inline char const * jsmn_token_get(struct nDPIsrvd_socket const * const sock, int current_token_index)
{
return sock->buffer.json_string + sock->jsmn.tokens[current_token_index].start;
}
static inline int jsmn_token_size(struct nDPIsrvd_socket const * const sock, int current_token_index)
{
return sock->jsmn.tokens[current_token_index].end - sock->jsmn.tokens[current_token_index].start;
}
static inline int jsmn_token_is_jsmn_type(struct nDPIsrvd_socket const * const sock,
int current_token_index,
jsmntype_t type_to_check)
{
return sock->jsmn.tokens[current_token_index].type == type_to_check;
}
static inline struct nDPIsrvd_json_token const * token_get(struct nDPIsrvd_socket const * const sock,
char const * const key,
size_t key_length)
{
struct nDPIsrvd_json_token * token = NULL;
HASH_FIND(hh, sock->json.token_table, key, key_length, token);
if (token != NULL && token->value_length > 0 && token->value != NULL)
{
return token;
}
return NULL;
}
static inline char const * token_get_value(struct nDPIsrvd_socket const * const sock,
char const * const key,
size_t key_length,
size_t * value_length)
{
struct nDPIsrvd_json_token const * const token = token_get(sock, key, key_length);
if (token != NULL)
{
if (value_length != NULL)
{
*value_length = token->value_length;
}
return token->value;
}
return NULL;
}
static inline int token_value_equals(struct nDPIsrvd_json_token const * const token,
char const * const value,
size_t value_length)
{
if (token == NULL)
{
return 0;
}
return strncmp(token->value, value, token->value_length) == 0 && token->value_length == (int)value_length;
}
static inline enum nDPIsrvd_conversion_return str_value_to_ull(char const * const value_as_string,
nDPIsrvd_ull_ptr const value)
{
char * endptr = NULL;
*value = strtoull(value_as_string, &endptr, 10);
if (value_as_string == endptr)
{
return CONVERSION_NOT_A_NUMBER;
}
if (errno == ERANGE)
{
return CONVERSION_RANGE_EXCEEDED;
}
return CONVERSION_OK;
}
static inline enum nDPIsrvd_conversion_return token_value_to_ull(struct nDPIsrvd_json_token const * const token,
nDPIsrvd_ull_ptr const value)
{
if (token == NULL)
{
return CONVERISON_KEY_NOT_FOUND;
}
return str_value_to_ull(token->value, value);
}
static inline int nDPIsrvd_build_flow_key(struct nDPIsrvd_flow_key * const key,
struct nDPIsrvd_json_token const * const tokens[nDPIsrvd_FLOW_KEY_TOKENS])
{
if (tokens[0] == NULL || tokens[1] == NULL || tokens[2] == NULL)
{
return 1;
}
if (snprintf(key->key,
nDPIsrvd_FLOW_KEY_STRLEN,
"%.*s-%.*s-%.*s",
tokens[0]->value_length,
tokens[0]->value,
tokens[1]->value_length,
tokens[1]->value,
tokens[2]->value_length,
tokens[2]->value) <= 0)
{
return 1;
}
return 0;
}
static inline struct nDPIsrvd_flow * nDPIsrvd_get_flow(struct nDPIsrvd_socket * const sock,
struct nDPIsrvd_json_token const * const flow_id)
{
struct nDPIsrvd_json_token const * const tokens[nDPIsrvd_FLOW_KEY_TOKENS] = {
flow_id,
TOKEN_GET_SZ(sock, "alias"),
TOKEN_GET_SZ(sock, "source"),
};
struct nDPIsrvd_flow_key key = {};
if (nDPIsrvd_build_flow_key(&key, tokens) != 0)
{
return NULL;
}
struct nDPIsrvd_flow * flow = NULL;
HASH_FIND(hh, sock->flow_table, &key, sizeof(key), flow);
if (flow == NULL)
{
flow = (struct nDPIsrvd_flow *)calloc(1, sizeof(*flow) + sock->flow_user_data_size);
if (flow == NULL)
{
return NULL;
}
TOKEN_VALUE_TO_ULL(tokens[0], &flow->id_as_ull);
memcpy(flow->flow_key.key, key.key, nDPIsrvd_FLOW_KEY_STRLEN);
HASH_ADD(hh, sock->flow_table, flow_key, sizeof(flow->flow_key), flow);
}
return flow;
}
static inline int nDPIsrvd_check_flow_end(struct nDPIsrvd_socket * const sock,
struct nDPIsrvd_flow * const current_flow)
{
if (current_flow == NULL)
{
return 1;
}
struct nDPIsrvd_json_token const * const flow_event_name = TOKEN_GET_SZ(sock, "flow_event_name");
if (TOKEN_VALUE_EQUALS_SZ(flow_event_name, "idle") != 0 || TOKEN_VALUE_EQUALS_SZ(flow_event_name, "end") != 0)
{
if (sock->flow_end_callback != NULL)
{
sock->flow_end_callback(sock, current_flow);
}
HASH_DEL(sock->flow_table, current_flow);
free(current_flow);
}
return 0;
}
static inline enum nDPIsrvd_parse_return nDPIsrvd_parse_line(struct nDPIsrvd_buffer * const buffer,
struct nDPIsrvd_jsmn * const jsmn)
{
if (buffer->used < NETWORK_BUFFER_LENGTH_DIGITS + 1)
{
return PARSE_NEED_MORE_DATA;
}
if (buffer->raw[NETWORK_BUFFER_LENGTH_DIGITS] != '{')
{
return PARSE_INVALID_OPENING_CHAR;
}
errno = 0;
buffer->json_string_length = strtoull((const char *)buffer->raw, &buffer->json_string, 10);
buffer->json_string_length += buffer->json_string - buffer->raw;
buffer->json_string_start = buffer->json_string - buffer->raw;
if (errno == ERANGE)
{
return PARSE_SIZE_EXCEEDS_CONVERSION_LIMIT;
}
if (buffer->json_string == buffer->raw)
{
return PARSE_SIZE_MISSING;
}
if (buffer->json_string_length > sizeof(buffer->raw))
{
return PARSE_STRING_TOO_BIG;
}
if (buffer->json_string_length > buffer->used)
{
return PARSE_NEED_MORE_DATA;
}
if (buffer->raw[buffer->json_string_length - 2] != '}' || buffer->raw[buffer->json_string_length - 1] != '\n')
{
return PARSE_INVALID_CLOSING_CHAR;
}
jsmn_init(&jsmn->parser);
jsmn->tokens_found = jsmn_parse(&jsmn->parser,
(char *)(buffer->raw + buffer->json_string_start),
buffer->json_string_length - buffer->json_string_start,
jsmn->tokens,
nDPIsrvd_MAX_JSON_TOKENS);
if (jsmn->tokens_found < 0 || jsmn->tokens[0].type != JSMN_OBJECT)
{
return PARSE_JSMN_ERROR;
}
return PARSE_OK;
}
static void nDPIsrvd_drain_buffer(struct nDPIsrvd_buffer * const buffer)
{
memmove(buffer->raw, buffer->raw + buffer->json_string_length, buffer->used - buffer->json_string_length);
buffer->used -= buffer->json_string_length;
buffer->json_string_length = 0;
buffer->json_string_start = 0;
}
static inline enum nDPIsrvd_parse_return nDPIsrvd_parse_all(struct nDPIsrvd_socket * const sock)
{
enum nDPIsrvd_parse_return ret;
while ((ret = nDPIsrvd_parse_line(&sock->buffer, &sock->jsmn)) == PARSE_OK)
{
char const * key = NULL;
int key_length = 0;
for (int current_token = 1; current_token < sock->jsmn.tokens_found; current_token++)
{
if (jsmn_token_is_key(current_token) == 1)
{
if (key != NULL)
{
ret = PARSE_JSMN_ERROR;
break;
}
key = jsmn_token_get(sock, current_token);
key_length = jsmn_token_size(sock, current_token);
if (key == NULL)
{
ret = PARSE_JSMN_ERROR;
break;
}
}
else
{
struct nDPIsrvd_json_token * token = NULL;
HASH_FIND(hh, sock->json.token_table, key, (size_t)key_length, token);
if (token != NULL)
{
token->value = jsmn_token_get(sock, current_token);
token->value_length = jsmn_token_size(sock, current_token);
}
else
{
struct nDPIsrvd_json_token jt = {.value = jsmn_token_get(sock, current_token),
.value_length = jsmn_token_size(sock, current_token),
.hh = {}};
if (key == NULL || key_length > nDPIsrvd_JSON_KEY_STRLEN ||
utarray_len(sock->json.tokens) == nDPIsrvd_MAX_JSON_TOKENS)
{
ret = PARSE_JSON_MGMT_ERROR;
break;
}
jt.key_length = key_length;
snprintf(jt.key, nDPIsrvd_JSON_KEY_STRLEN, "%.*s", key_length, key);
utarray_push_back(sock->json.tokens, &jt);
HASH_ADD_STR(sock->json.token_table,
key,
(struct nDPIsrvd_json_token *)utarray_back(sock->json.tokens));
}
key = NULL;
key_length = 0;
}
}
struct nDPIsrvd_json_token const * const flow_id = TOKEN_GET_SZ(sock, "flow_id");
struct nDPIsrvd_flow * flow = NULL;
if (flow_id != NULL)
{
flow = nDPIsrvd_get_flow(sock, flow_id);
if (flow == NULL)
{
ret = PARSE_FLOW_MGMT_ERROR;
}
}
if (ret == PARSE_OK && sock->json_callback(sock, flow) != CALLBACK_OK)
{
ret = PARSE_JSON_CALLBACK_ERROR;
}
if (flow_id != NULL && nDPIsrvd_check_flow_end(sock, flow) != 0)
{
ret = PARSE_FLOW_MGMT_ERROR;
}
sock->jsmn.tokens_found = 0;
{
struct nDPIsrvd_json_token * current_token = NULL;
struct nDPIsrvd_json_token * jtmp = NULL;
HASH_ITER(hh, sock->json.token_table, current_token, jtmp)
{
current_token->value = NULL;
current_token->value_length = 0;
}
}
nDPIsrvd_drain_buffer(&sock->buffer);
}
return ret;
}
#ifdef ENABLE_MEMORY_PROFILING
static inline void * nDPIsrvd_uthash_malloc(size_t const size)
{
void * p = malloc(size);
if (p == NULL)
{
return NULL;
}
printf("malloc(%zu)\n", size);
return p;
}
static inline void nDPIsrvd_uthash_free(void * const freeable, size_t const size)
{
printf("free(%zu)\n", size);
free(freeable);
}
#endif
#endif

416
dependencies/nDPIsrvd.py vendored Normal file
View File

@@ -0,0 +1,416 @@
#!/usr/bin/env python3
import argparse
import array
import base64
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
try:
import scapy.all
except ImportError:
sys.stderr.write('Python module scapy not found, PCAP generation will fail!\n')
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 = 12288 # 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 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):
if USE_COLORAMA is True:
fg_color, bg_color = TermColor.getColorsByHash(string)
color_hash = TermColor.calcColorHash(string)
return '{}{}{}{}{}'.format(Style.BRIGHT, fg_color, bg_color, string, Style.RESET_ALL)
else:
return '{}{}{}'.format(TermColor.BOLD, string, TermColor.END)
class Flow:
flow_id = -1
class FlowManager:
def __init__(self):
self.__flows = dict()
def __buildFlowKey(self, json_dict):
if 'flow_id' not in json_dict or \
'alias' not in json_dict or \
'source' not in json_dict:
return None
return str(json_dict['alias']) + str(json_dict['source']) + str(json_dict['flow_id'])
def getFlow(self, json_dict):
event = json_dict['flow_event_name'].lower() if 'flow_event_name' in json_dict else ''
flow_key = self.__buildFlowKey(json_dict)
flow = None
if flow_key is None:
return None
if flow_key not in self.__flows:
self.__flows[flow_key] = Flow()
self.__flows[flow_key].flow_id = int(json_dict['flow_id'])
flow = self.__flows[flow_key]
if event == 'end' or event == 'idle':
flow = self.__flows[flow_key]
del self.__flows[flow_key]
return flow
class nDPIsrvdException(Exception):
UNSUPPORTED_ADDRESS_TYPE = 1
BUFFER_CAPACITY_REACHED = 2
SOCKET_CONNECTION_BROKEN = 3
INVALID_LINE_RECEIVED = 4
CALLBACK_RETURNED_FALSE = 5
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 nDPIsrvdSocket:
def __init__(self):
self.sock_family = None
self.flow_mgr = FlowManager()
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 = []
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()
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.msglen = 0
self.digitlen = 0
if connection_finished is True:
raise SocketConnectionBroken()
return new_data_avail
def parse(self, callback, global_user_data):
retval = True
index = 0
for received_json_line in self.lines:
json_dict = json.loads(received_json_line[0].decode('ascii', errors='replace'), strict=True)
if callback(json_dict, self.flow_mgr.getFlow(json_dict), global_user_data) is not True:
retval = False
break
index += 1
self.lines = self.lines[index:]
return retval
def loop(self, callback, 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, global_user_data) is False:
raise CallbackReturnedFalse()
if throw_ex is not None:
raise throw_ex
class PcapPacket:
def __init__(self):
self.pktdump = None
self.flow_id = 0
self.packets = []
self.__suffix = ''
self.__dump = False
self.__dumped = False
@staticmethod
def isInitialized(current_flow):
return current_flow is not None and hasattr(current_flow, 'pcap_packet')
@staticmethod
def handleJSON(json_dict, current_flow):
if 'flow_event_name' in json_dict:
if json_dict['flow_event_name'] == 'new':
current_flow.pcap_packet = PcapPacket()
current_flow.pcap_packet.current_packet = 0
current_flow.pcap_packet.max_packets = json_dict['flow_max_packets']
current_flow.pcap_packet.flow_id = json_dict['flow_id']
elif PcapPacket.isInitialized(current_flow) is not True:
pass
elif json_dict['flow_event_name'] == 'end' or json_dict['flow_event_name'] == 'idle':
try:
current_flow.pcap_packet.fin()
except RuntimeError:
pass
elif PcapPacket.isInitialized(current_flow) is True and \
('packet_event_name' in json_dict and json_dict['packet_event_name'] == 'packet-flow' and current_flow.pcap_packet.flow_id > 0) or \
('packet_event_name' in json_dict and json_dict['packet_event_name'] == 'packet' and 'pkt' in json_dict):
buffer_decoded = base64.b64decode(json_dict['pkt'], validate=True)
current_flow.pcap_packet.packets += [ ( buffer_decoded, json_dict['pkt_type'], json_dict['pkt_l3_offset'] ) ]
current_flow.pcap_packet.current_packet += 1
if current_flow.pcap_packet.current_packet != int(json_dict['flow_packet_id']):
raise RuntimeError('Packet IDs not in sync (local: {}, remote: {}).'.format(current_flow.pcap_packet.current_packet, int(json_dict['flow_packet_id'])))
@staticmethod
def getIp(packet):
if packet[1] == PKT_TYPE_ETH_IP4:
return scapy.all.IP(packet[0][packet[2]:])
elif packet[1] == PKT_TYPE_ETH_IP6:
return scapy.all.IPv6(packet[0][packet[2]:])
else:
raise RuntimeError('packet type unknown: {}'.format(packet[1]))
@staticmethod
def getTCPorUDP(packet):
p = PcapPacket.getIp(packet)
if p.haslayer(scapy.all.TCP):
return p.getlayer(scapy.all.TCP)
elif p.haslayer(scapy.all.UDP):
return p.getlayer(scapy.all.UDP)
else:
return None
def setSuffix(self, filename_suffix):
self.__suffix = filename_suffix
def doDump(self):
self.__dump = True
def fin(self):
if self.__dumped is True:
raise RuntimeError('Flow {} already dumped.'.format(self.flow_id))
if self.__dump is False:
raise RuntimeError('Flow {} should not be dumped.'.format(self.flow_id))
emptyTCPorUDPcount = 0;
for packet in self.packets:
p = PcapPacket.getTCPorUDP(packet)
if p is not None:
if p.haslayer(scapy.all.Padding) and len(p.payload) - len(p[scapy.all.Padding]) == 0:
emptyTCPorUDPcount += 1
elif len(p.payload) == 0:
emptyTCPorUDPcount += 1
if emptyTCPorUDPcount == len(self.packets):
raise RuntimeError('Flow {} does not contain any packets({}) with non-empty layer4 payload.'.format(self.flow_id, len(self.packets)))
if self.pktdump is None:
if self.flow_id == 0:
self.pktdump = scapy.all.PcapWriter('packet-{}.pcap'.format(self.__suffix),
append=True, sync=True)
else:
self.pktdump = scapy.all.PcapWriter('flow-{}-{}.pcap'.format(self.__suffix, self.flow_id),
append=False, sync=True)
for packet in self.packets:
self.pktdump.write(PcapPacket.getIp(packet))
self.pktdump.close()
self.__dumped = True
return True
def defaultArgumentParser():
parser = argparse.ArgumentParser(description='nDPIsrvd options', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
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')
return parser
def validateAddress(args):
tcp_addr_set = False
address = None
if args.host is None:
address_tcpip = (DEFAULT_HOST, DEFAULT_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
global schema
schema = {'packet_event_schema' : None, 'basic_event_schema' : None, 'daemon_event_schema' : None, 'flow_event_schema' : None}
def initSchemaValidator(schema_dir='./schema'):
for key in schema:
with open(schema_dir + '/' + str(key) + '.json', 'r') as schema_file:
schema[key] = json.load(schema_file)
def validateAgainstSchema(json_dict):
import jsonschema
if 'packet_event_id' in json_dict:
jsonschema.validate(instance=json_dict, schema=schema['packet_event_schema'])
return True
if 'basic_event_id' in json_dict:
jsonschema.validate(instance=json_dict, schema=schema['basic_event_schema'])
return True
if 'daemon_event_id' in json_dict:
jsonschema.validate(instance=json_dict, schema=schema['daemon_event_schema'])
return True
if 'flow_event_id' in json_dict:
jsonschema.validate(instance=json_dict, schema=schema['flow_event_schema'])
return True
return False

6
dependencies/update_jsmn.sh vendored Executable file
View File

@@ -0,0 +1,6 @@
#!/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 Executable file
View File

@@ -0,0 +1,6 @@
#!/usr/bin/env sh
MYDIR="$(dirname ${0})"
cd "${MYDIR}/.."
git subtree pull --squash --prefix=dependencies/uthash https://github.com/troydhanson/uthash.git master

3
dependencies/uthash/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
*~
*.out
keystat.*

14
dependencies/uthash/.travis.yml vendored Normal file
View File

@@ -0,0 +1,14 @@
language: cpp
matrix:
include:
- os: linux
compiler: gcc
- os: linux
compiler: clang
- os: osx
script:
- make -C tests EXTRA_CFLAGS="-W -Wall -Wextra"
- 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 Normal file
View File

@@ -0,0 +1,21 @@
Copyright (c) 2005-2018, Troy D. Hanson http://troydhanson.github.com/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.

8
dependencies/uthash/README.md vendored Normal file
View File

@@ -0,0 +1,8 @@
[![Build status](https://travis-ci.org/Quuxplusone/uthash.svg?branch=travis-ci)](https://travis-ci.org/troydhanson/uthash)
Documentation for uthash is available at:
http://troydhanson.github.com/uthash/

7
dependencies/uthash/doc/.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
ChangeLog.html
userguide.html
utarray.html
utlist.html
utringbuffer.html
utstack.html
utstring.html

267
dependencies/uthash/doc/ChangeLog.txt vendored Normal file
View File

@@ -0,0 +1,267 @@
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.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.com/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 Normal file
View File

@@ -0,0 +1,18 @@
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 Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

451
dependencies/uthash/doc/banner.svg vendored Normal file
View File

@@ -0,0 +1,451 @@
<?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>

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1 @@
google-site-verification: google315d692c9c632ed0.html

122
dependencies/uthash/doc/index.html vendored Normal file
View File

@@ -0,0 +1,122 @@
<!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="http://github.com/troydhanson/uthash">GitHub page</a> &gt;
uthash home <!-- http://troydhanson.github.com/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="http://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, &amp;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 Normal file
View File

@@ -0,0 +1,55 @@
<!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="http://troydhanson.github.com/uthash/">uthash home</a> &gt;
BSD license
</div>
<hr />
<div id="mid">
<div id="main">
<pre>
Copyright (c) 2005-2018, Troy D. Hanson http://troydhanson.github.com/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 Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 689 B

141
dependencies/uthash/doc/styles.css vendored Normal file
View File

@@ -0,0 +1,141 @@
#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;
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%;
}

1901
dependencies/uthash/doc/userguide.txt vendored Normal file

File diff suppressed because it is too large Load Diff

383
dependencies/uthash/doc/utarray.txt vendored Normal file
View File

@@ -0,0 +1,383 @@
utarray: dynamic array macros for C
===================================
Troy D. Hanson <tdh@tkhanson.net>
v2.1.0, December 2018
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 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 Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

288
dependencies/uthash/doc/uthash-mini.svg vendored Normal file
View File

@@ -0,0 +1,288 @@
<?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>

After

Width:  |  Height:  |  Size: 12 KiB

BIN
dependencies/uthash/doc/uthash.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

293
dependencies/uthash/doc/utlist.txt vendored Normal file
View File

@@ -0,0 +1,293 @@
utlist: linked list macros for C structures
===========================================
Troy D. Hanson <tdh@tkhanson.net>
v2.1.0, December 2018
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 Normal file
View File

@@ -0,0 +1,374 @@
utringbuffer: dynamic ring-buffer macros for C
==============================================
Arthur O'Dwyer <arthur.j.odwyer@gmail.com>
v2.1.0, December 2018
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 Normal file
View File

@@ -0,0 +1,158 @@
utstack: intrusive stack macros for C
=====================================
Arthur O'Dwyer <arthur.j.odwyer@gmail.com>
v2.1.0, December 2018
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 Normal file
View File

@@ -0,0 +1,239 @@
utstring: dynamic string macros for C
=====================================
Troy D. Hanson <tdh@tkhanson.net>
v2.1.0, December 2018
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 Symbolic link
View File

@@ -0,0 +1 @@
src

28
dependencies/uthash/package.json vendored Normal file
View File

@@ -0,0 +1,28 @@
{
"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.1.0"
}

247
dependencies/uthash/src/utarray.h vendored Normal file
View File

@@ -0,0 +1,247 @@
/*
Copyright (c) 2008-2018, Troy D. Hanson http://troydhanson.github.com/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.1.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
#ifdef oom
#error "The name of macro 'oom' has been changed to 'utarray_oom'. Please update your code."
#define utarray_oom() oom()
#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 **_src = (char**)src, **_dst = (char**)dst;
*_dst = (*_src == NULL) ? NULL : strdup(*_src);
}
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 */

1150
dependencies/uthash/src/uthash.h vendored Normal file

File diff suppressed because it is too large Load Diff

1073
dependencies/uthash/src/utlist.h vendored Normal file

File diff suppressed because it is too large Load Diff

108
dependencies/uthash/src/utringbuffer.h vendored Normal file
View File

@@ -0,0 +1,108 @@
/*
Copyright (c) 2015-2018, Troy D. Hanson http://troydhanson.github.com/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.1.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 Normal file
View File

@@ -0,0 +1,88 @@
/*
Copyright (c) 2018-2018, Troy D. Hanson http://troydhanson.github.com/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.1.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 Normal file
View File

@@ -0,0 +1,407 @@
/*
Copyright (c) 2008-2018, Troy D. Hanson http://troydhanson.github.com/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.1.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 Normal file
View File

@@ -0,0 +1,120 @@
#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
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

127
dependencies/uthash/tests/README vendored Normal file
View File

@@ -0,0 +1,127 @@
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 memcmp 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 memcmp and strlen macros
test89: test code from the tinydtls project
test90: regression-test HASH_ADD_KEYPTR_INORDER (IronBug)
test91: test LL_INSERT_INORDER etc.
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 Executable file
View File

@@ -0,0 +1,13 @@
#!/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 Normal file
View File

@@ -0,0 +1,82 @@
#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 Executable file
View File

@@ -0,0 +1,17 @@
#!/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 Executable file
View File

@@ -0,0 +1,21 @@
#!/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 Executable file
View File

@@ -0,0 +1,22 @@
#!/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;

View File

@@ -0,0 +1,20 @@
#!/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"

View File

@@ -0,0 +1,16 @@
:: 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 Normal file
View File

@@ -0,0 +1,48 @@
#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;
}

149
dependencies/uthash/tests/example.c vendored Normal file
View File

@@ -0,0 +1,149 @@
#include <stdio.h> /* gets */
#include <stdlib.h> /* atoi, malloc */
#include <string.h> /* strcpy */
#include "uthash.h"
struct my_struct {
int id; /* key */
char name[10];
UT_hash_handle hh; /* makes this structure hashable */
};
struct my_struct *users = NULL;
void add_user(int user_id, 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(struct my_struct));
s->id = user_id;
HASH_ADD_INT( users, id, s ); /* id: name of 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, *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 name_sort(struct my_struct *a, struct my_struct *b)
{
return strcmp(a->name,b->name);
}
int id_sort(struct my_struct *a, struct my_struct *b)
{
return (a->id - b->id);
}
void sort_by_name()
{
HASH_SORT(users, name_sort);
}
void sort_by_id()
{
HASH_SORT(users, id_sort);
}
int main()
{
char in[10];
int id=1, running=1;
struct my_struct *s;
unsigned num_users;
while (running) {
printf(" 1. add user\n");
printf(" 2. add/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");
gets(in);
switch(atoi(in)) {
case 1:
printf("name?\n");
add_user(id++, gets(in));
break;
case 2:
printf("id?\n");
gets(in);
id = atoi(in);
printf("name?\n");
add_user(id, gets(in));
break;
case 3:
printf("id?\n");
s = find_user(atoi(gets(in)));
printf("user: %s\n", s ? s->name : "unknown");
break;
case 4:
printf("id?\n");
s = find_user(atoi(gets(in)));
if (s) {
delete_user(s);
} else {
printf("id unknown\n");
}
break;
case 5:
delete_all();
break;
case 6:
sort_by_name();
break;
case 7:
sort_by_id();
break;
case 8:
print_users();
break;
case 9:
num_users=HASH_COUNT(users);
printf("there are %u users\n", num_users);
break;
case 10:
running=0;
break;
}
}
delete_all(); /* free any structures */
return 0;
}

678
dependencies/uthash/tests/hashscan.c vendored Normal file
View File

@@ -0,0 +1,678 @@
/*
Copyright (c) 2005-2018, Troy D. Hanson http://troydhanson.github.com/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 Normal file
View File

@@ -0,0 +1,255 @@
#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;
}

40
dependencies/uthash/tests/keystats vendored Executable file
View File

@@ -0,0 +1,40 @@
#!/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) {
$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]);
}

View File

@@ -0,0 +1,21 @@
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

View File

@@ -0,0 +1,221 @@
/*
* =====================================================================================
*
* 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;
}

View File

@@ -0,0 +1,31 @@
/*
* =====================================================================================
*
* 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

View File

@@ -0,0 +1,191 @@
#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 Executable file
View File

@@ -0,0 +1,28 @@
#!/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 Normal file
View File

@@ -0,0 +1,32 @@
#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 Normal file
View File

@@ -0,0 +1,34 @@
// 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 Normal file
View File

@@ -0,0 +1,10 @@
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 Normal file
View File

@@ -0,0 +1,31 @@
#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 Normal file
View File

@@ -0,0 +1,4 @@
9 found in hh
9 found in alth
10 not found in hh
10 found in alth

51
dependencies/uthash/tests/test10.c vendored Normal file
View File

@@ -0,0 +1,51 @@
#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");
return 0;
}

51
dependencies/uthash/tests/test11.ans vendored Normal file
View File

@@ -0,0 +1,51 @@
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 Normal file
View File

@@ -0,0 +1,57 @@
#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;
}

51
dependencies/uthash/tests/test11.dat vendored Normal file
View File

@@ -0,0 +1,51 @@
JOHN
WILLIAM
WALTER
DOUGLAS
GERALD
FREDERICK
WARREN
SHANE
LESTER
RON
HARVEY
ADRIAN
CODY
NELSON
CLIFTON
WILLARD
DOUG
ORLANDO
REX
OMAR
DAMON
LOWELL
IRVING
CARROLL
LAURENCE
ROLANDO
CARY
XAVIER
ISAIAH
GUS
JARVIS
WINFRED
RAYMUNDO
LINCOLN
CORNELL
NIGEL
NORMAND
FRITZ
DONN
TRINIDAD
ODIS
DANNIE
DARIO
KENTON
CHONG
NEVILLE
TONEY
WARNER
WES
COLTON
ARNOLDO

20
dependencies/uthash/tests/test12.ans vendored Normal file
View File

@@ -0,0 +1,20 @@
added bob (id 0)
added jack (id 1)
added gary (id 2)
added ty (id 3)
added bo (id 4)
added phil (id 5)
added art (id 6)
added gil (id 7)
added buck (id 8)
added ted (id 9)
found bob (id 0)
found jack (id 1)
found gary (id 2)
found ty (id 3)
found bo (id 4)
found phil (id 5)
found art (id 6)
found gil (id 7)
found buck (id 8)
found ted (id 9)

40
dependencies/uthash/tests/test12.c vendored Normal file
View File

@@ -0,0 +1,40 @@
#include "uthash.h"
#include <stdio.h>
#include <stdlib.h> /* malloc */
typedef struct person_t {
char first_name[10];
int id;
UT_hash_handle hh;
} person_t;
int main()
{
person_t *people=NULL, *person;
const char **name;
const char * names[] = { "bob", "jack", "gary", "ty", "bo", "phil", "art",
"gil", "buck", "ted", NULL
};
int id=0;
for(name=names; *name != NULL; name++) {
person = (person_t*)malloc(sizeof(person_t));
if (person == NULL) {
exit(-1);
}
strcpy(person->first_name, *name);
person->id = id++;
HASH_ADD_STR(people,first_name,person);
printf("added %s (id %d)\n", person->first_name, person->id);
}
for(name=names; *name != NULL; name++) {
HASH_FIND_STR(people,*name,person);
if (person != NULL) {
printf("found %s (id %d)\n", person->first_name, person->id);
} else {
printf("failed to find %s\n", *name);
}
}
return 0;
}

5
dependencies/uthash/tests/test13.ans vendored Normal file
View File

@@ -0,0 +1,5 @@
id 9, following prev...
id 7, following prev...
id 5, following prev...
id 3, following prev...
id 1, following prev...

50
dependencies/uthash/tests/test13.c vendored Normal file
View File

@@ -0,0 +1,50 @@
#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, *tmp, *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);
}
/* delete each even ID */
for(i=0; i<10; i+=2) {
HASH_FIND_INT(users,&i,tmp);
if (tmp != NULL) {
HASH_DEL(users,tmp);
free(tmp);
} else {
printf("user id %d not found\n", i);
}
}
i=9;
HASH_FIND_INT(users,&i,tmp);
if (tmp != NULL) {
while (tmp != NULL) {
printf("id %d, following prev...\n", tmp->id);
tmp = (example_user_t*)tmp->hh.prev;
}
} else {
printf("user id %d not found\n", i);
}
return 0;
}

1
dependencies/uthash/tests/test14.ans vendored Normal file
View File

@@ -0,0 +1 @@
lookup on 1219 of 1219 names succeeded

54
dependencies/uthash/tests/test14.c vendored Normal file
View File

@@ -0,0 +1,54 @@
#include "uthash.h"
#include <stdlib.h> /* malloc */
#include <errno.h> /* perror */
#include <stdio.h> /* printf */
#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()
{
name_rec *name, *names=NULL;
char linebuf[BUFLEN];
FILE *file;
int i=0,j=0;
file = fopen( "test14.dat", "r" );
if (file == NULL ) {
perror("can't open: ");
exit(-1);
}
while (fgets(linebuf,BUFLEN,file) != NULL) {
i++;
name = (name_rec*)malloc(sizeof(name_rec));
if (name == NULL) {
exit(-1);
}
strcpy(name->boy_name, linebuf);
HASH_ADD_STR(names,boy_name,name);
}
fseek(file,0L,SEEK_SET);
while (fgets(linebuf,BUFLEN,file) != NULL) {
HASH_FIND_STR(names,linebuf,name);
if (!name) {
printf("failed to find: %s", linebuf);
} else {
j++;
}
}
fclose(file);
printf("lookup on %d of %d names succeeded\n", j, i);
return 0;
}

1219
dependencies/uthash/tests/test14.dat vendored Normal file

File diff suppressed because it is too large Load Diff

1
dependencies/uthash/tests/test15.ans vendored Normal file
View File

@@ -0,0 +1 @@
betty's id is 2

40
dependencies/uthash/tests/test15.c vendored Normal file
View File

@@ -0,0 +1,40 @@
#include <string.h> /* strcpy */
#include <stdlib.h> /* malloc */
#include <stdio.h> /* printf */
#include "uthash.h"
struct my_struct {
char name[10]; /* key */
int id;
UT_hash_handle hh; /* makes this structure hashable */
};
int main()
{
const char **n, *names[] = { "joe", "bob", "betty", NULL };
struct my_struct *s, *tmp, *users = NULL;
int i=0;
for (n = names; *n != NULL; n++) {
s = (struct my_struct*)malloc(sizeof(struct my_struct));
if (s == NULL) {
exit(-1);
}
strcpy(s->name, *n);
s->id = i++;
HASH_ADD_STR( users, name, s );
}
HASH_FIND_STR( users, "betty", s);
if (s != NULL) {
printf("betty's id is %d\n", s->id);
}
/* free the hash table contents */
HASH_ITER(hh, users, s, tmp) {
HASH_DEL(users, s);
free(s);
}
return 0;
}

1
dependencies/uthash/tests/test16.ans vendored Normal file
View File

@@ -0,0 +1 @@
found: user 5, unix time 157680000

53
dependencies/uthash/tests/test16.c vendored Normal file
View File

@@ -0,0 +1,53 @@
#include <stdlib.h> /* malloc */
#include <stddef.h> /* offsetof */
#include <stdio.h> /* printf */
#include <string.h> /* memset */
#include "uthash.h"
struct inner {
int a;
int b;
};
struct my_event {
struct inner is; /* key is aggregate of this field */
char event_code; /* and this field. */
int user_id;
UT_hash_handle hh; /* makes this structure hashable */
};
int main()
{
struct my_event *e, ev, *events = NULL;
unsigned keylen;
int i;
keylen = offsetof(struct my_event, event_code) + sizeof(char)
- offsetof(struct my_event, is);
for(i = 0; i < 10; i++) {
e = (struct my_event*)malloc(sizeof(struct my_event));
if (e == NULL) {
exit(-1);
}
memset(e,0,sizeof(struct my_event));
e->is.a = i * (60*60*24*365); /* i years (sec)*/
e->is.b = 0;
e->event_code = 'a'+(i%2); /* meaningless */
e->user_id = i;
HASH_ADD( hh, events, is, keylen, e);
}
/* look for one specific event */
memset(&ev,0,sizeof(struct my_event));
ev.is.a = 5 * (60*60*24*365);
ev.is.b = 0;
ev.event_code = 'b';
HASH_FIND( hh, events, &ev.is, keylen , e);
if (e != NULL) {
printf("found: user %d, unix time %d\n", e->user_id, e->is.a);
}
return 0;
}

134
dependencies/uthash/tests/test17.ans vendored Normal file
View File

@@ -0,0 +1,134 @@
user 9, cookie 81
user 8, cookie 64
user 7, cookie 49
user 6, cookie 36
user 5, cookie 25
user 4, cookie 16
user 3, cookie 9
user 2, cookie 4
user 1, cookie 1
user 0, cookie 0
sorting
called for a:9, b:8
called for a:7, b:6
called for a:5, b:4
called for a:3, b:2
called for a:1, b:0
called for a:8, b:6
called for a:8, b:7
called for a:4, b:2
called for a:4, b:3
called for a:6, b:2
called for a:6, b:3
called for a:6, b:4
called for a:6, b:5
called for a:2, b:0
called for a:2, b:1
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
adding 10-20
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
user 20, cookie 400
user 19, cookie 361
user 18, cookie 324
user 17, cookie 289
user 16, cookie 256
user 15, cookie 225
user 14, cookie 196
user 13, cookie 169
user 12, cookie 144
user 11, cookie 121
user 10, cookie 100
sorting
called for a:0, b:1
called for a:2, b:3
called for a:4, b:5
called for a:6, b:7
called for a:8, b:9
called for a:20, b:19
called for a:18, b:17
called for a:16, b:15
called for a:14, b:13
called for a:12, b:11
called for a:0, b:2
called for a:1, b:2
called for a:4, b:6
called for a:5, b:6
called for a:8, b:19
called for a:9, b:19
called for a:17, b:15
called for a:17, b:16
called for a:13, b:11
called for a:13, b:12
called for a:0, b:4
called for a:1, b:4
called for a:2, b:4
called for a:3, b:4
called for a:8, b:15
called for a:9, b:15
called for a:19, b:15
called for a:19, b:16
called for a:19, b:17
called for a:19, b:18
called for a:11, b:10
called for a:0, b:8
called for a:1, b:8
called for a:2, b:8
called for a:3, b:8
called for a:4, b:8
called for a:5, b:8
called for a:6, b:8
called for a:7, b:8
called for a:0, b:10
called for a:1, b:10
called for a:2, b:10
called for a:3, b:10
called for a:4, b:10
called for a:5, b:10
called for a:6, b:10
called for a:7, b:10
called for a:8, b:10
called for a:9, b:10
called for a:15, b:10
called for a:15, b:11
called for a:15, b:12
called for a:15, b:13
called for a:15, b:14
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
user 10, cookie 100
user 11, cookie 121
user 12, cookie 144
user 13, cookie 169
user 14, cookie 196
user 15, cookie 225
user 16, cookie 256
user 17, cookie 289
user 18, cookie 324
user 19, cookie 361
user 20, cookie 400

63
dependencies/uthash/tests/test17.c vendored Normal file
View File

@@ -0,0 +1,63 @@
#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;
static int rev(void *_a, void *_b)
{
example_user_t *a = (example_user_t*)_a;
example_user_t *b = (example_user_t*)_b;
printf("called for a:%d, b:%d\n",a->id, b->id);
return (a->id - b->id);
}
int main()
{
int i;
example_user_t *user, *users=NULL;
/* create elements */
for(i=9; i>=0; 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);
}
printf("sorting\n");
HASH_SORT(users,rev);
for(user=users; user != NULL; user=(example_user_t*)user->hh.next) {
printf("user %d, cookie %d\n", user->id, user->cookie);
}
printf("adding 10-20\n");
for(i=20; 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);
}
printf("sorting\n");
HASH_SORT(users,rev);
for(user=users; user != NULL; user=(example_user_t*)user->hh.next) {
printf("user %d, cookie %d\n", user->id, user->cookie);
}
return 0;
}

20
dependencies/uthash/tests/test18.ans vendored Normal file
View File

@@ -0,0 +1,20 @@
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
deleting id 0
deleting id 1
deleting id 2
deleting id 3
deleting id 4
deleting id 5
deleting id 6
deleting id 7
deleting id 8
deleting id 9

Some files were not shown because too many files have changed in this diff Show More