Merge pull request 'remove all the wrappers, update servicepoint' (#1) from next into main
Reviewed-on: #1
This commit is contained in:
		
						commit
						0eedbf4a7f
					
				
					 26 changed files with 2089 additions and 3295 deletions
				
			
		
							
								
								
									
										38
									
								
								.github/workflows/rust.yml
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										38
									
								
								.github/workflows/rust.yml
									
										
									
									
										vendored
									
									
								
							|  | @ -13,32 +13,44 @@ env: | |||
|   RUSTFLAGS: "-Dwarnings" | ||||
| 
 | ||||
| jobs: | ||||
|   build: | ||||
| 
 | ||||
|   build-gnu-apt: | ||||
|     runs-on: ubuntu-latest | ||||
| 
 | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
| 
 | ||||
|       - name: Update repos | ||||
|         run: sudo apt-get update -qq | ||||
|       - name: Install rust toolchain | ||||
|         run: sudo apt-get install -qy cargo rust-clippy | ||||
|       - name: install lzma | ||||
|         run: sudo apt-get install -qy liblzma-dev | ||||
|       - name: install gcc | ||||
|         run: sudo apt-get install -qy gcc make | ||||
|       - name: Install toolchain | ||||
|         run: sudo apt-get install -qy cargo rust-clippy liblzma-dev gcc make pkgconf | ||||
| 
 | ||||
|       - name: Run Clippy | ||||
|         run: cargo clippy | ||||
| 
 | ||||
|       - name: build | ||||
|         run: cargo build | ||||
| 
 | ||||
|       - name: generate bindings | ||||
|         run: ./generate-binding.sh | ||||
|       - name: check that generated files did not change | ||||
|         run: output=$(git status --porcelain) && [ -z "$output" ] | ||||
|          | ||||
|       - name: build | ||||
|         run: cargo build | ||||
| 
 | ||||
|       - name: build example | ||||
|         run: cd example && make | ||||
|       - name: build example -- glibc release | ||||
|         run: cd example && make clean && make TARGET=aarch64-unknown-linux-gnu PROFILE=release | ||||
|       - name: build example -- glibc debug | ||||
|         run: cd example && make clean && make TARGET=aarch64-unknown-linux-gnu PROFILE=debug | ||||
| 
 | ||||
|   build-size-gnu-unstable: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
| 
 | ||||
|       - name: Update repos | ||||
|         run: sudo apt-get update -qq | ||||
|       - name: Install toolchain | ||||
|         run: sudo apt-get install -qy liblzma-dev gcc make pkgconf rustup | ||||
|       - name: install rust targets | ||||
|         run: rustup toolchain install nightly -t aarch64-unknown-linux-gnu -c rust-src --no-self-update | ||||
| 
 | ||||
|       - name: build example -- glibc size-optimized | ||||
|         run: cd example && make clean && make TARGET=aarch64-unknown-linux-gnu PROFILE=size-optimized CARGO="rustup run nightly cargo" LTO=1 | ||||
|  |  | |||
							
								
								
									
										209
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										209
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							|  | @ -1,6 +1,6 @@ | |||
| # This file is automatically @generated by Cargo. | ||||
| # It is not intended for manual editing. | ||||
| version = 3 | ||||
| version = 4 | ||||
| 
 | ||||
| [[package]] | ||||
| name = "adler2" | ||||
|  | @ -49,19 +49,20 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "anstyle-wincon" | ||||
| version = "3.0.6" | ||||
| version = "3.0.7" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" | ||||
| checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" | ||||
| dependencies = [ | ||||
|  "anstyle", | ||||
|  "once_cell", | ||||
|  "windows-sys", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "bitflags" | ||||
| version = "2.7.0" | ||||
| version = "2.9.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be" | ||||
| checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "bitvec" | ||||
|  | @ -77,30 +78,28 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "bzip2" | ||||
| version = "0.5.0" | ||||
| version = "0.5.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "bafdbf26611df8c14810e268ddceda071c297570a5fb360ceddf617fe417ef58" | ||||
| checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47" | ||||
| dependencies = [ | ||||
|  "bzip2-sys", | ||||
|  "libc", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "bzip2-sys" | ||||
| version = "0.1.11+1.0.8" | ||||
| version = "0.1.13+1.0.8" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" | ||||
| checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" | ||||
| dependencies = [ | ||||
|  "cc", | ||||
|  "libc", | ||||
|  "pkg-config", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "cbindgen" | ||||
| version = "0.27.0" | ||||
| version = "0.28.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "3fce8dd7fcfcbf3a0a87d8f515194b49d6135acab73e18bd380d1d93bb1a15eb" | ||||
| checksum = "eadd868a2ce9ca38de7eeafdcec9c7065ef89b42b32f0839278d55f35c54d1ff" | ||||
| dependencies = [ | ||||
|  "clap", | ||||
|  "heck", | ||||
|  | @ -117,9 +116,9 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "cc" | ||||
| version = "1.2.9" | ||||
| version = "1.2.21" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "c8293772165d9345bdaaa39b45b2109591e63fe5e6fbc23c6ff930a048aa310b" | ||||
| checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0" | ||||
| dependencies = [ | ||||
|  "jobserver", | ||||
|  "libc", | ||||
|  | @ -134,18 +133,18 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" | |||
| 
 | ||||
| [[package]] | ||||
| name = "clap" | ||||
| version = "4.5.26" | ||||
| version = "4.5.37" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783" | ||||
| checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" | ||||
| dependencies = [ | ||||
|  "clap_builder", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "clap_builder" | ||||
| version = "4.5.26" | ||||
| version = "4.5.37" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121" | ||||
| checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" | ||||
| dependencies = [ | ||||
|  "anstream", | ||||
|  "anstyle", | ||||
|  | @ -176,15 +175,15 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "equivalent" | ||||
| version = "1.0.1" | ||||
| version = "1.0.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" | ||||
| checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "errno" | ||||
| version = "0.3.10" | ||||
| version = "0.3.11" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" | ||||
| checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" | ||||
| dependencies = [ | ||||
|  "libc", | ||||
|  "windows-sys", | ||||
|  | @ -198,9 +197,9 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" | |||
| 
 | ||||
| [[package]] | ||||
| name = "flate2" | ||||
| version = "1.0.35" | ||||
| version = "1.1.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" | ||||
| checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" | ||||
| dependencies = [ | ||||
|  "crc32fast", | ||||
|  "miniz_oxide", | ||||
|  | @ -214,20 +213,21 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" | |||
| 
 | ||||
| [[package]] | ||||
| name = "getrandom" | ||||
| version = "0.2.15" | ||||
| version = "0.3.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" | ||||
| checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" | ||||
| dependencies = [ | ||||
|  "cfg-if", | ||||
|  "libc", | ||||
|  "r-efi", | ||||
|  "wasi", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "hashbrown" | ||||
| version = "0.15.2" | ||||
| version = "0.15.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" | ||||
| checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "heck" | ||||
|  | @ -237,9 +237,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" | |||
| 
 | ||||
| [[package]] | ||||
| name = "indexmap" | ||||
| version = "2.7.0" | ||||
| version = "2.9.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" | ||||
| checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" | ||||
| dependencies = [ | ||||
|  "equivalent", | ||||
|  "hashbrown", | ||||
|  | @ -253,36 +253,37 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" | |||
| 
 | ||||
| [[package]] | ||||
| name = "itoa" | ||||
| version = "1.0.14" | ||||
| version = "1.0.15" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" | ||||
| checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "jobserver" | ||||
| version = "0.1.32" | ||||
| version = "0.1.33" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" | ||||
| checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" | ||||
| dependencies = [ | ||||
|  "getrandom", | ||||
|  "libc", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "libc" | ||||
| version = "0.2.169" | ||||
| version = "0.2.172" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" | ||||
| checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "linux-raw-sys" | ||||
| version = "0.4.15" | ||||
| version = "0.9.4" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" | ||||
| checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "log" | ||||
| version = "0.4.22" | ||||
| version = "0.4.27" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" | ||||
| checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "memchr" | ||||
|  | @ -292,43 +293,49 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" | |||
| 
 | ||||
| [[package]] | ||||
| name = "miniz_oxide" | ||||
| version = "0.8.2" | ||||
| version = "0.8.8" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" | ||||
| checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" | ||||
| dependencies = [ | ||||
|  "adler2", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "once_cell" | ||||
| version = "1.20.2" | ||||
| version = "1.21.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" | ||||
| checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "pkg-config" | ||||
| version = "0.3.31" | ||||
| version = "0.3.32" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" | ||||
| checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "proc-macro2" | ||||
| version = "1.0.93" | ||||
| version = "1.0.95" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" | ||||
| checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" | ||||
| dependencies = [ | ||||
|  "unicode-ident", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "quote" | ||||
| version = "1.0.38" | ||||
| version = "1.0.40" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" | ||||
| checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "r-efi" | ||||
| version = "5.2.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "radium" | ||||
| version = "0.7.0" | ||||
|  | @ -347,9 +354,9 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "rustix" | ||||
| version = "0.38.43" | ||||
| version = "1.0.7" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" | ||||
| checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" | ||||
| dependencies = [ | ||||
|  "bitflags", | ||||
|  "errno", | ||||
|  | @ -360,24 +367,24 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "ryu" | ||||
| version = "1.0.18" | ||||
| version = "1.0.20" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" | ||||
| checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "serde" | ||||
| version = "1.0.217" | ||||
| version = "1.0.219" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" | ||||
| checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" | ||||
| dependencies = [ | ||||
|  "serde_derive", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "serde_derive" | ||||
| version = "1.0.217" | ||||
| version = "1.0.219" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" | ||||
| checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  | @ -386,9 +393,9 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "serde_json" | ||||
| version = "1.0.135" | ||||
| version = "1.0.140" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" | ||||
| checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" | ||||
| dependencies = [ | ||||
|  "itoa", | ||||
|  "memchr", | ||||
|  | @ -407,9 +414,9 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "servicepoint" | ||||
| version = "0.13.1" | ||||
| version = "0.14.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "93b52049be55a15fe37c13249d7f96aa8a5de56e1a41838e74a822ee8316a0c4" | ||||
| checksum = "f6bd5cfa49c73aeecb344680ffbf697abf73e0563a441b93b9723ae43867500f" | ||||
| dependencies = [ | ||||
|  "bitvec", | ||||
|  "bzip2", | ||||
|  | @ -423,7 +430,7 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "servicepoint_binding_c" | ||||
| version = "0.13.1" | ||||
| version = "0.14.1" | ||||
| dependencies = [ | ||||
|  "cbindgen", | ||||
|  "servicepoint", | ||||
|  | @ -443,9 +450,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" | |||
| 
 | ||||
| [[package]] | ||||
| name = "syn" | ||||
| version = "2.0.96" | ||||
| version = "2.0.101" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" | ||||
| checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  | @ -460,11 +467,10 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" | |||
| 
 | ||||
| [[package]] | ||||
| name = "tempfile" | ||||
| version = "3.15.0" | ||||
| version = "3.19.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" | ||||
| checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" | ||||
| dependencies = [ | ||||
|  "cfg-if", | ||||
|  "fastrand", | ||||
|  "getrandom", | ||||
|  "once_cell", | ||||
|  | @ -474,18 +480,18 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "thiserror" | ||||
| version = "2.0.11" | ||||
| version = "2.0.12" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" | ||||
| checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" | ||||
| dependencies = [ | ||||
|  "thiserror-impl", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "thiserror-impl" | ||||
| version = "2.0.11" | ||||
| version = "2.0.12" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" | ||||
| checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  | @ -494,9 +500,9 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "toml" | ||||
| version = "0.8.19" | ||||
| version = "0.8.22" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" | ||||
| checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" | ||||
| dependencies = [ | ||||
|  "serde", | ||||
|  "serde_spanned", | ||||
|  | @ -506,31 +512,38 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "toml_datetime" | ||||
| version = "0.6.8" | ||||
| version = "0.6.9" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" | ||||
| checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" | ||||
| dependencies = [ | ||||
|  "serde", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "toml_edit" | ||||
| version = "0.22.22" | ||||
| version = "0.22.26" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" | ||||
| checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" | ||||
| dependencies = [ | ||||
|  "indexmap", | ||||
|  "serde", | ||||
|  "serde_spanned", | ||||
|  "toml_datetime", | ||||
|  "toml_write", | ||||
|  "winnow", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "unicode-ident" | ||||
| version = "1.0.14" | ||||
| name = "toml_write" | ||||
| version = "0.1.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" | ||||
| checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "unicode-ident" | ||||
| version = "1.0.18" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "utf8parse" | ||||
|  | @ -546,9 +559,12 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" | |||
| 
 | ||||
| [[package]] | ||||
| name = "wasi" | ||||
| version = "0.11.0+wasi-snapshot-preview1" | ||||
| version = "0.14.2+wasi-0.2.4" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" | ||||
| checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" | ||||
| dependencies = [ | ||||
|  "wit-bindgen-rt", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "windows-sys" | ||||
|  | @ -625,13 +641,22 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" | |||
| 
 | ||||
| [[package]] | ||||
| name = "winnow" | ||||
| version = "0.6.24" | ||||
| version = "0.7.9" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" | ||||
| checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3" | ||||
| dependencies = [ | ||||
|  "memchr", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "wit-bindgen-rt" | ||||
| version = "0.39.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" | ||||
| dependencies = [ | ||||
|  "bitflags", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "wyz" | ||||
| version = "0.5.1" | ||||
|  | @ -643,27 +668,27 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "zstd" | ||||
| version = "0.13.2" | ||||
| version = "0.13.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" | ||||
| checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" | ||||
| dependencies = [ | ||||
|  "zstd-safe", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "zstd-safe" | ||||
| version = "7.2.1" | ||||
| version = "7.2.4" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" | ||||
| checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" | ||||
| dependencies = [ | ||||
|  "zstd-sys", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "zstd-sys" | ||||
| version = "2.0.13+zstd.1.5.6" | ||||
| version = "2.0.15+zstd.1.5.7" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" | ||||
| checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" | ||||
| dependencies = [ | ||||
|  "cc", | ||||
|  "pkg-config", | ||||
|  |  | |||
							
								
								
									
										25
									
								
								Cargo.toml
									
										
									
									
									
								
							
							
						
						
									
										25
									
								
								Cargo.toml
									
										
									
									
									
								
							|  | @ -1,6 +1,6 @@ | |||
| [package] | ||||
| name = "servicepoint_binding_c" | ||||
| version = "0.13.1" | ||||
| version = "0.14.1" | ||||
| publish = true | ||||
| edition = "2021" | ||||
| license = "GPL-3.0-or-later" | ||||
|  | @ -15,14 +15,31 @@ keywords = ["cccb", "cccb-servicepoint", "cbindgen"] | |||
| crate-type = ["staticlib", "cdylib", "rlib"] | ||||
| 
 | ||||
| [build-dependencies] | ||||
| cbindgen = "0.27.0" | ||||
| cbindgen = "0.28.0" | ||||
| 
 | ||||
| [dependencies.servicepoint] | ||||
| version = "0.13.1" | ||||
| features = ["all_compressions"] | ||||
| package = "servicepoint" | ||||
| version = "0.14.1" | ||||
| default-features = false | ||||
| 
 | ||||
| [features] | ||||
| all_compressions = ["servicepoint/all_compressions"] | ||||
| default = ["all_compressions", "servicepoint/default"] | ||||
| 
 | ||||
| [lints.rust] | ||||
| missing-docs = "warn" | ||||
| unsafe_op_in_unsafe_fn = "warn" | ||||
| 
 | ||||
| [lints.clippy] | ||||
| missing_safety_doc = "allow" | ||||
| 
 | ||||
| [package.metadata.docs.rs] | ||||
| all-features = true | ||||
| 
 | ||||
| [profile.size-optimized] | ||||
| inherits = "release" | ||||
| opt-level = 'z'     # Optimize for size | ||||
| lto = true          # Enable link-time optimization | ||||
| codegen-units = 1   # Reduce number of codegen units to increase optimizations | ||||
| panic = 'abort'     # Abort on panic | ||||
| strip = true        # Strip symbols from binary | ||||
|  |  | |||
							
								
								
									
										66
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										66
									
								
								README.md
									
										
									
									
									
								
							|  | @ -43,32 +43,37 @@ Please specify the full version including patch in your Cargo.toml until 1.0 is | |||
| 
 | ||||
| ## Installation | ||||
| 
 | ||||
| 1. Add this repo as a submodule: | ||||
|     ```bash | ||||
|     git submodule add https://git.berlin.ccc.de/servicepoint/servicepoint-binding-c.git | ||||
|     git commit -m "add servicepoint-binding-c submodule" | ||||
|     ``` | ||||
| 2. Add a build step for the servicepoint library. If you use make, this could look something like this: | ||||
|     ``` | ||||
|     dependencies: FORCE | ||||
|     	cargo build --manifest-path=servicepoint-binding-c/Cargo.toml --release | ||||
|     FORCE: ;  | ||||
|     ``` | ||||
| 3. Link against the library. If you are on linux and linking statically: | ||||
|     ``` | ||||
|     ${CC} main.c \ | ||||
|     	-I servicepoint-binding-c/include \ | ||||
|     	-L servicepoint-binding-c/target/release \ | ||||
|     	-Wl,-Bstatic -lservicepoint_binding_c \ | ||||
|     	-Wl,-Bdynamic -llzma \ | ||||
|     	-o out/example | ||||
|     ``` | ||||
| This manual uses gnumake, though it should work similarly for other build systems. | ||||
| 
 | ||||
| Add this repo as a submodule: | ||||
| 
 | ||||
| ```bash | ||||
| git submodule add https://git.berlin.ccc.de/servicepoint/servicepoint-binding-c.git | ||||
| git commit -m "add servicepoint-binding-c submodule" | ||||
| ``` | ||||
| 
 | ||||
| Update your Makefile: | ||||
| 
 | ||||
| ```Makefile | ||||
| # provide header and binary to CC | ||||
| CFLAGS += -I servicepoint-binding-c/include -L servicepoint-binding-c/target/release | ||||
| # link statically against servicepoint, link against lzma dynamically | ||||
| CFLAGS += -Wl,-Bstatic -lservicepoint_binding_c -Wl,-Bdynamic -llzma | ||||
| 
 | ||||
| # add target to build rust library - cargo will check for dependency changes | ||||
| servicepoint-binding-c/target/release/libservicepoint.a: | ||||
| 	cargo build --manifest-path=servicepoint-binding-c/Cargo.toml --release | ||||
| 
 | ||||
| # add static lib as a dependency for your compilation | ||||
| # if you build .so files separately, this should be part of your build step | ||||
| my_bin: $(src) servicepoint-binding-c/target/release/libservicepoint.a | ||||
| 	$(CC) $(src) $(CFLAGS) -o $@ | ||||
| ``` | ||||
| 
 | ||||
| You have the choice of linking statically (recommended) or dynamically. | ||||
| 
 | ||||
| - The C example shows how to link statically against the `staticlib` variant. | ||||
| - When linked dynamically, you have to provide the `cdylib` at runtime in the _same_ version, as there are no API/ABI | ||||
| When linked dynamically, you have to provide the `cdylib` at runtime in the _same_ version, as there are no API/ABI | ||||
|   guarantees yet. | ||||
| The Makefile in the example directory contains a bunch of additional compiler flags you might want to add depending on your use case. | ||||
| 
 | ||||
| ## Notes on differences to rust library | ||||
| 
 | ||||
|  | @ -82,6 +87,21 @@ You have the choice of linking statically (recommended) or dynamically. | |||
| - documentation is included in the header and | ||||
|   available [online](https://docs.rs/servicepoint_binding_c/latest/servicepoint_binding_c/) | ||||
| 
 | ||||
| ## Safety | ||||
| 
 | ||||
| Functions expect that C code honors NonNull annotations. | ||||
| 
 | ||||
| Any created instances have to be freed in some way.  | ||||
| Pointers to those instances cannot be used anymore after that. | ||||
| 
 | ||||
| Instances cannot be shared between threads and need to be locked in the using code. | ||||
| 
 | ||||
| Enum values have to be used as-is. Do not pass values that are not part of the enum. | ||||
| 
 | ||||
| UTF-8 or UTF-32 encoding has to be used properly. | ||||
| 
 | ||||
| Brightness values provided as u8 parameters must be in range. | ||||
| 
 | ||||
| ## Everything else | ||||
| 
 | ||||
| Look at the main project [README](https://git.berlin.ccc.de/servicepoint/servicepoint/src/branch/main/README.md) for | ||||
|  |  | |||
							
								
								
									
										28
									
								
								build.rs
									
										
									
									
									
								
							
							
						
						
									
										28
									
								
								build.rs
									
										
									
									
									
								
							|  | @ -6,28 +6,30 @@ | |||
| 
 | ||||
| use std::{env, fs::copy}; | ||||
| 
 | ||||
| use cbindgen::{generate_with_config, Config}; | ||||
| 
 | ||||
| fn main() { | ||||
|     let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); | ||||
|     println!("cargo::rerun-if-changed={crate_dir}"); | ||||
| 
 | ||||
|     let config = | ||||
|         Config::from_file(crate_dir.clone() + "/cbindgen.toml").unwrap(); | ||||
|         cbindgen::Config::from_file(crate_dir.clone() + "/cbindgen.toml") | ||||
|             .unwrap(); | ||||
| 
 | ||||
|     let output_dir = env::var("OUT_DIR").unwrap(); | ||||
|     let header_file = output_dir.clone() + "/servicepoint.h"; | ||||
| 
 | ||||
|     generate_with_config(crate_dir, config) | ||||
|         .unwrap() | ||||
|         .write_to_file(&header_file); | ||||
|     println!("cargo:include={output_dir}"); | ||||
|     if let Ok(bindings) = cbindgen::generate_with_config(crate_dir, config) { | ||||
|         bindings.write_to_file(&header_file); | ||||
| 
 | ||||
|     println!("cargo::rerun-if-env-changed=SERVICEPOINT_HEADER_OUT"); | ||||
|     if let Ok(header_out) = env::var("SERVICEPOINT_HEADER_OUT") { | ||||
|         let header_copy = header_out + "/servicepoint.h"; | ||||
|         println!("cargo:warning=Copying header to {header_copy}"); | ||||
|         copy(header_file, &header_copy).unwrap(); | ||||
|         println!("cargo::rerun-if-changed={header_copy}"); | ||||
|         println!("cargo:include={output_dir}"); | ||||
| 
 | ||||
|         println!("cargo::rerun-if-env-changed=SERVICEPOINT_HEADER_OUT"); | ||||
|         if let Ok(header_out) = env::var("SERVICEPOINT_HEADER_OUT") { | ||||
|             let header_copy = header_out + "/servicepoint.h"; | ||||
|             println!("cargo:warning=Copying header to {header_copy}"); | ||||
|             copy(header_file, &header_copy).unwrap(); | ||||
|             println!("cargo::rerun-if-changed={header_copy}"); | ||||
|         } | ||||
|     } else { | ||||
|         eprintln!("cargo:warning=Servicepoint header could not be generated"); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -19,18 +19,27 @@ line_endings = "LF" | |||
| style = "type" | ||||
| usize_is_size_t = true | ||||
| 
 | ||||
| # this is needed because otherwise the order in the C# bindings is different on different machines | ||||
| # this is needed because otherwise the order in the C bindings is different on different machines | ||||
| sort_by = "Name" | ||||
| 
 | ||||
| [parse] | ||||
| parse_deps = false | ||||
| parse_deps = true | ||||
| include = ["servicepoint", "std"] | ||||
| extra_bindings = ["servicepoint"] | ||||
| 
 | ||||
| [parse.expand] | ||||
| all_features = true | ||||
| features = ["full"] | ||||
| 
 | ||||
| [export] | ||||
| include = [] | ||||
| exclude = [] | ||||
| 
 | ||||
| [export.rename] | ||||
| "SpBitVec" = "BitVec" | ||||
| "SpByteSlice" = "ByteSlice" | ||||
| 
 | ||||
| [enum] | ||||
| rename_variants = "QualifiedScreamingSnakeCase" | ||||
| 
 | ||||
| [ptr] | ||||
| non_null_attribute = "/*notnull*/" | ||||
|  |  | |||
							
								
								
									
										135
									
								
								example/Makefile
									
										
									
									
									
								
							
							
						
						
									
										135
									
								
								example/Makefile
									
										
									
									
									
								
							|  | @ -1,30 +1,129 @@ | |||
| CC := gcc | ||||
| CARGO ?= cargo | ||||
| STRIP ?= strip | ||||
| 
 | ||||
| FEATURES := "" | ||||
| 
 | ||||
| THIS_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST)))) | ||||
| REPO_ROOT := $(THIS_DIR)/.. | ||||
| REPO_ROOT := $(realpath $(THIS_DIR)/..) | ||||
| export SERVICEPOINT_HEADER_OUT := $(REPO_ROOT)/include | ||||
| 
 | ||||
| build: out/example | ||||
| override CFG_MUSL := $(if $(CFG_MUSL),$(CFG_MUSL),$(if $(MUSL),$(MUSL),0)) | ||||
| override CFG_PROFILE := $(if $(CFG_PROFILE),$(CFG_PROFILE),$(if $(PROFILE),$(PROFILE),release)) | ||||
| 
 | ||||
| clean: | ||||
| CCFLAGS += -Wall -fwhole-program -fPIE -pie | ||||
| 
 | ||||
| STRIPFLAGS := -s --strip-unneeded -R .comment -R .gnu.version -R .note -R .note.gnu.build-id -R .note.ABI-tag | ||||
| 
 | ||||
| ifeq ($(CFG_MUSL), 1) | ||||
| 	TARGET ?= x86_64-unknown-linux-musl | ||||
| 	CC ?= musl-gcc | ||||
| 	CCFLAGS += -static -lservicepoint_binding_c | ||||
| 	RUSTFLAGS += --crate-type=staticlib -Ctarget-feature=-crt-static | ||||
| else | ||||
| 	TARGET ?= x86_64-unknown-linux-gnu | ||||
| 	CC ?= gcc | ||||
| 	#CCFLAGS += -shared | ||||
| 	CCFLAGS += -Wl,-Bstatic -lservicepoint_binding_c -Wl,-Bdynamic | ||||
| endif | ||||
| 
 | ||||
| #ifeq ($(CFG_PROFILE), size-optimized)
 | ||||
| #	CCFLAGS += -nodefaultlibs -lc
 | ||||
| #endif
 | ||||
| 
 | ||||
| RUST_TARGET_DIR := $(REPO_ROOT)/target/$(TARGET)/$(CFG_PROFILE) | ||||
| 
 | ||||
| ifeq ($(CFG_PROFILE), size-optimized) | ||||
| 	CARGO_PROFILE := size-optimized | ||||
| 	CCFLAGS += -Oz \
 | ||||
| 		-fwrapv -fomit-frame-pointer -fno-stack-protector\
 | ||||
| 		-fno-unroll-loops \
 | ||||
| 		-fno-unwind-tables -fno-asynchronous-unwind-tables \
 | ||||
| 		-fmerge-all-constants \
 | ||||
| 		-Wl,-z,norelro \
 | ||||
| 		-Wl,--hash-style=gnu \
 | ||||
| 		-fvisibility=hidden \
 | ||||
| 		-Bsymbolic \
 | ||||
| 		-Wl,--exclude-libs,ALL \
 | ||||
| 		-fno-ident \
 | ||||
| 		-fno-exceptions | ||||
| 	CARGOFLAGS += -Zbuild-std="core,std,alloc,proc_macro,panic_abort" \
 | ||||
| 		-Zbuild-std-features="panic_immediate_abort" | ||||
| 	RUSTFLAGS += -Zlocation-detail=none \
 | ||||
| 		-Zfmt-debug=none \
 | ||||
| 		-C link-arg=-z,norelro \
 | ||||
| 		-C panic=abort | ||||
| 		#-C link-arg=--hash-style=gnu | ||||
| else ifeq ($(CFG_PROFILE), release) | ||||
| 	CARGO_PROFILE := release | ||||
| 	CCFLAGS += -O2 | ||||
| else ifeq ($(CFG_PROFILE), debug) | ||||
| 	CCFLAGS += -Og | ||||
| 	CARGO_PROFILE := dev | ||||
| else | ||||
| 	CFG_PROFILE := $(error "PROFILE has to be set to one of: debug, release, size-optimized") | ||||
| endif | ||||
| 
 | ||||
| CARGOFLAGS += --manifest-path=$(REPO_ROOT)/Cargo.toml \
 | ||||
| 	--profile=$(CARGO_PROFILE) \
 | ||||
| 	--no-default-features \
 | ||||
| 	--features=$(FEATURES) \
 | ||||
| 	--target=$(TARGET) | ||||
| 
 | ||||
| ifneq ($(CFG_PROFILE), debug) | ||||
| 	CCFLAGS += -ffunction-sections -fdata-sections -Wl,--gc-sections | ||||
| 	RUSTFLAGS += -C link-arg=-s -C link-arg=-Wl,--gc-sections | ||||
| endif | ||||
| 
 | ||||
| ifeq ($(LTO), 1) | ||||
| 	CCFLAGS += -flto | ||||
| endif | ||||
| 
 | ||||
| _c_src := $(wildcard ./src/*.c) | ||||
| _programs := $(basename $(notdir $(_c_src))) | ||||
| _bins := $(addprefix out/, $(_programs)) | ||||
| _unstripped_bins := $(addsuffix _unstripped, $(_bins)) | ||||
| _run_programs := $(addprefix run_, $(_programs)) | ||||
| _rs_src := $(wildcard ../src/**.rs) ../Cargo.lock ../Cargo.toml ../cbindgen.toml | ||||
| _sp_artifacts := $(SERVICEPOINT_HEADER_OUT)/servicepoint.h $(RUST_TARGET_DIR)/libservicepoint_binding_c.a $(RUST_TARGET_DIR)/libservicepoint_binding_c.so | ||||
| 
 | ||||
| all: $(_bins) | ||||
| 
 | ||||
| clean: clean-c clean-rust | ||||
| 
 | ||||
| clean-c: | ||||
| 	rm -r out || true | ||||
| 	rm include/servicepoint.h || true | ||||
| 
 | ||||
| clean-rust: | ||||
| 	rm $(SERVICEPOINT_HEADER_OUT)/servicepoint.h || true | ||||
| 	cargo clean | ||||
| 
 | ||||
| run: out/example | ||||
| 	out/example | ||||
| .PHONY: all clean sizes $(_run_programs) clean-c clean-rust | ||||
| 
 | ||||
| PHONY: build clean dependencies run | ||||
| 
 | ||||
| out/example: dependencies main.c | ||||
| $(_unstripped_bins): out/%_unstripped: src/%.c $(SERVICEPOINT_HEADER_OUT)/servicepoint.h $(_sp_artifacts) | ||||
| 	mkdir -p out || true | ||||
| 	${CC} main.c \
 | ||||
| 		-I $(REPO_ROOT)/include \
 | ||||
| 		-L $(REPO_ROOT)/target/release \
 | ||||
| 		-Wl,-Bstatic -lservicepoint_binding_c \
 | ||||
| 		-Wl,-Bdynamic -llzma \
 | ||||
| 		-o out/example | ||||
| 	$(CC) $< \
 | ||||
| 		-I $(SERVICEPOINT_HEADER_OUT) \
 | ||||
| 		-L $(RUST_TARGET_DIR) \
 | ||||
| 		$(CCFLAGS) \
 | ||||
| 		-o $@ | ||||
| 
 | ||||
| dependencies: FORCE | ||||
| 	cargo build --manifest-path=$(REPO_ROOT)/Cargo.toml --release | ||||
| $(_bins): out/%: out/%_unstripped | ||||
| 	$(STRIP) $(STRIPFLAGS) $^ -o $@ | ||||
| 
 | ||||
| $(_sp_artifacts): $(_rs_src) | ||||
| 	mkdir -p $(SERVICEPOINT_HEADER_OUT) || true | ||||
| 	# generate servicepoint header and library to link against | ||||
| 	$(CARGO) rustc $(CARGOFLAGS) -- $(RUSTFLAGS) | ||||
| 
 | ||||
| $(_run_programs): run_%: out/% FORCE | ||||
| 	./$< | ||||
| 
 | ||||
| sizes: $(_bins) | ||||
| 	ls -lB out | ||||
| 
 | ||||
| analyze-size: out/$(BIN)_unstripped | ||||
| 	nm --print-size --size-sort --reverse-sort --radix=d --demangle out/$(BIN)_unstripped \
 | ||||
| 		| awk '{size=$$2+0; print size "\t" $$4}' \
 | ||||
| 		| less | ||||
| 
 | ||||
| FORCE: ; | ||||
|  |  | |||
|  | @ -1,17 +0,0 @@ | |||
| #include <stdio.h> | ||||
| #include "servicepoint.h" | ||||
| 
 | ||||
| int main(void) { | ||||
|     SPConnection *connection = sp_connection_open("localhost:2342"); | ||||
|     if (connection == NULL) | ||||
|         return 1; | ||||
| 
 | ||||
|     SPBitmap *pixels = sp_bitmap_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT); | ||||
|     sp_bitmap_fill(pixels, true); | ||||
| 
 | ||||
|     SPCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, SP_COMPRESSION_CODE_UNCOMPRESSED); | ||||
|     sp_connection_send_command(connection, command); | ||||
| 
 | ||||
|     sp_connection_free(connection); | ||||
|     return 0; | ||||
| } | ||||
							
								
								
									
										37
									
								
								example/src/announce.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								example/src/announce.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | |||
| #include <stdio.h> | ||||
| #include "servicepoint.h" | ||||
| 
 | ||||
| 
 | ||||
| int main(void) { | ||||
|     printf("test\n"); | ||||
| 
 | ||||
|     UdpSocket *connection = sp_udp_open_ipv4(172, 23, 42, 29, 2342); | ||||
|     //UdpSocket *connection = sp_udp_open_ipv4(127, 0, 0, 1, 2342);
 | ||||
|     if (connection == NULL) | ||||
|         return 1; | ||||
| 
 | ||||
|     sp_udp_send_header(connection, (Header) {.command_code = COMMAND_CODE_CLEAR}); | ||||
| 
 | ||||
|     CharGrid *grid = sp_char_grid_new(5, 2); | ||||
|     if (grid == NULL) | ||||
|         return 1; | ||||
| 
 | ||||
|     sp_char_grid_set(grid, 0, 0, 'H'); | ||||
|     sp_char_grid_set(grid, 1, 0, 'e'); | ||||
|     sp_char_grid_set(grid, 2, 0, 'l'); | ||||
|     sp_char_grid_set(grid, 3, 0, 'l'); | ||||
|     sp_char_grid_set(grid, 4, 0, 'o'); | ||||
|     sp_char_grid_set(grid, 0, 1, 'W'); | ||||
|     sp_char_grid_set(grid, 1, 1, 'o'); | ||||
|     sp_char_grid_set(grid, 2, 1, 'r'); | ||||
|     sp_char_grid_set(grid, 3, 1, 'l'); | ||||
|     sp_char_grid_set(grid, 4, 1, 'd'); | ||||
| 
 | ||||
|     Packet *packet = sp_char_grid_into_packet(grid, 0, 0); | ||||
|     if (packet == NULL) | ||||
|         return 1; | ||||
|     sp_udp_send_packet(connection, packet); | ||||
| 
 | ||||
|     sp_udp_free(connection); | ||||
|     return 0; | ||||
| } | ||||
							
								
								
									
										30
									
								
								example/src/brightness_tester.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								example/src/brightness_tester.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,30 @@ | |||
| #include "servicepoint.h" | ||||
| 
 | ||||
| int main(void) { | ||||
|     UdpSocket *connection = sp_udp_open_ipv4(172, 23, 42, 29, 2342); | ||||
|     //UdpSocket *connection = sp_udp_open_ipv4(127, 0, 0, 1, 2342);
 | ||||
|     if (connection == NULL) | ||||
|         return -1; | ||||
| 
 | ||||
|     Bitmap *all_on = sp_bitmap_new_max_sized(); | ||||
|     sp_bitmap_fill(all_on, true); | ||||
| 
 | ||||
|     Packet *packet = sp_bitmap_into_packet(all_on, 0, 0, COMPRESSION_CODE_UNCOMPRESSED); | ||||
|     if (packet == NULL) | ||||
|         return -1; | ||||
| 
 | ||||
|     sp_udp_send_packet(connection, packet); | ||||
| 
 | ||||
|     BrightnessGrid *grid = sp_brightness_grid_new(TILE_WIDTH, TILE_HEIGHT); | ||||
| 
 | ||||
|     ByteSlice slice = sp_brightness_grid_unsafe_data_ref(grid); | ||||
|     for (size_t index = 0; index < slice.length; index++) { | ||||
|         slice.start[index] = (uint8_t) (index % ((size_t) Brightness_MAX)); | ||||
|     } | ||||
| 
 | ||||
|     packet = sp_brightness_grid_into_packet(grid, 0, 0); | ||||
|     sp_udp_send_packet(connection, packet); | ||||
| 
 | ||||
|     sp_udp_free(connection); | ||||
|     return 0; | ||||
| } | ||||
							
								
								
									
										26
									
								
								example/src/random_stuff.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								example/src/random_stuff.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,26 @@ | |||
| #include <stdio.h> | ||||
| #include "servicepoint.h" | ||||
| 
 | ||||
| int main(void) { | ||||
|     UdpSocket *connection = sp_udp_open_ipv4(127, 0, 0, 1, 2342); | ||||
|     if (connection == NULL) | ||||
|         return 1; | ||||
| 
 | ||||
|     Bitmap *pixels = sp_bitmap_new(PIXEL_WIDTH, PIXEL_HEIGHT); | ||||
|     if (pixels == NULL) | ||||
|         return 1; | ||||
| 
 | ||||
|     sp_bitmap_fill(pixels, true); | ||||
| 
 | ||||
|     Packet *packet = sp_bitmap_into_packet(pixels, 0, 0, COMPRESSION_CODE_UNCOMPRESSED); | ||||
|     if (packet == NULL) | ||||
|         return 1; | ||||
| 
 | ||||
|     Header *header = sp_packet_get_header(packet); | ||||
|     printf("[%d, %d, %d, %d, %d]\n", header->command_code, header->a, header->b, header->c, header->d); | ||||
| 
 | ||||
|     sp_udp_send_packet(connection, packet); | ||||
| 
 | ||||
|     sp_udp_free(connection); | ||||
|     return 0; | ||||
| } | ||||
							
								
								
									
										15
									
								
								flake.nix
									
										
									
									
									
								
							
							
						
						
									
										15
									
								
								flake.nix
									
										
									
									
									
								
							|  | @ -33,7 +33,15 @@ | |||
|         { pkgs, system }: | ||||
|         { | ||||
|           default = pkgs.mkShell rec { | ||||
|             packages = with pkgs; [ | ||||
|             buildInputs = with pkgs; [ | ||||
|               xe | ||||
|               xz | ||||
|               libgcc | ||||
|               libunwind | ||||
|               pkgsStatic.musl | ||||
|             ]; | ||||
| 
 | ||||
|             nativeBuildInputs = with pkgs; [ | ||||
|               (pkgs.symlinkJoin { | ||||
|                 name = "rust-toolchain"; | ||||
|                 paths = with pkgs; [ | ||||
|  | @ -47,11 +55,12 @@ | |||
|                 ]; | ||||
|               }) | ||||
|               gcc | ||||
|               gdb | ||||
|               pkgsStatic.gcc | ||||
|               gnumake | ||||
|               xe | ||||
|               xz | ||||
|               pkg-config | ||||
|             ]; | ||||
| 
 | ||||
|             RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}"; | ||||
|           }; | ||||
|         } | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										298
									
								
								src/bitmap.rs
									
										
									
									
									
								
							
							
						
						
									
										298
									
								
								src/bitmap.rs
									
										
									
									
									
								
							|  | @ -1,13 +1,24 @@ | |||
| //! C functions for interacting with [SPBitmap]s
 | ||||
| //!
 | ||||
| //! prefix `sp_bitmap_`
 | ||||
| 
 | ||||
| use servicepoint::{DataRef, Grid}; | ||||
| use crate::byte_slice::ByteSlice; | ||||
| use crate::{heap_drop, heap_move, heap_move_nonnull, heap_remove, SPBitVec}; | ||||
| use servicepoint::{ | ||||
|     Bitmap, BitmapCommand, CompressionCode, DataRef, Grid, Origin, Packet, | ||||
| }; | ||||
| use std::ptr::NonNull; | ||||
| 
 | ||||
| use crate::byte_slice::SPByteSlice; | ||||
| 
 | ||||
| /// A grid of pixels.
 | ||||
| /// Creates a new [Bitmap] with the specified dimensions.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `width`: size in pixels in x-direction
 | ||||
| /// - `height`: size in pixels in y-direction
 | ||||
| ///
 | ||||
| /// returns: [Bitmap] initialized to all pixels off, or NULL in case of an error.
 | ||||
| ///
 | ||||
| /// # Errors
 | ||||
| ///
 | ||||
| /// In the following cases, this function will return NULL:
 | ||||
| ///
 | ||||
| /// - when the width is not dividable by 8
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
|  | @ -17,135 +28,81 @@ use crate::byte_slice::SPByteSlice; | |||
| /// sp_bitmap_set(grid, 0, 0, false);
 | ||||
| /// sp_bitmap_free(grid);
 | ||||
| /// ```
 | ||||
| pub struct SPBitmap(pub(crate) servicepoint::Bitmap); | ||||
| 
 | ||||
| /// Creates a new [SPBitmap] with the specified dimensions.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `width`: size in pixels in x-direction
 | ||||
| /// - `height`: size in pixels in y-direction
 | ||||
| ///
 | ||||
| /// returns: [SPBitmap] initialized to all pixels off. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when the width is not dividable by 8
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_bitmap_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitmap_new( | ||||
|     width: usize, | ||||
|     height: usize, | ||||
| ) -> NonNull<SPBitmap> { | ||||
|     let result = Box::new(SPBitmap(servicepoint::Bitmap::new(width, height))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| ) -> *mut Bitmap { | ||||
|     if let Some(bitmap) = Bitmap::new(width, height) { | ||||
|         heap_move(bitmap) | ||||
|     } else { | ||||
|         std::ptr::null_mut() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Creates a new [SPBitmap] with a size matching the screen.
 | ||||
| /// Creates a new [Bitmap] with a size matching the screen.
 | ||||
| ///
 | ||||
| /// returns: [SPBitmap] initialized to all pixels off. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling [sp_bitmap_free].
 | ||||
| /// returns: [Bitmap] initialized to all pixels off.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitmap_new_screen_sized() -> NonNull<SPBitmap> { | ||||
|     let result = Box::new(SPBitmap(servicepoint::Bitmap::max_sized())); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| pub unsafe extern "C" fn sp_bitmap_new_max_sized() -> NonNull<Bitmap> { | ||||
|     heap_move_nonnull(Bitmap::max_sized()) | ||||
| } | ||||
| 
 | ||||
| /// Loads a [SPBitmap] with the specified dimensions from the provided data.
 | ||||
| /// Loads a [Bitmap] with the specified dimensions from the provided data.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `width`: size in pixels in x-direction
 | ||||
| /// - `height`: size in pixels in y-direction
 | ||||
| ///
 | ||||
| /// returns: [SPBitmap] that contains a copy of the provided data. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `data` is NULL
 | ||||
| /// - when the dimensions and data size do not match exactly.
 | ||||
| /// - when the width is not dividable by 8
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `data` points to a valid memory location of at least `data_length` bytes in size.
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_bitmap_free`.
 | ||||
| /// returns: [Bitmap] that contains a copy of the provided data, or NULL in case of an error.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitmap_load( | ||||
|     width: usize, | ||||
|     height: usize, | ||||
|     data: *const u8, | ||||
|     data_length: usize, | ||||
| ) -> NonNull<SPBitmap> { | ||||
|     assert!(!data.is_null()); | ||||
|     let data = std::slice::from_raw_parts(data, data_length); | ||||
|     let result = | ||||
|         Box::new(SPBitmap(servicepoint::Bitmap::load(width, height, data))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
|     data: ByteSlice, | ||||
| ) -> *mut Bitmap { | ||||
|     let data = unsafe { data.as_slice() }; | ||||
|     if let Ok(bitmap) = Bitmap::load(width, height, data) { | ||||
|         heap_move(bitmap) | ||||
|     } else { | ||||
|         std::ptr::null_mut() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Clones a [SPBitmap].
 | ||||
| /// Tries to convert the BitVec to a Bitmap.
 | ||||
| ///
 | ||||
| /// Will never return NULL.
 | ||||
| /// The provided BitVec gets consumed.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bitmap` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bitmap` points to a valid [SPBitmap]
 | ||||
| /// - `bitmap` is not written to concurrently
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_bitmap_free`.
 | ||||
| /// Returns NULL in case of error.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitmap_from_bitvec( | ||||
|     width: usize, | ||||
|     bitvec: NonNull<SPBitVec>, | ||||
| ) -> *mut Bitmap { | ||||
|     let bitvec = unsafe { heap_remove(bitvec) }; | ||||
|     if let Ok(bitmap) = Bitmap::from_bitvec(width, bitvec.0) { | ||||
|         heap_move(bitmap) | ||||
|     } else { | ||||
|         std::ptr::null_mut() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Clones a [Bitmap].
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitmap_clone( | ||||
|     bitmap: *const SPBitmap, | ||||
| ) -> NonNull<SPBitmap> { | ||||
|     assert!(!bitmap.is_null()); | ||||
|     let result = Box::new(SPBitmap((*bitmap).0.clone())); | ||||
|     NonNull::from(Box::leak(result)) | ||||
|     bitmap: NonNull<Bitmap>, | ||||
| ) -> NonNull<Bitmap> { | ||||
|     heap_move_nonnull(unsafe { bitmap.as_ref().clone() }) | ||||
| } | ||||
| 
 | ||||
| /// Deallocates a [SPBitmap].
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bitmap` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bitmap` points to a valid [SPBitmap]
 | ||||
| /// - `bitmap` is not used concurrently or after bitmap call
 | ||||
| /// - `bitmap` was not passed to another consuming function, e.g. to create a [SPCommand]
 | ||||
| ///
 | ||||
| /// [SPCommand]: [crate::SPCommand]
 | ||||
| /// Deallocates a [Bitmap].
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitmap_free(bitmap: *mut SPBitmap) { | ||||
|     assert!(!bitmap.is_null()); | ||||
|     _ = Box::from_raw(bitmap); | ||||
| pub unsafe extern "C" fn sp_bitmap_free(bitmap: NonNull<Bitmap>) { | ||||
|     unsafe { heap_drop(bitmap) } | ||||
| } | ||||
| 
 | ||||
| /// Gets the current value at the specified position in the [SPBitmap].
 | ||||
| /// Gets the current value at the specified position in the [Bitmap].
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
|  | @ -154,26 +111,17 @@ pub unsafe extern "C" fn sp_bitmap_free(bitmap: *mut SPBitmap) { | |||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bitmap` is NULL
 | ||||
| /// - when accessing `x` or `y` out of bounds
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bitmap` points to a valid [SPBitmap]
 | ||||
| /// - `bitmap` is not written to concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitmap_get( | ||||
|     bitmap: *const SPBitmap, | ||||
|     bitmap: NonNull<Bitmap>, | ||||
|     x: usize, | ||||
|     y: usize, | ||||
| ) -> bool { | ||||
|     assert!(!bitmap.is_null()); | ||||
|     (*bitmap).0.get(x, y) | ||||
|     unsafe { bitmap.as_ref().get(x, y) } | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of the specified position in the [SPBitmap].
 | ||||
| /// Sets the value of the specified position in the [Bitmap].
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
|  | @ -181,54 +129,31 @@ pub unsafe extern "C" fn sp_bitmap_get( | |||
| /// - `x` and `y`: position of the cell
 | ||||
| /// - `value`: the value to write to the cell
 | ||||
| ///
 | ||||
| /// returns: old value of the cell
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bitmap` is NULL
 | ||||
| /// - when accessing `x` or `y` out of bounds
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bitmap` points to a valid [SPBitmap]
 | ||||
| /// - `bitmap` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitmap_set( | ||||
|     bitmap: *mut SPBitmap, | ||||
|     bitmap: NonNull<Bitmap>, | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     value: bool, | ||||
| ) { | ||||
|     assert!(!bitmap.is_null()); | ||||
|     (*bitmap).0.set(x, y, value); | ||||
|     unsafe { (*bitmap.as_ptr()).set(x, y, value) }; | ||||
| } | ||||
| 
 | ||||
| /// Sets the state of all pixels in the [SPBitmap].
 | ||||
| /// Sets the state of all pixels in the [Bitmap].
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `bitmap`: instance to write to
 | ||||
| /// - `value`: the value to set all pixels to
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bitmap` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bitmap` points to a valid [SPBitmap]
 | ||||
| /// - `bitmap` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitmap_fill(bitmap: *mut SPBitmap, value: bool) { | ||||
|     assert!(!bitmap.is_null()); | ||||
|     (*bitmap).0.fill(value); | ||||
| pub unsafe extern "C" fn sp_bitmap_fill(bitmap: NonNull<Bitmap>, value: bool) { | ||||
|     unsafe { (*bitmap.as_ptr()).fill(value) }; | ||||
| } | ||||
| 
 | ||||
| /// Gets the width in pixels of the [SPBitmap] instance.
 | ||||
| /// Gets the width in pixels of the [Bitmap] instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
|  | @ -242,55 +167,60 @@ pub unsafe extern "C" fn sp_bitmap_fill(bitmap: *mut SPBitmap, value: bool) { | |||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bitmap` points to a valid [SPBitmap]
 | ||||
| /// - `bitmap` points to a valid [Bitmap]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitmap_width(bitmap: *const SPBitmap) -> usize { | ||||
|     assert!(!bitmap.is_null()); | ||||
|     (*bitmap).0.width() | ||||
| pub unsafe extern "C" fn sp_bitmap_width(bitmap: NonNull<Bitmap>) -> usize { | ||||
|     unsafe { bitmap.as_ref().width() } | ||||
| } | ||||
| 
 | ||||
| /// Gets the height in pixels of the [SPBitmap] instance.
 | ||||
| /// Gets the height in pixels of the [Bitmap] instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `bitmap`: instance to read from
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bitmap` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bitmap` points to a valid [SPBitmap]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitmap_height(bitmap: *const SPBitmap) -> usize { | ||||
|     assert!(!bitmap.is_null()); | ||||
|     (*bitmap).0.height() | ||||
| pub unsafe extern "C" fn sp_bitmap_height(bitmap: NonNull<Bitmap>) -> usize { | ||||
|     unsafe { bitmap.as_ref().height() } | ||||
| } | ||||
| 
 | ||||
| /// Gets an unsafe reference to the data of the [SPBitmap] instance.
 | ||||
| /// Gets an unsafe reference to the data of the [Bitmap] instance.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bitmap` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bitmap` points to a valid [SPBitmap]
 | ||||
| /// - the returned memory range is never accessed after the passed [SPBitmap] has been freed
 | ||||
| /// - the returned memory range is never accessed concurrently, either via the [SPBitmap] or directly
 | ||||
| /// The returned memory is valid for the lifetime of the bitmap.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitmap_unsafe_data_ref( | ||||
|     bitmap: *mut SPBitmap, | ||||
| ) -> SPByteSlice { | ||||
|     assert!(!bitmap.is_null()); | ||||
|     let data = (*bitmap).0.data_ref_mut(); | ||||
|     SPByteSlice { | ||||
|         start: NonNull::new(data.as_mut_ptr_range().start).unwrap(), | ||||
|         length: data.len(), | ||||
|     mut bitmap: NonNull<Bitmap>, | ||||
| ) -> ByteSlice { | ||||
|     unsafe { ByteSlice::from_slice(bitmap.as_mut().data_ref_mut()) } | ||||
| } | ||||
| 
 | ||||
| /// Consumes the Bitmap and returns the contained BitVec
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitmap_into_bitvec( | ||||
|     bitmap: NonNull<Bitmap>, | ||||
| ) -> NonNull<SPBitVec> { | ||||
|     let bitmap = unsafe { heap_remove(bitmap) }; | ||||
|     heap_move_nonnull(SPBitVec(bitmap.into())) | ||||
| } | ||||
| 
 | ||||
| /// Creates a [BitmapCommand] and immediately turns that into a [Packet].
 | ||||
| ///
 | ||||
| /// The provided [Bitmap] gets consumed.
 | ||||
| ///
 | ||||
| /// Returns NULL in case of an error.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitmap_into_packet( | ||||
|     bitmap: NonNull<Bitmap>, | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     compression: CompressionCode, | ||||
| ) -> *mut Packet { | ||||
|     let bitmap = unsafe { heap_remove(bitmap) }; | ||||
|     match Packet::try_from(BitmapCommand { | ||||
|         bitmap, | ||||
|         origin: Origin::new(x, y), | ||||
|         compression, | ||||
|     }) { | ||||
|         Ok(packet) => heap_move(packet), | ||||
|         Err(_) => std::ptr::null_mut(), | ||||
|     } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										225
									
								
								src/bitvec.rs
									
										
									
									
									
								
							
							
						
						
									
										225
									
								
								src/bitvec.rs
									
										
									
									
									
								
							|  | @ -1,8 +1,7 @@ | |||
| //! C functions for interacting with [SPBitVec]s
 | ||||
| //!
 | ||||
| //! prefix `sp_bitvec_`
 | ||||
| 
 | ||||
| use crate::SPByteSlice; | ||||
| use crate::{heap_drop, heap_move, heap_move_nonnull, heap_remove, ByteSlice}; | ||||
| use servicepoint::{ | ||||
|     BinaryOperation, BitVecCommand, CompressionCode, DisplayBitVec, Packet, | ||||
| }; | ||||
| use std::ptr::NonNull; | ||||
| 
 | ||||
| /// A vector of bits
 | ||||
|  | @ -13,19 +12,7 @@ use std::ptr::NonNull; | |||
| /// sp_bitvec_set(vec, 5, true);
 | ||||
| /// sp_bitvec_free(vec);
 | ||||
| /// ```
 | ||||
| pub struct SPBitVec(servicepoint::BitVec); | ||||
| 
 | ||||
| impl From<servicepoint::BitVec> for SPBitVec { | ||||
|     fn from(actual: servicepoint::BitVec) -> Self { | ||||
|         Self(actual) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<SPBitVec> for servicepoint::BitVec { | ||||
|     fn from(value: SPBitVec) -> Self { | ||||
|         value.0 | ||||
|     } | ||||
| } | ||||
| pub struct SPBitVec(pub(crate) DisplayBitVec); | ||||
| 
 | ||||
| impl Clone for SPBitVec { | ||||
|     fn clone(&self) -> Self { | ||||
|  | @ -39,95 +26,37 @@ impl Clone for SPBitVec { | |||
| ///
 | ||||
| /// - `size`: size in bits.
 | ||||
| ///
 | ||||
| /// returns: [SPBitVec] with all bits set to false. Will never return NULL.
 | ||||
| /// returns: [SPBitVec] with all bits set to false.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `size` is not divisible by 8.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_bitvec_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitvec_new(size: usize) -> NonNull<SPBitVec> { | ||||
|     let result = Box::new(SPBitVec(servicepoint::BitVec::repeat(false, size))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
|     heap_move_nonnull(SPBitVec(DisplayBitVec::repeat(false, size))) | ||||
| } | ||||
| 
 | ||||
| /// Interpret the data as a series of bits and load then into a new [SPBitVec] instance.
 | ||||
| ///
 | ||||
| /// returns: [SPBitVec] instance containing data. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `data` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `data` points to a valid memory location of at least `data_length`
 | ||||
| ///   bytes in size.
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_bitvec_free`.
 | ||||
| /// returns: [SPBitVec] instance containing data.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitvec_load( | ||||
|     data: *const u8, | ||||
|     data_length: usize, | ||||
| ) -> NonNull<SPBitVec> { | ||||
|     assert!(!data.is_null()); | ||||
|     let data = std::slice::from_raw_parts(data, data_length); | ||||
|     let result = Box::new(SPBitVec(servicepoint::BitVec::from_slice(data))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| pub unsafe extern "C" fn sp_bitvec_load(data: ByteSlice) -> NonNull<SPBitVec> { | ||||
|     let data = unsafe { data.as_slice() }; | ||||
|     heap_move_nonnull(SPBitVec(DisplayBitVec::from_slice(data))) | ||||
| } | ||||
| 
 | ||||
| /// Clones a [SPBitVec].
 | ||||
| ///
 | ||||
| /// returns: new [SPBitVec] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bit_vec` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bit_vec` points to a valid [SPBitVec]
 | ||||
| /// - `bit_vec` is not written to concurrently
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_bitvec_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitvec_clone( | ||||
|     bit_vec: *const SPBitVec, | ||||
|     bit_vec: NonNull<SPBitVec>, | ||||
| ) -> NonNull<SPBitVec> { | ||||
|     assert!(!bit_vec.is_null()); | ||||
|     let result = Box::new((*bit_vec).clone()); | ||||
|     NonNull::from(Box::leak(result)) | ||||
|     heap_move_nonnull(unsafe { bit_vec.as_ref().clone() }) | ||||
| } | ||||
| 
 | ||||
| /// Deallocates a [SPBitVec].
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `but_vec` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bit_vec` points to a valid [SPBitVec]
 | ||||
| /// - `bit_vec` is not used concurrently or after this call
 | ||||
| /// - `bit_vec` was not passed to another consuming function, e.g. to create a [SPCommand]
 | ||||
| ///
 | ||||
| /// [SPCommand]: [crate::SPCommand]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitvec_free(bit_vec: *mut SPBitVec) { | ||||
|     assert!(!bit_vec.is_null()); | ||||
|     _ = Box::from_raw(bit_vec); | ||||
| pub unsafe extern "C" fn sp_bitvec_free(bit_vec: NonNull<SPBitVec>) { | ||||
|     unsafe { heap_drop(bit_vec) } | ||||
| } | ||||
| 
 | ||||
| /// Gets the value of a bit from the [SPBitVec].
 | ||||
|  | @ -141,22 +70,13 @@ pub unsafe extern "C" fn sp_bitvec_free(bit_vec: *mut SPBitVec) { | |||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bit_vec` is NULL
 | ||||
| /// - when accessing `index` out of bounds
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bit_vec` points to a valid [SPBitVec]
 | ||||
| /// - `bit_vec` is not written to concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitvec_get( | ||||
|     bit_vec: *const SPBitVec, | ||||
|     bit_vec: NonNull<SPBitVec>, | ||||
|     index: usize, | ||||
| ) -> bool { | ||||
|     assert!(!bit_vec.is_null()); | ||||
|     *(*bit_vec).0.get(index).unwrap() | ||||
|     unsafe { *bit_vec.as_ref().0.get(index).unwrap() } | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of a bit in the [SPBitVec].
 | ||||
|  | @ -169,23 +89,14 @@ pub unsafe extern "C" fn sp_bitvec_get( | |||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bit_vec` is NULL
 | ||||
| /// - when accessing `index` out of bounds
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bit_vec` points to a valid [SPBitVec]
 | ||||
| /// - `bit_vec` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitvec_set( | ||||
|     bit_vec: *mut SPBitVec, | ||||
|     bit_vec: NonNull<SPBitVec>, | ||||
|     index: usize, | ||||
|     value: bool, | ||||
| ) { | ||||
|     assert!(!bit_vec.is_null()); | ||||
|     (*bit_vec).0.set(index, value) | ||||
|     unsafe { (*bit_vec.as_ptr()).0.set(index, value) } | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of all bits in the [SPBitVec].
 | ||||
|  | @ -194,21 +105,12 @@ pub unsafe extern "C" fn sp_bitvec_set( | |||
| ///
 | ||||
| /// - `bit_vec`: instance to write to
 | ||||
| /// - `value`: the value to set all bits to
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bit_vec` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bit_vec` points to a valid [SPBitVec]
 | ||||
| /// - `bit_vec` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitvec_fill(bit_vec: *mut SPBitVec, value: bool) { | ||||
|     assert!(!bit_vec.is_null()); | ||||
|     (*bit_vec).0.fill(value) | ||||
| pub unsafe extern "C" fn sp_bitvec_fill( | ||||
|     bit_vec: NonNull<SPBitVec>, | ||||
|     value: bool, | ||||
| ) { | ||||
|     unsafe { (*bit_vec.as_ptr()).0.fill(value) } | ||||
| } | ||||
| 
 | ||||
| /// Gets the length of the [SPBitVec] in bits.
 | ||||
|  | @ -216,20 +118,9 @@ pub unsafe extern "C" fn sp_bitvec_fill(bit_vec: *mut SPBitVec, value: bool) { | |||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `bit_vec`: instance to write to
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bit_vec` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bit_vec` points to a valid [SPBitVec]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitvec_len(bit_vec: *const SPBitVec) -> usize { | ||||
|     assert!(!bit_vec.is_null()); | ||||
|     (*bit_vec).0.len() | ||||
| pub unsafe extern "C" fn sp_bitvec_len(bit_vec: NonNull<SPBitVec>) -> usize { | ||||
|     unsafe { bit_vec.as_ref().0.len() } | ||||
| } | ||||
| 
 | ||||
| /// Returns true if length is 0.
 | ||||
|  | @ -237,47 +128,47 @@ pub unsafe extern "C" fn sp_bitvec_len(bit_vec: *const SPBitVec) -> usize { | |||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `bit_vec`: instance to write to
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bit_vec` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bit_vec` points to a valid [SPBitVec]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitvec_is_empty(bit_vec: *const SPBitVec) -> bool { | ||||
|     assert!(!bit_vec.is_null()); | ||||
|     (*bit_vec).0.is_empty() | ||||
| pub unsafe extern "C" fn sp_bitvec_is_empty( | ||||
|     bit_vec: NonNull<SPBitVec>, | ||||
| ) -> bool { | ||||
|     unsafe { bit_vec.as_ref().0.is_empty() } | ||||
| } | ||||
| 
 | ||||
| /// Gets an unsafe reference to the data of the [SPBitVec] instance.
 | ||||
| ///
 | ||||
| /// The returned memory is valid for the lifetime of the bitvec.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `bit_vec`: instance to write to
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bit_vec` is NULL
 | ||||
| ///
 | ||||
| /// ## Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bit_vec` points to a valid [SPBitVec]
 | ||||
| /// - the returned memory range is never accessed after the passed [SPBitVec] has been freed
 | ||||
| /// - the returned memory range is never accessed concurrently, either via the [SPBitVec] or directly
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitvec_unsafe_data_ref( | ||||
|     bit_vec: *mut SPBitVec, | ||||
| ) -> SPByteSlice { | ||||
|     assert!(!bit_vec.is_null()); | ||||
|     let data = (*bit_vec).0.as_raw_mut_slice(); | ||||
|     SPByteSlice { | ||||
|         start: NonNull::new(data.as_mut_ptr_range().start).unwrap(), | ||||
|         length: data.len(), | ||||
|     bit_vec: NonNull<SPBitVec>, | ||||
| ) -> ByteSlice { | ||||
|     unsafe { ByteSlice::from_slice((*bit_vec.as_ptr()).0.as_raw_mut_slice()) } | ||||
| } | ||||
| 
 | ||||
| /// Creates a [BitVecCommand] and immediately turns that into a [Packet].
 | ||||
| ///
 | ||||
| /// The provided [SPBitVec] gets consumed.
 | ||||
| ///
 | ||||
| /// Returns NULL in case of an error.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitvec_into_packet( | ||||
|     bitvec: NonNull<SPBitVec>, | ||||
|     offset: usize, | ||||
|     operation: BinaryOperation, | ||||
|     compression: CompressionCode, | ||||
| ) -> *mut Packet { | ||||
|     let bitvec = unsafe { heap_remove(bitvec) }.0; | ||||
|     match Packet::try_from(BitVecCommand { | ||||
|         bitvec, | ||||
|         offset, | ||||
|         operation, | ||||
|         compression, | ||||
|     }) { | ||||
|         Ok(packet) => heap_move(packet), | ||||
|         Err(_) => std::ptr::null_mut(), | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,146 +1,71 @@ | |||
| //! C functions for interacting with [SPBrightnessGrid]s
 | ||||
| //!
 | ||||
| //! prefix `sp_brightness_grid_`
 | ||||
| 
 | ||||
| use crate::SPByteSlice; | ||||
| use servicepoint::{DataRef, Grid}; | ||||
| use std::convert::Into; | ||||
| use std::intrinsics::transmute; | ||||
| use crate::{heap_drop, heap_move, heap_move_nonnull, heap_remove, ByteSlice}; | ||||
| use servicepoint::{ | ||||
|     Brightness, BrightnessGrid, BrightnessGridCommand, ByteGrid, DataRef, Grid, | ||||
|     Origin, Packet, | ||||
| }; | ||||
| use std::mem::transmute; | ||||
| use std::ptr::NonNull; | ||||
| 
 | ||||
| /// see [servicepoint::Brightness::MIN]
 | ||||
| pub const SP_BRIGHTNESS_MIN: u8 = 0; | ||||
| /// see [servicepoint::Brightness::MAX]
 | ||||
| pub const SP_BRIGHTNESS_MAX: u8 = 11; | ||||
| /// Count of possible brightness values
 | ||||
| pub const SP_BRIGHTNESS_LEVELS: u8 = 12; | ||||
| 
 | ||||
| /// A grid containing brightness values.
 | ||||
| /// Creates a new [BrightnessGrid] with the specified dimensions.
 | ||||
| ///
 | ||||
| /// returns: [BrightnessGrid] initialized to 0.
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| /// ```C
 | ||||
| /// SPConnection connection = sp_connection_open("127.0.0.1:2342");
 | ||||
| /// UdpConnection connection = sp_udp_open("127.0.0.1:2342");
 | ||||
| /// if (connection == NULL)
 | ||||
| ///     return 1;
 | ||||
| ///
 | ||||
| /// SPBrightnessGrid grid = sp_brightness_grid_new(2, 2);
 | ||||
| /// BrightnessGrid grid = sp_brightness_grid_new(2, 2);
 | ||||
| /// sp_brightness_grid_set(grid, 0, 0, 0);
 | ||||
| /// sp_brightness_grid_set(grid, 1, 1, 10);
 | ||||
| ///
 | ||||
| /// SPCommand command = sp_command_char_brightness(grid);
 | ||||
| /// sp_connection_free(connection);
 | ||||
| /// TypedCommand command = sp_command_char_brightness(grid);
 | ||||
| /// sp_udp_free(connection);
 | ||||
| /// ```
 | ||||
| #[derive(Clone)] | ||||
| pub struct SPBrightnessGrid(pub(crate) servicepoint::BrightnessGrid); | ||||
| 
 | ||||
| /// Creates a new [SPBrightnessGrid] with the specified dimensions.
 | ||||
| ///
 | ||||
| /// returns: [SPBrightnessGrid] initialized to 0. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_brightness_grid_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_new( | ||||
|     width: usize, | ||||
|     height: usize, | ||||
| ) -> NonNull<SPBrightnessGrid> { | ||||
|     let result = Box::new(SPBrightnessGrid(servicepoint::BrightnessGrid::new( | ||||
|         width, height, | ||||
|     ))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| ) -> NonNull<BrightnessGrid> { | ||||
|     heap_move_nonnull(BrightnessGrid::new(width, height)) | ||||
| } | ||||
| 
 | ||||
| /// Loads a [SPBrightnessGrid] with the specified dimensions from the provided data.
 | ||||
| /// Loads a [BrightnessGrid] with the specified dimensions from the provided data.
 | ||||
| ///
 | ||||
| /// returns: new [SPBrightnessGrid] instance. Will never return NULL.
 | ||||
| /// Any out of range values will be set to [Brightness::MAX] or [Brightness::MIN].
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `data` is NULL
 | ||||
| /// - when the provided `data_length` does not match `height` and `width`
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `data` points to a valid memory location of at least `data_length`
 | ||||
| ///   bytes in size.
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_brightness_grid_free`.
 | ||||
| /// returns: new [BrightnessGrid] instance, or NULL in case of an error.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_load( | ||||
|     width: usize, | ||||
|     height: usize, | ||||
|     data: *const u8, | ||||
|     data_length: usize, | ||||
| ) -> NonNull<SPBrightnessGrid> { | ||||
|     assert!(!data.is_null()); | ||||
|     let data = std::slice::from_raw_parts(data, data_length); | ||||
|     let grid = servicepoint::ByteGrid::load(width, height, data); | ||||
|     let grid = servicepoint::BrightnessGrid::try_from(grid) | ||||
|         .expect("invalid brightness value"); | ||||
|     let result = Box::new(SPBrightnessGrid(grid)); | ||||
|     NonNull::from(Box::leak(result)) | ||||
|     data: ByteSlice, | ||||
| ) -> *mut BrightnessGrid { | ||||
|     let data = unsafe { data.as_slice() }; | ||||
| 
 | ||||
|     match ByteGrid::load(width, height, data) | ||||
|         .map(move |grid| grid.map(Brightness::saturating_from)) | ||||
|     { | ||||
|         None => std::ptr::null_mut(), | ||||
|         Some(grid) => heap_move(grid), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Clones a [SPBrightnessGrid].
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `brightness_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// returns: new [SPBrightnessGrid] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `brightness_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `brightness_grid` points to a valid [SPBrightnessGrid]
 | ||||
| /// - `brightness_grid` is not written to concurrently
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_brightness_grid_free`.
 | ||||
| /// Clones a [BrightnessGrid].
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_clone( | ||||
|     brightness_grid: *const SPBrightnessGrid, | ||||
| ) -> NonNull<SPBrightnessGrid> { | ||||
|     assert!(!brightness_grid.is_null()); | ||||
|     let result = Box::new((*brightness_grid).clone()); | ||||
|     NonNull::from(Box::leak(result)) | ||||
|     brightness_grid: NonNull<BrightnessGrid>, | ||||
| ) -> NonNull<BrightnessGrid> { | ||||
|     heap_move_nonnull(unsafe { brightness_grid.as_ref().clone() }) | ||||
| } | ||||
| 
 | ||||
| /// Deallocates a [SPBrightnessGrid].
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `brightness_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `brightness_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `brightness_grid` points to a valid [SPBrightnessGrid]
 | ||||
| /// - `brightness_grid` is not used concurrently or after this call
 | ||||
| /// - `brightness_grid` was not passed to another consuming function, e.g. to create a [SPCommand]
 | ||||
| ///
 | ||||
| /// [SPCommand]: [crate::SPCommand]
 | ||||
| /// Deallocates a [BrightnessGrid].
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_free( | ||||
|     brightness_grid: *mut SPBrightnessGrid, | ||||
|     brightness_grid: NonNull<BrightnessGrid>, | ||||
| ) { | ||||
|     assert!(!brightness_grid.is_null()); | ||||
|     _ = Box::from_raw(brightness_grid); | ||||
|     unsafe { heap_drop(brightness_grid) } | ||||
| } | ||||
| 
 | ||||
| /// Gets the current value at the specified position.
 | ||||
|  | @ -153,27 +78,17 @@ pub unsafe extern "C" fn sp_brightness_grid_free( | |||
| /// returns: value at position
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `brightness_grid` is NULL
 | ||||
| /// - When accessing `x` or `y` out of bounds.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `brightness_grid` points to a valid [SPBrightnessGrid]
 | ||||
| /// - `brightness_grid` is not written to concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_get( | ||||
|     brightness_grid: *const SPBrightnessGrid, | ||||
|     brightness_grid: NonNull<BrightnessGrid>, | ||||
|     x: usize, | ||||
|     y: usize, | ||||
| ) -> u8 { | ||||
|     assert!(!brightness_grid.is_null()); | ||||
|     (*brightness_grid).0.get(x, y).into() | ||||
| ) -> Brightness { | ||||
|     unsafe { brightness_grid.as_ref().get(x, y) } | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of the specified position in the [SPBrightnessGrid].
 | ||||
| /// Sets the value of the specified position in the [BrightnessGrid].
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
|  | @ -185,138 +100,98 @@ pub unsafe extern "C" fn sp_brightness_grid_get( | |||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `brightness_grid` is NULL
 | ||||
| /// - When accessing `x` or `y` out of bounds.
 | ||||
| /// - When providing an invalid brightness value
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `brightness_grid` points to a valid [SPBrightnessGrid]
 | ||||
| /// - `brightness_grid` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_set( | ||||
|     brightness_grid: *mut SPBrightnessGrid, | ||||
|     brightness_grid: NonNull<BrightnessGrid>, | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     value: u8, | ||||
|     value: Brightness, | ||||
| ) { | ||||
|     assert!(!brightness_grid.is_null()); | ||||
|     let brightness = servicepoint::Brightness::try_from(value) | ||||
|         .expect("invalid brightness value"); | ||||
|     (*brightness_grid).0.set(x, y, brightness); | ||||
|     unsafe { (*brightness_grid.as_ptr()).set(x, y, value) }; | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of all cells in the [SPBrightnessGrid].
 | ||||
| /// Sets the value of all cells in the [BrightnessGrid].
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `brightness_grid`: instance to write to
 | ||||
| /// - `value`: the value to set all cells to
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `brightness_grid` is NULL
 | ||||
| /// - When providing an invalid brightness value
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `brightness_grid` points to a valid [SPBrightnessGrid]
 | ||||
| /// - `brightness_grid` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_fill( | ||||
|     brightness_grid: *mut SPBrightnessGrid, | ||||
|     value: u8, | ||||
|     brightness_grid: NonNull<BrightnessGrid>, | ||||
|     value: Brightness, | ||||
| ) { | ||||
|     assert!(!brightness_grid.is_null()); | ||||
|     let brightness = servicepoint::Brightness::try_from(value) | ||||
|         .expect("invalid brightness value"); | ||||
|     (*brightness_grid).0.fill(brightness); | ||||
|     unsafe { (*brightness_grid.as_ptr()).fill(value) }; | ||||
| } | ||||
| 
 | ||||
| /// Gets the width of the [SPBrightnessGrid] instance.
 | ||||
| /// Gets the width of the [BrightnessGrid] instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `brightness_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// returns: width
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `brightness_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `brightness_grid` points to a valid [SPBrightnessGrid]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_width( | ||||
|     brightness_grid: *const SPBrightnessGrid, | ||||
|     brightness_grid: NonNull<BrightnessGrid>, | ||||
| ) -> usize { | ||||
|     assert!(!brightness_grid.is_null()); | ||||
|     (*brightness_grid).0.width() | ||||
|     unsafe { brightness_grid.as_ref().width() } | ||||
| } | ||||
| 
 | ||||
| /// Gets the height of the [SPBrightnessGrid] instance.
 | ||||
| /// Gets the height of the [BrightnessGrid] instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `brightness_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// returns: height
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `brightness_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `brightness_grid` points to a valid [SPBrightnessGrid]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_height( | ||||
|     brightness_grid: *const SPBrightnessGrid, | ||||
|     brightness_grid: NonNull<BrightnessGrid>, | ||||
| ) -> usize { | ||||
|     assert!(!brightness_grid.is_null()); | ||||
|     (*brightness_grid).0.height() | ||||
|     unsafe { brightness_grid.as_ref().height() } | ||||
| } | ||||
| 
 | ||||
| /// Gets an unsafe reference to the data of the [SPBrightnessGrid] instance.
 | ||||
| /// Gets an unsafe reference to the data of the [BrightnessGrid] instance.
 | ||||
| ///
 | ||||
| /// The returned memory is valid for the lifetime of the brightness grid.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `brightness_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// returns: slice of bytes underlying the `brightness_grid`.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `brightness_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `brightness_grid` points to a valid [SPBrightnessGrid]
 | ||||
| /// - the returned memory range is never accessed after the passed [SPBrightnessGrid] has been freed
 | ||||
| /// - the returned memory range is never accessed concurrently, either via the [SPBrightnessGrid] or directly
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_unsafe_data_ref( | ||||
|     brightness_grid: *mut SPBrightnessGrid, | ||||
| ) -> SPByteSlice { | ||||
|     assert!(!brightness_grid.is_null()); | ||||
|     assert_eq!(core::mem::size_of::<servicepoint::Brightness>(), 1); | ||||
|     let data = (*brightness_grid).0.data_ref_mut(); | ||||
|     // this assumes more about the memory layout than rust guarantees. yikes!
 | ||||
|     let data: &mut [u8] = transmute(data); | ||||
|     SPByteSlice { | ||||
|         start: NonNull::new(data.as_mut_ptr_range().start).unwrap(), | ||||
|         length: data.len(), | ||||
|     brightness_grid: NonNull<BrightnessGrid>, | ||||
| ) -> ByteSlice { | ||||
|     //noinspection RsAssertEqual
 | ||||
|     const _: () = assert!(size_of::<Brightness>() == 1); | ||||
| 
 | ||||
|     let data = unsafe { (*brightness_grid.as_ptr()).data_ref_mut() }; | ||||
|     unsafe { | ||||
|         ByteSlice::from_slice(transmute::<&mut [Brightness], &mut [u8]>(data)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Creates a [BrightnessGridCommand] and immediately turns that into a [Packet].
 | ||||
| ///
 | ||||
| /// The provided [BrightnessGrid] gets consumed.
 | ||||
| ///
 | ||||
| /// Returns NULL in case of an error.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_into_packet( | ||||
|     grid: NonNull<BrightnessGrid>, | ||||
|     x: usize, | ||||
|     y: usize, | ||||
| ) -> *mut Packet { | ||||
|     let grid = unsafe { heap_remove(grid) }; | ||||
|     match Packet::try_from(BrightnessGridCommand { | ||||
|         grid, | ||||
|         origin: Origin::new(x, y), | ||||
|     }) { | ||||
|         Ok(packet) => heap_move(packet), | ||||
|         Err(_) => std::ptr::null_mut(), | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -2,10 +2,7 @@ | |||
| 
 | ||||
| use std::ptr::NonNull; | ||||
| 
 | ||||
| #[repr(C)] | ||||
| /// Represents a span of memory (`&mut [u8]` ) as a struct usable by C code.
 | ||||
| ///
 | ||||
| /// You should not create an instance of this type in your C code.
 | ||||
| /// Represents a span of memory (`&mut [u8]` ) as a struct.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
|  | @ -14,11 +11,29 @@ use std::ptr::NonNull; | |||
| /// - accesses to the memory pointed to with `start` is never accessed outside `length`
 | ||||
| /// - the lifetime of the `CByteSlice` does not outlive the memory it points to, as described in
 | ||||
| ///   the function returning this type.
 | ||||
| /// - an instance of this created from C is never passed to a consuming function, as the rust code
 | ||||
| ///   will try to free the memory of a potentially separate allocator.
 | ||||
| pub struct SPByteSlice { | ||||
|     /// The start address of the memory
 | ||||
| #[repr(C)] | ||||
| pub struct ByteSlice { | ||||
|     /// The start address of the memory.
 | ||||
|     pub start: NonNull<u8>, | ||||
|     /// The amount of memory in bytes
 | ||||
|     /// The amount of memory in bytes.
 | ||||
|     pub length: usize, | ||||
| } | ||||
| 
 | ||||
| impl ByteSlice { | ||||
|     pub(crate) unsafe fn as_slice(&self) -> &[u8] { | ||||
|         unsafe { std::slice::from_raw_parts(self.start.as_ptr(), self.length) } | ||||
|     } | ||||
| 
 | ||||
|     pub(crate) unsafe fn as_slice_mut(&mut self) -> &mut [u8] { | ||||
|         unsafe { | ||||
|             std::slice::from_raw_parts_mut(self.start.as_ptr(), self.length) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub(crate) unsafe fn from_slice(slice: &mut [u8]) -> Self { | ||||
|         Self { | ||||
|             start: NonNull::new(slice.as_mut_ptr()).unwrap(), | ||||
|             length: slice.len(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										226
									
								
								src/char_grid.rs
									
										
									
									
									
								
							
							
						
						
									
										226
									
								
								src/char_grid.rs
									
										
									
									
									
								
							|  | @ -1,16 +1,10 @@ | |||
| //! C functions for interacting with [SPCharGrid]s
 | ||||
| //!
 | ||||
| //! prefix `sp_char_grid_`
 | ||||
| 
 | ||||
| use servicepoint::Grid; | ||||
| use crate::{heap_drop, heap_move, heap_move_nonnull, heap_remove, ByteSlice}; | ||||
| use servicepoint::{CharGrid, CharGridCommand, Grid, Origin, Packet}; | ||||
| use std::ptr::NonNull; | ||||
| 
 | ||||
| /// A C-wrapper for grid containing UTF-8 characters.
 | ||||
| /// Creates a new [CharGrid] with the specified dimensions.
 | ||||
| ///
 | ||||
| /// As the rust [char] type is not FFI-safe, characters are passed in their UTF-32 form as 32bit unsigned integers.
 | ||||
| ///
 | ||||
| /// The encoding is enforced in most cases by the rust standard library
 | ||||
| /// and will panic when provided with illegal characters.
 | ||||
| /// returns: [CharGrid] initialized to 0.
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
|  | @ -20,115 +14,46 @@ use std::ptr::NonNull; | |||
| /// sp_char_grid_set(grid, 0, 0, '!');
 | ||||
| /// sp_char_grid_free(grid);
 | ||||
| /// ```
 | ||||
| pub struct SPCharGrid(pub(crate) servicepoint::CharGrid); | ||||
| 
 | ||||
| impl Clone for SPCharGrid { | ||||
|     fn clone(&self) -> Self { | ||||
|         SPCharGrid(self.0.clone()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Creates a new [SPCharGrid] with the specified dimensions.
 | ||||
| ///
 | ||||
| /// returns: [SPCharGrid] initialized to 0. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_char_grid_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_char_grid_new( | ||||
|     width: usize, | ||||
|     height: usize, | ||||
| ) -> NonNull<SPCharGrid> { | ||||
|     let result = | ||||
|         Box::new(SPCharGrid(servicepoint::CharGrid::new(width, height))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| ) -> NonNull<CharGrid> { | ||||
|     heap_move_nonnull(CharGrid::new(width, height)) | ||||
| } | ||||
| 
 | ||||
| /// Loads a [SPCharGrid] with the specified dimensions from the provided data.
 | ||||
| /// Loads a [CharGrid] with the specified dimensions from the provided data.
 | ||||
| ///
 | ||||
| /// Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `data` is NULL
 | ||||
| /// - when the provided `data_length` does not match `height` and `width`
 | ||||
| /// - when `data` is not valid UTF-8
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `data` points to a valid memory location of at least `data_length`
 | ||||
| ///   bytes in size.
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_char_grid_free`.
 | ||||
| /// returns: new CharGrid or NULL in case of an error
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_char_grid_load( | ||||
|     width: usize, | ||||
|     height: usize, | ||||
|     data: *const u8, | ||||
|     data_length: usize, | ||||
| ) -> NonNull<SPCharGrid> { | ||||
|     assert!(data.is_null()); | ||||
|     let data = std::slice::from_raw_parts(data, data_length); | ||||
|     let result = Box::new(SPCharGrid( | ||||
|         servicepoint::CharGrid::load_utf8(width, height, data.to_vec()) | ||||
|             .unwrap(), | ||||
|     )); | ||||
|     NonNull::from(Box::leak(result)) | ||||
|     data: ByteSlice, | ||||
| ) -> *mut CharGrid { | ||||
|     let data = unsafe { data.as_slice() }; | ||||
|     if let Ok(grid) = CharGrid::load_utf8(width, height, data.to_vec()) { | ||||
|         heap_move(grid) | ||||
|     } else { | ||||
|         std::ptr::null_mut() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Clones a [SPCharGrid].
 | ||||
| ///
 | ||||
| /// Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `char_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `char_grid` points to a valid [SPCharGrid]
 | ||||
| /// - `char_grid` is not written to concurrently
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_char_grid_free`.
 | ||||
| /// Clones a [CharGrid].
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_char_grid_clone( | ||||
|     char_grid: *const SPCharGrid, | ||||
| ) -> NonNull<SPCharGrid> { | ||||
|     assert!(!char_grid.is_null()); | ||||
|     let result = Box::new((*char_grid).clone()); | ||||
|     NonNull::from(Box::leak(result)) | ||||
|     char_grid: NonNull<CharGrid>, | ||||
| ) -> NonNull<CharGrid> { | ||||
|     heap_move_nonnull(unsafe { char_grid.as_ref().clone() }) | ||||
| } | ||||
| 
 | ||||
| /// Deallocates a [SPCharGrid].
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `char_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `char_grid` points to a valid [SPCharGrid]
 | ||||
| /// - `char_grid` is not used concurrently or after char_grid call
 | ||||
| /// - `char_grid` was not passed to another consuming function, e.g. to create a [SPCommand]
 | ||||
| ///
 | ||||
| /// [SPCommand]: [crate::SPCommand]
 | ||||
| /// Deallocates a [CharGrid].
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_char_grid_free(char_grid: *mut SPCharGrid) { | ||||
|     assert!(!char_grid.is_null()); | ||||
|     _ = Box::from_raw(char_grid); | ||||
| pub unsafe extern "C" fn sp_char_grid_free(char_grid: NonNull<CharGrid>) { | ||||
|     unsafe { heap_drop(char_grid) } | ||||
| } | ||||
| 
 | ||||
| /// Gets the current value at the specified position.
 | ||||
| /// Returns the current value at the specified position.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
|  | @ -137,26 +62,17 @@ pub unsafe extern "C" fn sp_char_grid_free(char_grid: *mut SPCharGrid) { | |||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `char_grid` is NULL
 | ||||
| /// - when accessing `x` or `y` out of bounds
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `char_grid` points to a valid [SPCharGrid]
 | ||||
| /// - `char_grid` is not written to concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_char_grid_get( | ||||
|     char_grid: *const SPCharGrid, | ||||
|     char_grid: NonNull<CharGrid>, | ||||
|     x: usize, | ||||
|     y: usize, | ||||
| ) -> u32 { | ||||
|     assert!(!char_grid.is_null()); | ||||
|     (*char_grid).0.get(x, y) as u32 | ||||
|     unsafe { char_grid.as_ref().get(x, y) as u32 } | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of the specified position in the [SPCharGrid].
 | ||||
| /// Sets the value of the specified position in the [CharGrid].
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
|  | @ -168,96 +84,72 @@ pub unsafe extern "C" fn sp_char_grid_get( | |||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `char_grid` is NULL
 | ||||
| /// - when accessing `x` or `y` out of bounds
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `char_grid` points to a valid [SPBitVec]
 | ||||
| /// - `char_grid` is not written to or read from concurrently
 | ||||
| ///
 | ||||
| /// [SPBitVec]: [crate::SPBitVec]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_char_grid_set( | ||||
|     char_grid: *mut SPCharGrid, | ||||
|     char_grid: NonNull<CharGrid>, | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     value: u32, | ||||
| ) { | ||||
|     assert!(!char_grid.is_null()); | ||||
|     (*char_grid).0.set(x, y, char::from_u32(value).unwrap()); | ||||
|     unsafe { (*char_grid.as_ptr()).set(x, y, char::from_u32(value).unwrap()) }; | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of all cells in the [SPCharGrid].
 | ||||
| /// Sets the value of all cells in the [CharGrid].
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `char_grid`: instance to write to
 | ||||
| /// - `value`: the value to set all cells to
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `char_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `char_grid` points to a valid [SPCharGrid]
 | ||||
| /// - `char_grid` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_char_grid_fill( | ||||
|     char_grid: *mut SPCharGrid, | ||||
|     char_grid: NonNull<CharGrid>, | ||||
|     value: u32, | ||||
| ) { | ||||
|     assert!(!char_grid.is_null()); | ||||
|     (*char_grid).0.fill(char::from_u32(value).unwrap()); | ||||
|     unsafe { (*char_grid.as_ptr()).fill(char::from_u32(value).unwrap()) }; | ||||
| } | ||||
| 
 | ||||
| /// Gets the width of the [SPCharGrid] instance.
 | ||||
| /// Gets the width of the [CharGrid] instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `char_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `char_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `char_grid` points to a valid [SPCharGrid]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_char_grid_width( | ||||
|     char_grid: *const SPCharGrid, | ||||
|     char_grid: NonNull<CharGrid>, | ||||
| ) -> usize { | ||||
|     assert!(!char_grid.is_null()); | ||||
|     (*char_grid).0.width() | ||||
|     unsafe { char_grid.as_ref().width() } | ||||
| } | ||||
| 
 | ||||
| /// Gets the height of the [SPCharGrid] instance.
 | ||||
| /// Gets the height of the [CharGrid] instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `char_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `char_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `char_grid` points to a valid [SPCharGrid]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_char_grid_height( | ||||
|     char_grid: *const SPCharGrid, | ||||
|     char_grid: NonNull<CharGrid>, | ||||
| ) -> usize { | ||||
|     assert!(!char_grid.is_null()); | ||||
|     (*char_grid).0.height() | ||||
|     unsafe { char_grid.as_ref().height() } | ||||
| } | ||||
| 
 | ||||
| /// Creates a [CharGridCommand] and immediately turns that into a [Packet].
 | ||||
| ///
 | ||||
| /// The provided [CharGrid] gets consumed.
 | ||||
| ///
 | ||||
| /// Returns NULL in case of an error.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_char_grid_into_packet( | ||||
|     grid: NonNull<CharGrid>, | ||||
|     x: usize, | ||||
|     y: usize, | ||||
| ) -> *mut Packet { | ||||
|     let grid = unsafe { heap_remove(grid) }; | ||||
|     match Packet::try_from(CharGridCommand { | ||||
|         grid, | ||||
|         origin: Origin::new(x, y), | ||||
|     }) { | ||||
|         Ok(packet) => heap_move(packet), | ||||
|         Err(_) => std::ptr::null_mut(), | ||||
|     } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										498
									
								
								src/command.rs
									
										
									
									
									
								
							
							
						
						
									
										498
									
								
								src/command.rs
									
										
									
									
									
								
							|  | @ -1,498 +0,0 @@ | |||
| //! C functions for interacting with [SPCommand]s
 | ||||
| //!
 | ||||
| //! prefix `sp_command_`
 | ||||
| 
 | ||||
| use std::ptr::{null_mut, NonNull}; | ||||
| 
 | ||||
| use crate::{ | ||||
|     SPBitVec, SPBitmap, SPBrightnessGrid, SPCharGrid, SPCompressionCode, | ||||
|     SPCp437Grid, SPPacket, | ||||
| }; | ||||
| 
 | ||||
| /// A low-level display command.
 | ||||
| ///
 | ||||
| /// This struct and associated functions implement the UDP protocol for the display.
 | ||||
| ///
 | ||||
| /// To send a [SPCommand], use a [SPConnection].
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```C
 | ||||
| /// sp_connection_send_command(connection, sp_command_clear());
 | ||||
| /// sp_connection_send_command(connection, sp_command_brightness(5));
 | ||||
| /// ```
 | ||||
| ///
 | ||||
| /// [SPConnection]: [crate::SPConnection]
 | ||||
| pub struct SPCommand(pub(crate) servicepoint::Command); | ||||
| 
 | ||||
| impl Clone for SPCommand { | ||||
|     fn clone(&self) -> Self { | ||||
|         SPCommand(self.0.clone()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Tries to turn a [SPPacket] into a [SPCommand].
 | ||||
| ///
 | ||||
| /// The packet is deallocated in the process.
 | ||||
| ///
 | ||||
| /// Returns: pointer to new [SPCommand] instance or NULL if parsing failed.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `packet` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - [SPPacket] points to a valid instance of [SPPacket]
 | ||||
| /// - [SPPacket] is not used concurrently or after this call
 | ||||
| /// - the result is checked for NULL
 | ||||
| /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_try_from_packet( | ||||
|     packet: *mut SPPacket, | ||||
| ) -> *mut SPCommand { | ||||
|     let packet = *Box::from_raw(packet); | ||||
|     match servicepoint::Command::try_from(packet.0) { | ||||
|         Err(_) => null_mut(), | ||||
|         Ok(command) => Box::into_raw(Box::new(SPCommand(command))), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Clones a [SPCommand] instance.
 | ||||
| ///
 | ||||
| /// returns: new [SPCommand] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `command` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `command` points to a valid instance of [SPCommand]
 | ||||
| /// - `command` is not written to concurrently
 | ||||
| /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_clone( | ||||
|     command: *const SPCommand, | ||||
| ) -> NonNull<SPCommand> { | ||||
|     assert!(!command.is_null()); | ||||
|     let result = Box::new((*command).clone()); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Set all pixels to the off state.
 | ||||
| ///
 | ||||
| /// Does not affect brightness.
 | ||||
| ///
 | ||||
| /// Returns: a new [servicepoint::Command::Clear] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```C
 | ||||
| /// sp_connection_send_command(connection, sp_command_clear());
 | ||||
| /// ```
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_clear() -> NonNull<SPCommand> { | ||||
|     let result = Box::new(SPCommand(servicepoint::Command::Clear)); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Kills the udp daemon on the display, which usually results in a restart.
 | ||||
| ///
 | ||||
| /// Please do not send this in your normal program flow.
 | ||||
| ///
 | ||||
| /// Returns: a new [servicepoint::Command::HardReset] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_hard_reset() -> NonNull<SPCommand> { | ||||
|     let result = Box::new(SPCommand(servicepoint::Command::HardReset)); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// A yet-to-be-tested command.
 | ||||
| ///
 | ||||
| /// Returns: a new [servicepoint::Command::FadeOut] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_fade_out() -> NonNull<SPCommand> { | ||||
|     let result = Box::new(SPCommand(servicepoint::Command::FadeOut)); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Set the brightness of all tiles to the same value.
 | ||||
| ///
 | ||||
| /// Returns: a new [servicepoint::Command::Brightness] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - When the provided brightness value is out of range (0-11).
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_brightness( | ||||
|     brightness: u8, | ||||
| ) -> NonNull<SPCommand> { | ||||
|     let brightness = servicepoint::Brightness::try_from(brightness) | ||||
|         .expect("invalid brightness"); | ||||
|     let result = | ||||
|         Box::new(SPCommand(servicepoint::Command::Brightness(brightness))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Set the brightness of individual tiles in a rectangular area of the display.
 | ||||
| ///
 | ||||
| /// The passed [SPBrightnessGrid] gets consumed.
 | ||||
| ///
 | ||||
| /// Returns: a new [servicepoint::Command::CharBrightness] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `grid` points to a valid instance of [SPBrightnessGrid]
 | ||||
| /// - `grid` is not used concurrently or after this call
 | ||||
| /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_char_brightness( | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     grid: *mut SPBrightnessGrid, | ||||
| ) -> NonNull<SPCommand> { | ||||
|     assert!(!grid.is_null()); | ||||
|     let byte_grid = *Box::from_raw(grid); | ||||
|     let result = Box::new(SPCommand(servicepoint::Command::CharBrightness( | ||||
|         servicepoint::Origin::new(x, y), | ||||
|         byte_grid.0, | ||||
|     ))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Set pixel data starting at the pixel offset on screen.
 | ||||
| ///
 | ||||
| /// The screen will continuously overwrite more pixel data without regarding the offset, meaning
 | ||||
| /// once the starting row is full, overwriting will continue on column 0.
 | ||||
| ///
 | ||||
| /// The contained [SPBitVec] is always uncompressed.
 | ||||
| ///
 | ||||
| /// The passed [SPBitVec] gets consumed.
 | ||||
| ///
 | ||||
| /// Returns: a new [servicepoint::Command::BitmapLinear] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bit_vec` is null
 | ||||
| /// - when `compression_code` is not a valid value
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bit_vec` points to a valid instance of [SPBitVec]
 | ||||
| /// - `bit_vec` is not used concurrently or after this call
 | ||||
| /// - `compression` matches one of the allowed enum values
 | ||||
| /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_bitmap_linear( | ||||
|     offset: usize, | ||||
|     bit_vec: *mut SPBitVec, | ||||
|     compression: SPCompressionCode, | ||||
| ) -> NonNull<SPCommand> { | ||||
|     assert!(!bit_vec.is_null()); | ||||
|     let bit_vec = *Box::from_raw(bit_vec); | ||||
|     let result = Box::new(SPCommand(servicepoint::Command::BitmapLinear( | ||||
|         offset, | ||||
|         bit_vec.into(), | ||||
|         compression.try_into().expect("invalid compression code"), | ||||
|     ))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Set pixel data according to an and-mask starting at the offset.
 | ||||
| ///
 | ||||
| /// The screen will continuously overwrite more pixel data without regarding the offset, meaning
 | ||||
| /// once the starting row is full, overwriting will continue on column 0.
 | ||||
| ///
 | ||||
| /// The contained [SPBitVec] is always uncompressed.
 | ||||
| ///
 | ||||
| /// The passed [SPBitVec] gets consumed.
 | ||||
| ///
 | ||||
| /// Returns: a new [servicepoint::Command::BitmapLinearAnd] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bit_vec` is null
 | ||||
| /// - when `compression_code` is not a valid value
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bit_vec` points to a valid instance of [SPBitVec]
 | ||||
| /// - `bit_vec` is not used concurrently or after this call
 | ||||
| /// - `compression` matches one of the allowed enum values
 | ||||
| /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_bitmap_linear_and( | ||||
|     offset: usize, | ||||
|     bit_vec: *mut SPBitVec, | ||||
|     compression: SPCompressionCode, | ||||
| ) -> NonNull<SPCommand> { | ||||
|     assert!(!bit_vec.is_null()); | ||||
|     let bit_vec = *Box::from_raw(bit_vec); | ||||
|     let result = Box::new(SPCommand(servicepoint::Command::BitmapLinearAnd( | ||||
|         offset, | ||||
|         bit_vec.into(), | ||||
|         compression.try_into().expect("invalid compression code"), | ||||
|     ))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Set pixel data according to an or-mask starting at the offset.
 | ||||
| ///
 | ||||
| /// The screen will continuously overwrite more pixel data without regarding the offset, meaning
 | ||||
| /// once the starting row is full, overwriting will continue on column 0.
 | ||||
| ///
 | ||||
| /// The contained [SPBitVec] is always uncompressed.
 | ||||
| ///
 | ||||
| /// The passed [SPBitVec] gets consumed.
 | ||||
| ///
 | ||||
| /// Returns: a new [servicepoint::Command::BitmapLinearOr] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bit_vec` is null
 | ||||
| /// - when `compression_code` is not a valid value
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bit_vec` points to a valid instance of [SPBitVec]
 | ||||
| /// - `bit_vec` is not used concurrently or after this call
 | ||||
| /// - `compression` matches one of the allowed enum values
 | ||||
| /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_bitmap_linear_or( | ||||
|     offset: usize, | ||||
|     bit_vec: *mut SPBitVec, | ||||
|     compression: SPCompressionCode, | ||||
| ) -> NonNull<SPCommand> { | ||||
|     assert!(!bit_vec.is_null()); | ||||
|     let bit_vec = *Box::from_raw(bit_vec); | ||||
|     let result = Box::new(SPCommand(servicepoint::Command::BitmapLinearOr( | ||||
|         offset, | ||||
|         bit_vec.into(), | ||||
|         compression.try_into().expect("invalid compression code"), | ||||
|     ))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Set pixel data according to a xor-mask starting at the offset.
 | ||||
| ///
 | ||||
| /// The screen will continuously overwrite more pixel data without regarding the offset, meaning
 | ||||
| /// once the starting row is full, overwriting will continue on column 0.
 | ||||
| ///
 | ||||
| /// The contained [SPBitVec] is always uncompressed.
 | ||||
| ///
 | ||||
| /// The passed [SPBitVec] gets consumed.
 | ||||
| ///
 | ||||
| /// Returns: a new [servicepoint::Command::BitmapLinearXor] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bit_vec` is null
 | ||||
| /// - when `compression_code` is not a valid value
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bit_vec` points to a valid instance of [SPBitVec]
 | ||||
| /// - `bit_vec` is not used concurrently or after this call
 | ||||
| /// - `compression` matches one of the allowed enum values
 | ||||
| /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_bitmap_linear_xor( | ||||
|     offset: usize, | ||||
|     bit_vec: *mut SPBitVec, | ||||
|     compression: SPCompressionCode, | ||||
| ) -> NonNull<SPCommand> { | ||||
|     assert!(!bit_vec.is_null()); | ||||
|     let bit_vec = *Box::from_raw(bit_vec); | ||||
|     let result = Box::new(SPCommand(servicepoint::Command::BitmapLinearXor( | ||||
|         offset, | ||||
|         bit_vec.into(), | ||||
|         compression.try_into().expect("invalid compression code"), | ||||
|     ))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Show codepage 437 encoded text on the screen.
 | ||||
| ///
 | ||||
| /// The passed [SPCp437Grid] gets consumed.
 | ||||
| ///
 | ||||
| /// Returns: a new [servicepoint::Command::Cp437Data] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `grid` is null
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `grid` points to a valid instance of [SPCp437Grid]
 | ||||
| /// - `grid` is not used concurrently or after this call
 | ||||
| /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_cp437_data( | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     grid: *mut SPCp437Grid, | ||||
| ) -> NonNull<SPCommand> { | ||||
|     assert!(!grid.is_null()); | ||||
|     let grid = *Box::from_raw(grid); | ||||
|     let result = Box::new(SPCommand(servicepoint::Command::Cp437Data( | ||||
|         servicepoint::Origin::new(x, y), | ||||
|         grid.0, | ||||
|     ))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Show UTF-8 encoded text on the screen.
 | ||||
| ///
 | ||||
| /// The passed [SPCharGrid] gets consumed.
 | ||||
| ///
 | ||||
| /// Returns: a new [servicepoint::Command::Utf8Data] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `grid` is null
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `grid` points to a valid instance of [SPCharGrid]
 | ||||
| /// - `grid` is not used concurrently or after this call
 | ||||
| /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_utf8_data( | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     grid: *mut SPCharGrid, | ||||
| ) -> NonNull<SPCommand> { | ||||
|     assert!(!grid.is_null()); | ||||
|     let grid = *Box::from_raw(grid); | ||||
|     let result = Box::new(SPCommand(servicepoint::Command::Utf8Data( | ||||
|         servicepoint::Origin::new(x, y), | ||||
|         grid.0, | ||||
|     ))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Sets a window of pixels to the specified values.
 | ||||
| ///
 | ||||
| /// The passed [SPBitmap] gets consumed.
 | ||||
| ///
 | ||||
| /// Returns: a new [servicepoint::Command::BitmapLinearWin] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bitmap` is null
 | ||||
| /// - when `compression_code` is not a valid value
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bitmap` points to a valid instance of [SPBitmap]
 | ||||
| /// - `bitmap` is not used concurrently or after this call
 | ||||
| /// - `compression` matches one of the allowed enum values
 | ||||
| /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_bitmap_linear_win( | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     bitmap: *mut SPBitmap, | ||||
|     compression_code: SPCompressionCode, | ||||
| ) -> NonNull<SPCommand> { | ||||
|     assert!(!bitmap.is_null()); | ||||
|     let byte_grid = (*Box::from_raw(bitmap)).0; | ||||
|     let result = Box::new(SPCommand(servicepoint::Command::BitmapLinearWin( | ||||
|         servicepoint::Origin::new(x, y), | ||||
|         byte_grid, | ||||
|         compression_code | ||||
|             .try_into() | ||||
|             .expect("invalid compression code"), | ||||
|     ))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Deallocates a [SPCommand].
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```C
 | ||||
| /// SPCommand c = sp_command_clear();
 | ||||
| /// sp_command_free(c);
 | ||||
| /// ```
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `command` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `command` points to a valid [SPCommand]
 | ||||
| /// - `command` is not used concurrently or after this call
 | ||||
| /// - `command` was not passed to another consuming function, e.g. to create a [SPPacket]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_free(command: *mut SPCommand) { | ||||
|     assert!(!command.is_null()); | ||||
|     _ = Box::from_raw(command); | ||||
| } | ||||
|  | @ -1,139 +0,0 @@ | |||
| //! C functions for interacting with [SPConnection]s
 | ||||
| //!
 | ||||
| //! prefix `sp_connection_`
 | ||||
| 
 | ||||
| use std::ffi::{c_char, CStr}; | ||||
| use std::ptr::{null_mut, NonNull}; | ||||
| 
 | ||||
| use crate::{SPCommand, SPPacket}; | ||||
| 
 | ||||
| /// A connection to the display.
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```C
 | ||||
| /// CConnection connection = sp_connection_open("172.23.42.29:2342");
 | ||||
| /// if (connection != NULL)
 | ||||
| ///     sp_connection_send_command(connection, sp_command_clear());
 | ||||
| /// ```
 | ||||
| pub struct SPConnection(pub(crate) servicepoint::Connection); | ||||
| 
 | ||||
| /// Creates a new instance of [SPConnection].
 | ||||
| ///
 | ||||
| /// returns: NULL if connection fails, or connected instance
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `host` is null or an invalid host
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_connection_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_connection_open( | ||||
|     host: *const c_char, | ||||
| ) -> *mut SPConnection { | ||||
|     assert!(!host.is_null()); | ||||
|     let host = CStr::from_ptr(host).to_str().expect("Bad encoding"); | ||||
|     let connection = match servicepoint::Connection::open(host) { | ||||
|         Err(_) => return null_mut(), | ||||
|         Ok(value) => value, | ||||
|     }; | ||||
| 
 | ||||
|     Box::into_raw(Box::new(SPConnection(connection))) | ||||
| } | ||||
| 
 | ||||
| /// Creates a new instance of [SPConnection] for testing that does not actually send anything.
 | ||||
| ///
 | ||||
| /// returns: a new instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_connection_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_connection_fake() -> NonNull<SPConnection> { | ||||
|     let result = Box::new(SPConnection(servicepoint::Connection::Fake)); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Sends a [SPPacket] to the display using the [SPConnection].
 | ||||
| ///
 | ||||
| /// The passed `packet` gets consumed.
 | ||||
| ///
 | ||||
| /// returns: true in case of success
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `connection` is NULL
 | ||||
| /// - when `packet` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `connection` points to a valid instance of [SPConnection]
 | ||||
| /// - `packet` points to a valid instance of [SPPacket]
 | ||||
| /// - `packet` is not used concurrently or after this call
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_connection_send_packet( | ||||
|     connection: *const SPConnection, | ||||
|     packet: *mut SPPacket, | ||||
| ) -> bool { | ||||
|     assert!(!connection.is_null()); | ||||
|     assert!(!packet.is_null()); | ||||
|     let packet = Box::from_raw(packet); | ||||
|     (*connection).0.send((*packet).0).is_ok() | ||||
| } | ||||
| 
 | ||||
| /// Sends a [SPCommand] to the display using the [SPConnection].
 | ||||
| ///
 | ||||
| /// The passed `command` gets consumed.
 | ||||
| ///
 | ||||
| /// returns: true in case of success
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `connection` is NULL
 | ||||
| /// - when `command` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `connection` points to a valid instance of [SPConnection]
 | ||||
| /// - `command` points to a valid instance of [SPPacket]
 | ||||
| /// - `command` is not used concurrently or after this call
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_connection_send_command( | ||||
|     connection: *const SPConnection, | ||||
|     command: *mut SPCommand, | ||||
| ) -> bool { | ||||
|     assert!(!connection.is_null()); | ||||
|     assert!(!command.is_null()); | ||||
|     let command = (*Box::from_raw(command)).0; | ||||
|     (*connection).0.send(command).is_ok() | ||||
| } | ||||
| 
 | ||||
| /// Closes and deallocates a [SPConnection].
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `connection` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `connection` points to a valid [SPConnection]
 | ||||
| /// - `connection` is not used concurrently or after this call
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_connection_free(connection: *mut SPConnection) { | ||||
|     assert!(!connection.is_null()); | ||||
|     _ = Box::from_raw(connection); | ||||
| } | ||||
|  | @ -1,48 +0,0 @@ | |||
| //! re-exported constants for use in C
 | ||||
| 
 | ||||
| use servicepoint::CompressionCode; | ||||
| use std::time::Duration; | ||||
| 
 | ||||
| /// size of a single tile in one dimension
 | ||||
| pub const SP_TILE_SIZE: usize = 8; | ||||
| 
 | ||||
| /// Display tile count in the x-direction
 | ||||
| pub const SP_TILE_WIDTH: usize = 56; | ||||
| 
 | ||||
| /// Display tile count in the y-direction
 | ||||
| pub const SP_TILE_HEIGHT: usize = 20; | ||||
| 
 | ||||
| /// Display width in pixels
 | ||||
| pub const SP_PIXEL_WIDTH: usize = SP_TILE_WIDTH * SP_TILE_SIZE; | ||||
| 
 | ||||
| /// Display height in pixels
 | ||||
| pub const SP_PIXEL_HEIGHT: usize = SP_TILE_HEIGHT * SP_TILE_SIZE; | ||||
| 
 | ||||
| /// pixel count on whole screen
 | ||||
| pub const SP_PIXEL_COUNT: usize = SP_PIXEL_WIDTH * SP_PIXEL_HEIGHT; | ||||
| 
 | ||||
| /// Actual hardware limit is around 28-29ms/frame. Rounded up for less dropped packets.
 | ||||
| pub const SP_FRAME_PACING_MS: u128 = Duration::from_millis(30).as_millis(); | ||||
| 
 | ||||
| /// Specifies the kind of compression to use.
 | ||||
| #[repr(u16)] | ||||
| pub enum SPCompressionCode { | ||||
|     /// no compression
 | ||||
|     Uncompressed = 0x0, | ||||
|     /// compress using flate2 with zlib header
 | ||||
|     Zlib = 0x677a, | ||||
|     /// compress using bzip2
 | ||||
|     Bzip2 = 0x627a, | ||||
|     /// compress using lzma
 | ||||
|     Lzma = 0x6c7a, | ||||
|     /// compress using Zstandard
 | ||||
|     Zstd = 0x7a73, | ||||
| } | ||||
| 
 | ||||
| impl TryFrom<SPCompressionCode> for CompressionCode { | ||||
|     type Error = (); | ||||
| 
 | ||||
|     fn try_from(value: SPCompressionCode) -> Result<Self, Self::Error> { | ||||
|         CompressionCode::try_from(value as u16) | ||||
|     } | ||||
| } | ||||
|  | @ -1,127 +1,48 @@ | |||
| //! C functions for interacting with [SPCp437Grid]s
 | ||||
| //!
 | ||||
| //! prefix `sp_cp437_grid_`
 | ||||
| 
 | ||||
| use crate::SPByteSlice; | ||||
| use servicepoint::{DataRef, Grid}; | ||||
| use crate::{heap_drop, heap_move, heap_move_nonnull, heap_remove, ByteSlice}; | ||||
| use servicepoint::{ | ||||
|     Cp437Grid, Cp437GridCommand, DataRef, Grid, Origin, Packet, | ||||
| }; | ||||
| use std::ptr::NonNull; | ||||
| 
 | ||||
| /// A C-wrapper for grid containing codepage 437 characters.
 | ||||
| /// Creates a new [Cp437Grid] with the specified dimensions.
 | ||||
| ///
 | ||||
| /// The encoding is currently not enforced.
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```C
 | ||||
| /// Cp437Grid grid = sp_cp437_grid_new(4, 3);
 | ||||
| /// sp_cp437_grid_fill(grid, '?');
 | ||||
| /// sp_cp437_grid_set(grid, 0, 0, '!');
 | ||||
| /// sp_cp437_grid_free(grid);
 | ||||
| /// ```
 | ||||
| pub struct SPCp437Grid(pub(crate) servicepoint::Cp437Grid); | ||||
| 
 | ||||
| impl Clone for SPCp437Grid { | ||||
|     fn clone(&self) -> Self { | ||||
|         SPCp437Grid(self.0.clone()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Creates a new [SPCp437Grid] with the specified dimensions.
 | ||||
| ///
 | ||||
| /// returns: [SPCp437Grid] initialized to 0. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_cp437_grid_free`.
 | ||||
| /// returns: [Cp437Grid] initialized to 0.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_new( | ||||
|     width: usize, | ||||
|     height: usize, | ||||
| ) -> NonNull<SPCp437Grid> { | ||||
|     let result = | ||||
|         Box::new(SPCp437Grid(servicepoint::Cp437Grid::new(width, height))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| ) -> NonNull<Cp437Grid> { | ||||
|     heap_move_nonnull(Cp437Grid::new(width, height)) | ||||
| } | ||||
| 
 | ||||
| /// Loads a [SPCp437Grid] with the specified dimensions from the provided data.
 | ||||
| ///
 | ||||
| /// Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `data` is NULL
 | ||||
| /// - when the provided `data_length` does not match `height` and `width`
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `data` points to a valid memory location of at least `data_length`
 | ||||
| ///   bytes in size.
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_cp437_grid_free`.
 | ||||
| /// Loads a [Cp437Grid] with the specified dimensions from the provided data.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_load( | ||||
|     width: usize, | ||||
|     height: usize, | ||||
|     data: *const u8, | ||||
|     data_length: usize, | ||||
| ) -> NonNull<SPCp437Grid> { | ||||
|     assert!(data.is_null()); | ||||
|     let data = std::slice::from_raw_parts(data, data_length); | ||||
|     let result = Box::new(SPCp437Grid(servicepoint::Cp437Grid::load( | ||||
|         width, height, data, | ||||
|     ))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
|     data: ByteSlice, | ||||
| ) -> *mut Cp437Grid { | ||||
|     let data = unsafe { data.as_slice() }; | ||||
|     let grid = Cp437Grid::load(width, height, data); | ||||
|     if let Some(grid) = grid { | ||||
|         heap_move(grid) | ||||
|     } else { | ||||
|         std::ptr::null_mut() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Clones a [SPCp437Grid].
 | ||||
| ///
 | ||||
| /// Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `cp437_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `cp437_grid` points to a valid [SPCp437Grid]
 | ||||
| /// - `cp437_grid` is not written to concurrently
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_cp437_grid_free`.
 | ||||
| /// Clones a [Cp437Grid].
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_clone( | ||||
|     cp437_grid: *const SPCp437Grid, | ||||
| ) -> NonNull<SPCp437Grid> { | ||||
|     assert!(!cp437_grid.is_null()); | ||||
|     let result = Box::new((*cp437_grid).clone()); | ||||
|     NonNull::from(Box::leak(result)) | ||||
|     cp437_grid: NonNull<Cp437Grid>, | ||||
| ) -> NonNull<Cp437Grid> { | ||||
|     heap_move_nonnull(unsafe { cp437_grid.as_ref().clone() }) | ||||
| } | ||||
| 
 | ||||
| /// Deallocates a [SPCp437Grid].
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `cp437_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `cp437_grid` points to a valid [SPCp437Grid]
 | ||||
| /// - `cp437_grid` is not used concurrently or after cp437_grid call
 | ||||
| /// - `cp437_grid` was not passed to another consuming function, e.g. to create a [SPCommand]
 | ||||
| ///
 | ||||
| /// [SPCommand]: [crate::SPCommand]
 | ||||
| /// Deallocates a [Cp437Grid].
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_free(cp437_grid: *mut SPCp437Grid) { | ||||
|     assert!(!cp437_grid.is_null()); | ||||
|     _ = Box::from_raw(cp437_grid); | ||||
| pub unsafe extern "C" fn sp_cp437_grid_free(cp437_grid: NonNull<Cp437Grid>) { | ||||
|     unsafe { heap_drop(cp437_grid) } | ||||
| } | ||||
| 
 | ||||
| /// Gets the current value at the specified position.
 | ||||
|  | @ -133,26 +54,17 @@ pub unsafe extern "C" fn sp_cp437_grid_free(cp437_grid: *mut SPCp437Grid) { | |||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `cp437_grid` is NULL
 | ||||
| /// - when accessing `x` or `y` out of bounds
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `cp437_grid` points to a valid [SPCp437Grid]
 | ||||
| /// - `cp437_grid` is not written to concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_get( | ||||
|     cp437_grid: *const SPCp437Grid, | ||||
|     cp437_grid: NonNull<Cp437Grid>, | ||||
|     x: usize, | ||||
|     y: usize, | ||||
| ) -> u8 { | ||||
|     assert!(!cp437_grid.is_null()); | ||||
|     (*cp437_grid).0.get(x, y) | ||||
|     unsafe { cp437_grid.as_ref().get(x, y) } | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of the specified position in the [SPCp437Grid].
 | ||||
| /// Sets the value of the specified position in the [Cp437Grid].
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
|  | @ -164,122 +76,82 @@ pub unsafe extern "C" fn sp_cp437_grid_get( | |||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `cp437_grid` is NULL
 | ||||
| /// - when accessing `x` or `y` out of bounds
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `cp437_grid` points to a valid [SPBitVec]
 | ||||
| /// - `cp437_grid` is not written to or read from concurrently
 | ||||
| ///
 | ||||
| /// [SPBitVec]: [crate::SPBitVec]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_set( | ||||
|     cp437_grid: *mut SPCp437Grid, | ||||
|     cp437_grid: NonNull<Cp437Grid>, | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     value: u8, | ||||
| ) { | ||||
|     assert!(!cp437_grid.is_null()); | ||||
|     (*cp437_grid).0.set(x, y, value); | ||||
|     unsafe { (*cp437_grid.as_ptr()).set(x, y, value) }; | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of all cells in the [SPCp437Grid].
 | ||||
| /// Sets the value of all cells in the [Cp437Grid].
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `cp437_grid`: instance to write to
 | ||||
| /// - `value`: the value to set all cells to
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `cp437_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `cp437_grid` points to a valid [SPCp437Grid]
 | ||||
| /// - `cp437_grid` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_fill( | ||||
|     cp437_grid: *mut SPCp437Grid, | ||||
|     cp437_grid: NonNull<Cp437Grid>, | ||||
|     value: u8, | ||||
| ) { | ||||
|     assert!(!cp437_grid.is_null()); | ||||
|     (*cp437_grid).0.fill(value); | ||||
|     unsafe { (*cp437_grid.as_ptr()).fill(value) }; | ||||
| } | ||||
| 
 | ||||
| /// Gets the width of the [SPCp437Grid] instance.
 | ||||
| /// Gets the width of the [Cp437Grid] instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `cp437_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `cp437_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `cp437_grid` points to a valid [SPCp437Grid]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_width( | ||||
|     cp437_grid: *const SPCp437Grid, | ||||
|     cp437_grid: NonNull<Cp437Grid>, | ||||
| ) -> usize { | ||||
|     assert!(!cp437_grid.is_null()); | ||||
|     (*cp437_grid).0.width() | ||||
|     unsafe { cp437_grid.as_ref().width() } | ||||
| } | ||||
| 
 | ||||
| /// Gets the height of the [SPCp437Grid] instance.
 | ||||
| /// Gets the height of the [Cp437Grid] instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `cp437_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `cp437_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `cp437_grid` points to a valid [SPCp437Grid]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_height( | ||||
|     cp437_grid: *const SPCp437Grid, | ||||
|     cp437_grid: NonNull<Cp437Grid>, | ||||
| ) -> usize { | ||||
|     assert!(!cp437_grid.is_null()); | ||||
|     (*cp437_grid).0.height() | ||||
|     unsafe { cp437_grid.as_ref().height() } | ||||
| } | ||||
| 
 | ||||
| /// Gets an unsafe reference to the data of the [SPCp437Grid] instance.
 | ||||
| /// Gets an unsafe reference to the data of the [Cp437Grid] instance.
 | ||||
| ///
 | ||||
| /// Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `cp437_grid` is NULL
 | ||||
| ///
 | ||||
| /// ## Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `cp437_grid` points to a valid [SPCp437Grid]
 | ||||
| /// - the returned memory range is never accessed after the passed [SPCp437Grid] has been freed
 | ||||
| /// - the returned memory range is never accessed concurrently, either via the [SPCp437Grid] or directly
 | ||||
| /// The returned memory is valid for the lifetime of the grid.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_unsafe_data_ref( | ||||
|     cp437_grid: *mut SPCp437Grid, | ||||
| ) -> SPByteSlice { | ||||
|     let data = (*cp437_grid).0.data_ref_mut(); | ||||
|     SPByteSlice { | ||||
|         start: NonNull::new(data.as_mut_ptr_range().start).unwrap(), | ||||
|         length: data.len(), | ||||
|     cp437_grid: NonNull<Cp437Grid>, | ||||
| ) -> ByteSlice { | ||||
|     unsafe { ByteSlice::from_slice((*cp437_grid.as_ptr()).data_ref_mut()) } | ||||
| } | ||||
| 
 | ||||
| /// Creates a [Cp437GridCommand] and immediately turns that into a [Packet].
 | ||||
| ///
 | ||||
| /// The provided [Cp437Grid] gets consumed.
 | ||||
| ///
 | ||||
| /// Returns NULL in case of an error.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_into_packet( | ||||
|     grid: NonNull<Cp437Grid>, | ||||
|     x: usize, | ||||
|     y: usize, | ||||
| ) -> *mut Packet { | ||||
|     let grid = unsafe { heap_remove(grid) }; | ||||
|     match Packet::try_from(Cp437GridCommand { | ||||
|         grid, | ||||
|         origin: Origin::new(x, y), | ||||
|     }) { | ||||
|         Ok(packet) => heap_move(packet), | ||||
|         Err(_) => std::ptr::null_mut(), | ||||
|     } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										46
									
								
								src/lib.rs
									
										
									
									
									
								
							
							
						
						
									
										46
									
								
								src/lib.rs
									
										
									
									
									
								
							|  | @ -9,18 +9,18 @@ | |||
| //! #include "servicepoint.h"
 | ||||
| //!
 | ||||
| //! int main(void) {
 | ||||
| //!     SPConnection *connection = sp_connection_open("172.23.42.29:2342");
 | ||||
| //!     UdpConnection *connection = sp_udp_open("172.23.42.29:2342");
 | ||||
| //!     if (connection == NULL)
 | ||||
| //!         return 1;
 | ||||
| //!
 | ||||
| //!     SPBitmap *pixels = sp_bitmap_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT);
 | ||||
| //!     Bitmap *pixels = sp_bitmap_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT);
 | ||||
| //!     sp_bitmap_fill(pixels, true);
 | ||||
| //!
 | ||||
| //!     SPCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, Uncompressed);
 | ||||
| //!     while (sp_connection_send_command(connection, sp_command_clone(command)));
 | ||||
| //!     TypedCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, Uncompressed);
 | ||||
| //!     while (sp_udp_send_command(connection, sp_command_clone(command)));
 | ||||
| //!
 | ||||
| //!     sp_command_free(command);
 | ||||
| //!     sp_connection_free(connection);
 | ||||
| //!     sp_udp_free(connection);
 | ||||
| //!     return 0;
 | ||||
| //! }
 | ||||
| //! ```
 | ||||
|  | @ -30,19 +30,43 @@ pub use crate::bitvec::*; | |||
| pub use crate::brightness_grid::*; | ||||
| pub use crate::byte_slice::*; | ||||
| pub use crate::char_grid::*; | ||||
| pub use crate::command::*; | ||||
| pub use crate::connection::*; | ||||
| pub use crate::constants::*; | ||||
| pub use crate::cp437_grid::*; | ||||
| pub use crate::packet::*; | ||||
| pub use crate::typed_command::*; | ||||
| pub use crate::udp::*; | ||||
| pub use servicepoint::CommandCode; | ||||
| use std::ptr::NonNull; | ||||
| 
 | ||||
| mod bitmap; | ||||
| mod bitvec; | ||||
| mod brightness_grid; | ||||
| mod byte_slice; | ||||
| mod char_grid; | ||||
| mod command; | ||||
| mod connection; | ||||
| mod constants; | ||||
| mod cp437_grid; | ||||
| mod packet; | ||||
| mod typed_command; | ||||
| mod udp; | ||||
| 
 | ||||
| use std::time::Duration; | ||||
| 
 | ||||
| /// Actual hardware limit is around 28-29ms/frame. Rounded up for less dropped packets.
 | ||||
| pub const SP_FRAME_PACING_MS: u128 = Duration::from_millis(30).as_millis(); | ||||
| 
 | ||||
| pub(crate) fn heap_move<T>(x: T) -> *mut T { | ||||
|     Box::into_raw(Box::new(x)) | ||||
| } | ||||
| 
 | ||||
| pub(crate) fn heap_move_nonnull<T>(x: T) -> NonNull<T> { | ||||
|     NonNull::from(Box::leak(Box::new(x))) | ||||
| } | ||||
| 
 | ||||
| pub(crate) unsafe fn heap_drop<T>(x: NonNull<T>) { | ||||
|     drop(unsafe { heap_remove(x) }); | ||||
| } | ||||
| 
 | ||||
| pub(crate) unsafe fn heap_remove<T>(x: NonNull<T>) -> T { | ||||
|     unsafe { *Box::from_raw(x.as_ptr()) } | ||||
| } | ||||
| 
 | ||||
| /// This is a type only used by cbindgen to have a type for pointers.
 | ||||
| pub struct UdpSocket; | ||||
|  |  | |||
							
								
								
									
										234
									
								
								src/packet.rs
									
										
									
									
									
								
							
							
						
						
									
										234
									
								
								src/packet.rs
									
										
									
									
									
								
							|  | @ -1,166 +1,128 @@ | |||
| //! C functions for interacting with [SPPacket]s
 | ||||
| //!
 | ||||
| //! prefix `sp_packet_`
 | ||||
| use crate::{heap_drop, heap_move, heap_move_nonnull, heap_remove, ByteSlice}; | ||||
| use servicepoint::{CommandCode, Header, Packet, TypedCommand}; | ||||
| use std::ptr::NonNull; | ||||
| 
 | ||||
| use std::ptr::{null_mut, NonNull}; | ||||
| 
 | ||||
| use crate::SPCommand; | ||||
| 
 | ||||
| /// The raw packet
 | ||||
| pub struct SPPacket(pub(crate) servicepoint::Packet); | ||||
| 
 | ||||
| /// Turns a [SPCommand] into a [SPPacket].
 | ||||
| /// The [SPCommand] gets consumed.
 | ||||
| /// Turns a [TypedCommand] into a [Packet].
 | ||||
| /// The [TypedCommand] gets consumed.
 | ||||
| ///
 | ||||
| /// Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `command` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - [SPCommand] points to a valid instance of [SPCommand]
 | ||||
| /// - [SPCommand] is not used concurrently or after this call
 | ||||
| /// - the returned [SPPacket] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_packet_free`.
 | ||||
| /// Returns NULL in case of an error.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_packet_from_command( | ||||
|     command: *mut SPCommand, | ||||
| ) -> NonNull<SPPacket> { | ||||
|     assert!(!command.is_null()); | ||||
|     let command = *Box::from_raw(command); | ||||
|     let result = Box::new(SPPacket(command.0.into())); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Tries to load a [SPPacket] from the passed array with the specified length.
 | ||||
| ///
 | ||||
| /// returns: NULL in case of an error, pointer to the allocated packet otherwise
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `data` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `data` points to a valid memory region of at least `length` bytes
 | ||||
| /// - `data` is not written to concurrently
 | ||||
| /// - the returned [SPPacket] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_packet_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_packet_try_load( | ||||
|     data: *const u8, | ||||
|     length: usize, | ||||
| ) -> *mut SPPacket { | ||||
|     assert!(!data.is_null()); | ||||
|     let data = std::slice::from_raw_parts(data, length); | ||||
|     match servicepoint::Packet::try_from(data) { | ||||
|         Err(_) => null_mut(), | ||||
|         Ok(packet) => Box::into_raw(Box::new(SPPacket(packet))), | ||||
|     command: NonNull<TypedCommand>, | ||||
| ) -> *mut Packet { | ||||
|     let command = unsafe { heap_remove(command) }; | ||||
|     if let Ok(packet) = command.try_into() { | ||||
|         heap_move(packet) | ||||
|     } else { | ||||
|         std::ptr::null_mut() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Creates a raw [SPPacket] from parts.
 | ||||
| /// Tries to load a [Packet] from the passed array with the specified length.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `command_code` specifies which command this packet contains
 | ||||
| /// - `a`, `b`, `c` and `d` are command-specific header values
 | ||||
| /// - `payload` is the optional data that is part of the command
 | ||||
| /// - `payload_len` is the size of the payload
 | ||||
| /// returns: NULL in case of an error, pointer to the allocated packet otherwise
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_packet_try_load(data: ByteSlice) -> *mut Packet { | ||||
|     let data = unsafe { data.as_slice() }; | ||||
|     match servicepoint::Packet::try_from(data) { | ||||
|         Err(_) => std::ptr::null_mut(), | ||||
|         Ok(packet) => heap_move(packet), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Creates a raw [Packet] from parts.
 | ||||
| ///
 | ||||
| /// returns: new instance. Will never return null.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `payload` is null, but `payload_len` is not zero
 | ||||
| /// - when `payload_len` is zero, but `payload` is nonnull
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `payload` points to a valid memory region of at least `payload_len` bytes
 | ||||
| /// - `payload` is not written to concurrently
 | ||||
| /// - the returned [SPPacket] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling [sp_packet_free].
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_packet_from_parts( | ||||
|     command_code: u16, | ||||
|     a: u16, | ||||
|     b: u16, | ||||
|     c: u16, | ||||
|     d: u16, | ||||
|     payload: *const u8, | ||||
|     payload_len: usize, | ||||
| ) -> NonNull<SPPacket> { | ||||
|     assert_eq!(payload.is_null(), payload_len == 0); | ||||
| 
 | ||||
|     header: Header, | ||||
|     payload: *const ByteSlice, | ||||
| ) -> NonNull<Packet> { | ||||
|     let payload = if payload.is_null() { | ||||
|         vec![] | ||||
|     } else { | ||||
|         let payload = std::slice::from_raw_parts(payload, payload_len); | ||||
|         let payload = unsafe { (*payload).as_slice() }; | ||||
|         Vec::from(payload) | ||||
|     }; | ||||
| 
 | ||||
|     let packet = servicepoint::Packet { | ||||
|         header: servicepoint::Header { | ||||
|             command_code, | ||||
|             a, | ||||
|             b, | ||||
|             c, | ||||
|             d, | ||||
|         }, | ||||
|         payload, | ||||
|     }; | ||||
|     let result = Box::new(SPPacket(packet)); | ||||
|     NonNull::from(Box::leak(result)) | ||||
|     heap_move_nonnull(Packet { header, payload }) | ||||
| } | ||||
| 
 | ||||
| /// Clones a [SPPacket].
 | ||||
| /// Returns a pointer to the header field of the provided packet.
 | ||||
| ///
 | ||||
| /// Will never return NULL.
 | ||||
| /// The returned header can be changed and will be valid for the lifetime of the packet.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_packet_get_header( | ||||
|     packet: NonNull<Packet>, | ||||
| ) -> NonNull<Header> { | ||||
|     NonNull::from(&mut unsafe { (*packet.as_ptr()).header }) | ||||
| } | ||||
| 
 | ||||
| /// Returns a pointer to the current payload of the provided packet.
 | ||||
| ///
 | ||||
| /// The returned memory can be changed and will be valid until a new payload is set.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_packet_get_payload( | ||||
|     packet: NonNull<Packet>, | ||||
| ) -> ByteSlice { | ||||
|     unsafe { ByteSlice::from_slice(&mut (*packet.as_ptr()).payload) } | ||||
| } | ||||
| 
 | ||||
| /// Sets the payload of the provided packet to the provided data.
 | ||||
| ///
 | ||||
| /// This makes previous payload pointers invalid.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_packet_set_payload( | ||||
|     packet: NonNull<Packet>, | ||||
|     data: ByteSlice, | ||||
| ) { | ||||
|     unsafe { (*packet.as_ptr()).payload = data.as_slice().to_vec() } | ||||
| } | ||||
| 
 | ||||
| /// Serialize the packet into the provided buffer.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `packet` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `packet` points to a valid [SPPacket]
 | ||||
| /// - `packet` is not written to concurrently
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_packet_free`.
 | ||||
| /// - if the buffer is not big enough to hold header+payload.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_packet_serialize_to( | ||||
|     packet: NonNull<Packet>, | ||||
|     mut buffer: ByteSlice, | ||||
| ) { | ||||
|     unsafe { | ||||
|         packet.as_ref().serialize_to(buffer.as_slice_mut()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Clones a [Packet].
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_packet_clone( | ||||
|     packet: *const SPPacket, | ||||
| ) -> NonNull<SPPacket> { | ||||
|     assert!(!packet.is_null()); | ||||
|     let result = Box::new(SPPacket((*packet).0.clone())); | ||||
|     NonNull::from(Box::leak(result)) | ||||
|     packet: NonNull<Packet>, | ||||
| ) -> NonNull<Packet> { | ||||
|     heap_move_nonnull(unsafe { packet.as_ref().clone() }) | ||||
| } | ||||
| 
 | ||||
| /// Deallocates a [SPPacket].
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `packet` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `packet` points to a valid [SPPacket]
 | ||||
| /// - `packet` is not used concurrently or after this call
 | ||||
| /// Deallocates a [Packet].
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_packet_free(packet: *mut SPPacket) { | ||||
|     assert!(!packet.is_null()); | ||||
|     _ = Box::from_raw(packet) | ||||
| pub unsafe extern "C" fn sp_packet_free(packet: NonNull<Packet>) { | ||||
|     unsafe { heap_drop(packet) } | ||||
| } | ||||
| 
 | ||||
| /// Converts u16 into [CommandCode].
 | ||||
| ///
 | ||||
| /// If the provided value is not valid, false is returned and result is not changed.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_u16_to_command_code( | ||||
|     code: u16, | ||||
|     result: *mut CommandCode, | ||||
| ) -> bool { | ||||
|     match CommandCode::try_from(code) { | ||||
|         Ok(code) => { | ||||
|             unsafe { | ||||
|                 *result = code; | ||||
|             } | ||||
|             true | ||||
|         } | ||||
|         Err(_) => false, | ||||
|     } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										201
									
								
								src/typed_command.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								src/typed_command.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,201 @@ | |||
| use crate::{heap_drop, heap_move, heap_move_nonnull, SPBitVec}; | ||||
| use servicepoint::{ | ||||
|     BinaryOperation, Bitmap, Brightness, BrightnessGrid, CharGrid, | ||||
|     CompressionCode, Cp437Grid, GlobalBrightnessCommand, Packet, TypedCommand, | ||||
| }; | ||||
| use std::ptr::NonNull; | ||||
| 
 | ||||
| /// Tries to turn a [Packet] into a [TypedCommand].
 | ||||
| ///
 | ||||
| /// The packet is deallocated in the process.
 | ||||
| ///
 | ||||
| /// Returns: pointer to new [TypedCommand] instance or NULL if parsing failed.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_try_from_packet( | ||||
|     packet: NonNull<Packet>, | ||||
| ) -> *mut TypedCommand { | ||||
|     let packet = *unsafe { Box::from_raw(packet.as_ptr()) }; | ||||
|     match servicepoint::TypedCommand::try_from(packet) { | ||||
|         Err(_) => std::ptr::null_mut(), | ||||
|         Ok(command) => heap_move(command), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Clones a [TypedCommand] instance.
 | ||||
| ///
 | ||||
| /// returns: new [TypedCommand] instance.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_clone( | ||||
|     command: NonNull<TypedCommand>, | ||||
| ) -> NonNull<TypedCommand> { | ||||
|     heap_move_nonnull(unsafe { command.as_ref().clone() }) | ||||
| } | ||||
| 
 | ||||
| /// Set all pixels to the off state.
 | ||||
| ///
 | ||||
| /// Does not affect brightness.
 | ||||
| ///
 | ||||
| /// Returns: a new [servicepoint::Command::Clear] instance.
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```C
 | ||||
| /// sp_udp_send_command(connection, sp_command_clear());
 | ||||
| /// ```
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_clear() -> NonNull<TypedCommand> { | ||||
|     heap_move_nonnull(servicepoint::ClearCommand.into()) | ||||
| } | ||||
| 
 | ||||
| /// Kills the udp daemon on the display, which usually results in a restart.
 | ||||
| ///
 | ||||
| /// Please do not send this in your normal program flow.
 | ||||
| ///
 | ||||
| /// Returns: a new [servicepoint::Command::HardReset] instance.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_hard_reset() -> NonNull<TypedCommand> { | ||||
|     heap_move_nonnull(servicepoint::HardResetCommand.into()) | ||||
| } | ||||
| 
 | ||||
| /// A yet-to-be-tested command.
 | ||||
| ///
 | ||||
| /// Returns: a new [servicepoint::Command::FadeOut] instance.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_fade_out() -> NonNull<TypedCommand> { | ||||
|     heap_move_nonnull(servicepoint::FadeOutCommand.into()) | ||||
| } | ||||
| 
 | ||||
| /// Set the brightness of all tiles to the same value.
 | ||||
| ///
 | ||||
| /// Returns: a new [servicepoint::Command::Brightness] instance.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_global_brightness( | ||||
|     brightness: Brightness, | ||||
| ) -> NonNull<TypedCommand> { | ||||
|     heap_move_nonnull(GlobalBrightnessCommand::from(brightness).into()) | ||||
| } | ||||
| 
 | ||||
| /// Set the brightness of individual tiles in a rectangular area of the display.
 | ||||
| ///
 | ||||
| /// The passed [BrightnessGrid] gets consumed.
 | ||||
| ///
 | ||||
| /// Returns: a new [servicepoint::Command::CharBrightness] instance.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_brightness_grid( | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     grid: NonNull<BrightnessGrid>, | ||||
| ) -> NonNull<TypedCommand> { | ||||
|     let grid = unsafe { *Box::from_raw(grid.as_ptr()) }; | ||||
|     let result = servicepoint::BrightnessGridCommand { | ||||
|         origin: servicepoint::Origin::new(x, y), | ||||
|         grid, | ||||
|     } | ||||
|     .into(); | ||||
|     heap_move_nonnull(result) | ||||
| } | ||||
| 
 | ||||
| /// Set pixel data starting at the pixel offset on screen.
 | ||||
| ///
 | ||||
| /// The screen will continuously overwrite more pixel data without regarding the offset, meaning
 | ||||
| /// once the starting row is full, overwriting will continue on column 0.
 | ||||
| ///
 | ||||
| /// The [`BinaryOperation`] will be applied on the display comparing old and sent bit.
 | ||||
| ///
 | ||||
| /// `new_bit = old_bit op sent_bit`
 | ||||
| ///
 | ||||
| /// For example, [`BinaryOperation::Or`] can be used to turn on some pixels without affecting other pixels.
 | ||||
| ///
 | ||||
| /// The contained [`BitVecU8Msb0`] is always uncompressed.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_bitvec( | ||||
|     offset: usize, | ||||
|     bit_vec: NonNull<SPBitVec>, | ||||
|     compression: CompressionCode, | ||||
|     operation: BinaryOperation, | ||||
| ) -> *mut TypedCommand { | ||||
|     let bit_vec = unsafe { *Box::from_raw(bit_vec.as_ptr()) }; | ||||
|     let command = servicepoint::BitVecCommand { | ||||
|         offset, | ||||
|         operation, | ||||
|         bitvec: bit_vec.0, | ||||
|         compression, | ||||
|     } | ||||
|     .into(); | ||||
|     heap_move(command) | ||||
| } | ||||
| 
 | ||||
| /// Show codepage 437 encoded text on the screen.
 | ||||
| ///
 | ||||
| /// The passed [Cp437Grid] gets consumed.
 | ||||
| ///
 | ||||
| /// Returns: a new [servicepoint::Cp437GridCommand] instance.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_cp437_grid( | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     grid: NonNull<Cp437Grid>, | ||||
| ) -> NonNull<TypedCommand> { | ||||
|     let grid = *unsafe { Box::from_raw(grid.as_ptr()) }; | ||||
|     let result = servicepoint::Cp437GridCommand { | ||||
|         origin: servicepoint::Origin::new(x, y), | ||||
|         grid, | ||||
|     } | ||||
|     .into(); | ||||
|     heap_move_nonnull(result) | ||||
| } | ||||
| 
 | ||||
| /// Show UTF-8 encoded text on the screen.
 | ||||
| ///
 | ||||
| /// The passed [CharGrid] gets consumed.
 | ||||
| ///
 | ||||
| /// Returns: a new [servicepoint::CharGridCommand] instance.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_char_grid( | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     grid: NonNull<CharGrid>, | ||||
| ) -> NonNull<TypedCommand> { | ||||
|     let grid = unsafe { *Box::from_raw(grid.as_ptr()) }; | ||||
|     let result = servicepoint::CharGridCommand { | ||||
|         origin: servicepoint::Origin::new(x, y), | ||||
|         grid, | ||||
|     } | ||||
|     .into(); | ||||
|     heap_move_nonnull(result) | ||||
| } | ||||
| 
 | ||||
| /// Sets a window of pixels to the specified values.
 | ||||
| ///
 | ||||
| /// The passed [Bitmap] gets consumed.
 | ||||
| ///
 | ||||
| /// Returns: a new [servicepoint::BitmapCommand] instance.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_bitmap( | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     bitmap: NonNull<Bitmap>, | ||||
|     compression: CompressionCode, | ||||
| ) -> *mut TypedCommand { | ||||
|     let bitmap = unsafe { *Box::from_raw(bitmap.as_ptr()) }; | ||||
|     let command = servicepoint::BitmapCommand { | ||||
|         origin: servicepoint::Origin::new(x, y), | ||||
|         bitmap, | ||||
|         compression, | ||||
|     } | ||||
|     .into(); | ||||
|     heap_move(command) | ||||
| } | ||||
| 
 | ||||
| /// Deallocates a [TypedCommand].
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```C
 | ||||
| /// TypedCommand c = sp_command_clear();
 | ||||
| /// sp_command_free(c);
 | ||||
| /// ```
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_free(command: NonNull<TypedCommand>) { | ||||
|     unsafe { heap_drop(command) } | ||||
| } | ||||
							
								
								
									
										119
									
								
								src/udp.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								src/udp.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,119 @@ | |||
| use crate::{heap_drop, heap_move, heap_remove}; | ||||
| use servicepoint::{Header, Packet, TypedCommand, UdpSocketExt}; | ||||
| use std::ffi::{c_char, CStr}; | ||||
| use std::net::{Ipv4Addr, SocketAddrV4, UdpSocket}; | ||||
| use std::ptr::NonNull; | ||||
| 
 | ||||
| /// Creates a new instance of [UdpConnection].
 | ||||
| ///
 | ||||
| /// returns: NULL if connection fails, or connected instance
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```C
 | ||||
| /// UdpConnection connection = sp_udp_open("172.23.42.29:2342");
 | ||||
| /// if (connection != NULL)
 | ||||
| ///     sp_udp_send_command(connection, sp_command_clear());
 | ||||
| /// ```
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_udp_open(host: NonNull<c_char>) -> *mut UdpSocket { | ||||
|     let host = unsafe { CStr::from_ptr(host.as_ptr()) } | ||||
|         .to_str() | ||||
|         .expect("Bad encoding"); | ||||
|     let connection = match UdpSocket::bind_connect(host) { | ||||
|         Err(_) => return std::ptr::null_mut(), | ||||
|         Ok(value) => value, | ||||
|     }; | ||||
| 
 | ||||
|     heap_move(connection) | ||||
| } | ||||
| 
 | ||||
| /// Creates a new instance of [UdpConnection].
 | ||||
| ///
 | ||||
| /// returns: NULL if connection fails, or connected instance
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```C
 | ||||
| /// UdpConnection connection = sp_udp_open_ipv4(172, 23, 42, 29, 2342);
 | ||||
| /// if (connection != NULL)
 | ||||
| ///     sp_udp_send_command(connection, sp_command_clear());
 | ||||
| /// ```
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_udp_open_ipv4( | ||||
|     ip1: u8, | ||||
|     ip2: u8, | ||||
|     ip3: u8, | ||||
|     ip4: u8, | ||||
|     port: u16, | ||||
| ) -> *mut UdpSocket { | ||||
|     let addr = SocketAddrV4::new(Ipv4Addr::from([ip1, ip2, ip3, ip4]), port); | ||||
|     let connection = match UdpSocket::bind_connect(addr) { | ||||
|         Err(_) => return std::ptr::null_mut(), | ||||
|         Ok(value) => value, | ||||
|     }; | ||||
|     heap_move(connection) | ||||
| } | ||||
| 
 | ||||
| /// Sends a [Packet] to the display using the [UdpConnection].
 | ||||
| ///
 | ||||
| /// The passed `packet` gets consumed.
 | ||||
| ///
 | ||||
| /// returns: true in case of success
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_udp_send_packet( | ||||
|     connection: NonNull<UdpSocket>, | ||||
|     packet: NonNull<Packet>, | ||||
| ) -> bool { | ||||
|     let packet = unsafe { heap_remove(packet) }; | ||||
|     unsafe { connection.as_ref().send(&Vec::from(packet)) }.is_ok() | ||||
| } | ||||
| 
 | ||||
| /// Sends a [TypedCommand] to the display using the [UdpConnection].
 | ||||
| ///
 | ||||
| /// The passed `command` gets consumed.
 | ||||
| ///
 | ||||
| /// returns: true in case of success
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```C
 | ||||
| /// sp_udp_send_command(connection, sp_command_brightness(5));
 | ||||
| /// ```
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_udp_send_command( | ||||
|     connection: NonNull<UdpSocket>, | ||||
|     command: NonNull<TypedCommand>, | ||||
| ) -> bool { | ||||
|     let command = unsafe { heap_remove(command) }; | ||||
|     unsafe { connection.as_ref().send_command(command) }.is_some() | ||||
| } | ||||
| 
 | ||||
| /// Sends a [Header] to the display using the [UdpConnection].
 | ||||
| ///
 | ||||
| /// returns: true in case of success
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```C
 | ||||
| /// sp_udp_send_header(connection, sp_command_brightness(5));
 | ||||
| /// ```
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_udp_send_header( | ||||
|     udp_connection: NonNull<UdpSocket>, | ||||
|     header: Header, | ||||
| ) -> bool { | ||||
|     let packet = Packet { | ||||
|         header, | ||||
|         payload: vec![], | ||||
|     }; | ||||
|     unsafe { udp_connection.as_ref() } | ||||
|         .send(&Vec::from(packet)) | ||||
|         .is_ok() | ||||
| } | ||||
| 
 | ||||
| /// Closes and deallocates a [UdpConnection].
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_udp_free(connection: NonNull<UdpSocket>) { | ||||
|     unsafe { heap_drop(connection) } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue